1 /*
2  * QuickJS Javascript Engine
3  *
4  * Copyright (c) 2017-2021 Fabrice Bellard
5  * Copyright (c) 2017-2021 Charlie Gordon
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include <inttypes.h>
29 #include <string.h>
30 #include <assert.h>
31 #if !defined(QUICKJS_HAVE_SYS_TIME_H)
32   #if defined(__linux__) || defined(__APPLE__)
33     #define QUICKJS_HAVE_SYS_TIME_H
34   #endif
35 #endif
36 #if defined(QUICKJS_HAVE_SYS_TIME_H)
37 #include <sys/time.h>
38 #endif
39 #include <time.h>
40 #include <fenv.h>
41 #include <math.h>
42 #if defined(__APPLE__)
43 #include <malloc/malloc.h>
44 #elif defined(__linux__)
45 #include <malloc.h>
46 #elif defined(__FreeBSD__)
47 #include <malloc_np.h>
48 #elif defined(_WIN32)
49 #include <malloc.h>
50 #endif
51 
52 #include "cutils.h"
53 #include "list.h"
54 #include "quickjs.h"
55 #include "libregexp.h"
56 #ifdef CONFIG_BIGNUM
57 #include "libbf.h"
58 #endif
59 
60 #define OPTIMIZE         1
61 #define SHORT_OPCODES    1
62 #if defined(EMSCRIPTEN) || defined(_MSC_VER)
63 #define DIRECT_DISPATCH  0
64 #else
65 #define DIRECT_DISPATCH  1
66 #endif
67 
68 #if defined(__APPLE__)
69 #define MALLOC_OVERHEAD  0
70 #else
71 #define MALLOC_OVERHEAD  8
72 #endif
73 
74 #if !defined(_WIN32)
75 /* define it if printf uses the RNDN rounding mode instead of RNDNA */
76 #define CONFIG_PRINTF_RNDN
77 #endif
78 
79 /* define to include Atomics.* operations which depend on the OS
80    threads */
81 #if !defined(EMSCRIPTEN) && !defined(QUICKJS_DISABLE_ATOMICS)
82 #define CONFIG_ATOMICS
83 #endif
84 
85 #if !defined(EMSCRIPTEN)
86 /* enable stack limitation */
87 #define CONFIG_STACK_CHECK
88 #endif
89 
90 
91 /* dump object free */
92 //#define DUMP_FREE
93 //#define DUMP_CLOSURE
94 /* dump the bytecode of the compiled functions: combination of bits
95    1: dump pass 3 final byte code
96    2: dump pass 2 code
97    4: dump pass 1 code
98    8: dump stdlib functions
99   16: dump bytecode in hex
100   32: dump line number table
101  */
102 //#define DUMP_BYTECODE  (1)
103 /* dump the occurence of the automatic GC */
104 //#define DUMP_GC
105 /* dump objects freed by the garbage collector */
106 //#define DUMP_GC_FREE
107 /* dump objects leaking when freeing the runtime */
108 //#define DUMP_LEAKS  1
109 /* dump memory usage before running the garbage collector */
110 //#define DUMP_MEM
111 //#define DUMP_OBJECTS    /* dump objects in JS_FreeContext */
112 //#define DUMP_ATOMS      /* dump atoms in JS_FreeContext */
113 //#define DUMP_SHAPES     /* dump shapes in JS_FreeContext */
114 //#define DUMP_MODULE_RESOLVE
115 //#define DUMP_PROMISE
116 //#define DUMP_READ_OBJECT
117 
118 /* test the GC by forcing it before each object allocation */
119 //#define FORCE_GC_AT_MALLOC
120 
121 #ifdef CONFIG_ATOMICS
122 #include <pthread.h>
123 #include <stdatomic.h>
124 #include <errno.h>
125 #endif
126 
127 enum {
128     /* classid tag        */    /* union usage   | properties */
129     JS_CLASS_OBJECT = 1,        /* must be first */
130     JS_CLASS_ARRAY,             /* u.array       | length */
131     JS_CLASS_ERROR,
132     JS_CLASS_NUMBER,            /* u.object_data */
133     JS_CLASS_STRING,            /* u.object_data */
134     JS_CLASS_BOOLEAN,           /* u.object_data */
135     JS_CLASS_SYMBOL,            /* u.object_data */
136     JS_CLASS_ARGUMENTS,         /* u.array       | length */
137     JS_CLASS_MAPPED_ARGUMENTS,  /*               | length */
138     JS_CLASS_DATE,              /* u.object_data */
139     JS_CLASS_MODULE_NS,
140     JS_CLASS_C_FUNCTION,        /* u.cfunc */
141     JS_CLASS_BYTECODE_FUNCTION, /* u.func */
142     JS_CLASS_BOUND_FUNCTION,    /* u.bound_function */
143     JS_CLASS_C_FUNCTION_DATA,   /* u.c_function_data_record */
144     JS_CLASS_GENERATOR_FUNCTION, /* u.func */
145     JS_CLASS_FOR_IN_ITERATOR,   /* u.for_in_iterator */
146     JS_CLASS_REGEXP,            /* u.regexp */
147     JS_CLASS_ARRAY_BUFFER,      /* u.array_buffer */
148     JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */
149     JS_CLASS_UINT8C_ARRAY,      /* u.array (typed_array) */
150     JS_CLASS_INT8_ARRAY,        /* u.array (typed_array) */
151     JS_CLASS_UINT8_ARRAY,       /* u.array (typed_array) */
152     JS_CLASS_INT16_ARRAY,       /* u.array (typed_array) */
153     JS_CLASS_UINT16_ARRAY,      /* u.array (typed_array) */
154     JS_CLASS_INT32_ARRAY,       /* u.array (typed_array) */
155     JS_CLASS_UINT32_ARRAY,      /* u.array (typed_array) */
156 #ifdef CONFIG_BIGNUM
157     JS_CLASS_BIG_INT64_ARRAY,   /* u.array (typed_array) */
158     JS_CLASS_BIG_UINT64_ARRAY,  /* u.array (typed_array) */
159 #endif
160     JS_CLASS_FLOAT32_ARRAY,     /* u.array (typed_array) */
161     JS_CLASS_FLOAT64_ARRAY,     /* u.array (typed_array) */
162     JS_CLASS_DATAVIEW,          /* u.typed_array */
163 #ifdef CONFIG_BIGNUM
164     JS_CLASS_BIG_INT,           /* u.object_data */
165     JS_CLASS_BIG_FLOAT,         /* u.object_data */
166     JS_CLASS_FLOAT_ENV,         /* u.float_env */
167     JS_CLASS_BIG_DECIMAL,       /* u.object_data */
168     JS_CLASS_OPERATOR_SET,      /* u.operator_set */
169 #endif
170     JS_CLASS_MAP,               /* u.map_state */
171     JS_CLASS_SET,               /* u.map_state */
172     JS_CLASS_WEAKMAP,           /* u.map_state */
173     JS_CLASS_WEAKSET,           /* u.map_state */
174     JS_CLASS_MAP_ITERATOR,      /* u.map_iterator_data */
175     JS_CLASS_SET_ITERATOR,      /* u.map_iterator_data */
176     JS_CLASS_ARRAY_ITERATOR,    /* u.array_iterator_data */
177     JS_CLASS_STRING_ITERATOR,   /* u.array_iterator_data */
178     JS_CLASS_REGEXP_STRING_ITERATOR,   /* u.regexp_string_iterator_data */
179     JS_CLASS_GENERATOR,         /* u.generator_data */
180     JS_CLASS_PROXY,             /* u.proxy_data */
181     JS_CLASS_PROMISE,           /* u.promise_data */
182     JS_CLASS_PROMISE_RESOLVE_FUNCTION,  /* u.promise_function_data */
183     JS_CLASS_PROMISE_REJECT_FUNCTION,   /* u.promise_function_data */
184     JS_CLASS_ASYNC_FUNCTION,            /* u.func */
185     JS_CLASS_ASYNC_FUNCTION_RESOLVE,    /* u.async_function_data */
186     JS_CLASS_ASYNC_FUNCTION_REJECT,     /* u.async_function_data */
187     JS_CLASS_ASYNC_FROM_SYNC_ITERATOR,  /* u.async_from_sync_iterator_data */
188     JS_CLASS_ASYNC_GENERATOR_FUNCTION,  /* u.func */
189     JS_CLASS_ASYNC_GENERATOR,   /* u.async_generator_data */
190 
191     JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
192 };
193 
194 /* number of typed array types */
195 #define JS_TYPED_ARRAY_COUNT  (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1)
196 static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT];
197 #define typed_array_size_log2(classid)  (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY])
198 
199 typedef enum JSErrorEnum {
200     JS_EVAL_ERROR,
201     JS_RANGE_ERROR,
202     JS_REFERENCE_ERROR,
203     JS_SYNTAX_ERROR,
204     JS_TYPE_ERROR,
205     JS_URI_ERROR,
206     JS_INTERNAL_ERROR,
207     JS_AGGREGATE_ERROR,
208 
209     JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */
210 } JSErrorEnum;
211 
212 #define JS_MAX_LOCAL_VARS 65536
213 #define JS_STACK_SIZE_MAX 65534
214 #define JS_STRING_LEN_MAX ((1 << 30) - 1)
215 
216 #define __exception __attribute__((warn_unused_result))
217 
218 typedef struct JSShape JSShape;
219 typedef struct JSString JSString;
220 typedef struct JSString JSAtomStruct;
221 
222 typedef enum {
223     JS_GC_PHASE_NONE,
224     JS_GC_PHASE_DECREF,
225     JS_GC_PHASE_REMOVE_CYCLES,
226 } JSGCPhaseEnum;
227 
228 typedef enum OPCodeEnum OPCodeEnum;
229 
230 #ifdef CONFIG_BIGNUM
231 /* function pointers are used for numeric operations so that it is
232    possible to remove some numeric types */
233 typedef struct {
234     JSValue (*to_string)(JSContext *ctx, JSValueConst val);
235     JSValue (*from_string)(JSContext *ctx, const char *buf,
236                            int radix, int flags, slimb_t *pexponent);
237     int (*unary_arith)(JSContext *ctx,
238                        JSValue *pres, OPCodeEnum op, JSValue op1);
239     int (*binary_arith)(JSContext *ctx, OPCodeEnum op,
240                         JSValue *pres, JSValue op1, JSValue op2);
241     int (*compare)(JSContext *ctx, OPCodeEnum op,
242                    JSValue op1, JSValue op2);
243     /* only for bigfloat: */
244     JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a,
245                                     int64_t exponent);
246     int (*mul_pow10)(JSContext *ctx, JSValue *sp);
247 } JSNumericOperations;
248 #endif
249 
250 struct JSRuntime {
251     JSMallocFunctions mf;
252     JSMallocState malloc_state;
253     const char *rt_info;
254 
255     int atom_hash_size; /* power of two */
256     int atom_count;
257     int atom_size;
258     int atom_count_resize; /* resize hash table at this count */
259     uint32_t *atom_hash;
260     JSAtomStruct **atom_array;
261     int atom_free_index; /* 0 = none */
262 
263     int class_count;    /* size of class_array */
264     JSClass *class_array;
265 
266     struct list_head context_list; /* list of JSContext.link */
267     /* list of JSGCObjectHeader.link. List of allocated GC objects (used
268        by the garbage collector) */
269     struct list_head gc_obj_list;
270     /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */
271     struct list_head gc_zero_ref_count_list;
272     struct list_head tmp_obj_list; /* used during GC */
273     JSGCPhaseEnum gc_phase : 8;
274     size_t malloc_gc_threshold;
275 #ifdef DUMP_LEAKS
276     struct list_head string_list; /* list of JSString.link */
277 #endif
278     /* stack limitation */
279     uintptr_t stack_size; /* in bytes, 0 if no limit */
280     uintptr_t stack_top;
281     uintptr_t stack_limit; /* lower stack limit */
282 
283     JSValue current_exception;
284     /* true if inside an out of memory error, to avoid recursing */
285     BOOL in_out_of_memory : 8;
286 
287     struct JSStackFrame *current_stack_frame;
288 
289     JSInterruptHandler *interrupt_handler;
290     void *interrupt_opaque;
291 
292     JSHostPromiseRejectionTracker *host_promise_rejection_tracker;
293     void *host_promise_rejection_tracker_opaque;
294 
295     struct list_head job_list; /* list of JSJobEntry.link */
296 
297     JSModuleNormalizeFunc *module_normalize_func;
298     JSModuleLoaderFunc *module_loader_func;
299     void *module_loader_opaque;
300 
301     BOOL can_block : 8; /* TRUE if Atomics.wait can block */
302     /* used to allocate, free and clone SharedArrayBuffers */
303     JSSharedArrayBufferFunctions sab_funcs;
304 
305     /* Shape hash table */
306     int shape_hash_bits;
307     int shape_hash_size;
308     int shape_hash_count; /* number of hashed shapes */
309     JSShape **shape_hash;
310 #ifdef CONFIG_BIGNUM
311     bf_context_t bf_ctx;
312     JSNumericOperations bigint_ops;
313     JSNumericOperations bigfloat_ops;
314     JSNumericOperations bigdecimal_ops;
315     uint32_t operator_count;
316 #endif
317     void *user_opaque;
318 };
319 
320 struct JSClass {
321     uint32_t class_id; /* 0 means free entry */
322     JSAtom class_name;
323     JSClassFinalizer *finalizer;
324     JSClassGCMark *gc_mark;
325     JSClassCall *call;
326     /* pointers for exotic behavior, can be NULL if none are present */
327     const JSClassExoticMethods *exotic;
328 };
329 
330 #define JS_MODE_STRICT (1 << 0)
331 #define JS_MODE_STRIP  (1 << 1)
332 #define JS_MODE_MATH   (1 << 2)
333 
334 typedef struct JSStackFrame {
335     struct JSStackFrame *prev_frame; /* NULL if first stack frame */
336     JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
337     JSValue *arg_buf; /* arguments */
338     JSValue *var_buf; /* variables */
339     struct list_head var_ref_list; /* list of JSVarRef.link */
340     const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
341                         instruction after the call */
342     int arg_count;
343     int js_mode; /* 0 or JS_MODE_MATH for C functions */
344     /* only used in generators. Current stack pointer value. NULL if
345        the function is running. */
346     JSValue *cur_sp;
347 } JSStackFrame;
348 
349 typedef enum {
350     JS_GC_OBJ_TYPE_JS_OBJECT,
351     JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,
352     JS_GC_OBJ_TYPE_SHAPE,
353     JS_GC_OBJ_TYPE_VAR_REF,
354     JS_GC_OBJ_TYPE_ASYNC_FUNCTION,
355     JS_GC_OBJ_TYPE_JS_CONTEXT,
356 } JSGCObjectTypeEnum;
357 
358 /* header for GC objects. GC objects are C data structures with a
359    reference count that can reference other GC objects. JS Objects are
360    a particular type of GC object. */
361 struct JSGCObjectHeader {
362     int ref_count; /* must come first, 32-bit */
363     JSGCObjectTypeEnum gc_obj_type : 4;
364     uint8_t mark : 4; /* used by the GC */
365     uint8_t dummy1; /* not used by the GC */
366     uint16_t dummy2; /* not used by the GC */
367     struct list_head link;
368 };
369 
370 typedef struct JSVarRef {
371     union {
372         JSGCObjectHeader header; /* must come first */
373         struct {
374             int __gc_ref_count; /* corresponds to header.ref_count */
375             uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
376 
377             /* 0 : the JSVarRef is on the stack. header.link is an element
378                of JSStackFrame.var_ref_list.
379                1 : the JSVarRef is detached. header.link has the normal meanning
380             */
381             uint8_t is_detached : 1;
382             uint8_t is_arg : 1;
383             uint16_t var_idx; /* index of the corresponding function variable on
384                                  the stack */
385         };
386     };
387     JSValue *pvalue; /* pointer to the value, either on the stack or
388                         to 'value' */
389     JSValue value; /* used when the variable is no longer on the stack */
390 } JSVarRef;
391 
392 #ifdef CONFIG_BIGNUM
393 typedef struct JSFloatEnv {
394     limb_t prec;
395     bf_flags_t flags;
396     unsigned int status;
397 } JSFloatEnv;
398 
399 /* the same structure is used for big integers and big floats. Big
400    integers are never infinite or NaNs */
401 typedef struct JSBigFloat {
402     JSRefCountHeader header; /* must come first, 32-bit */
403     bf_t num;
404 } JSBigFloat;
405 
406 typedef struct JSBigDecimal {
407     JSRefCountHeader header; /* must come first, 32-bit */
408     bfdec_t num;
409 } JSBigDecimal;
410 #endif
411 
412 typedef enum {
413     JS_AUTOINIT_ID_PROTOTYPE,
414     JS_AUTOINIT_ID_MODULE_NS,
415     JS_AUTOINIT_ID_PROP,
416 } JSAutoInitIDEnum;
417 
418 /* must be large enough to have a negligible runtime cost and small
419    enough to call the interrupt callback often. */
420 #define JS_INTERRUPT_COUNTER_INIT 10000
421 
422 struct JSContext {
423     JSGCObjectHeader header; /* must come first */
424     JSRuntime *rt;
425     struct list_head link;
426 
427     uint16_t binary_object_count;
428     int binary_object_size;
429 
430     JSShape *array_shape;   /* initial shape for Array objects */
431 
432     JSValue *class_proto;
433     JSValue function_proto;
434     JSValue function_ctor;
435     JSValue array_ctor;
436     JSValue regexp_ctor;
437     JSValue promise_ctor;
438     JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];
439     JSValue iterator_proto;
440     JSValue async_iterator_proto;
441     JSValue array_proto_values;
442     JSValue throw_type_error;
443     JSValue eval_obj;
444 
445     JSValue global_obj; /* global object */
446     JSValue global_var_obj; /* contains the global let/const definitions */
447 
448     uint64_t random_state;
449 #ifdef CONFIG_BIGNUM
450     bf_context_t *bf_ctx;   /* points to rt->bf_ctx, shared by all contexts */
451     JSFloatEnv fp_env; /* global FP environment */
452     BOOL bignum_ext : 8; /* enable math mode */
453     BOOL allow_operator_overloading : 8;
454 #endif
455     /* when the counter reaches zero, JSRutime.interrupt_handler is called */
456     int interrupt_counter;
457     BOOL is_error_property_enabled;
458 
459     struct list_head loaded_modules; /* list of JSModuleDef.link */
460 
461     /* if NULL, RegExp compilation is not supported */
462     JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern,
463                               JSValueConst flags);
464     /* if NULL, eval is not supported */
465     JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj,
466                              const char *input, size_t input_len,
467                              const char *filename, int flags, int scope_idx);
468     void *user_opaque;
469 };
470 
471 typedef union JSFloat64Union {
472     double d;
473     uint64_t u64;
474     uint32_t u32[2];
475 } JSFloat64Union;
476 
477 enum {
478     JS_ATOM_TYPE_STRING = 1,
479     JS_ATOM_TYPE_GLOBAL_SYMBOL,
480     JS_ATOM_TYPE_SYMBOL,
481     JS_ATOM_TYPE_PRIVATE,
482 };
483 
484 enum {
485     JS_ATOM_HASH_SYMBOL,
486     JS_ATOM_HASH_PRIVATE,
487 };
488 
489 typedef enum {
490     JS_ATOM_KIND_STRING,
491     JS_ATOM_KIND_SYMBOL,
492     JS_ATOM_KIND_PRIVATE,
493 } JSAtomKindEnum;
494 
495 #define JS_ATOM_HASH_MASK  ((1 << 30) - 1)
496 
497 struct JSString {
498     JSRefCountHeader header; /* must come first, 32-bit */
499     uint32_t len : 31;
500     uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */
501     /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3,
502        for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3
503        XXX: could change encoding to have one more bit in hash */
504     uint32_t hash : 30;
505     uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */
506     uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */
507 #ifdef DUMP_LEAKS
508     struct list_head link; /* string list */
509 #endif
510     union {
511         uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */
512         uint16_t str16[0];
513     } u;
514 };
515 
516 typedef struct JSClosureVar {
517     uint8_t is_local : 1;
518     uint8_t is_arg : 1;
519     uint8_t is_const : 1;
520     uint8_t is_lexical : 1;
521     uint8_t var_kind : 4; /* see JSVarKindEnum */
522     /* 8 bits available */
523     uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the
524                     parent function. otherwise: index to a closure
525                     variable of the parent function */
526     JSAtom var_name;
527 } JSClosureVar;
528 
529 #define ARG_SCOPE_INDEX 1
530 #define ARG_SCOPE_END (-2)
531 
532 typedef struct JSVarScope {
533     int parent;  /* index into fd->scopes of the enclosing scope */
534     int first;   /* index into fd->vars of the last variable in this scope */
535 } JSVarScope;
536 
537 typedef enum {
538     /* XXX: add more variable kinds here instead of using bit fields */
539     JS_VAR_NORMAL,
540     JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */
541     JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator
542                                  function declaration */
543     JS_VAR_CATCH,
544     JS_VAR_FUNCTION_NAME, /* function expression name */
545     JS_VAR_PRIVATE_FIELD,
546     JS_VAR_PRIVATE_METHOD,
547     JS_VAR_PRIVATE_GETTER,
548     JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */
549     JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */
550 } JSVarKindEnum;
551 
552 /* XXX: could use a different structure in bytecode functions to save
553    memory */
554 typedef struct JSVarDef {
555     JSAtom var_name;
556     /* index into fd->scopes of this variable lexical scope */
557     int scope_level;
558     /* during compilation:
559         - if scope_level = 0: scope in which the variable is defined
560         - if scope_level != 0: index into fd->vars of the next
561           variable in the same or enclosing lexical scope
562        in a bytecode function:
563        index into fd->vars of the next
564        variable in the same or enclosing lexical scope
565     */
566     int scope_next;
567     uint8_t is_const : 1;
568     uint8_t is_lexical : 1;
569     uint8_t is_captured : 1;
570     uint8_t var_kind : 4; /* see JSVarKindEnum */
571     /* only used during compilation: function pool index for lexical
572        variables with var_kind =
573        JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of
574        the definition of the 'var' variables (they have scope_level =
575        0) */
576     int func_pool_idx : 24; /* only used during compilation : index in
577                                the constant pool for hoisted function
578                                definition */
579 } JSVarDef;
580 
581 /* for the encoding of the pc2line table */
582 #define PC2LINE_BASE     (-1)
583 #define PC2LINE_RANGE    5
584 #define PC2LINE_OP_FIRST 1
585 #define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE)
586 
587 typedef enum JSFunctionKindEnum {
588     JS_FUNC_NORMAL = 0,
589     JS_FUNC_GENERATOR = (1 << 0),
590     JS_FUNC_ASYNC = (1 << 1),
591     JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC),
592 } JSFunctionKindEnum;
593 
594 typedef struct JSFunctionBytecode {
595     JSGCObjectHeader header; /* must come first */
596     uint8_t js_mode;
597     uint8_t has_prototype : 1; /* true if a prototype field is necessary */
598     uint8_t has_simple_parameter_list : 1;
599     uint8_t is_derived_class_constructor : 1;
600     /* true if home_object needs to be initialized */
601     uint8_t need_home_object : 1;
602     uint8_t func_kind : 2;
603     uint8_t new_target_allowed : 1;
604     uint8_t super_call_allowed : 1;
605     uint8_t super_allowed : 1;
606     uint8_t arguments_allowed : 1;
607     uint8_t has_debug : 1;
608     uint8_t backtrace_barrier : 1; /* stop backtrace on this function */
609     uint8_t read_only_bytecode : 1;
610     /* XXX: 4 bits available */
611     uint8_t *byte_code_buf; /* (self pointer) */
612     int byte_code_len;
613     JSAtom func_name;
614     JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */
615     JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */
616     uint16_t arg_count;
617     uint16_t var_count;
618     uint16_t defined_arg_count; /* for length function property */
619     uint16_t stack_size; /* maximum stack size */
620     JSContext *realm; /* function realm */
621     JSValue *cpool; /* constant pool (self pointer) */
622     int cpool_count;
623     int closure_var_count;
624     struct {
625         /* debug info, move to separate structure to save memory? */
626         JSAtom filename;
627         int line_num;
628         int source_len;
629         int pc2line_len;
630         uint8_t *pc2line_buf;
631         char *source;
632     } debug;
633 } JSFunctionBytecode;
634 
635 typedef struct JSBoundFunction {
636     JSValue func_obj;
637     JSValue this_val;
638     int argc;
639     JSValue argv[0];
640 } JSBoundFunction;
641 
642 typedef enum JSIteratorKindEnum {
643     JS_ITERATOR_KIND_KEY,
644     JS_ITERATOR_KIND_VALUE,
645     JS_ITERATOR_KIND_KEY_AND_VALUE,
646 } JSIteratorKindEnum;
647 
648 typedef struct JSForInIterator {
649     JSValue obj;
650     BOOL is_array;
651     uint32_t array_length;
652     uint32_t idx;
653 } JSForInIterator;
654 
655 typedef struct JSRegExp {
656     JSString *pattern;
657     JSString *bytecode; /* also contains the flags */
658 } JSRegExp;
659 
660 typedef struct JSProxyData {
661     JSValue target;
662     JSValue handler;
663     uint8_t is_func;
664     uint8_t is_revoked;
665 } JSProxyData;
666 
667 typedef struct JSArrayBuffer {
668     int byte_length; /* 0 if detached */
669     uint8_t detached;
670     uint8_t shared; /* if shared, the array buffer cannot be detached */
671     uint8_t *data; /* NULL if detached */
672     struct list_head array_list;
673     void *opaque;
674     JSFreeArrayBufferDataFunc *free_func;
675 } JSArrayBuffer;
676 
677 typedef struct JSTypedArray {
678     struct list_head link; /* link to arraybuffer */
679     JSObject *obj; /* back pointer to the TypedArray/DataView object */
680     JSObject *buffer; /* based array buffer */
681     uint32_t offset; /* offset in the array buffer */
682     uint32_t length; /* length in the array buffer */
683 } JSTypedArray;
684 
685 typedef struct JSAsyncFunctionState {
686     JSValue this_val; /* 'this' generator argument */
687     int argc; /* number of function arguments */
688     BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */
689     JSStackFrame frame;
690 } JSAsyncFunctionState;
691 
692 /* XXX: could use an object instead to avoid the
693    JS_TAG_ASYNC_FUNCTION tag for the GC */
694 typedef struct JSAsyncFunctionData {
695     JSGCObjectHeader header; /* must come first */
696     JSValue resolving_funcs[2];
697     BOOL is_active; /* true if the async function state is valid */
698     JSAsyncFunctionState func_state;
699 } JSAsyncFunctionData;
700 
701 typedef enum {
702    /* binary operators */
703    JS_OVOP_ADD,
704    JS_OVOP_SUB,
705    JS_OVOP_MUL,
706    JS_OVOP_DIV,
707    JS_OVOP_MOD,
708    JS_OVOP_POW,
709    JS_OVOP_OR,
710    JS_OVOP_AND,
711    JS_OVOP_XOR,
712    JS_OVOP_SHL,
713    JS_OVOP_SAR,
714    JS_OVOP_SHR,
715    JS_OVOP_EQ,
716    JS_OVOP_LESS,
717 
718    JS_OVOP_BINARY_COUNT,
719    /* unary operators */
720    JS_OVOP_POS = JS_OVOP_BINARY_COUNT,
721    JS_OVOP_NEG,
722    JS_OVOP_INC,
723    JS_OVOP_DEC,
724    JS_OVOP_NOT,
725 
726    JS_OVOP_COUNT,
727 } JSOverloadableOperatorEnum;
728 
729 typedef struct {
730     uint32_t operator_index;
731     JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */
732 } JSBinaryOperatorDefEntry;
733 
734 typedef struct {
735     int count;
736     JSBinaryOperatorDefEntry *tab;
737 } JSBinaryOperatorDef;
738 
739 typedef struct {
740     uint32_t operator_counter;
741     BOOL is_primitive; /* OperatorSet for a primitive type */
742     /* NULL if no operator is defined */
743     JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */
744     JSBinaryOperatorDef left;
745     JSBinaryOperatorDef right;
746 } JSOperatorSetData;
747 
748 typedef struct JSReqModuleEntry {
749     JSAtom module_name;
750     JSModuleDef *module; /* used using resolution */
751 } JSReqModuleEntry;
752 
753 typedef enum JSExportTypeEnum {
754     JS_EXPORT_TYPE_LOCAL,
755     JS_EXPORT_TYPE_INDIRECT,
756 } JSExportTypeEnum;
757 
758 typedef struct JSExportEntry {
759     union {
760         struct {
761             int var_idx; /* closure variable index */
762             JSVarRef *var_ref; /* if != NULL, reference to the variable */
763         } local; /* for local export */
764         int req_module_idx; /* module for indirect export */
765     } u;
766     JSExportTypeEnum export_type;
767     JSAtom local_name; /* '*' if export ns from. not used for local
768                           export after compilation */
769     JSAtom export_name; /* exported variable name */
770 } JSExportEntry;
771 
772 typedef struct JSStarExportEntry {
773     int req_module_idx; /* in req_module_entries */
774 } JSStarExportEntry;
775 
776 typedef struct JSImportEntry {
777     int var_idx; /* closure variable index */
778     JSAtom import_name;
779     int req_module_idx; /* in req_module_entries */
780 } JSImportEntry;
781 
782 struct JSModuleDef {
783     JSRefCountHeader header; /* must come first, 32-bit */
784     JSAtom module_name;
785     struct list_head link;
786 
787     JSReqModuleEntry *req_module_entries;
788     int req_module_entries_count;
789     int req_module_entries_size;
790 
791     JSExportEntry *export_entries;
792     int export_entries_count;
793     int export_entries_size;
794 
795     JSStarExportEntry *star_export_entries;
796     int star_export_entries_count;
797     int star_export_entries_size;
798 
799     JSImportEntry *import_entries;
800     int import_entries_count;
801     int import_entries_size;
802 
803     JSValue module_ns;
804     JSValue func_obj; /* only used for JS modules */
805     JSModuleInitFunc *init_func; /* only used for C modules */
806     BOOL resolved : 8;
807     BOOL func_created : 8;
808     BOOL instantiated : 8;
809     BOOL evaluated : 8;
810     BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */
811     /* true if evaluation yielded an exception. It is saved in
812        eval_exception */
813     BOOL eval_has_exception : 8;
814     JSValue eval_exception;
815     JSValue meta_obj; /* for import.meta */
816 };
817 
818 typedef struct JSJobEntry {
819     struct list_head link;
820     JSContext *ctx;
821     JSJobFunc *job_func;
822     int argc;
823     JSValue argv[0];
824 } JSJobEntry;
825 
826 typedef struct JSProperty {
827     union {
828         JSValue value;      /* JS_PROP_NORMAL */
829         struct {            /* JS_PROP_GETSET */
830             JSObject *getter; /* NULL if undefined */
831             JSObject *setter; /* NULL if undefined */
832         } getset;
833         JSVarRef *var_ref;  /* JS_PROP_VARREF */
834         struct {            /* JS_PROP_AUTOINIT */
835             /* in order to use only 2 pointers, we compress the realm
836                and the init function pointer */
837             uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)
838                                        in the 2 low bits */
839             void *opaque;
840         } init;
841     } u;
842 } JSProperty;
843 
844 #define JS_PROP_INITIAL_SIZE 2
845 #define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */
846 #define JS_ARRAY_INITIAL_SIZE 2
847 
848 typedef struct JSShapeProperty {
849     uint32_t hash_next : 26; /* 0 if last in list */
850     uint32_t flags : 6;   /* JS_PROP_XXX */
851     JSAtom atom; /* JS_ATOM_NULL = free property entry */
852 } JSShapeProperty;
853 
854 struct JSShape {
855     /* hash table of size hash_mask + 1 before the start of the
856        structure (see prop_hash_end()). */
857     JSGCObjectHeader header;
858     /* true if the shape is inserted in the shape hash table. If not,
859        JSShape.hash is not valid */
860     uint8_t is_hashed;
861     /* If true, the shape may have small array index properties 'n' with 0
862        <= n <= 2^31-1. If false, the shape is guaranteed not to have
863        small array index properties */
864     uint8_t has_small_array_index;
865     uint32_t hash; /* current hash value */
866     uint32_t prop_hash_mask;
867     int prop_size; /* allocated properties */
868     int prop_count; /* include deleted properties */
869     int deleted_prop_count;
870     JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */
871     JSObject *proto;
872     JSShapeProperty prop[0]; /* prop_size elements */
873 };
874 
875 struct JSObject {
876     union {
877         JSGCObjectHeader header;
878         struct {
879             int __gc_ref_count; /* corresponds to header.ref_count */
880             uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
881 
882             uint8_t extensible : 1;
883             uint8_t free_mark : 1; /* only used when freeing objects with cycles */
884             uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */
885             uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */
886             uint8_t is_constructor : 1; /* TRUE if object is a constructor function */
887             uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */
888             uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */
889             uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */
890             uint16_t class_id; /* see JS_CLASS_x */
891         };
892     };
893     /* byte offsets: 16/24 */
894     JSShape *shape; /* prototype and property names + flag */
895     JSProperty *prop; /* array of properties */
896     /* byte offsets: 24/40 */
897     struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */
898     /* byte offsets: 28/48 */
899     union {
900         void *opaque;
901         struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */
902         struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */
903         struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */
904         struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */
905         struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */
906 #ifdef CONFIG_BIGNUM
907         struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */
908         struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */
909 #endif
910         struct JSMapState *map_state;   /* JS_CLASS_MAP..JS_CLASS_WEAKSET */
911         struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */
912         struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */
913         struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */
914         struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */
915         struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */
916         struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */
917         struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */
918         struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
919         struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
920         struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */
921         struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */
922             /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */
923             struct JSFunctionBytecode *function_bytecode;
924             JSVarRef **var_refs;
925             JSObject *home_object; /* for 'super' access */
926         } func;
927         struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */
928             JSContext *realm;
929             JSCFunctionType c_function;
930             uint8_t length;
931             uint8_t cproto;
932             int16_t magic;
933         } cfunc;
934         /* array part for fast arrays and typed arrays */
935         struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
936             union {
937                 uint32_t size;          /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
938                 struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
939             } u1;
940             union {
941                 JSValue *values;        /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
942                 void *ptr;              /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
943                 int8_t *int8_ptr;       /* JS_CLASS_INT8_ARRAY */
944                 uint8_t *uint8_ptr;     /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */
945                 int16_t *int16_ptr;     /* JS_CLASS_INT16_ARRAY */
946                 uint16_t *uint16_ptr;   /* JS_CLASS_UINT16_ARRAY */
947                 int32_t *int32_ptr;     /* JS_CLASS_INT32_ARRAY */
948                 uint32_t *uint32_ptr;   /* JS_CLASS_UINT32_ARRAY */
949                 int64_t *int64_ptr;     /* JS_CLASS_INT64_ARRAY */
950                 uint64_t *uint64_ptr;   /* JS_CLASS_UINT64_ARRAY */
951                 float *float_ptr;       /* JS_CLASS_FLOAT32_ARRAY */
952                 double *double_ptr;     /* JS_CLASS_FLOAT64_ARRAY */
953             } u;
954             uint32_t count; /* <= 2^31-1. 0 for a detached typed array */
955         } array;    /* 12/20 bytes */
956         JSRegExp regexp;    /* JS_CLASS_REGEXP: 8/16 bytes */
957         JSValue object_data;    /* for JS_SetObjectData(): 8/16/16 bytes */
958     } u;
959     /* byte sizes: 40/48/72 */
960 };
961 enum {
962     __JS_ATOM_NULL = JS_ATOM_NULL,
963 #define DEF(name, str) JS_ATOM_ ## name,
964 #include "quickjs-atom.h"
965 #undef DEF
966     JS_ATOM_END,
967 };
968 #define JS_ATOM_LAST_KEYWORD JS_ATOM_super
969 #define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield
970 
971 static const char js_atom_init[] =
972 #define DEF(name, str) str "\0"
973 #include "quickjs-atom.h"
974 #undef DEF
975 ;
976 
977 typedef enum OPCodeFormat {
978 #define FMT(f) OP_FMT_ ## f,
979 #define DEF(id, size, n_pop, n_push, f)
980 #include "quickjs-opcode.h"
981 #undef DEF
982 #undef FMT
983 } OPCodeFormat;
984 
985 enum OPCodeEnum {
986 #define FMT(f)
987 #define DEF(id, size, n_pop, n_push, f) OP_ ## id,
988 #define def(id, size, n_pop, n_push, f)
989 #include "quickjs-opcode.h"
990 #undef def
991 #undef DEF
992 #undef FMT
993     OP_COUNT, /* excluding temporary opcodes */
994     /* temporary opcodes : overlap with the short opcodes */
995     OP_TEMP_START = OP_nop + 1,
996     OP___dummy = OP_TEMP_START - 1,
997 #define FMT(f)
998 #define DEF(id, size, n_pop, n_push, f)
999 #define def(id, size, n_pop, n_push, f) OP_ ## id,
1000 #include "quickjs-opcode.h"
1001 #undef def
1002 #undef DEF
1003 #undef FMT
1004     OP_TEMP_END,
1005 };
1006 
1007 static int JS_InitAtoms(JSRuntime *rt);
1008 static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
1009                                int atom_type);
1010 static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p);
1011 static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);
1012 static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
1013                                   JSValueConst this_obj,
1014                                   int argc, JSValueConst *argv, int flags);
1015 static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
1016                                       JSValueConst this_obj,
1017                                       int argc, JSValueConst *argv, int flags);
1018 static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
1019                                JSValueConst this_obj, JSValueConst new_target,
1020                                int argc, JSValue *argv, int flags);
1021 static JSValue JS_CallConstructorInternal(JSContext *ctx,
1022                                           JSValueConst func_obj,
1023                                           JSValueConst new_target,
1024                                           int argc, JSValue *argv, int flags);
1025 static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
1026                            int argc, JSValueConst *argv);
1027 static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
1028                              int argc, JSValueConst *argv);
1029 static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
1030                                             JSValue val, BOOL is_array_ctor);
1031 static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
1032                              JSValueConst val, int flags, int scope_idx);
1033 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
1034 static __maybe_unused void JS_DumpAtoms(JSRuntime *rt);
1035 static __maybe_unused void JS_DumpString(JSRuntime *rt,
1036                                                   const JSString *p);
1037 static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
1038 static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
1039 static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
1040 static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
1041                                                       JSValueConst val);
1042 static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);
1043 static __maybe_unused void JS_PrintValue(JSContext *ctx,
1044                                                   const char *str,
1045                                                   JSValueConst val);
1046 static __maybe_unused void JS_DumpShapes(JSRuntime *rt);
1047 static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
1048                                  int argc, JSValueConst *argv, int magic);
1049 static void js_array_finalizer(JSRuntime *rt, JSValue val);
1050 static void js_array_mark(JSRuntime *rt, JSValueConst val,
1051                           JS_MarkFunc *mark_func);
1052 static void js_object_data_finalizer(JSRuntime *rt, JSValue val);
1053 static void js_object_data_mark(JSRuntime *rt, JSValueConst val,
1054                                 JS_MarkFunc *mark_func);
1055 static void js_c_function_finalizer(JSRuntime *rt, JSValue val);
1056 static void js_c_function_mark(JSRuntime *rt, JSValueConst val,
1057                                JS_MarkFunc *mark_func);
1058 static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val);
1059 static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
1060                                 JS_MarkFunc *mark_func);
1061 static void js_bound_function_finalizer(JSRuntime *rt, JSValue val);
1062 static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
1063                                 JS_MarkFunc *mark_func);
1064 static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val);
1065 static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
1066                                 JS_MarkFunc *mark_func);
1067 static void js_regexp_finalizer(JSRuntime *rt, JSValue val);
1068 static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val);
1069 static void js_typed_array_finalizer(JSRuntime *rt, JSValue val);
1070 static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
1071                                 JS_MarkFunc *mark_func);
1072 static void js_proxy_finalizer(JSRuntime *rt, JSValue val);
1073 static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
1074                                 JS_MarkFunc *mark_func);
1075 static void js_map_finalizer(JSRuntime *rt, JSValue val);
1076 static void js_map_mark(JSRuntime *rt, JSValueConst val,
1077                                 JS_MarkFunc *mark_func);
1078 static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val);
1079 static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
1080                                 JS_MarkFunc *mark_func);
1081 static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val);
1082 static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
1083                                 JS_MarkFunc *mark_func);
1084 static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val);
1085 static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
1086                                 JS_MarkFunc *mark_func);
1087 static void js_generator_finalizer(JSRuntime *rt, JSValue obj);
1088 static void js_generator_mark(JSRuntime *rt, JSValueConst val,
1089                                 JS_MarkFunc *mark_func);
1090 static void js_promise_finalizer(JSRuntime *rt, JSValue val);
1091 static void js_promise_mark(JSRuntime *rt, JSValueConst val,
1092                                 JS_MarkFunc *mark_func);
1093 static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);
1094 static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
1095                                 JS_MarkFunc *mark_func);
1096 #ifdef CONFIG_BIGNUM
1097 static void js_operator_set_finalizer(JSRuntime *rt, JSValue val);
1098 static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
1099                                  JS_MarkFunc *mark_func);
1100 #endif
1101 static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
1102 static int JS_ToBoolFree(JSContext *ctx, JSValue val);
1103 static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
1104 static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val);
1105 static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val);
1106 static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
1107                                  JSValueConst flags);
1108 static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
1109                                               JSValue pattern, JSValue bc);
1110 static void gc_decref(JSRuntime *rt);
1111 static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
1112                         const JSClassDef *class_def, JSAtom name);
1113 
1114 typedef enum JSStrictEqModeEnum {
1115     JS_EQ_STRICT,
1116     JS_EQ_SAME_VALUE,
1117     JS_EQ_SAME_VALUE_ZERO,
1118 } JSStrictEqModeEnum;
1119 
1120 static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
1121                           JSStrictEqModeEnum eq_mode);
1122 static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2);
1123 static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2);
1124 static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2);
1125 static JSValue JS_ToObject(JSContext *ctx, JSValueConst val);
1126 static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);
1127 static JSProperty *add_property(JSContext *ctx,
1128                                 JSObject *p, JSAtom prop, int prop_flags);
1129 #ifdef CONFIG_BIGNUM
1130 static void js_float_env_finalizer(JSRuntime *rt, JSValue val);
1131 static JSValue JS_NewBigFloat(JSContext *ctx);
JS_GetBigFloat(JSValueConst val)1132 static inline bf_t *JS_GetBigFloat(JSValueConst val)
1133 {
1134     JSBigFloat *p = JS_VALUE_GET_PTR(val);
1135     return &p->num;
1136 }
1137 static JSValue JS_NewBigDecimal(JSContext *ctx);
JS_GetBigDecimal(JSValueConst val)1138 static inline bfdec_t *JS_GetBigDecimal(JSValueConst val)
1139 {
1140     JSBigDecimal *p = JS_VALUE_GET_PTR(val);
1141     return &p->num;
1142 }
1143 static JSValue JS_NewBigInt(JSContext *ctx);
JS_GetBigInt(JSValueConst val)1144 static inline bf_t *JS_GetBigInt(JSValueConst val)
1145 {
1146     JSBigFloat *p = JS_VALUE_GET_PTR(val);
1147     return &p->num;
1148 }
1149 static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
1150                                  BOOL convert_to_safe_integer);
1151 static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val);
1152 static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
1153 static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val);
1154 static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf);
1155 static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val);
1156 static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
1157                                    BOOL allow_null_or_undefined);
1158 static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);
1159 #endif
1160 JSValue JS_ThrowOutOfMemory(JSContext *ctx);
1161 static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
1162 static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
1163 static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
1164                                    JSValueConst proto_val, BOOL throw_flag);
1165 static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj);
1166 static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj);
1167 static int js_proxy_isArray(JSContext *ctx, JSValueConst obj);
1168 static int JS_CreateProperty(JSContext *ctx, JSObject *p,
1169                              JSAtom prop, JSValueConst val,
1170                              JSValueConst getter, JSValueConst setter,
1171                              int flags);
1172 static int js_string_memcmp(const JSString *p1, const JSString *p2, int len);
1173 static void reset_weak_ref(JSRuntime *rt, JSObject *p);
1174 static JSValue js_array_buffer_constructor3(JSContext *ctx,
1175                                             JSValueConst new_target,
1176                                             uint64_t len, JSClassID class_id,
1177                                             uint8_t *buf,
1178                                             JSFreeArrayBufferDataFunc *free_func,
1179                                             void *opaque, BOOL alloc_flag);
1180 static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj);
1181 static JSValue js_typed_array_constructor(JSContext *ctx,
1182                                           JSValueConst this_val,
1183                                           int argc, JSValueConst *argv,
1184                                           int classid);
1185 static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p);
1186 static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p);
1187 static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
1188 static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
1189                              BOOL is_arg);
1190 static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
1191                                           JSValueConst this_obj,
1192                                           int argc, JSValueConst *argv,
1193                                           int flags);
1194 static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val);
1195 static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
1196                                            JS_MarkFunc *mark_func);
1197 static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
1198                                const char *input, size_t input_len,
1199                                const char *filename, int flags, int scope_idx);
1200 static void js_free_module_def(JSContext *ctx, JSModuleDef *m);
1201 static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
1202                                JS_MarkFunc *mark_func);
1203 static JSValue js_import_meta(JSContext *ctx);
1204 static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier);
1205 static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);
1206 static JSValue js_new_promise_capability(JSContext *ctx,
1207                                          JSValue *resolving_funcs,
1208                                          JSValueConst ctor);
1209 static __exception int perform_promise_then(JSContext *ctx,
1210                                             JSValueConst promise,
1211                                             JSValueConst *resolve_reject,
1212                                             JSValueConst *cap_resolving_funcs);
1213 static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
1214                                   int argc, JSValueConst *argv, int magic);
1215 static int js_string_compare(JSContext *ctx,
1216                              const JSString *p1, const JSString *p2);
1217 static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val);
1218 static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
1219                                JSValue prop, JSValue val, int flags);
1220 static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val);
1221 static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val);
1222 static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);
1223 static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
1224                                      JSObject *p, JSAtom prop);
1225 static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);
1226 static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,
1227                             JS_MarkFunc *mark_func);
1228 static void JS_AddIntrinsicBasicObjects(JSContext *ctx);
1229 static void js_free_shape(JSRuntime *rt, JSShape *sh);
1230 static void js_free_shape_null(JSRuntime *rt, JSShape *sh);
1231 static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
1232                                    JSShapeProperty **pprs);
1233 static int init_shape_hash(JSRuntime *rt);
1234 static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
1235                                        JSValueConst obj);
1236 static __exception int js_get_length64(JSContext *ctx, int64_t *pres,
1237                                        JSValueConst obj);
1238 static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len);
1239 static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
1240                                JSValueConst array_arg);
1241 static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
1242                               JSValue **arrpp, uint32_t *countp);
1243 static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
1244                                               JSValueConst sync_iter);
1245 static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val);
1246 static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
1247                                     JS_MarkFunc *mark_func);
1248 static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
1249                                        JSValueConst this_val,
1250                                        int argc, JSValueConst *argv, int flags);
1251 static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val);
1252 static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
1253                           JSGCObjectTypeEnum type);
1254 static void remove_gc_object(JSGCObjectHeader *h);
1255 static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s);
1256 static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
1257 static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
1258                                  void *opaque);
1259 static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
1260                                                JSAtom atom, void *opaque);
1261 void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag);
1262 
1263 static const JSClassExoticMethods js_arguments_exotic_methods;
1264 static const JSClassExoticMethods js_string_exotic_methods;
1265 static const JSClassExoticMethods js_proxy_exotic_methods;
1266 static const JSClassExoticMethods js_module_ns_exotic_methods;
1267 static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
1268 
js_trigger_gc(JSRuntime * rt,size_t size)1269 static void js_trigger_gc(JSRuntime *rt, size_t size)
1270 {
1271     BOOL force_gc;
1272 #ifdef FORCE_GC_AT_MALLOC
1273     force_gc = TRUE;
1274 #else
1275     force_gc = ((rt->malloc_state.malloc_size + size) >
1276                 rt->malloc_gc_threshold);
1277 #endif
1278     if (force_gc) {
1279 #ifdef DUMP_GC
1280         printf("GC: size=%" PRIu64 "\n",
1281                (uint64_t)rt->malloc_state.malloc_size);
1282 #endif
1283         JS_RunGC(rt);
1284         rt->malloc_gc_threshold = rt->malloc_state.malloc_size +
1285             (rt->malloc_state.malloc_size >> 1);
1286     }
1287 }
1288 
js_malloc_usable_size_unknown(const void * ptr)1289 static size_t js_malloc_usable_size_unknown(const void *ptr)
1290 {
1291     return 0;
1292 }
1293 
js_malloc_rt(JSRuntime * rt,size_t size)1294 void *js_malloc_rt(JSRuntime *rt, size_t size)
1295 {
1296     return rt->mf.js_malloc(&rt->malloc_state, size);
1297 }
1298 
js_free_rt(JSRuntime * rt,void * ptr)1299 void js_free_rt(JSRuntime *rt, void *ptr)
1300 {
1301     rt->mf.js_free(&rt->malloc_state, ptr);
1302 }
1303 
js_realloc_rt(JSRuntime * rt,void * ptr,size_t size)1304 void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)
1305 {
1306     return rt->mf.js_realloc(&rt->malloc_state, ptr, size);
1307 }
1308 
js_malloc_usable_size_rt(JSRuntime * rt,const void * ptr)1309 size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr)
1310 {
1311     return rt->mf.js_malloc_usable_size(ptr);
1312 }
1313 
js_mallocz_rt(JSRuntime * rt,size_t size)1314 void *js_mallocz_rt(JSRuntime *rt, size_t size)
1315 {
1316     void *ptr;
1317     ptr = js_malloc_rt(rt, size);
1318     if (!ptr)
1319         return NULL;
1320     return memset(ptr, 0, size);
1321 }
1322 
1323 #ifdef CONFIG_BIGNUM
1324 /* called by libbf */
js_bf_realloc(void * opaque,void * ptr,size_t size)1325 static void *js_bf_realloc(void *opaque, void *ptr, size_t size)
1326 {
1327     JSRuntime *rt = opaque;
1328     return js_realloc_rt(rt, ptr, size);
1329 }
1330 #endif /* CONFIG_BIGNUM */
1331 
1332 /* Throw out of memory in case of error */
js_malloc(JSContext * ctx,size_t size)1333 void *js_malloc(JSContext *ctx, size_t size)
1334 {
1335     void *ptr;
1336     ptr = js_malloc_rt(ctx->rt, size);
1337     if (unlikely(!ptr)) {
1338         JS_ThrowOutOfMemory(ctx);
1339         return NULL;
1340     }
1341     return ptr;
1342 }
1343 
1344 /* Throw out of memory in case of error */
js_mallocz(JSContext * ctx,size_t size)1345 void *js_mallocz(JSContext *ctx, size_t size)
1346 {
1347     void *ptr;
1348     ptr = js_mallocz_rt(ctx->rt, size);
1349     if (unlikely(!ptr)) {
1350         JS_ThrowOutOfMemory(ctx);
1351         return NULL;
1352     }
1353     return ptr;
1354 }
1355 
js_free(JSContext * ctx,void * ptr)1356 void js_free(JSContext *ctx, void *ptr)
1357 {
1358     js_free_rt(ctx->rt, ptr);
1359 }
1360 
1361 /* Throw out of memory in case of error */
js_realloc(JSContext * ctx,void * ptr,size_t size)1362 void *js_realloc(JSContext *ctx, void *ptr, size_t size)
1363 {
1364     void *ret;
1365     ret = js_realloc_rt(ctx->rt, ptr, size);
1366     if (unlikely(!ret && size != 0)) {
1367         JS_ThrowOutOfMemory(ctx);
1368         return NULL;
1369     }
1370     return ret;
1371 }
1372 
1373 /* store extra allocated size in *pslack if successful */
js_realloc2(JSContext * ctx,void * ptr,size_t size,size_t * pslack)1374 void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack)
1375 {
1376     void *ret;
1377     ret = js_realloc_rt(ctx->rt, ptr, size);
1378     if (unlikely(!ret && size != 0)) {
1379         JS_ThrowOutOfMemory(ctx);
1380         return NULL;
1381     }
1382     if (pslack) {
1383         size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret);
1384         *pslack = (new_size > size) ? new_size - size : 0;
1385     }
1386     return ret;
1387 }
1388 
js_malloc_usable_size(JSContext * ctx,const void * ptr)1389 size_t js_malloc_usable_size(JSContext *ctx, const void *ptr)
1390 {
1391     return js_malloc_usable_size_rt(ctx->rt, ptr);
1392 }
1393 
1394 /* Throw out of memory exception in case of error */
js_strndup(JSContext * ctx,const char * s,size_t n)1395 char *js_strndup(JSContext *ctx, const char *s, size_t n)
1396 {
1397     char *ptr;
1398     ptr = js_malloc(ctx, n + 1);
1399     if (ptr) {
1400         memcpy(ptr, s, n);
1401         ptr[n] = '\0';
1402     }
1403     return ptr;
1404 }
1405 
js_strdup(JSContext * ctx,const char * str)1406 char *js_strdup(JSContext *ctx, const char *str)
1407 {
1408     return js_strndup(ctx, str, strlen(str));
1409 }
1410 
js_realloc_array(JSContext * ctx,void ** parray,int elem_size,int * psize,int req_size)1411 static no_inline int js_realloc_array(JSContext *ctx, void **parray,
1412                                       int elem_size, int *psize, int req_size)
1413 {
1414     int new_size;
1415     size_t slack;
1416     void *new_array;
1417     /* XXX: potential arithmetic overflow */
1418     new_size = max_int(req_size, *psize * 3 / 2);
1419     new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack);
1420     if (!new_array)
1421         return -1;
1422     new_size += slack / elem_size;
1423     *psize = new_size;
1424     *parray = new_array;
1425     return 0;
1426 }
1427 
1428 /* resize the array and update its size if req_size > *psize */
js_resize_array(JSContext * ctx,void ** parray,int elem_size,int * psize,int req_size)1429 static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size,
1430                                   int *psize, int req_size)
1431 {
1432     if (unlikely(req_size > *psize))
1433         return js_realloc_array(ctx, parray, elem_size, psize, req_size);
1434     else
1435         return 0;
1436 }
1437 
js_dbuf_init(JSContext * ctx,DynBuf * s)1438 static inline void js_dbuf_init(JSContext *ctx, DynBuf *s)
1439 {
1440     dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
1441 }
1442 
is_digit(int c)1443 static inline int is_digit(int c) {
1444     return c >= '0' && c <= '9';
1445 }
1446 
1447 typedef struct JSClassShortDef {
1448     JSAtom class_name;
1449     JSClassFinalizer *finalizer;
1450     JSClassGCMark *gc_mark;
1451 } JSClassShortDef;
1452 
1453 static JSClassShortDef const js_std_class_def[] = {
1454     { JS_ATOM_Object, NULL, NULL },                             /* JS_CLASS_OBJECT */
1455     { JS_ATOM_Array, js_array_finalizer, js_array_mark },       /* JS_CLASS_ARRAY */
1456     { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */
1457     { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */
1458     { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */
1459     { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */
1460     { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */
1461     { JS_ATOM_Arguments, js_array_finalizer, js_array_mark },   /* JS_CLASS_ARGUMENTS */
1462     { JS_ATOM_Arguments, NULL, NULL },                          /* JS_CLASS_MAPPED_ARGUMENTS */
1463     { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */
1464     { JS_ATOM_Object, NULL, NULL },                             /* JS_CLASS_MODULE_NS */
1465     { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */
1466     { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */
1467     { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */
1468     { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */
1469     { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark },  /* JS_CLASS_GENERATOR_FUNCTION */
1470     { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark },      /* JS_CLASS_FOR_IN_ITERATOR */
1471     { JS_ATOM_RegExp, js_regexp_finalizer, NULL },                              /* JS_CLASS_REGEXP */
1472     { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL },                   /* JS_CLASS_ARRAY_BUFFER */
1473     { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL },             /* JS_CLASS_SHARED_ARRAY_BUFFER */
1474     { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */
1475     { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark },       /* JS_CLASS_INT8_ARRAY */
1476     { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark },      /* JS_CLASS_UINT8_ARRAY */
1477     { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark },      /* JS_CLASS_INT16_ARRAY */
1478     { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark },     /* JS_CLASS_UINT16_ARRAY */
1479     { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark },      /* JS_CLASS_INT32_ARRAY */
1480     { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark },     /* JS_CLASS_UINT32_ARRAY */
1481 #ifdef CONFIG_BIGNUM
1482     { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark },   /* JS_CLASS_BIG_INT64_ARRAY */
1483     { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark },  /* JS_CLASS_BIG_UINT64_ARRAY */
1484 #endif
1485     { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark },    /* JS_CLASS_FLOAT32_ARRAY */
1486     { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark },    /* JS_CLASS_FLOAT64_ARRAY */
1487     { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark },        /* JS_CLASS_DATAVIEW */
1488 #ifdef CONFIG_BIGNUM
1489     { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark },      /* JS_CLASS_BIG_INT */
1490     { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark },    /* JS_CLASS_BIG_FLOAT */
1491     { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL },      /* JS_CLASS_FLOAT_ENV */
1492     { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark },    /* JS_CLASS_BIG_DECIMAL */
1493     { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark },    /* JS_CLASS_OPERATOR_SET */
1494 #endif
1495     { JS_ATOM_Map, js_map_finalizer, js_map_mark },             /* JS_CLASS_MAP */
1496     { JS_ATOM_Set, js_map_finalizer, js_map_mark },             /* JS_CLASS_SET */
1497     { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark },         /* JS_CLASS_WEAKMAP */
1498     { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark },         /* JS_CLASS_WEAKSET */
1499     { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */
1500     { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */
1501     { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */
1502     { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */
1503     { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */
1504     { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */
1505 };
1506 
init_class_range(JSRuntime * rt,JSClassShortDef const * tab,int start,int count)1507 static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,
1508                             int start, int count)
1509 {
1510     JSClassDef cm_s, *cm = &cm_s;
1511     int i, class_id;
1512 
1513     for(i = 0; i < count; i++) {
1514         class_id = i + start;
1515         memset(cm, 0, sizeof(*cm));
1516         cm->finalizer = tab[i].finalizer;
1517         cm->gc_mark = tab[i].gc_mark;
1518         if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0)
1519             return -1;
1520     }
1521     return 0;
1522 }
1523 
1524 #ifdef CONFIG_BIGNUM
JS_ThrowUnsupportedOperation(JSContext * ctx)1525 static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx)
1526 {
1527     return JS_ThrowTypeError(ctx, "unsupported operation");
1528 }
1529 
invalid_to_string(JSContext * ctx,JSValueConst val)1530 static JSValue invalid_to_string(JSContext *ctx, JSValueConst val)
1531 {
1532     return JS_ThrowUnsupportedOperation(ctx);
1533 }
1534 
invalid_from_string(JSContext * ctx,const char * buf,int radix,int flags,slimb_t * pexponent)1535 static JSValue invalid_from_string(JSContext *ctx, const char *buf,
1536                                    int radix, int flags, slimb_t *pexponent)
1537 {
1538     return JS_NAN;
1539 }
1540 
invalid_unary_arith(JSContext * ctx,JSValue * pres,OPCodeEnum op,JSValue op1)1541 static int invalid_unary_arith(JSContext *ctx,
1542                                JSValue *pres, OPCodeEnum op, JSValue op1)
1543 {
1544     JS_FreeValue(ctx, op1);
1545     JS_ThrowUnsupportedOperation(ctx);
1546     return -1;
1547 }
1548 
invalid_binary_arith(JSContext * ctx,OPCodeEnum op,JSValue * pres,JSValue op1,JSValue op2)1549 static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op,
1550                                 JSValue *pres, JSValue op1, JSValue op2)
1551 {
1552     JS_FreeValue(ctx, op1);
1553     JS_FreeValue(ctx, op2);
1554     JS_ThrowUnsupportedOperation(ctx);
1555     return -1;
1556 }
1557 
invalid_mul_pow10_to_float64(JSContext * ctx,const bf_t * a,int64_t exponent)1558 static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
1559                                             int64_t exponent)
1560 {
1561     return JS_ThrowUnsupportedOperation(ctx);
1562 }
1563 
invalid_mul_pow10(JSContext * ctx,JSValue * sp)1564 static int invalid_mul_pow10(JSContext *ctx, JSValue *sp)
1565 {
1566     JS_ThrowUnsupportedOperation(ctx);
1567     return -1;
1568 }
1569 
set_dummy_numeric_ops(JSNumericOperations * ops)1570 static void set_dummy_numeric_ops(JSNumericOperations *ops)
1571 {
1572     ops->to_string = invalid_to_string;
1573     ops->from_string = invalid_from_string;
1574     ops->unary_arith = invalid_unary_arith;
1575     ops->binary_arith = invalid_binary_arith;
1576     ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64;
1577     ops->mul_pow10 = invalid_mul_pow10;
1578 }
1579 
1580 #endif /* CONFIG_BIGNUM */
1581 
1582 #if !defined(CONFIG_STACK_CHECK)
1583 /* no stack limitation */
js_get_stack_pointer(void)1584 static inline uintptr_t js_get_stack_pointer(void)
1585 {
1586     return 0;
1587 }
1588 
js_check_stack_overflow(JSRuntime * rt,size_t alloca_size)1589 static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
1590 {
1591     return FALSE;
1592 }
1593 #else
1594 
1595 #if !defined(__has_builtin)
1596   #define __has_builtin(x) 0
1597 #endif
1598 
1599 #if defined(__GNUC__) || __has_builtin(__builtin_frame_address)
1600 /* Note: OS and CPU dependent */
js_get_stack_pointer(void)1601 static inline uintptr_t js_get_stack_pointer(void)
1602 {
1603     return (uintptr_t)__builtin_frame_address(0);
1604 }
1605 #elif defined(_MSC_VER) && !defined(_WIN64)
js_get_stack_pointer(void)1606 static inline uintptr_t js_get_stack_pointer(void)
1607 {
1608     return (uintptr_t)_AddressOfReturnAddress();
1609 }
1610 #else
js_get_stack_pointer(void)1611 static inline uint8_t *js_get_stack_pointer(void)
1612 {
1613     return NULL;
1614 }
1615 #define NO_VALID_STACK_POINTER
1616 #endif
1617 
js_check_stack_overflow(JSRuntime * rt,size_t alloca_size)1618 static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
1619 {
1620 #if defined(NO_VALID_STACK_POINTER)
1621     return FALSE;
1622 #else
1623     uintptr_t sp;
1624     sp = js_get_stack_pointer() - alloca_size;
1625     return unlikely(sp < rt->stack_limit);
1626 #endif
1627 }
1628 #endif
1629 
JS_NewRuntime2(const JSMallocFunctions * mf,void * opaque)1630 JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
1631 {
1632     JSRuntime *rt;
1633     JSMallocState ms;
1634 
1635     memset(&ms, 0, sizeof(ms));
1636     ms.opaque = opaque;
1637     ms.malloc_limit = -1;
1638 
1639     rt = mf->js_malloc(&ms, sizeof(JSRuntime));
1640     if (!rt)
1641         return NULL;
1642     memset(rt, 0, sizeof(*rt));
1643     rt->mf = *mf;
1644     if (!rt->mf.js_malloc_usable_size) {
1645         /* use dummy function if none provided */
1646         rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;
1647     }
1648     rt->malloc_state = ms;
1649     rt->malloc_gc_threshold = 256 * 1024;
1650 
1651 #ifdef CONFIG_BIGNUM
1652     bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);
1653     set_dummy_numeric_ops(&rt->bigint_ops);
1654     set_dummy_numeric_ops(&rt->bigfloat_ops);
1655     set_dummy_numeric_ops(&rt->bigdecimal_ops);
1656 #endif
1657 
1658     init_list_head(&rt->context_list);
1659     init_list_head(&rt->gc_obj_list);
1660     init_list_head(&rt->gc_zero_ref_count_list);
1661     rt->gc_phase = JS_GC_PHASE_NONE;
1662 
1663 #ifdef DUMP_LEAKS
1664     init_list_head(&rt->string_list);
1665 #endif
1666     init_list_head(&rt->job_list);
1667 
1668     if (JS_InitAtoms(rt))
1669         goto fail;
1670 
1671     /* create the object, array and function classes */
1672     if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT,
1673                          countof(js_std_class_def)) < 0)
1674         goto fail;
1675     rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods;
1676     rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods;
1677     rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods;
1678 
1679     rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;
1680     rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call;
1681     rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;
1682     rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call;
1683     if (init_shape_hash(rt))
1684         goto fail;
1685 
1686     rt->stack_size = JS_DEFAULT_STACK_SIZE;
1687     JS_UpdateStackTop(rt);
1688 
1689     rt->current_exception = JS_NULL;
1690 
1691     return rt;
1692  fail:
1693     JS_FreeRuntime(rt);
1694     return NULL;
1695 }
1696 
JS_GetRuntimeOpaque(JSRuntime * rt)1697 void *JS_GetRuntimeOpaque(JSRuntime *rt)
1698 {
1699     return rt->user_opaque;
1700 }
1701 
JS_SetRuntimeOpaque(JSRuntime * rt,void * opaque)1702 void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)
1703 {
1704     rt->user_opaque = opaque;
1705 }
1706 
1707 /* default memory allocation functions with memory limitation */
js_def_malloc_usable_size(void * ptr)1708 static inline size_t js_def_malloc_usable_size(void *ptr)
1709 {
1710 #if defined(__APPLE__)
1711     return malloc_size(ptr);
1712 #elif defined(_WIN32)
1713     return _msize(ptr);
1714 #elif defined(EMSCRIPTEN) || defined(__OpenBSD__) || defined(__NetBSD__)
1715     return 0;
1716 #elif defined(__linux__)
1717     return malloc_usable_size(ptr);
1718 #elif defined(__DragonFly__)
1719     return 0;
1720 #else
1721     /* change this to `return 0;` if compilation fails */
1722     return malloc_usable_size(ptr);
1723 #endif
1724 }
1725 
js_def_malloc(JSMallocState * s,size_t size)1726 static void *js_def_malloc(JSMallocState *s, size_t size)
1727 {
1728     void *ptr;
1729 
1730     /* Do not allocate zero bytes: behavior is platform dependent */
1731     assert(size != 0);
1732 
1733     if (unlikely(s->malloc_size + size > s->malloc_limit))
1734         return NULL;
1735 
1736     ptr = malloc(size);
1737     if (!ptr)
1738         return NULL;
1739 
1740     s->malloc_count++;
1741     s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
1742     return ptr;
1743 }
1744 
js_def_free(JSMallocState * s,void * ptr)1745 static void js_def_free(JSMallocState *s, void *ptr)
1746 {
1747     if (!ptr)
1748         return;
1749 
1750     s->malloc_count--;
1751     s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
1752     free(ptr);
1753 }
1754 
js_def_realloc(JSMallocState * s,void * ptr,size_t size)1755 static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)
1756 {
1757     size_t old_size;
1758 
1759     if (!ptr) {
1760         if (size == 0)
1761             return NULL;
1762         return js_def_malloc(s, size);
1763     }
1764     old_size = js_def_malloc_usable_size(ptr);
1765     if (size == 0) {
1766         s->malloc_count--;
1767         s->malloc_size -= old_size + MALLOC_OVERHEAD;
1768         free(ptr);
1769         return NULL;
1770     }
1771     if (s->malloc_size + size - old_size > s->malloc_limit)
1772         return NULL;
1773 
1774     ptr = realloc(ptr, size);
1775     if (!ptr)
1776         return NULL;
1777 
1778     s->malloc_size += js_def_malloc_usable_size(ptr) - old_size;
1779     return ptr;
1780 }
1781 
1782 static const JSMallocFunctions def_malloc_funcs = {
1783     js_def_malloc,
1784     js_def_free,
1785     js_def_realloc,
1786 #if defined(__APPLE__)
1787     malloc_size,
1788 #elif defined(_WIN32)
1789     (size_t (*)(const void *))_msize,
1790 #elif defined(EMSCRIPTEN) || defined(__OpenBSD__) || defined(__NetBSD__) || \
1791 	defined(__DragonFly__)
1792     NULL,
1793 #elif defined(__linux__)
1794     (size_t (*)(const void *))malloc_usable_size,
1795 #else
1796     /* change this to `NULL,` if compilation fails */
1797     malloc_usable_size,
1798 #endif
1799 };
1800 
JS_NewRuntime(void)1801 JSRuntime *JS_NewRuntime(void)
1802 {
1803     return JS_NewRuntime2(&def_malloc_funcs, NULL);
1804 }
1805 
JS_SetMemoryLimit(JSRuntime * rt,size_t limit)1806 void JS_SetMemoryLimit(JSRuntime *rt, size_t limit)
1807 {
1808     rt->malloc_state.malloc_limit = limit;
1809 }
1810 
1811 /* use -1 to disable automatic GC */
JS_SetGCThreshold(JSRuntime * rt,size_t gc_threshold)1812 void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold)
1813 {
1814     rt->malloc_gc_threshold = gc_threshold;
1815 }
1816 
1817 #define malloc(s) malloc_is_forbidden(s)
1818 #define free(p) free_is_forbidden(p)
1819 #define realloc(p,s) realloc_is_forbidden(p,s)
1820 
JS_SetInterruptHandler(JSRuntime * rt,JSInterruptHandler * cb,void * opaque)1821 void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque)
1822 {
1823     rt->interrupt_handler = cb;
1824     rt->interrupt_opaque = opaque;
1825 }
1826 
JS_SetCanBlock(JSRuntime * rt,BOOL can_block)1827 void JS_SetCanBlock(JSRuntime *rt, BOOL can_block)
1828 {
1829     rt->can_block = can_block;
1830 }
1831 
JS_SetSharedArrayBufferFunctions(JSRuntime * rt,const JSSharedArrayBufferFunctions * sf)1832 void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
1833                                       const JSSharedArrayBufferFunctions *sf)
1834 {
1835     rt->sab_funcs = *sf;
1836 }
1837 
1838 /* return 0 if OK, < 0 if exception */
JS_EnqueueJob(JSContext * ctx,JSJobFunc * job_func,int argc,JSValueConst * argv)1839 int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
1840                   int argc, JSValueConst *argv)
1841 {
1842     JSRuntime *rt = ctx->rt;
1843     JSJobEntry *e;
1844     int i;
1845 
1846     e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));
1847     if (!e)
1848         return -1;
1849     e->ctx = ctx;
1850     e->job_func = job_func;
1851     e->argc = argc;
1852     for(i = 0; i < argc; i++) {
1853         e->argv[i] = JS_DupValue(ctx, argv[i]);
1854     }
1855     list_add_tail(&e->link, &rt->job_list);
1856     return 0;
1857 }
1858 
JS_IsJobPending(JSRuntime * rt)1859 BOOL JS_IsJobPending(JSRuntime *rt)
1860 {
1861     return !list_empty(&rt->job_list);
1862 }
1863 
1864 /* return < 0 if exception, 0 if no job pending, 1 if a job was
1865    executed successfully. the context of the job is stored in '*pctx' */
JS_ExecutePendingJob(JSRuntime * rt,JSContext ** pctx)1866 int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)
1867 {
1868     JSContext *ctx;
1869     JSJobEntry *e;
1870     JSValue res;
1871     int i, ret;
1872 
1873     if (list_empty(&rt->job_list)) {
1874         *pctx = NULL;
1875         return 0;
1876     }
1877 
1878     /* get the first pending job and execute it */
1879     e = list_entry(rt->job_list.next, JSJobEntry, link);
1880     list_del(&e->link);
1881     ctx = e->ctx;
1882     res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv);
1883     for(i = 0; i < e->argc; i++)
1884         JS_FreeValue(ctx, e->argv[i]);
1885     if (JS_IsException(res))
1886         ret = -1;
1887     else
1888         ret = 1;
1889     JS_FreeValue(ctx, res);
1890     js_free(ctx, e);
1891     *pctx = ctx;
1892     return ret;
1893 }
1894 
atom_get_free(const JSAtomStruct * p)1895 static inline uint32_t atom_get_free(const JSAtomStruct *p)
1896 {
1897     return (uintptr_t)p >> 1;
1898 }
1899 
atom_is_free(const JSAtomStruct * p)1900 static inline BOOL atom_is_free(const JSAtomStruct *p)
1901 {
1902     return (uintptr_t)p & 1;
1903 }
1904 
atom_set_free(uint32_t v)1905 static inline JSAtomStruct *atom_set_free(uint32_t v)
1906 {
1907     return (JSAtomStruct *)(((uintptr_t)v << 1) | 1);
1908 }
1909 
1910 /* Note: the string contents are uninitialized */
js_alloc_string_rt(JSRuntime * rt,int max_len,int is_wide_char)1911 static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char)
1912 {
1913     JSString *str;
1914     str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char);
1915     if (unlikely(!str))
1916         return NULL;
1917     str->header.ref_count = 1;
1918     str->is_wide_char = is_wide_char;
1919     str->len = max_len;
1920     str->atom_type = 0;
1921     str->hash = 0;          /* optional but costless */
1922     str->hash_next = 0;     /* optional */
1923 #ifdef DUMP_LEAKS
1924     list_add_tail(&str->link, &rt->string_list);
1925 #endif
1926     return str;
1927 }
1928 
js_alloc_string(JSContext * ctx,int max_len,int is_wide_char)1929 static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)
1930 {
1931     JSString *p;
1932     p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char);
1933     if (unlikely(!p)) {
1934         JS_ThrowOutOfMemory(ctx);
1935         return NULL;
1936     }
1937     return p;
1938 }
1939 
1940 /* same as JS_FreeValueRT() but faster */
js_free_string(JSRuntime * rt,JSString * str)1941 static inline void js_free_string(JSRuntime *rt, JSString *str)
1942 {
1943     if (--str->header.ref_count <= 0) {
1944         if (str->atom_type) {
1945             JS_FreeAtomStruct(rt, str);
1946         } else {
1947 #ifdef DUMP_LEAKS
1948             list_del(&str->link);
1949 #endif
1950             js_free_rt(rt, str);
1951         }
1952     }
1953 }
1954 
JS_SetRuntimeInfo(JSRuntime * rt,const char * s)1955 void JS_SetRuntimeInfo(JSRuntime *rt, const char *s)
1956 {
1957     if (rt)
1958         rt->rt_info = s;
1959 }
1960 
JS_FreeRuntime(JSRuntime * rt)1961 void JS_FreeRuntime(JSRuntime *rt)
1962 {
1963 	JS_FreeRuntime2(rt, NULL);
1964 }
1965 
JS_FreeRuntime2(JSRuntime * rt,void (* gc_leak_handler)(const char * msg))1966 void JS_FreeRuntime2(JSRuntime *rt, void (*gc_leak_handler)(const char* msg))
1967 {
1968     struct list_head *el, *el1;
1969     int i;
1970 
1971     JS_FreeValueRT(rt, rt->current_exception);
1972 
1973     list_for_each_safe(el, el1, &rt->job_list) {
1974         JSJobEntry *e = list_entry(el, JSJobEntry, link);
1975         for(i = 0; i < e->argc; i++)
1976             JS_FreeValueRT(rt, e->argv[i]);
1977         js_free_rt(rt, e);
1978     }
1979     init_list_head(&rt->job_list);
1980 
1981     JS_RunGC(rt);
1982 
1983 #ifdef DUMP_LEAKS
1984     /* leaking objects */
1985     {
1986         BOOL header_done;
1987         JSGCObjectHeader *p;
1988         int count;
1989 
1990         /* remove the internal refcounts to display only the object
1991            referenced externally */
1992         list_for_each(el, &rt->gc_obj_list) {
1993             p = list_entry(el, JSGCObjectHeader, link);
1994             p->mark = 0;
1995         }
1996         gc_decref(rt);
1997 
1998         header_done = FALSE;
1999         list_for_each(el, &rt->gc_obj_list) {
2000             p = list_entry(el, JSGCObjectHeader, link);
2001             if (p->ref_count != 0) {
2002                 if (!header_done) {
2003                     printf("Object leaks:\n");
2004                     JS_DumpObjectHeader(rt);
2005                     header_done = TRUE;
2006                 }
2007                 JS_DumpGCObject(rt, p);
2008             }
2009         }
2010 
2011         count = 0;
2012         list_for_each(el, &rt->gc_obj_list) {
2013             p = list_entry(el, JSGCObjectHeader, link);
2014             if (p->ref_count == 0) {
2015                 count++;
2016             }
2017         }
2018         if (count != 0)
2019             printf("Secondary object leaks: %d\n", count);
2020     }
2021 #endif
2022     if (!list_empty(&rt->gc_obj_list)) {
2023 		if (gc_leak_handler != NULL) {
2024 			gc_leak_handler("gc_obj_list is not empty");
2025 		}
2026 	}
2027 
2028     /* free the classes */
2029     for(i = 0; i < rt->class_count; i++) {
2030         JSClass *cl = &rt->class_array[i];
2031         if (cl->class_id != 0) {
2032             JS_FreeAtomRT(rt, cl->class_name);
2033         }
2034     }
2035     js_free_rt(rt, rt->class_array);
2036 
2037 #ifdef CONFIG_BIGNUM
2038     bf_context_end(&rt->bf_ctx);
2039 #endif
2040 
2041 #ifdef DUMP_LEAKS
2042     /* only the atoms defined in JS_InitAtoms() should be left */
2043     {
2044         BOOL header_done = FALSE;
2045 
2046         for(i = 0; i < rt->atom_size; i++) {
2047             JSAtomStruct *p = rt->atom_array[i];
2048             if (!atom_is_free(p) /* && p->str*/) {
2049                 if (i >= JS_ATOM_END || p->header.ref_count != 1) {
2050                     if (!header_done) {
2051                         header_done = TRUE;
2052                         if (rt->rt_info) {
2053                             printf("%s:1: atom leakage:", rt->rt_info);
2054                         } else {
2055                             printf("Atom leaks:\n"
2056                                    "    %6s %6s %s\n",
2057                                    "ID", "REFCNT", "NAME");
2058                         }
2059                     }
2060                     if (rt->rt_info) {
2061                         printf(" ");
2062                     } else {
2063                         printf("    %6u %6u ", i, p->header.ref_count);
2064                     }
2065                     switch (p->atom_type) {
2066                     case JS_ATOM_TYPE_STRING:
2067                         JS_DumpString(rt, p);
2068                         break;
2069                     case JS_ATOM_TYPE_GLOBAL_SYMBOL:
2070                         printf("Symbol.for(");
2071                         JS_DumpString(rt, p);
2072                         printf(")");
2073                         break;
2074                     case JS_ATOM_TYPE_SYMBOL:
2075                         if (p->hash == JS_ATOM_HASH_SYMBOL) {
2076                             printf("Symbol(");
2077                             JS_DumpString(rt, p);
2078                             printf(")");
2079                         } else {
2080                             printf("Private(");
2081                             JS_DumpString(rt, p);
2082                             printf(")");
2083                         }
2084                         break;
2085                     }
2086                     if (rt->rt_info) {
2087                         printf(":%u", p->header.ref_count);
2088                     } else {
2089                         printf("\n");
2090                     }
2091                 }
2092             }
2093         }
2094         if (rt->rt_info && header_done)
2095             printf("\n");
2096     }
2097 #endif
2098 
2099     /* free the atoms */
2100     for(i = 0; i < rt->atom_size; i++) {
2101         JSAtomStruct *p = rt->atom_array[i];
2102         if (!atom_is_free(p)) {
2103 #ifdef DUMP_LEAKS
2104             list_del(&p->link);
2105 #endif
2106             js_free_rt(rt, p);
2107         }
2108     }
2109     js_free_rt(rt, rt->atom_array);
2110     js_free_rt(rt, rt->atom_hash);
2111     js_free_rt(rt, rt->shape_hash);
2112 #ifdef DUMP_LEAKS
2113     if (!list_empty(&rt->string_list)) {
2114         if (rt->rt_info) {
2115             printf("%s:1: string leakage:", rt->rt_info);
2116         } else {
2117             printf("String leaks:\n"
2118                    "    %6s %s\n",
2119                    "REFCNT", "VALUE");
2120         }
2121         list_for_each_safe(el, el1, &rt->string_list) {
2122             JSString *str = list_entry(el, JSString, link);
2123             if (rt->rt_info) {
2124                 printf(" ");
2125             } else {
2126                 printf("    %6u ", str->header.ref_count);
2127             }
2128             JS_DumpString(rt, str);
2129             if (rt->rt_info) {
2130                 printf(":%u", str->header.ref_count);
2131             } else {
2132                 printf("\n");
2133             }
2134             list_del(&str->link);
2135             js_free_rt(rt, str);
2136         }
2137         if (rt->rt_info)
2138             printf("\n");
2139     }
2140     {
2141         JSMallocState *s = &rt->malloc_state;
2142         if (s->malloc_count > 1) {
2143             if (rt->rt_info)
2144                 printf("%s:1: ", rt->rt_info);
2145             printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n",
2146                    (uint64_t)(s->malloc_size - sizeof(JSRuntime)),
2147                    (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]);
2148         }
2149     }
2150 #endif
2151 
2152     {
2153         JSMallocState ms = rt->malloc_state;
2154         rt->mf.js_free(&ms, rt);
2155     }
2156 }
2157 
JS_NewContextRaw(JSRuntime * rt)2158 JSContext *JS_NewContextRaw(JSRuntime *rt)
2159 {
2160     JSContext *ctx;
2161     int i;
2162 
2163     ctx = js_mallocz_rt(rt, sizeof(JSContext));
2164     if (!ctx)
2165         return NULL;
2166     ctx->header.ref_count = 1;
2167     add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT);
2168 
2169     ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) *
2170                                     rt->class_count);
2171     if (!ctx->class_proto) {
2172         js_free_rt(rt, ctx);
2173         return NULL;
2174     }
2175     ctx->rt = rt;
2176     list_add_tail(&ctx->link, &rt->context_list);
2177 #ifdef CONFIG_BIGNUM
2178     ctx->bf_ctx = &rt->bf_ctx;
2179     ctx->fp_env.prec = 113;
2180     ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL;
2181 #endif
2182     for(i = 0; i < rt->class_count; i++)
2183         ctx->class_proto[i] = JS_NULL;
2184     ctx->array_ctor = JS_NULL;
2185     ctx->regexp_ctor = JS_NULL;
2186     ctx->promise_ctor = JS_NULL;
2187     init_list_head(&ctx->loaded_modules);
2188 
2189     JS_AddIntrinsicBasicObjects(ctx);
2190     return ctx;
2191 }
2192 
JS_NewContext(JSRuntime * rt)2193 JSContext *JS_NewContext(JSRuntime *rt)
2194 {
2195     JSContext *ctx;
2196 
2197     ctx = JS_NewContextRaw(rt);
2198     if (!ctx)
2199         return NULL;
2200 
2201     JS_AddIntrinsicBaseObjects(ctx);
2202     JS_AddIntrinsicDate(ctx);
2203     JS_AddIntrinsicEval(ctx);
2204     JS_AddIntrinsicStringNormalize(ctx);
2205     JS_AddIntrinsicRegExp(ctx);
2206     JS_AddIntrinsicJSON(ctx);
2207     JS_AddIntrinsicProxy(ctx);
2208     JS_AddIntrinsicMapSet(ctx);
2209     JS_AddIntrinsicTypedArrays(ctx);
2210     JS_AddIntrinsicPromise(ctx);
2211 #ifdef CONFIG_BIGNUM
2212     JS_AddIntrinsicBigInt(ctx);
2213 #endif
2214     return ctx;
2215 }
2216 
JS_GetContextOpaque(JSContext * ctx)2217 void *JS_GetContextOpaque(JSContext *ctx)
2218 {
2219     return ctx->user_opaque;
2220 }
2221 
JS_SetContextOpaque(JSContext * ctx,void * opaque)2222 void JS_SetContextOpaque(JSContext *ctx, void *opaque)
2223 {
2224     ctx->user_opaque = opaque;
2225 }
2226 
2227 /* set the new value and free the old value after (freeing the value
2228    can reallocate the object data) */
set_value(JSContext * ctx,JSValue * pval,JSValue new_val)2229 static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val)
2230 {
2231     JSValue old_val;
2232     old_val = *pval;
2233     *pval = new_val;
2234     JS_FreeValue(ctx, old_val);
2235 }
2236 
JS_SetClassProto(JSContext * ctx,JSClassID class_id,JSValue obj)2237 void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj)
2238 {
2239     JSRuntime *rt = ctx->rt;
2240     assert(class_id < rt->class_count);
2241     set_value(ctx, &ctx->class_proto[class_id], obj);
2242 }
2243 
JS_GetClassProto(JSContext * ctx,JSClassID class_id)2244 JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id)
2245 {
2246     JSRuntime *rt = ctx->rt;
2247     assert(class_id < rt->class_count);
2248     return JS_DupValue(ctx, ctx->class_proto[class_id]);
2249 }
2250 
2251 typedef enum JSFreeModuleEnum {
2252     JS_FREE_MODULE_ALL,
2253     JS_FREE_MODULE_NOT_RESOLVED,
2254     JS_FREE_MODULE_NOT_EVALUATED,
2255 } JSFreeModuleEnum;
2256 
2257 /* XXX: would be more efficient with separate module lists */
js_free_modules(JSContext * ctx,JSFreeModuleEnum flag)2258 static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)
2259 {
2260     struct list_head *el, *el1;
2261     list_for_each_safe(el, el1, &ctx->loaded_modules) {
2262         JSModuleDef *m = list_entry(el, JSModuleDef, link);
2263         if (flag == JS_FREE_MODULE_ALL ||
2264             (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) ||
2265             (flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) {
2266             js_free_module_def(ctx, m);
2267         }
2268     }
2269 }
2270 
JS_DupContext(JSContext * ctx)2271 JSContext *JS_DupContext(JSContext *ctx)
2272 {
2273     ctx->header.ref_count++;
2274     return ctx;
2275 }
2276 
2277 /* used by the GC */
JS_MarkContext(JSRuntime * rt,JSContext * ctx,JS_MarkFunc * mark_func)2278 static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,
2279                            JS_MarkFunc *mark_func)
2280 {
2281     int i;
2282     struct list_head *el;
2283 
2284     /* modules are not seen by the GC, so we directly mark the objects
2285        referenced by each module */
2286     list_for_each(el, &ctx->loaded_modules) {
2287         JSModuleDef *m = list_entry(el, JSModuleDef, link);
2288         js_mark_module_def(rt, m, mark_func);
2289     }
2290 
2291     JS_MarkValue(rt, ctx->global_obj, mark_func);
2292     JS_MarkValue(rt, ctx->global_var_obj, mark_func);
2293 
2294     JS_MarkValue(rt, ctx->throw_type_error, mark_func);
2295     JS_MarkValue(rt, ctx->eval_obj, mark_func);
2296 
2297     JS_MarkValue(rt, ctx->array_proto_values, mark_func);
2298     for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
2299         JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);
2300     }
2301     for(i = 0; i < rt->class_count; i++) {
2302         JS_MarkValue(rt, ctx->class_proto[i], mark_func);
2303     }
2304     JS_MarkValue(rt, ctx->iterator_proto, mark_func);
2305     JS_MarkValue(rt, ctx->async_iterator_proto, mark_func);
2306     JS_MarkValue(rt, ctx->promise_ctor, mark_func);
2307     JS_MarkValue(rt, ctx->array_ctor, mark_func);
2308     JS_MarkValue(rt, ctx->regexp_ctor, mark_func);
2309     JS_MarkValue(rt, ctx->function_ctor, mark_func);
2310     JS_MarkValue(rt, ctx->function_proto, mark_func);
2311 
2312     if (ctx->array_shape)
2313         mark_func(rt, &ctx->array_shape->header);
2314 }
2315 
JS_FreeContext(JSContext * ctx)2316 void JS_FreeContext(JSContext *ctx)
2317 {
2318     JSRuntime *rt = ctx->rt;
2319     int i;
2320 
2321     if (--ctx->header.ref_count > 0)
2322         return;
2323     assert(ctx->header.ref_count == 0);
2324 
2325 #ifdef DUMP_ATOMS
2326     JS_DumpAtoms(ctx->rt);
2327 #endif
2328 #ifdef DUMP_SHAPES
2329     JS_DumpShapes(ctx->rt);
2330 #endif
2331 #ifdef DUMP_OBJECTS
2332     {
2333         struct list_head *el;
2334         JSGCObjectHeader *p;
2335         printf("JSObjects: {\n");
2336         JS_DumpObjectHeader(ctx->rt);
2337         list_for_each(el, &rt->gc_obj_list) {
2338             p = list_entry(el, JSGCObjectHeader, link);
2339             JS_DumpGCObject(rt, p);
2340         }
2341         printf("}\n");
2342     }
2343 #endif
2344 #ifdef DUMP_MEM
2345     {
2346         JSMemoryUsage stats;
2347         JS_ComputeMemoryUsage(rt, &stats);
2348         JS_DumpMemoryUsage(stdout, &stats, rt);
2349     }
2350 #endif
2351 
2352     js_free_modules(ctx, JS_FREE_MODULE_ALL);
2353 
2354     JS_FreeValue(ctx, ctx->global_obj);
2355     JS_FreeValue(ctx, ctx->global_var_obj);
2356 
2357     JS_FreeValue(ctx, ctx->throw_type_error);
2358     JS_FreeValue(ctx, ctx->eval_obj);
2359 
2360     JS_FreeValue(ctx, ctx->array_proto_values);
2361     for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
2362         JS_FreeValue(ctx, ctx->native_error_proto[i]);
2363     }
2364     for(i = 0; i < rt->class_count; i++) {
2365         JS_FreeValue(ctx, ctx->class_proto[i]);
2366     }
2367     js_free_rt(rt, ctx->class_proto);
2368     JS_FreeValue(ctx, ctx->iterator_proto);
2369     JS_FreeValue(ctx, ctx->async_iterator_proto);
2370     JS_FreeValue(ctx, ctx->promise_ctor);
2371     JS_FreeValue(ctx, ctx->array_ctor);
2372     JS_FreeValue(ctx, ctx->regexp_ctor);
2373     JS_FreeValue(ctx, ctx->function_ctor);
2374     JS_FreeValue(ctx, ctx->function_proto);
2375 
2376     js_free_shape_null(ctx->rt, ctx->array_shape);
2377 
2378     list_del(&ctx->link);
2379     remove_gc_object(&ctx->header);
2380     js_free_rt(ctx->rt, ctx);
2381 }
2382 
JS_GetRuntime(JSContext * ctx)2383 JSRuntime *JS_GetRuntime(JSContext *ctx)
2384 {
2385     return ctx->rt;
2386 }
2387 
update_stack_limit(JSRuntime * rt)2388 static void update_stack_limit(JSRuntime *rt)
2389 {
2390     if (rt->stack_size == 0) {
2391         rt->stack_limit = 0; /* no limit */
2392     } else {
2393         rt->stack_limit = rt->stack_top - rt->stack_size;
2394     }
2395 }
2396 
JS_SetMaxStackSize(JSRuntime * rt,size_t stack_size)2397 void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size)
2398 {
2399     rt->stack_size = stack_size;
2400     update_stack_limit(rt);
2401 }
2402 
JS_UpdateStackTop(JSRuntime * rt)2403 void JS_UpdateStackTop(JSRuntime *rt)
2404 {
2405     rt->stack_top = js_get_stack_pointer();
2406     update_stack_limit(rt);
2407 }
2408 
is_strict_mode(JSContext * ctx)2409 static inline BOOL is_strict_mode(JSContext *ctx)
2410 {
2411     JSStackFrame *sf = ctx->rt->current_stack_frame;
2412     return (sf && (sf->js_mode & JS_MODE_STRICT));
2413 }
2414 
2415 #ifdef CONFIG_BIGNUM
is_math_mode(JSContext * ctx)2416 static inline BOOL is_math_mode(JSContext *ctx)
2417 {
2418     JSStackFrame *sf = ctx->rt->current_stack_frame;
2419     return (sf && (sf->js_mode & JS_MODE_MATH));
2420 }
2421 #endif
2422 
2423 /* JSAtom support */
2424 
2425 #define JS_ATOM_TAG_INT (1U << 31)
2426 #define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1)
2427 #define JS_ATOM_MAX     ((1U << 30) - 1)
2428 
2429 /* return the max count from the hash size */
2430 #define JS_ATOM_COUNT_RESIZE(n) ((n) * 2)
2431 
__JS_AtomIsConst(JSAtom v)2432 static inline BOOL __JS_AtomIsConst(JSAtom v)
2433 {
2434 #if defined(DUMP_LEAKS) && DUMP_LEAKS > 1
2435         return (int32_t)v <= 0;
2436 #else
2437         return (int32_t)v < JS_ATOM_END;
2438 #endif
2439 }
2440 
__JS_AtomIsTaggedInt(JSAtom v)2441 static inline BOOL __JS_AtomIsTaggedInt(JSAtom v)
2442 {
2443     return (v & JS_ATOM_TAG_INT) != 0;
2444 }
2445 
__JS_AtomFromUInt32(uint32_t v)2446 static inline JSAtom __JS_AtomFromUInt32(uint32_t v)
2447 {
2448     return v | JS_ATOM_TAG_INT;
2449 }
2450 
__JS_AtomToUInt32(JSAtom atom)2451 static inline uint32_t __JS_AtomToUInt32(JSAtom atom)
2452 {
2453     return atom & ~JS_ATOM_TAG_INT;
2454 }
2455 
is_num(int c)2456 static inline int is_num(int c)
2457 {
2458     return c >= '0' && c <= '9';
2459 }
2460 
2461 /* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */
is_num_string(uint32_t * pval,const JSString * p)2462 static inline BOOL is_num_string(uint32_t *pval, const JSString *p)
2463 {
2464     uint32_t n;
2465     uint64_t n64;
2466     int c, i, len;
2467 
2468     len = p->len;
2469     if (len == 0 || len > 10)
2470         return FALSE;
2471     if (p->is_wide_char)
2472         c = p->u.str16[0];
2473     else
2474         c = p->u.str8[0];
2475     if (is_num(c)) {
2476         if (c == '0') {
2477             if (len != 1)
2478                 return FALSE;
2479             n = 0;
2480         } else {
2481             n = c - '0';
2482             for(i = 1; i < len; i++) {
2483                 if (p->is_wide_char)
2484                     c = p->u.str16[i];
2485                 else
2486                     c = p->u.str8[i];
2487                 if (!is_num(c))
2488                     return FALSE;
2489                 n64 = (uint64_t)n * 10 + (c - '0');
2490                 if ((n64 >> 32) != 0)
2491                     return FALSE;
2492                 n = n64;
2493             }
2494         }
2495         *pval = n;
2496         return TRUE;
2497     } else {
2498         return FALSE;
2499     }
2500 }
2501 
2502 /* XXX: could use faster version ? */
hash_string8(const uint8_t * str,size_t len,uint32_t h)2503 static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h)
2504 {
2505     size_t i;
2506 
2507     for(i = 0; i < len; i++)
2508         h = h * 263 + str[i];
2509     return h;
2510 }
2511 
hash_string16(const uint16_t * str,size_t len,uint32_t h)2512 static inline uint32_t hash_string16(const uint16_t *str,
2513                                      size_t len, uint32_t h)
2514 {
2515     size_t i;
2516 
2517     for(i = 0; i < len; i++)
2518         h = h * 263 + str[i];
2519     return h;
2520 }
2521 
hash_string(const JSString * str,uint32_t h)2522 static uint32_t hash_string(const JSString *str, uint32_t h)
2523 {
2524     if (str->is_wide_char)
2525         h = hash_string16(str->u.str16, str->len, h);
2526     else
2527         h = hash_string8(str->u.str8, str->len, h);
2528     return h;
2529 }
2530 
JS_DumpString(JSRuntime * rt,const JSString * p)2531 static __maybe_unused void JS_DumpString(JSRuntime *rt,
2532                                                   const JSString *p)
2533 {
2534     int i, c, sep;
2535 
2536     if (p == NULL) {
2537         printf("<null>");
2538         return;
2539     }
2540     printf("%d", p->header.ref_count);
2541     sep = (p->header.ref_count == 1) ? '\"' : '\'';
2542     putchar(sep);
2543     for(i = 0; i < p->len; i++) {
2544         if (p->is_wide_char)
2545             c = p->u.str16[i];
2546         else
2547             c = p->u.str8[i];
2548         if (c == sep || c == '\\') {
2549             putchar('\\');
2550             putchar(c);
2551         } else if (c >= ' ' && c <= 126) {
2552             putchar(c);
2553         } else if (c == '\n') {
2554             putchar('\\');
2555             putchar('n');
2556         } else {
2557             printf("\\u%04x", c);
2558         }
2559     }
2560     putchar(sep);
2561 }
2562 
JS_DumpAtoms(JSRuntime * rt)2563 static __maybe_unused void JS_DumpAtoms(JSRuntime *rt)
2564 {
2565     JSAtomStruct *p;
2566     int h, i;
2567     /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */
2568     printf("JSAtom count=%d size=%d hash_size=%d:\n",
2569            rt->atom_count, rt->atom_size, rt->atom_hash_size);
2570     printf("JSAtom hash table: {\n");
2571     for(i = 0; i < rt->atom_hash_size; i++) {
2572         h = rt->atom_hash[i];
2573         if (h) {
2574             printf("  %d:", i);
2575             while (h) {
2576                 p = rt->atom_array[h];
2577                 printf(" ");
2578                 JS_DumpString(rt, p);
2579                 h = p->hash_next;
2580             }
2581             printf("\n");
2582         }
2583     }
2584     printf("}\n");
2585     printf("JSAtom table: {\n");
2586     for(i = 0; i < rt->atom_size; i++) {
2587         p = rt->atom_array[i];
2588         if (!atom_is_free(p)) {
2589             printf("  %d: { %d %08x ", i, p->atom_type, p->hash);
2590             if (!(p->len == 0 && p->is_wide_char != 0))
2591                 JS_DumpString(rt, p);
2592             printf(" %d }\n", p->hash_next);
2593         }
2594     }
2595     printf("}\n");
2596 }
2597 
JS_ResizeAtomHash(JSRuntime * rt,int new_hash_size)2598 static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size)
2599 {
2600     JSAtomStruct *p;
2601     uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash;
2602 
2603     assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */
2604     new_hash_mask = new_hash_size - 1;
2605     new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size);
2606     if (!new_hash)
2607         return -1;
2608     for(i = 0; i < rt->atom_hash_size; i++) {
2609         h = rt->atom_hash[i];
2610         while (h != 0) {
2611             p = rt->atom_array[h];
2612             hash_next1 = p->hash_next;
2613             /* add in new hash table */
2614             j = p->hash & new_hash_mask;
2615             p->hash_next = new_hash[j];
2616             new_hash[j] = h;
2617             h = hash_next1;
2618         }
2619     }
2620     js_free_rt(rt, rt->atom_hash);
2621     rt->atom_hash = new_hash;
2622     rt->atom_hash_size = new_hash_size;
2623     rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size);
2624     //    JS_DumpAtoms(rt);
2625     return 0;
2626 }
2627 
JS_InitAtoms(JSRuntime * rt)2628 static int JS_InitAtoms(JSRuntime *rt)
2629 {
2630     int i, len, atom_type;
2631     const char *p;
2632 
2633     rt->atom_hash_size = 0;
2634     rt->atom_hash = NULL;
2635     rt->atom_count = 0;
2636     rt->atom_size = 0;
2637     rt->atom_free_index = 0;
2638     if (JS_ResizeAtomHash(rt, 256))     /* there are at least 195 predefined atoms */
2639         return -1;
2640 
2641     p = js_atom_init;
2642     for(i = 1; i < JS_ATOM_END; i++) {
2643         if (i == JS_ATOM_Private_brand)
2644             atom_type = JS_ATOM_TYPE_PRIVATE;
2645         else if (i >= JS_ATOM_Symbol_toPrimitive)
2646             atom_type = JS_ATOM_TYPE_SYMBOL;
2647         else
2648             atom_type = JS_ATOM_TYPE_STRING;
2649         len = strlen(p);
2650         if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL)
2651             return -1;
2652         p = p + len + 1;
2653     }
2654     return 0;
2655 }
2656 
JS_DupAtomRT(JSRuntime * rt,JSAtom v)2657 static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)
2658 {
2659     JSAtomStruct *p;
2660 
2661     if (!__JS_AtomIsConst(v)) {
2662         p = rt->atom_array[v];
2663         p->header.ref_count++;
2664     }
2665     return v;
2666 }
2667 
JS_DupAtom(JSContext * ctx,JSAtom v)2668 JSAtom JS_DupAtom(JSContext *ctx, JSAtom v)
2669 {
2670     JSRuntime *rt;
2671     JSAtomStruct *p;
2672 
2673     if (!__JS_AtomIsConst(v)) {
2674         rt = ctx->rt;
2675         p = rt->atom_array[v];
2676         p->header.ref_count++;
2677     }
2678     return v;
2679 }
2680 
JS_AtomGetKind(JSContext * ctx,JSAtom v)2681 static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v)
2682 {
2683     JSRuntime *rt;
2684     JSAtomStruct *p;
2685 
2686     rt = ctx->rt;
2687     if (__JS_AtomIsTaggedInt(v))
2688         return JS_ATOM_KIND_STRING;
2689     p = rt->atom_array[v];
2690     switch(p->atom_type) {
2691     case JS_ATOM_TYPE_STRING:
2692         return JS_ATOM_KIND_STRING;
2693     case JS_ATOM_TYPE_GLOBAL_SYMBOL:
2694         return JS_ATOM_KIND_SYMBOL;
2695     case JS_ATOM_TYPE_SYMBOL:
2696         switch(p->hash) {
2697         case JS_ATOM_HASH_SYMBOL:
2698             return JS_ATOM_KIND_SYMBOL;
2699         case JS_ATOM_HASH_PRIVATE:
2700             return JS_ATOM_KIND_PRIVATE;
2701         default:
2702             abort();
2703         }
2704     default:
2705         abort();
2706     }
2707 }
2708 
JS_AtomIsString(JSContext * ctx,JSAtom v)2709 static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v)
2710 {
2711     return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING;
2712 }
2713 
js_get_atom_index(JSRuntime * rt,JSAtomStruct * p)2714 static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)
2715 {
2716     uint32_t i = p->hash_next;  /* atom_index */
2717     if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
2718         JSAtomStruct *p1;
2719 
2720         i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)];
2721         p1 = rt->atom_array[i];
2722         while (p1 != p) {
2723             assert(i != 0);
2724             i = p1->hash_next;
2725             p1 = rt->atom_array[i];
2726         }
2727     }
2728     return i;
2729 }
2730 
2731 /* string case (internal). Return JS_ATOM_NULL if error. 'str' is
2732    freed. */
__JS_NewAtom(JSRuntime * rt,JSString * str,int atom_type)2733 static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
2734 {
2735     uint32_t h, h1, i;
2736     JSAtomStruct *p;
2737     int len;
2738 
2739 #if 0
2740     printf("__JS_NewAtom: ");  JS_DumpString(rt, str); printf("\n");
2741 #endif
2742     if (atom_type < JS_ATOM_TYPE_SYMBOL) {
2743         /* str is not NULL */
2744         if (str->atom_type == atom_type) {
2745             /* str is the atom, return its index */
2746             i = js_get_atom_index(rt, str);
2747             /* reduce string refcount and increase atom's unless constant */
2748             if (__JS_AtomIsConst(i))
2749                 str->header.ref_count--;
2750             return i;
2751         }
2752         /* try and locate an already registered atom */
2753         len = str->len;
2754         h = hash_string(str, atom_type);
2755         h &= JS_ATOM_HASH_MASK;
2756         h1 = h & (rt->atom_hash_size - 1);
2757         i = rt->atom_hash[h1];
2758         while (i != 0) {
2759             p = rt->atom_array[i];
2760             if (p->hash == h &&
2761                 p->atom_type == atom_type &&
2762                 p->len == len &&
2763                 js_string_memcmp(p, str, len) == 0) {
2764                 if (!__JS_AtomIsConst(i))
2765                     p->header.ref_count++;
2766                 goto done;
2767             }
2768             i = p->hash_next;
2769         }
2770     } else {
2771         h1 = 0; /* avoid warning */
2772         if (atom_type == JS_ATOM_TYPE_SYMBOL) {
2773             h = JS_ATOM_HASH_SYMBOL;
2774         } else {
2775             h = JS_ATOM_HASH_PRIVATE;
2776             atom_type = JS_ATOM_TYPE_SYMBOL;
2777         }
2778     }
2779 
2780     if (rt->atom_free_index == 0) {
2781         /* allow new atom entries */
2782         uint32_t new_size, start;
2783         JSAtomStruct **new_array;
2784 
2785         /* alloc new with size progression 3/2:
2786            4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092
2787            preallocating space for predefined atoms (at least 195).
2788          */
2789         new_size = max_int(211, rt->atom_size * 3 / 2);
2790         if (new_size > JS_ATOM_MAX)
2791             goto fail;
2792         /* XXX: should use realloc2 to use slack space */
2793         new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size);
2794         if (!new_array)
2795             goto fail;
2796         /* Note: the atom 0 is not used */
2797         start = rt->atom_size;
2798         if (start == 0) {
2799             /* JS_ATOM_NULL entry */
2800             p = js_mallocz_rt(rt, sizeof(JSAtomStruct));
2801             if (!p) {
2802                 js_free_rt(rt, new_array);
2803                 goto fail;
2804             }
2805             p->header.ref_count = 1;  /* not refcounted */
2806             p->atom_type = JS_ATOM_TYPE_SYMBOL;
2807 #ifdef DUMP_LEAKS
2808             list_add_tail(&p->link, &rt->string_list);
2809 #endif
2810             new_array[0] = p;
2811             rt->atom_count++;
2812             start = 1;
2813         }
2814         rt->atom_size = new_size;
2815         rt->atom_array = new_array;
2816         rt->atom_free_index = start;
2817         for(i = start; i < new_size; i++) {
2818             uint32_t next;
2819             if (i == (new_size - 1))
2820                 next = 0;
2821             else
2822                 next = i + 1;
2823             rt->atom_array[i] = atom_set_free(next);
2824         }
2825     }
2826 
2827     if (str) {
2828         if (str->atom_type == 0) {
2829             p = str;
2830             p->atom_type = atom_type;
2831         } else {
2832             p = js_malloc_rt(rt, sizeof(JSString) +
2833                              (str->len << str->is_wide_char) +
2834                              1 - str->is_wide_char);
2835             if (unlikely(!p))
2836                 goto fail;
2837             p->header.ref_count = 1;
2838             p->is_wide_char = str->is_wide_char;
2839             p->len = str->len;
2840 #ifdef DUMP_LEAKS
2841             list_add_tail(&p->link, &rt->string_list);
2842 #endif
2843             memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) +
2844                    1 - str->is_wide_char);
2845             js_free_string(rt, str);
2846         }
2847     } else {
2848         p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */
2849         if (!p)
2850             return JS_ATOM_NULL;
2851         p->header.ref_count = 1;
2852         p->is_wide_char = 1;    /* Hack to represent NULL as a JSString */
2853         p->len = 0;
2854 #ifdef DUMP_LEAKS
2855         list_add_tail(&p->link, &rt->string_list);
2856 #endif
2857     }
2858 
2859     /* use an already free entry */
2860     i = rt->atom_free_index;
2861     rt->atom_free_index = atom_get_free(rt->atom_array[i]);
2862     rt->atom_array[i] = p;
2863 
2864     p->hash = h;
2865     p->hash_next = i;   /* atom_index */
2866     p->atom_type = atom_type;
2867 
2868     rt->atom_count++;
2869 
2870     if (atom_type != JS_ATOM_TYPE_SYMBOL) {
2871         p->hash_next = rt->atom_hash[h1];
2872         rt->atom_hash[h1] = i;
2873         if (unlikely(rt->atom_count >= rt->atom_count_resize))
2874             JS_ResizeAtomHash(rt, rt->atom_hash_size * 2);
2875     }
2876 
2877     //    JS_DumpAtoms(rt);
2878     return i;
2879 
2880  fail:
2881     i = JS_ATOM_NULL;
2882  done:
2883     if (str)
2884         js_free_string(rt, str);
2885     return i;
2886 }
2887 
2888 /* only works with zero terminated 8 bit strings */
__JS_NewAtomInit(JSRuntime * rt,const char * str,int len,int atom_type)2889 static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
2890                                int atom_type)
2891 {
2892     JSString *p;
2893     p = js_alloc_string_rt(rt, len, 0);
2894     if (!p)
2895         return JS_ATOM_NULL;
2896     memcpy(p->u.str8, str, len);
2897     p->u.str8[len] = '\0';
2898     return __JS_NewAtom(rt, p, atom_type);
2899 }
2900 
__JS_FindAtom(JSRuntime * rt,const char * str,size_t len,int atom_type)2901 static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
2902                             int atom_type)
2903 {
2904     uint32_t h, h1, i;
2905     JSAtomStruct *p;
2906 
2907     h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING);
2908     h &= JS_ATOM_HASH_MASK;
2909     h1 = h & (rt->atom_hash_size - 1);
2910     i = rt->atom_hash[h1];
2911     while (i != 0) {
2912         p = rt->atom_array[i];
2913         if (p->hash == h &&
2914             p->atom_type == JS_ATOM_TYPE_STRING &&
2915             p->len == len &&
2916             p->is_wide_char == 0 &&
2917             memcmp(p->u.str8, str, len) == 0) {
2918             if (!__JS_AtomIsConst(i))
2919                 p->header.ref_count++;
2920             return i;
2921         }
2922         i = p->hash_next;
2923     }
2924     return JS_ATOM_NULL;
2925 }
2926 
JS_FreeAtomStruct(JSRuntime * rt,JSAtomStruct * p)2927 static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)
2928 {
2929 #if 0   /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */
2930     if (unlikely(i == JS_ATOM_NULL)) {
2931         p->header.ref_count = INT32_MAX / 2;
2932         return;
2933     }
2934 #endif
2935     uint32_t i = p->hash_next;  /* atom_index */
2936     if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
2937         JSAtomStruct *p0, *p1;
2938         uint32_t h0;
2939 
2940         h0 = p->hash & (rt->atom_hash_size - 1);
2941         i = rt->atom_hash[h0];
2942         p1 = rt->atom_array[i];
2943         if (p1 == p) {
2944             rt->atom_hash[h0] = p1->hash_next;
2945         } else {
2946             for(;;) {
2947                 assert(i != 0);
2948                 p0 = p1;
2949                 i = p1->hash_next;
2950                 p1 = rt->atom_array[i];
2951                 if (p1 == p) {
2952                     p0->hash_next = p1->hash_next;
2953                     break;
2954                 }
2955             }
2956         }
2957     }
2958     /* insert in free atom list */
2959     rt->atom_array[i] = atom_set_free(rt->atom_free_index);
2960     rt->atom_free_index = i;
2961     /* free the string structure */
2962 #ifdef DUMP_LEAKS
2963     list_del(&p->link);
2964 #endif
2965     js_free_rt(rt, p);
2966     rt->atom_count--;
2967     assert(rt->atom_count >= 0);
2968 }
2969 
__JS_FreeAtom(JSRuntime * rt,uint32_t i)2970 static void __JS_FreeAtom(JSRuntime *rt, uint32_t i)
2971 {
2972     JSAtomStruct *p;
2973 
2974     p = rt->atom_array[i];
2975     if (--p->header.ref_count > 0)
2976         return;
2977     JS_FreeAtomStruct(rt, p);
2978 }
2979 
2980 /* Warning: 'p' is freed */
JS_NewAtomStr(JSContext * ctx,JSString * p)2981 static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)
2982 {
2983     JSRuntime *rt = ctx->rt;
2984     uint32_t n;
2985     if (is_num_string(&n, p)) {
2986         if (n <= JS_ATOM_MAX_INT) {
2987             js_free_string(rt, p);
2988             return __JS_AtomFromUInt32(n);
2989         }
2990     }
2991     /* XXX: should generate an exception */
2992     return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING);
2993 }
2994 
JS_NewAtomLen(JSContext * ctx,const char * str,size_t len)2995 JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
2996 {
2997     JSValue val;
2998 
2999     if (len == 0 || !is_digit(*str)) {
3000         JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
3001         if (atom)
3002             return atom;
3003     }
3004     val = JS_NewStringLen(ctx, str, len);
3005     if (JS_IsException(val))
3006         return JS_ATOM_NULL;
3007     return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val));
3008 }
3009 
JS_NewAtom(JSContext * ctx,const char * str)3010 JSAtom JS_NewAtom(JSContext *ctx, const char *str)
3011 {
3012     return JS_NewAtomLen(ctx, str, strlen(str));
3013 }
3014 
JS_NewAtomUInt32(JSContext * ctx,uint32_t n)3015 JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
3016 {
3017     if (n <= JS_ATOM_MAX_INT) {
3018         return __JS_AtomFromUInt32(n);
3019     } else {
3020         char buf[11];
3021         JSValue val;
3022         snprintf(buf, sizeof(buf), "%u", n);
3023         val = JS_NewString(ctx, buf);
3024         if (JS_IsException(val))
3025             return JS_ATOM_NULL;
3026         return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
3027                             JS_ATOM_TYPE_STRING);
3028     }
3029 }
3030 
JS_NewAtomInt64(JSContext * ctx,int64_t n)3031 static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)
3032 {
3033     if ((uint64_t)n <= JS_ATOM_MAX_INT) {
3034         return __JS_AtomFromUInt32((uint32_t)n);
3035     } else {
3036         char buf[24];
3037         JSValue val;
3038         snprintf(buf, sizeof(buf), "%" PRId64 , n);
3039         val = JS_NewString(ctx, buf);
3040         if (JS_IsException(val))
3041             return JS_ATOM_NULL;
3042         return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
3043                             JS_ATOM_TYPE_STRING);
3044     }
3045 }
3046 
3047 /* 'p' is freed */
JS_NewSymbol(JSContext * ctx,JSString * p,int atom_type)3048 static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type)
3049 {
3050     JSRuntime *rt = ctx->rt;
3051     JSAtom atom;
3052     atom = __JS_NewAtom(rt, p, atom_type);
3053     if (atom == JS_ATOM_NULL)
3054         return JS_ThrowOutOfMemory(ctx);
3055     return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]);
3056 }
3057 
3058 /* descr must be a non-numeric string atom */
JS_NewSymbolFromAtom(JSContext * ctx,JSAtom descr,int atom_type)3059 static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
3060                                     int atom_type)
3061 {
3062     JSRuntime *rt = ctx->rt;
3063     JSString *p;
3064 
3065     assert(!__JS_AtomIsTaggedInt(descr));
3066     assert(descr < rt->atom_size);
3067     p = rt->atom_array[descr];
3068     JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
3069     return JS_NewSymbol(ctx, p, atom_type);
3070 }
3071 
3072 #define ATOM_GET_STR_BUF_SIZE 64
3073 
3074 /* Should only be used for debug. */
JS_AtomGetStrRT(JSRuntime * rt,char * buf,int buf_size,JSAtom atom)3075 static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size,
3076                                    JSAtom atom)
3077 {
3078     if (__JS_AtomIsTaggedInt(atom)) {
3079         snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom));
3080     } else {
3081         JSAtomStruct *p;
3082         assert(atom < rt->atom_size);
3083         if (atom == JS_ATOM_NULL) {
3084             snprintf(buf, buf_size, "<null>");
3085         } else {
3086             int i, c;
3087             char *q;
3088             JSString *str;
3089 
3090             q = buf;
3091             p = rt->atom_array[atom];
3092             assert(!atom_is_free(p));
3093             str = p;
3094             if (str) {
3095                 if (!str->is_wide_char) {
3096                     /* special case ASCII strings */
3097                     c = 0;
3098                     for(i = 0; i < str->len; i++) {
3099                         c |= str->u.str8[i];
3100                     }
3101                     if (c < 0x80)
3102                         return (const char *)str->u.str8;
3103                 }
3104                 for(i = 0; i < str->len; i++) {
3105                     if (str->is_wide_char)
3106                         c = str->u.str16[i];
3107                     else
3108                         c = str->u.str8[i];
3109                     if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX)
3110                         break;
3111                     if (c < 128) {
3112                         *q++ = c;
3113                     } else {
3114                         q += unicode_to_utf8((uint8_t *)q, c);
3115                     }
3116                 }
3117             }
3118             *q = '\0';
3119         }
3120     }
3121     return buf;
3122 }
3123 
JS_AtomGetStr(JSContext * ctx,char * buf,int buf_size,JSAtom atom)3124 static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom)
3125 {
3126     return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);
3127 }
3128 
__JS_AtomToValue(JSContext * ctx,JSAtom atom,BOOL force_string)3129 static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)
3130 {
3131     char buf[ATOM_GET_STR_BUF_SIZE];
3132 
3133     if (__JS_AtomIsTaggedInt(atom)) {
3134         snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom));
3135         return JS_NewString(ctx, buf);
3136     } else {
3137         JSRuntime *rt = ctx->rt;
3138         JSAtomStruct *p;
3139         assert(atom < rt->atom_size);
3140         p = rt->atom_array[atom];
3141         if (p->atom_type == JS_ATOM_TYPE_STRING) {
3142             goto ret_string;
3143         } else if (force_string) {
3144             if (p->len == 0 && p->is_wide_char != 0) {
3145                 /* no description string */
3146                 p = rt->atom_array[JS_ATOM_empty_string];
3147             }
3148         ret_string:
3149             return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
3150         } else {
3151             return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p));
3152         }
3153     }
3154 }
3155 
JS_AtomToValue(JSContext * ctx,JSAtom atom)3156 JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)
3157 {
3158     return __JS_AtomToValue(ctx, atom, FALSE);
3159 }
3160 
JS_AtomToString(JSContext * ctx,JSAtom atom)3161 JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)
3162 {
3163     return __JS_AtomToValue(ctx, atom, TRUE);
3164 }
3165 
3166 /* return TRUE if the atom is an array index (i.e. 0 <= index <=
3167    2^32-2 and return its value */
JS_AtomIsArrayIndex(JSContext * ctx,uint32_t * pval,JSAtom atom)3168 static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)
3169 {
3170     if (__JS_AtomIsTaggedInt(atom)) {
3171         *pval = __JS_AtomToUInt32(atom);
3172         return TRUE;
3173     } else {
3174         JSRuntime *rt = ctx->rt;
3175         JSAtomStruct *p;
3176         uint32_t val;
3177 
3178         assert(atom < rt->atom_size);
3179         p = rt->atom_array[atom];
3180         if (p->atom_type == JS_ATOM_TYPE_STRING &&
3181             is_num_string(&val, p) && val != -1) {
3182             *pval = val;
3183             return TRUE;
3184         } else {
3185             *pval = 0;
3186             return FALSE;
3187         }
3188     }
3189 }
3190 
3191 /* This test must be fast if atom is not a numeric index (e.g. a
3192    method name). Return JS_UNDEFINED if not a numeric
3193    index. JS_EXCEPTION can also be returned. */
JS_AtomIsNumericIndex1(JSContext * ctx,JSAtom atom)3194 static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
3195 {
3196     JSRuntime *rt = ctx->rt;
3197     JSAtomStruct *p1;
3198     JSString *p;
3199     int c, len, ret;
3200     JSValue num, str;
3201 
3202     if (__JS_AtomIsTaggedInt(atom))
3203         return JS_NewInt32(ctx, __JS_AtomToUInt32(atom));
3204     assert(atom < rt->atom_size);
3205     p1 = rt->atom_array[atom];
3206     if (p1->atom_type != JS_ATOM_TYPE_STRING)
3207         return JS_UNDEFINED;
3208     p = p1;
3209     len = p->len;
3210     if (p->is_wide_char) {
3211         const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len;
3212         if (r >= r_end)
3213             return JS_UNDEFINED;
3214         c = *r;
3215         if (c == '-') {
3216             if (r >= r_end)
3217                 return JS_UNDEFINED;
3218             r++;
3219             c = *r;
3220             /* -0 case is specific */
3221             if (c == '0' && len == 2)
3222                 goto minus_zero;
3223         }
3224         /* XXX: should test NaN, but the tests do not check it */
3225         if (!is_num(c)) {
3226             /* XXX: String should be normalized, therefore 8-bit only */
3227             const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' };
3228             if (!(c =='I' && (r_end - r) == 8 &&
3229                   !memcmp(r + 1, nfinity16, sizeof(nfinity16))))
3230                 return JS_UNDEFINED;
3231         }
3232     } else {
3233         const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len;
3234         if (r >= r_end)
3235             return JS_UNDEFINED;
3236         c = *r;
3237         if (c == '-') {
3238             if (r >= r_end)
3239                 return JS_UNDEFINED;
3240             r++;
3241             c = *r;
3242             /* -0 case is specific */
3243             if (c == '0' && len == 2) {
3244             minus_zero:
3245                 return __JS_NewFloat64(ctx, -0.0);
3246             }
3247         }
3248         if (!is_num(c)) {
3249             if (!(c =='I' && (r_end - r) == 8 &&
3250                   !memcmp(r + 1, "nfinity", 7)))
3251                 return JS_UNDEFINED;
3252         }
3253     }
3254     /* XXX: bignum: would be better to only accept integer to avoid
3255        relying on current floating point precision */
3256     /* this is ECMA CanonicalNumericIndexString primitive */
3257     num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p));
3258     if (JS_IsException(num))
3259         return num;
3260     str = JS_ToString(ctx, num);
3261     if (JS_IsException(str)) {
3262         JS_FreeValue(ctx, num);
3263         return str;
3264     }
3265     ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str));
3266     JS_FreeValue(ctx, str);
3267     if (ret == 0) {
3268         return num;
3269     } else {
3270         JS_FreeValue(ctx, num);
3271         return JS_UNDEFINED;
3272     }
3273 }
3274 
3275 /* return -1 if exception or TRUE/FALSE */
JS_AtomIsNumericIndex(JSContext * ctx,JSAtom atom)3276 static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)
3277 {
3278     JSValue num;
3279     num = JS_AtomIsNumericIndex1(ctx, atom);
3280     if (likely(JS_IsUndefined(num)))
3281         return FALSE;
3282     if (JS_IsException(num))
3283         return -1;
3284     JS_FreeValue(ctx, num);
3285     return TRUE;
3286 }
3287 
JS_FreeAtom(JSContext * ctx,JSAtom v)3288 void JS_FreeAtom(JSContext *ctx, JSAtom v)
3289 {
3290     if (!__JS_AtomIsConst(v))
3291         __JS_FreeAtom(ctx->rt, v);
3292 }
3293 
JS_FreeAtomRT(JSRuntime * rt,JSAtom v)3294 void JS_FreeAtomRT(JSRuntime *rt, JSAtom v)
3295 {
3296     if (!__JS_AtomIsConst(v))
3297         __JS_FreeAtom(rt, v);
3298 }
3299 
3300 /* return TRUE if 'v' is a symbol with a string description */
JS_AtomSymbolHasDescription(JSContext * ctx,JSAtom v)3301 static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)
3302 {
3303     JSRuntime *rt;
3304     JSAtomStruct *p;
3305 
3306     rt = ctx->rt;
3307     if (__JS_AtomIsTaggedInt(v))
3308         return FALSE;
3309     p = rt->atom_array[v];
3310     return (((p->atom_type == JS_ATOM_TYPE_SYMBOL &&
3311               p->hash == JS_ATOM_HASH_SYMBOL) ||
3312              p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) &&
3313             !(p->len == 0 && p->is_wide_char != 0));
3314 }
3315 
print_atom(JSContext * ctx,JSAtom atom)3316 static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom)
3317 {
3318     char buf[ATOM_GET_STR_BUF_SIZE];
3319     const char *p;
3320     int i;
3321 
3322     /* XXX: should handle embedded null characters */
3323     /* XXX: should move encoding code to JS_AtomGetStr */
3324     p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom);
3325     for (i = 0; p[i]; i++) {
3326         int c = (unsigned char)p[i];
3327         if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
3328               (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))
3329             break;
3330     }
3331     if (i > 0 && p[i] == '\0') {
3332         printf("%s", p);
3333     } else {
3334         putchar('"');
3335         printf("%.*s", i, p);
3336         for (; p[i]; i++) {
3337             int c = (unsigned char)p[i];
3338             if (c == '\"' || c == '\\') {
3339                 putchar('\\');
3340                 putchar(c);
3341             } else if (c >= ' ' && c <= 126) {
3342                 putchar(c);
3343             } else if (c == '\n') {
3344                 putchar('\\');
3345                 putchar('n');
3346             } else {
3347                 printf("\\u%04x", c);
3348             }
3349         }
3350         putchar('\"');
3351     }
3352 }
3353 
3354 /* free with JS_FreeCString() */
JS_AtomToCString(JSContext * ctx,JSAtom atom)3355 const char *JS_AtomToCString(JSContext *ctx, JSAtom atom)
3356 {
3357     JSValue str;
3358     const char *cstr;
3359 
3360     str = JS_AtomToString(ctx, atom);
3361     if (JS_IsException(str))
3362         return NULL;
3363     cstr = JS_ToCString(ctx, str);
3364     JS_FreeValue(ctx, str);
3365     return cstr;
3366 }
3367 
3368 /* return a string atom containing name concatenated with str1 */
js_atom_concat_str(JSContext * ctx,JSAtom name,const char * str1)3369 static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1)
3370 {
3371     JSValue str;
3372     JSAtom atom;
3373     const char *cstr;
3374     char *cstr2;
3375     size_t len, len1;
3376 
3377     str = JS_AtomToString(ctx, name);
3378     if (JS_IsException(str))
3379         return JS_ATOM_NULL;
3380     cstr = JS_ToCStringLen(ctx, &len, str);
3381     if (!cstr)
3382         goto fail;
3383     len1 = strlen(str1);
3384     cstr2 = js_malloc(ctx, len + len1 + 1);
3385     if (!cstr2)
3386         goto fail;
3387     memcpy(cstr2, cstr, len);
3388     memcpy(cstr2 + len, str1, len1);
3389     cstr2[len + len1] = '\0';
3390     atom = JS_NewAtomLen(ctx, cstr2, len + len1);
3391     js_free(ctx, cstr2);
3392     JS_FreeCString(ctx, cstr);
3393     JS_FreeValue(ctx, str);
3394     return atom;
3395  fail:
3396     JS_FreeCString(ctx, cstr);
3397     JS_FreeValue(ctx, str);
3398     return JS_ATOM_NULL;
3399 }
3400 
js_atom_concat_num(JSContext * ctx,JSAtom name,uint32_t n)3401 static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n)
3402 {
3403     char buf[16];
3404     snprintf(buf, sizeof(buf), "%u", n);
3405     return js_atom_concat_str(ctx, name, buf);
3406 }
3407 
JS_IsEmptyString(JSValueConst v)3408 static inline BOOL JS_IsEmptyString(JSValueConst v)
3409 {
3410     return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0;
3411 }
3412 
3413 /* JSClass support */
3414 
3415 /* a new class ID is allocated if *pclass_id != 0 */
JS_NewClassID(JSClassID * pclass_id)3416 JSClassID JS_NewClassID(JSClassID *pclass_id)
3417 {
3418     JSClassID class_id;
3419     /* XXX: make it thread safe */
3420     class_id = *pclass_id;
3421     if (class_id == 0) {
3422         class_id = js_class_id_alloc++;
3423         *pclass_id = class_id;
3424     }
3425     return class_id;
3426 }
3427 
JS_IsRegisteredClass(JSRuntime * rt,JSClassID class_id)3428 BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id)
3429 {
3430     return (class_id < rt->class_count &&
3431             rt->class_array[class_id].class_id != 0);
3432 }
3433 
3434 /* create a new object internal class. Return -1 if error, 0 if
3435    OK. The finalizer can be NULL if none is needed. */
JS_NewClass1(JSRuntime * rt,JSClassID class_id,const JSClassDef * class_def,JSAtom name)3436 static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
3437                         const JSClassDef *class_def, JSAtom name)
3438 {
3439     int new_size, i;
3440     JSClass *cl, *new_class_array;
3441     struct list_head *el;
3442 
3443     if (class_id >= (1 << 16))
3444         return -1;
3445     if (class_id < rt->class_count &&
3446         rt->class_array[class_id].class_id != 0)
3447         return -1;
3448 
3449     if (class_id >= rt->class_count) {
3450         new_size = max_int(JS_CLASS_INIT_COUNT,
3451                            max_int(class_id + 1, rt->class_count * 3 / 2));
3452 
3453         /* reallocate the context class prototype array, if any */
3454         list_for_each(el, &rt->context_list) {
3455             JSContext *ctx = list_entry(el, JSContext, link);
3456             JSValue *new_tab;
3457             new_tab = js_realloc_rt(rt, ctx->class_proto,
3458                                     sizeof(ctx->class_proto[0]) * new_size);
3459             if (!new_tab)
3460                 return -1;
3461             for(i = rt->class_count; i < new_size; i++)
3462                 new_tab[i] = JS_NULL;
3463             ctx->class_proto = new_tab;
3464         }
3465         /* reallocate the class array */
3466         new_class_array = js_realloc_rt(rt, rt->class_array,
3467                                         sizeof(JSClass) * new_size);
3468         if (!new_class_array)
3469             return -1;
3470         memset(new_class_array + rt->class_count, 0,
3471                (new_size - rt->class_count) * sizeof(JSClass));
3472         rt->class_array = new_class_array;
3473         rt->class_count = new_size;
3474     }
3475     cl = &rt->class_array[class_id];
3476     cl->class_id = class_id;
3477     cl->class_name = JS_DupAtomRT(rt, name);
3478     cl->finalizer = class_def->finalizer;
3479     cl->gc_mark = class_def->gc_mark;
3480     cl->call = class_def->call;
3481     cl->exotic = class_def->exotic;
3482     return 0;
3483 }
3484 
JS_NewClass(JSRuntime * rt,JSClassID class_id,const JSClassDef * class_def)3485 int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def)
3486 {
3487     int ret, len;
3488     JSAtom name;
3489 
3490     len = strlen(class_def->class_name);
3491     name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
3492     if (name == JS_ATOM_NULL) {
3493         name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
3494         if (name == JS_ATOM_NULL)
3495             return -1;
3496     }
3497     ret = JS_NewClass1(rt, class_id, class_def, name);
3498     JS_FreeAtomRT(rt, name);
3499     return ret;
3500 }
3501 
js_new_string8(JSContext * ctx,const uint8_t * buf,int len)3502 static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len)
3503 {
3504     JSString *str;
3505 
3506     if (len <= 0) {
3507         return JS_AtomToString(ctx, JS_ATOM_empty_string);
3508     }
3509     str = js_alloc_string(ctx, len, 0);
3510     if (!str)
3511         return JS_EXCEPTION;
3512     memcpy(str->u.str8, buf, len);
3513     str->u.str8[len] = '\0';
3514     return JS_MKPTR(JS_TAG_STRING, str);
3515 }
3516 
js_new_string16(JSContext * ctx,const uint16_t * buf,int len)3517 static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len)
3518 {
3519     JSString *str;
3520     str = js_alloc_string(ctx, len, 1);
3521     if (!str)
3522         return JS_EXCEPTION;
3523     memcpy(str->u.str16, buf, len * 2);
3524     return JS_MKPTR(JS_TAG_STRING, str);
3525 }
3526 
js_new_string_char(JSContext * ctx,uint16_t c)3527 static JSValue js_new_string_char(JSContext *ctx, uint16_t c)
3528 {
3529     if (c < 0x100) {
3530         uint8_t ch8 = c;
3531         return js_new_string8(ctx, &ch8, 1);
3532     } else {
3533         uint16_t ch16 = c;
3534         return js_new_string16(ctx, &ch16, 1);
3535     }
3536 }
3537 
js_sub_string(JSContext * ctx,JSString * p,int start,int end)3538 static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end)
3539 {
3540     int len = end - start;
3541     if (start == 0 && end == p->len) {
3542         return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
3543     }
3544     if (p->is_wide_char && len > 0) {
3545         JSString *str;
3546         int i;
3547         uint16_t c = 0;
3548         for (i = start; i < end; i++) {
3549             c |= p->u.str16[i];
3550         }
3551         if (c > 0xFF)
3552             return js_new_string16(ctx, p->u.str16 + start, len);
3553 
3554         str = js_alloc_string(ctx, len, 0);
3555         if (!str)
3556             return JS_EXCEPTION;
3557         for (i = 0; i < len; i++) {
3558             str->u.str8[i] = p->u.str16[start + i];
3559         }
3560         str->u.str8[len] = '\0';
3561         return JS_MKPTR(JS_TAG_STRING, str);
3562     } else {
3563         return js_new_string8(ctx, p->u.str8 + start, len);
3564     }
3565 }
3566 
3567 typedef struct StringBuffer {
3568     JSContext *ctx;
3569     JSString *str;
3570     int len;
3571     int size;
3572     int is_wide_char;
3573     int error_status;
3574 } StringBuffer;
3575 
3576 /* It is valid to call string_buffer_end() and all string_buffer functions even
3577    if string_buffer_init() or another string_buffer function returns an error.
3578    If the error_status is set, string_buffer_end() returns JS_EXCEPTION.
3579  */
string_buffer_init2(JSContext * ctx,StringBuffer * s,int size,int is_wide)3580 static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size,
3581                                int is_wide)
3582 {
3583     s->ctx = ctx;
3584     s->size = size;
3585     s->len = 0;
3586     s->is_wide_char = is_wide;
3587     s->error_status = 0;
3588     s->str = js_alloc_string(ctx, size, is_wide);
3589     if (unlikely(!s->str)) {
3590         s->size = 0;
3591         return s->error_status = -1;
3592     }
3593 #ifdef DUMP_LEAKS
3594     /* the StringBuffer may reallocate the JSString, only link it at the end */
3595     list_del(&s->str->link);
3596 #endif
3597     return 0;
3598 }
3599 
string_buffer_init(JSContext * ctx,StringBuffer * s,int size)3600 static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)
3601 {
3602     return string_buffer_init2(ctx, s, size, 0);
3603 }
3604 
string_buffer_free(StringBuffer * s)3605 static void string_buffer_free(StringBuffer *s)
3606 {
3607     js_free(s->ctx, s->str);
3608     s->str = NULL;
3609 }
3610 
string_buffer_set_error(StringBuffer * s)3611 static int string_buffer_set_error(StringBuffer *s)
3612 {
3613     js_free(s->ctx, s->str);
3614     s->str = NULL;
3615     s->size = 0;
3616     s->len = 0;
3617     return s->error_status = -1;
3618 }
3619 
string_buffer_widen(StringBuffer * s,int size)3620 static no_inline int string_buffer_widen(StringBuffer *s, int size)
3621 {
3622     JSString *str;
3623     size_t slack;
3624     int i;
3625 
3626     if (s->error_status)
3627         return -1;
3628 
3629     str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);
3630     if (!str)
3631         return string_buffer_set_error(s);
3632     size += slack >> 1;
3633     for(i = s->len; i-- > 0;) {
3634         str->u.str16[i] = str->u.str8[i];
3635     }
3636     s->is_wide_char = 1;
3637     s->size = size;
3638     s->str = str;
3639     return 0;
3640 }
3641 
string_buffer_realloc(StringBuffer * s,int new_len,int c)3642 static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c)
3643 {
3644     JSString *new_str;
3645     int new_size;
3646     size_t new_size_bytes, slack;
3647 
3648     if (s->error_status)
3649         return -1;
3650 
3651     if (new_len > JS_STRING_LEN_MAX) {
3652         JS_ThrowInternalError(s->ctx, "string too long");
3653         return string_buffer_set_error(s);
3654     }
3655     new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);
3656     if (!s->is_wide_char && c >= 0x100) {
3657         return string_buffer_widen(s, new_size);
3658     }
3659     new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;
3660     new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);
3661     if (!new_str)
3662         return string_buffer_set_error(s);
3663     new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);
3664     s->size = new_size;
3665     s->str = new_str;
3666     return 0;
3667 }
3668 
string_buffer_putc_slow(StringBuffer * s,uint32_t c)3669 static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c)
3670 {
3671     if (unlikely(s->len >= s->size)) {
3672         if (string_buffer_realloc(s, s->len + 1, c))
3673             return -1;
3674     }
3675     if (s->is_wide_char) {
3676         s->str->u.str16[s->len++] = c;
3677     } else if (c < 0x100) {
3678         s->str->u.str8[s->len++] = c;
3679     } else {
3680         if (string_buffer_widen(s, s->size))
3681             return -1;
3682         s->str->u.str16[s->len++] = c;
3683     }
3684     return 0;
3685 }
3686 
3687 /* 0 <= c <= 0xff */
string_buffer_putc8(StringBuffer * s,uint32_t c)3688 static int string_buffer_putc8(StringBuffer *s, uint32_t c)
3689 {
3690     if (unlikely(s->len >= s->size)) {
3691         if (string_buffer_realloc(s, s->len + 1, c))
3692             return -1;
3693     }
3694     if (s->is_wide_char) {
3695         s->str->u.str16[s->len++] = c;
3696     } else {
3697         s->str->u.str8[s->len++] = c;
3698     }
3699     return 0;
3700 }
3701 
3702 /* 0 <= c <= 0xffff */
string_buffer_putc16(StringBuffer * s,uint32_t c)3703 static int string_buffer_putc16(StringBuffer *s, uint32_t c)
3704 {
3705     if (likely(s->len < s->size)) {
3706         if (s->is_wide_char) {
3707             s->str->u.str16[s->len++] = c;
3708             return 0;
3709         } else if (c < 0x100) {
3710             s->str->u.str8[s->len++] = c;
3711             return 0;
3712         }
3713     }
3714     return string_buffer_putc_slow(s, c);
3715 }
3716 
3717 /* 0 <= c <= 0x10ffff */
string_buffer_putc(StringBuffer * s,uint32_t c)3718 static int string_buffer_putc(StringBuffer *s, uint32_t c)
3719 {
3720     if (unlikely(c >= 0x10000)) {
3721         /* surrogate pair */
3722         c -= 0x10000;
3723         if (string_buffer_putc16(s, (c >> 10) + 0xd800))
3724             return -1;
3725         c = (c & 0x3ff) + 0xdc00;
3726     }
3727     return string_buffer_putc16(s, c);
3728 }
3729 
string_get(const JSString * p,int idx)3730 static int string_get(const JSString *p, int idx) {
3731     return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx];
3732 }
3733 
string_getc(const JSString * p,int * pidx)3734 static int string_getc(const JSString *p, int *pidx)
3735 {
3736     int idx, c, c1;
3737     idx = *pidx;
3738     if (p->is_wide_char) {
3739         c = p->u.str16[idx++];
3740         if (c >= 0xd800 && c < 0xdc00 && idx < p->len) {
3741             c1 = p->u.str16[idx];
3742             if (c1 >= 0xdc00 && c1 < 0xe000) {
3743                 c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
3744                 idx++;
3745             }
3746         }
3747     } else {
3748         c = p->u.str8[idx++];
3749     }
3750     *pidx = idx;
3751     return c;
3752 }
3753 
string_buffer_write8(StringBuffer * s,const uint8_t * p,int len)3754 static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)
3755 {
3756     int i;
3757 
3758     if (s->len + len > s->size) {
3759         if (string_buffer_realloc(s, s->len + len, 0))
3760             return -1;
3761     }
3762     if (s->is_wide_char) {
3763         for (i = 0; i < len; i++) {
3764             s->str->u.str16[s->len + i] = p[i];
3765         }
3766         s->len += len;
3767     } else {
3768         memcpy(&s->str->u.str8[s->len], p, len);
3769         s->len += len;
3770     }
3771     return 0;
3772 }
3773 
string_buffer_write16(StringBuffer * s,const uint16_t * p,int len)3774 static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)
3775 {
3776     int c = 0, i;
3777 
3778     for (i = 0; i < len; i++) {
3779         c |= p[i];
3780     }
3781     if (s->len + len > s->size) {
3782         if (string_buffer_realloc(s, s->len + len, c))
3783             return -1;
3784     } else if (!s->is_wide_char && c >= 0x100) {
3785         if (string_buffer_widen(s, s->size))
3786             return -1;
3787     }
3788     if (s->is_wide_char) {
3789         memcpy(&s->str->u.str16[s->len], p, len << 1);
3790         s->len += len;
3791     } else {
3792         for (i = 0; i < len; i++) {
3793             s->str->u.str8[s->len + i] = p[i];
3794         }
3795         s->len += len;
3796     }
3797     return 0;
3798 }
3799 
3800 /* appending an ASCII string */
string_buffer_puts8(StringBuffer * s,const char * str)3801 static int string_buffer_puts8(StringBuffer *s, const char *str)
3802 {
3803     return string_buffer_write8(s, (const uint8_t *)str, strlen(str));
3804 }
3805 
string_buffer_concat(StringBuffer * s,const JSString * p,uint32_t from,uint32_t to)3806 static int string_buffer_concat(StringBuffer *s, const JSString *p,
3807                                 uint32_t from, uint32_t to)
3808 {
3809     if (to <= from)
3810         return 0;
3811     if (p->is_wide_char)
3812         return string_buffer_write16(s, p->u.str16 + from, to - from);
3813     else
3814         return string_buffer_write8(s, p->u.str8 + from, to - from);
3815 }
3816 
string_buffer_concat_value(StringBuffer * s,JSValueConst v)3817 static int string_buffer_concat_value(StringBuffer *s, JSValueConst v)
3818 {
3819     JSString *p;
3820     JSValue v1;
3821     int res;
3822 
3823     if (s->error_status) {
3824         /* prevent exception overload */
3825         return -1;
3826     }
3827     if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
3828         v1 = JS_ToString(s->ctx, v);
3829         if (JS_IsException(v1))
3830             return string_buffer_set_error(s);
3831         p = JS_VALUE_GET_STRING(v1);
3832         res = string_buffer_concat(s, p, 0, p->len);
3833         JS_FreeValue(s->ctx, v1);
3834         return res;
3835     }
3836     p = JS_VALUE_GET_STRING(v);
3837     return string_buffer_concat(s, p, 0, p->len);
3838 }
3839 
string_buffer_concat_value_free(StringBuffer * s,JSValue v)3840 static int string_buffer_concat_value_free(StringBuffer *s, JSValue v)
3841 {
3842     JSString *p;
3843     int res;
3844 
3845     if (s->error_status) {
3846         /* prevent exception overload */
3847         JS_FreeValue(s->ctx, v);
3848         return -1;
3849     }
3850     if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
3851         v = JS_ToStringFree(s->ctx, v);
3852         if (JS_IsException(v))
3853             return string_buffer_set_error(s);
3854     }
3855     p = JS_VALUE_GET_STRING(v);
3856     res = string_buffer_concat(s, p, 0, p->len);
3857     JS_FreeValue(s->ctx, v);
3858     return res;
3859 }
3860 
string_buffer_fill(StringBuffer * s,int c,int count)3861 static int string_buffer_fill(StringBuffer *s, int c, int count)
3862 {
3863     /* XXX: optimize */
3864     if (s->len + count > s->size) {
3865         if (string_buffer_realloc(s, s->len + count, c))
3866             return -1;
3867     }
3868     while (count-- > 0) {
3869         if (string_buffer_putc16(s, c))
3870             return -1;
3871     }
3872     return 0;
3873 }
3874 
string_buffer_end(StringBuffer * s)3875 static JSValue string_buffer_end(StringBuffer *s)
3876 {
3877     JSString *str;
3878     str = s->str;
3879     if (s->error_status)
3880         return JS_EXCEPTION;
3881     if (s->len == 0) {
3882         js_free(s->ctx, str);
3883         s->str = NULL;
3884         return JS_AtomToString(s->ctx, JS_ATOM_empty_string);
3885     }
3886     if (s->len < s->size) {
3887         /* smaller size so js_realloc should not fail, but OK if it does */
3888         /* XXX: should add some slack to avoid unnecessary calls */
3889         /* XXX: might need to use malloc+free to ensure smaller size */
3890         str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) +
3891                             (s->len << s->is_wide_char) + 1 - s->is_wide_char);
3892         if (str == NULL)
3893             str = s->str;
3894         s->str = str;
3895     }
3896     if (!s->is_wide_char)
3897         str->u.str8[s->len] = 0;
3898 #ifdef DUMP_LEAKS
3899     list_add_tail(&str->link, &s->ctx->rt->string_list);
3900 #endif
3901     str->is_wide_char = s->is_wide_char;
3902     str->len = s->len;
3903     s->str = NULL;
3904     return JS_MKPTR(JS_TAG_STRING, str);
3905 }
3906 
3907 /* create a string from a UTF-8 buffer */
JS_NewStringLen(JSContext * ctx,const char * buf,size_t buf_len)3908 JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
3909 {
3910     const uint8_t *p, *p_end, *p_start, *p_next;
3911     uint32_t c;
3912     StringBuffer b_s, *b = &b_s;
3913     size_t len1;
3914 
3915     p_start = (const uint8_t *)buf;
3916     p_end = p_start + buf_len;
3917     p = p_start;
3918     while (p < p_end && *p < 128)
3919         p++;
3920     len1 = p - p_start;
3921     if (len1 > JS_STRING_LEN_MAX)
3922         return JS_ThrowInternalError(ctx, "string too long");
3923     if (p == p_end) {
3924         /* ASCII string */
3925         return js_new_string8(ctx, (const uint8_t *)buf, buf_len);
3926     } else {
3927         if (string_buffer_init(ctx, b, buf_len))
3928             goto fail;
3929         string_buffer_write8(b, p_start, len1);
3930         while (p < p_end) {
3931             if (*p < 128) {
3932                 string_buffer_putc8(b, *p++);
3933             } else {
3934                 /* parse utf-8 sequence, return 0xFFFFFFFF for error */
3935                 c = unicode_from_utf8(p, p_end - p, &p_next);
3936                 if (c < 0x10000) {
3937                     p = p_next;
3938                 } else if (c <= 0x10FFFF) {
3939                     p = p_next;
3940                     /* surrogate pair */
3941                     c -= 0x10000;
3942                     string_buffer_putc16(b, (c >> 10) + 0xd800);
3943                     c = (c & 0x3ff) + 0xdc00;
3944                 } else {
3945                     /* invalid char */
3946                     c = 0xfffd;
3947                     /* skip the invalid chars */
3948                     /* XXX: seems incorrect. Why not just use c = *p++; ? */
3949                     while (p < p_end && (*p >= 0x80 && *p < 0xc0))
3950                         p++;
3951                     if (p < p_end) {
3952                         p++;
3953                         while (p < p_end && (*p >= 0x80 && *p < 0xc0))
3954                             p++;
3955                     }
3956                 }
3957                 string_buffer_putc16(b, c);
3958             }
3959         }
3960     }
3961     return string_buffer_end(b);
3962 
3963  fail:
3964     string_buffer_free(b);
3965     return JS_EXCEPTION;
3966 }
3967 
JS_ConcatString3(JSContext * ctx,const char * str1,JSValue str2,const char * str3)3968 static JSValue JS_ConcatString3(JSContext *ctx, const char *str1,
3969                                 JSValue str2, const char *str3)
3970 {
3971     StringBuffer b_s, *b = &b_s;
3972     int len1, len3;
3973     JSString *p;
3974 
3975     if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) {
3976         str2 = JS_ToStringFree(ctx, str2);
3977         if (JS_IsException(str2))
3978             goto fail;
3979     }
3980     p = JS_VALUE_GET_STRING(str2);
3981     len1 = strlen(str1);
3982     len3 = strlen(str3);
3983 
3984     if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char))
3985         goto fail;
3986 
3987     string_buffer_write8(b, (const uint8_t *)str1, len1);
3988     string_buffer_concat(b, p, 0, p->len);
3989     string_buffer_write8(b, (const uint8_t *)str3, len3);
3990 
3991     JS_FreeValue(ctx, str2);
3992     return string_buffer_end(b);
3993 
3994  fail:
3995     JS_FreeValue(ctx, str2);
3996     return JS_EXCEPTION;
3997 }
3998 
JS_NewString(JSContext * ctx,const char * str)3999 JSValue JS_NewString(JSContext *ctx, const char *str)
4000 {
4001     return JS_NewStringLen(ctx, str, strlen(str));
4002 }
4003 
JS_NewAtomString(JSContext * ctx,const char * str)4004 JSValue JS_NewAtomString(JSContext *ctx, const char *str)
4005 {
4006     JSAtom atom = JS_NewAtom(ctx, str);
4007     if (atom == JS_ATOM_NULL)
4008         return JS_EXCEPTION;
4009     JSValue val = JS_AtomToString(ctx, atom);
4010     JS_FreeAtom(ctx, atom);
4011     return val;
4012 }
4013 
4014 /* return (NULL, 0) if exception. */
4015 /* return pointer into a JSString with a live ref_count */
4016 /* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */
JS_ToCStringLen2(JSContext * ctx,size_t * plen,JSValueConst val1,BOOL cesu8)4017 const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8)
4018 {
4019     JSValue val;
4020     JSString *str, *str_new;
4021     int pos, len, c, c1;
4022     uint8_t *q;
4023 
4024     if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) {
4025         val = JS_ToString(ctx, val1);
4026         if (JS_IsException(val))
4027             goto fail;
4028     } else {
4029         val = JS_DupValue(ctx, val1);
4030     }
4031 
4032     str = JS_VALUE_GET_STRING(val);
4033     len = str->len;
4034     if (!str->is_wide_char) {
4035         const uint8_t *src = str->u.str8;
4036         int count;
4037 
4038         /* count the number of non-ASCII characters */
4039         /* Scanning the whole string is required for ASCII strings,
4040            and computing the number of non-ASCII bytes is less expensive
4041            than testing each byte, hence this method is faster for ASCII
4042            strings, which is the most common case.
4043          */
4044         count = 0;
4045         for (pos = 0; pos < len; pos++) {
4046             count += src[pos] >> 7;
4047         }
4048         if (count == 0) {
4049             if (plen)
4050                 *plen = len;
4051             return (const char *)src;
4052         }
4053         str_new = js_alloc_string(ctx, len + count, 0);
4054         if (!str_new)
4055             goto fail;
4056         q = str_new->u.str8;
4057         for (pos = 0; pos < len; pos++) {
4058             c = src[pos];
4059             if (c < 0x80) {
4060                 *q++ = c;
4061             } else {
4062                 *q++ = (c >> 6) | 0xc0;
4063                 *q++ = (c & 0x3f) | 0x80;
4064             }
4065         }
4066     } else {
4067         const uint16_t *src = str->u.str16;
4068         /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may
4069            produce 4 bytes but use 2 code points.
4070          */
4071         str_new = js_alloc_string(ctx, len * 3, 0);
4072         if (!str_new)
4073             goto fail;
4074         q = str_new->u.str8;
4075         pos = 0;
4076         while (pos < len) {
4077             c = src[pos++];
4078             if (c < 0x80) {
4079                 *q++ = c;
4080             } else {
4081                 if (c >= 0xd800 && c < 0xdc00) {
4082                     if (pos < len && !cesu8) {
4083                         c1 = src[pos];
4084                         if (c1 >= 0xdc00 && c1 < 0xe000) {
4085                             pos++;
4086                             /* surrogate pair */
4087                             c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
4088                         } else {
4089                             /* Keep unmatched surrogate code points */
4090                             /* c = 0xfffd; */ /* error */
4091                         }
4092                     } else {
4093                         /* Keep unmatched surrogate code points */
4094                         /* c = 0xfffd; */ /* error */
4095                     }
4096                 }
4097                 q += unicode_to_utf8(q, c);
4098             }
4099         }
4100     }
4101 
4102     *q = '\0';
4103     str_new->len = q - str_new->u.str8;
4104     JS_FreeValue(ctx, val);
4105     if (plen)
4106         *plen = str_new->len;
4107     return (const char *)str_new->u.str8;
4108  fail:
4109     if (plen)
4110         *plen = 0;
4111     return NULL;
4112 }
4113 
JS_FreeCString(JSContext * ctx,const char * ptr)4114 void JS_FreeCString(JSContext *ctx, const char *ptr)
4115 {
4116     JSString *p;
4117     if (!ptr)
4118         return;
4119     /* purposely removing constness */
4120     p = (JSString *)(void *)(ptr - offsetof(JSString, u));
4121     JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
4122 }
4123 
memcmp16_8(const uint16_t * src1,const uint8_t * src2,int len)4124 static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len)
4125 {
4126     int c, i;
4127     for(i = 0; i < len; i++) {
4128         c = src1[i] - src2[i];
4129         if (c != 0)
4130             return c;
4131     }
4132     return 0;
4133 }
4134 
memcmp16(const uint16_t * src1,const uint16_t * src2,int len)4135 static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len)
4136 {
4137     int c, i;
4138     for(i = 0; i < len; i++) {
4139         c = src1[i] - src2[i];
4140         if (c != 0)
4141             return c;
4142     }
4143     return 0;
4144 }
4145 
js_string_memcmp(const JSString * p1,const JSString * p2,int len)4146 static int js_string_memcmp(const JSString *p1, const JSString *p2, int len)
4147 {
4148     int res;
4149 
4150     if (likely(!p1->is_wide_char)) {
4151         if (likely(!p2->is_wide_char))
4152             res = memcmp(p1->u.str8, p2->u.str8, len);
4153         else
4154             res = -memcmp16_8(p2->u.str16, p1->u.str8, len);
4155     } else {
4156         if (!p2->is_wide_char)
4157             res = memcmp16_8(p1->u.str16, p2->u.str8, len);
4158         else
4159             res = memcmp16(p1->u.str16, p2->u.str16, len);
4160     }
4161     return res;
4162 }
4163 
4164 /* return < 0, 0 or > 0 */
js_string_compare(JSContext * ctx,const JSString * p1,const JSString * p2)4165 static int js_string_compare(JSContext *ctx,
4166                              const JSString *p1, const JSString *p2)
4167 {
4168     int res, len;
4169     len = min_int(p1->len, p2->len);
4170     res = js_string_memcmp(p1, p2, len);
4171     if (res == 0) {
4172         if (p1->len == p2->len)
4173             res = 0;
4174         else if (p1->len < p2->len)
4175             res = -1;
4176         else
4177             res = 1;
4178     }
4179     return res;
4180 }
4181 
copy_str16(uint16_t * dst,const JSString * p,int offset,int len)4182 static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len)
4183 {
4184     if (p->is_wide_char) {
4185         memcpy(dst, p->u.str16 + offset, len * 2);
4186     } else {
4187         const uint8_t *src1 = p->u.str8 + offset;
4188         int i;
4189 
4190         for(i = 0; i < len; i++)
4191             dst[i] = src1[i];
4192     }
4193 }
4194 
JS_ConcatString1(JSContext * ctx,const JSString * p1,const JSString * p2)4195 static JSValue JS_ConcatString1(JSContext *ctx,
4196                                 const JSString *p1, const JSString *p2)
4197 {
4198     JSString *p;
4199     uint32_t len;
4200     int is_wide_char;
4201 
4202     len = p1->len + p2->len;
4203     if (len > JS_STRING_LEN_MAX)
4204         return JS_ThrowInternalError(ctx, "string too long");
4205     is_wide_char = p1->is_wide_char | p2->is_wide_char;
4206     p = js_alloc_string(ctx, len, is_wide_char);
4207     if (!p)
4208         return JS_EXCEPTION;
4209     if (!is_wide_char) {
4210         memcpy(p->u.str8, p1->u.str8, p1->len);
4211         memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len);
4212         p->u.str8[len] = '\0';
4213     } else {
4214         copy_str16(p->u.str16, p1, 0, p1->len);
4215         copy_str16(p->u.str16 + p1->len, p2, 0, p2->len);
4216     }
4217     return JS_MKPTR(JS_TAG_STRING, p);
4218 }
4219 
4220 /* op1 and op2 are converted to strings. For convience, op1 or op2 =
4221    JS_EXCEPTION are accepted and return JS_EXCEPTION.  */
JS_ConcatString(JSContext * ctx,JSValue op1,JSValue op2)4222 static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2)
4223 {
4224     JSValue ret;
4225     JSString *p1, *p2;
4226 
4227     if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) {
4228         op1 = JS_ToStringFree(ctx, op1);
4229         if (JS_IsException(op1)) {
4230             JS_FreeValue(ctx, op2);
4231             return JS_EXCEPTION;
4232         }
4233     }
4234     if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) {
4235         op2 = JS_ToStringFree(ctx, op2);
4236         if (JS_IsException(op2)) {
4237             JS_FreeValue(ctx, op1);
4238             return JS_EXCEPTION;
4239         }
4240     }
4241     p1 = JS_VALUE_GET_STRING(op1);
4242     p2 = JS_VALUE_GET_STRING(op2);
4243 
4244     /* XXX: could also check if p1 is empty */
4245     if (p2->len == 0) {
4246         goto ret_op1;
4247     }
4248     if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char
4249     &&  js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) {
4250         /* Concatenate in place in available space at the end of p1 */
4251         if (p1->is_wide_char) {
4252             memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1);
4253             p1->len += p2->len;
4254         } else {
4255             memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len);
4256             p1->len += p2->len;
4257             p1->u.str8[p1->len] = '\0';
4258         }
4259     ret_op1:
4260         JS_FreeValue(ctx, op2);
4261         return op1;
4262     }
4263     ret = JS_ConcatString1(ctx, p1, p2);
4264     JS_FreeValue(ctx, op1);
4265     JS_FreeValue(ctx, op2);
4266     return ret;
4267 }
4268 
4269 /* Shape support */
4270 
get_shape_size(size_t hash_size,size_t prop_size)4271 static inline size_t get_shape_size(size_t hash_size, size_t prop_size)
4272 {
4273     return hash_size * sizeof(uint32_t) + sizeof(JSShape) +
4274         prop_size * sizeof(JSShapeProperty);
4275 }
4276 
get_shape_from_alloc(void * sh_alloc,size_t hash_size)4277 static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size)
4278 {
4279     return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size);
4280 }
4281 
prop_hash_end(JSShape * sh)4282 static inline uint32_t *prop_hash_end(JSShape *sh)
4283 {
4284     return (uint32_t *)sh;
4285 }
4286 
get_alloc_from_shape(JSShape * sh)4287 static inline void *get_alloc_from_shape(JSShape *sh)
4288 {
4289     return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1);
4290 }
4291 
get_shape_prop(JSShape * sh)4292 static inline JSShapeProperty *get_shape_prop(JSShape *sh)
4293 {
4294     return sh->prop;
4295 }
4296 
init_shape_hash(JSRuntime * rt)4297 static int init_shape_hash(JSRuntime *rt)
4298 {
4299     rt->shape_hash_bits = 4;   /* 16 shapes */
4300     rt->shape_hash_size = 1 << rt->shape_hash_bits;
4301     rt->shape_hash_count = 0;
4302     rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
4303                                    rt->shape_hash_size);
4304     if (!rt->shape_hash)
4305         return -1;
4306     return 0;
4307 }
4308 
4309 /* same magic hash multiplier as the Linux kernel */
shape_hash(uint32_t h,uint32_t val)4310 static uint32_t shape_hash(uint32_t h, uint32_t val)
4311 {
4312     return (h + val) * 0x9e370001;
4313 }
4314 
4315 /* truncate the shape hash to 'hash_bits' bits */
get_shape_hash(uint32_t h,int hash_bits)4316 static uint32_t get_shape_hash(uint32_t h, int hash_bits)
4317 {
4318     return h >> (32 - hash_bits);
4319 }
4320 
shape_initial_hash(JSObject * proto)4321 static uint32_t shape_initial_hash(JSObject *proto)
4322 {
4323     uint32_t h;
4324     h = shape_hash(1, (uintptr_t)proto);
4325     if (sizeof(proto) > 4)
4326         h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32);
4327     return h;
4328 }
4329 
resize_shape_hash(JSRuntime * rt,int new_shape_hash_bits)4330 static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits)
4331 {
4332     int new_shape_hash_size, i;
4333     uint32_t h;
4334     JSShape **new_shape_hash, *sh, *sh_next;
4335 
4336     new_shape_hash_size = 1 << new_shape_hash_bits;
4337     new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
4338                                    new_shape_hash_size);
4339     if (!new_shape_hash)
4340         return -1;
4341     for(i = 0; i < rt->shape_hash_size; i++) {
4342         for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) {
4343             sh_next = sh->shape_hash_next;
4344             h = get_shape_hash(sh->hash, new_shape_hash_bits);
4345             sh->shape_hash_next = new_shape_hash[h];
4346             new_shape_hash[h] = sh;
4347         }
4348     }
4349     js_free_rt(rt, rt->shape_hash);
4350     rt->shape_hash_bits = new_shape_hash_bits;
4351     rt->shape_hash_size = new_shape_hash_size;
4352     rt->shape_hash = new_shape_hash;
4353     return 0;
4354 }
4355 
js_shape_hash_link(JSRuntime * rt,JSShape * sh)4356 static void js_shape_hash_link(JSRuntime *rt, JSShape *sh)
4357 {
4358     uint32_t h;
4359     h = get_shape_hash(sh->hash, rt->shape_hash_bits);
4360     sh->shape_hash_next = rt->shape_hash[h];
4361     rt->shape_hash[h] = sh;
4362     rt->shape_hash_count++;
4363 }
4364 
js_shape_hash_unlink(JSRuntime * rt,JSShape * sh)4365 static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh)
4366 {
4367     uint32_t h;
4368     JSShape **psh;
4369 
4370     h = get_shape_hash(sh->hash, rt->shape_hash_bits);
4371     psh = &rt->shape_hash[h];
4372     while (*psh != sh)
4373         psh = &(*psh)->shape_hash_next;
4374     *psh = sh->shape_hash_next;
4375     rt->shape_hash_count--;
4376 }
4377 
4378 /* create a new empty shape with prototype 'proto' */
js_new_shape2(JSContext * ctx,JSObject * proto,int hash_size,int prop_size)4379 static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,
4380                                         int hash_size, int prop_size)
4381 {
4382     JSRuntime *rt = ctx->rt;
4383     void *sh_alloc;
4384     JSShape *sh;
4385 
4386     /* resize the shape hash table if necessary */
4387     if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) {
4388         resize_shape_hash(rt, rt->shape_hash_bits + 1);
4389     }
4390 
4391     sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size));
4392     if (!sh_alloc)
4393         return NULL;
4394     sh = get_shape_from_alloc(sh_alloc, hash_size);
4395     sh->header.ref_count = 1;
4396     add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
4397     if (proto)
4398         JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));
4399     sh->proto = proto;
4400     memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) *
4401            hash_size);
4402     sh->prop_hash_mask = hash_size - 1;
4403     sh->prop_size = prop_size;
4404     sh->prop_count = 0;
4405     sh->deleted_prop_count = 0;
4406 
4407     /* insert in the hash table */
4408     sh->hash = shape_initial_hash(proto);
4409     sh->is_hashed = TRUE;
4410     sh->has_small_array_index = FALSE;
4411     js_shape_hash_link(ctx->rt, sh);
4412     return sh;
4413 }
4414 
js_new_shape(JSContext * ctx,JSObject * proto)4415 static JSShape *js_new_shape(JSContext *ctx, JSObject *proto)
4416 {
4417     return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE,
4418                          JS_PROP_INITIAL_SIZE);
4419 }
4420 
4421 /* The shape is cloned. The new shape is not inserted in the shape
4422    hash table */
js_clone_shape(JSContext * ctx,JSShape * sh1)4423 static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)
4424 {
4425     JSShape *sh;
4426     void *sh_alloc, *sh_alloc1;
4427     size_t size;
4428     JSShapeProperty *pr;
4429     uint32_t i, hash_size;
4430 
4431     hash_size = sh1->prop_hash_mask + 1;
4432     size = get_shape_size(hash_size, sh1->prop_size);
4433     sh_alloc = js_malloc(ctx, size);
4434     if (!sh_alloc)
4435         return NULL;
4436     sh_alloc1 = get_alloc_from_shape(sh1);
4437     memcpy(sh_alloc, sh_alloc1, size);
4438     sh = get_shape_from_alloc(sh_alloc, hash_size);
4439     sh->header.ref_count = 1;
4440     add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
4441     sh->is_hashed = FALSE;
4442     if (sh->proto) {
4443         JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
4444     }
4445     for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
4446         JS_DupAtom(ctx, pr->atom);
4447     }
4448     return sh;
4449 }
4450 
js_dup_shape(JSShape * sh)4451 static JSShape *js_dup_shape(JSShape *sh)
4452 {
4453     sh->header.ref_count++;
4454     return sh;
4455 }
4456 
js_free_shape0(JSRuntime * rt,JSShape * sh)4457 static void js_free_shape0(JSRuntime *rt, JSShape *sh)
4458 {
4459     uint32_t i;
4460     JSShapeProperty *pr;
4461 
4462     assert(sh->header.ref_count == 0);
4463     if (sh->is_hashed)
4464         js_shape_hash_unlink(rt, sh);
4465     if (sh->proto != NULL) {
4466         JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
4467     }
4468     pr = get_shape_prop(sh);
4469     for(i = 0; i < sh->prop_count; i++) {
4470         JS_FreeAtomRT(rt, pr->atom);
4471         pr++;
4472     }
4473     remove_gc_object(&sh->header);
4474     js_free_rt(rt, get_alloc_from_shape(sh));
4475 }
4476 
js_free_shape(JSRuntime * rt,JSShape * sh)4477 static void js_free_shape(JSRuntime *rt, JSShape *sh)
4478 {
4479     if (unlikely(--sh->header.ref_count <= 0)) {
4480         js_free_shape0(rt, sh);
4481     }
4482 }
4483 
js_free_shape_null(JSRuntime * rt,JSShape * sh)4484 static void js_free_shape_null(JSRuntime *rt, JSShape *sh)
4485 {
4486     if (sh)
4487         js_free_shape(rt, sh);
4488 }
4489 
4490 /* make space to hold at least 'count' properties */
resize_properties(JSContext * ctx,JSShape ** psh,JSObject * p,uint32_t count)4491 static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
4492                                        JSObject *p, uint32_t count)
4493 {
4494     JSShape *sh;
4495     uint32_t new_size, new_hash_size, new_hash_mask, i;
4496     JSShapeProperty *pr;
4497     void *sh_alloc;
4498     intptr_t h;
4499 
4500     sh = *psh;
4501     new_size = max_int(count, sh->prop_size * 3 / 2);
4502     /* Reallocate prop array first to avoid crash or size inconsistency
4503        in case of memory allocation failure */
4504     if (p) {
4505         JSProperty *new_prop;
4506         new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
4507         if (unlikely(!new_prop))
4508             return -1;
4509         p->prop = new_prop;
4510     }
4511     new_hash_size = sh->prop_hash_mask + 1;
4512     while (new_hash_size < new_size)
4513         new_hash_size = 2 * new_hash_size;
4514     if (new_hash_size != (sh->prop_hash_mask + 1)) {
4515         JSShape *old_sh;
4516         /* resize the hash table and the properties */
4517         old_sh = sh;
4518         sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
4519         if (!sh_alloc)
4520             return -1;
4521         sh = get_shape_from_alloc(sh_alloc, new_hash_size);
4522         list_del(&old_sh->header.link);
4523         /* copy all the fields and the properties */
4524         memcpy(sh, old_sh,
4525                sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);
4526         list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
4527         new_hash_mask = new_hash_size - 1;
4528         sh->prop_hash_mask = new_hash_mask;
4529         memset(prop_hash_end(sh) - new_hash_size, 0,
4530                sizeof(prop_hash_end(sh)[0]) * new_hash_size);
4531         for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) {
4532             if (pr->atom != JS_ATOM_NULL) {
4533                 h = ((uintptr_t)pr->atom & new_hash_mask);
4534                 pr->hash_next = prop_hash_end(sh)[-h - 1];
4535                 prop_hash_end(sh)[-h - 1] = i + 1;
4536             }
4537         }
4538         js_free(ctx, get_alloc_from_shape(old_sh));
4539     } else {
4540         /* only resize the properties */
4541         list_del(&sh->header.link);
4542         sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh),
4543                               get_shape_size(new_hash_size, new_size));
4544         if (unlikely(!sh_alloc)) {
4545             /* insert again in the GC list */
4546             list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
4547             return -1;
4548         }
4549         sh = get_shape_from_alloc(sh_alloc, new_hash_size);
4550         list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
4551     }
4552     *psh = sh;
4553     sh->prop_size = new_size;
4554     return 0;
4555 }
4556 
4557 /* remove the deleted properties. */
compact_properties(JSContext * ctx,JSObject * p)4558 static int compact_properties(JSContext *ctx, JSObject *p)
4559 {
4560     JSShape *sh, *old_sh;
4561     void *sh_alloc;
4562     intptr_t h;
4563     uint32_t new_hash_size, i, j, new_hash_mask, new_size;
4564     JSShapeProperty *old_pr, *pr;
4565     JSProperty *prop, *new_prop;
4566 
4567     sh = p->shape;
4568     assert(!sh->is_hashed);
4569 
4570     new_size = max_int(JS_PROP_INITIAL_SIZE,
4571                        sh->prop_count - sh->deleted_prop_count);
4572     assert(new_size <= sh->prop_size);
4573 
4574     new_hash_size = sh->prop_hash_mask + 1;
4575     while ((new_hash_size / 2) >= new_size)
4576         new_hash_size = new_hash_size / 2;
4577     new_hash_mask = new_hash_size - 1;
4578 
4579     /* resize the hash table and the properties */
4580     old_sh = sh;
4581     sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
4582     if (!sh_alloc)
4583         return -1;
4584     sh = get_shape_from_alloc(sh_alloc, new_hash_size);
4585     list_del(&old_sh->header.link);
4586     memcpy(sh, old_sh, sizeof(JSShape));
4587     list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
4588 
4589     memset(prop_hash_end(sh) - new_hash_size, 0,
4590            sizeof(prop_hash_end(sh)[0]) * new_hash_size);
4591 
4592     j = 0;
4593     old_pr = old_sh->prop;
4594     pr = sh->prop;
4595     prop = p->prop;
4596     for(i = 0; i < sh->prop_count; i++) {
4597         if (old_pr->atom != JS_ATOM_NULL) {
4598             pr->atom = old_pr->atom;
4599             pr->flags = old_pr->flags;
4600             h = ((uintptr_t)old_pr->atom & new_hash_mask);
4601             pr->hash_next = prop_hash_end(sh)[-h - 1];
4602             prop_hash_end(sh)[-h - 1] = j + 1;
4603             prop[j] = prop[i];
4604             j++;
4605             pr++;
4606         }
4607         old_pr++;
4608     }
4609     assert(j == (sh->prop_count - sh->deleted_prop_count));
4610     sh->prop_hash_mask = new_hash_mask;
4611     sh->prop_size = new_size;
4612     sh->deleted_prop_count = 0;
4613     sh->prop_count = j;
4614 
4615     p->shape = sh;
4616     js_free(ctx, get_alloc_from_shape(old_sh));
4617 
4618     /* reduce the size of the object properties */
4619     new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
4620     if (new_prop)
4621         p->prop = new_prop;
4622     return 0;
4623 }
4624 
add_shape_property(JSContext * ctx,JSShape ** psh,JSObject * p,JSAtom atom,int prop_flags)4625 static int add_shape_property(JSContext *ctx, JSShape **psh,
4626                               JSObject *p, JSAtom atom, int prop_flags)
4627 {
4628     JSRuntime *rt = ctx->rt;
4629     JSShape *sh = *psh;
4630     JSShapeProperty *pr, *prop;
4631     uint32_t hash_mask, new_shape_hash = 0;
4632     intptr_t h;
4633 
4634     /* update the shape hash */
4635     if (sh->is_hashed) {
4636         js_shape_hash_unlink(rt, sh);
4637         new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags);
4638     }
4639 
4640     if (unlikely(sh->prop_count >= sh->prop_size)) {
4641         if (resize_properties(ctx, psh, p, sh->prop_count + 1)) {
4642             /* in case of error, reinsert in the hash table.
4643                sh is still valid if resize_properties() failed */
4644             if (sh->is_hashed)
4645                 js_shape_hash_link(rt, sh);
4646             return -1;
4647         }
4648         sh = *psh;
4649     }
4650     if (sh->is_hashed) {
4651         sh->hash = new_shape_hash;
4652         js_shape_hash_link(rt, sh);
4653     }
4654     /* Initialize the new shape property.
4655        The object property at p->prop[sh->prop_count] is uninitialized */
4656     prop = get_shape_prop(sh);
4657     pr = &prop[sh->prop_count++];
4658     pr->atom = JS_DupAtom(ctx, atom);
4659     pr->flags = prop_flags;
4660     sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom);
4661     /* add in hash table */
4662     hash_mask = sh->prop_hash_mask;
4663     h = atom & hash_mask;
4664     pr->hash_next = prop_hash_end(sh)[-h - 1];
4665     prop_hash_end(sh)[-h - 1] = sh->prop_count;
4666     return 0;
4667 }
4668 
4669 /* find a hashed empty shape matching the prototype. Return NULL if
4670    not found */
find_hashed_shape_proto(JSRuntime * rt,JSObject * proto)4671 static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto)
4672 {
4673     JSShape *sh1;
4674     uint32_t h, h1;
4675 
4676     h = shape_initial_hash(proto);
4677     h1 = get_shape_hash(h, rt->shape_hash_bits);
4678     for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
4679         if (sh1->hash == h &&
4680             sh1->proto == proto &&
4681             sh1->prop_count == 0) {
4682             return sh1;
4683         }
4684     }
4685     return NULL;
4686 }
4687 
4688 /* find a hashed shape matching sh + (prop, prop_flags). Return NULL if
4689    not found */
find_hashed_shape_prop(JSRuntime * rt,JSShape * sh,JSAtom atom,int prop_flags)4690 static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,
4691                                        JSAtom atom, int prop_flags)
4692 {
4693     JSShape *sh1;
4694     uint32_t h, h1, i, n;
4695 
4696     h = sh->hash;
4697     h = shape_hash(h, atom);
4698     h = shape_hash(h, prop_flags);
4699     h1 = get_shape_hash(h, rt->shape_hash_bits);
4700     for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
4701         /* we test the hash first so that the rest is done only if the
4702            shapes really match */
4703         if (sh1->hash == h &&
4704             sh1->proto == sh->proto &&
4705             sh1->prop_count == ((n = sh->prop_count) + 1)) {
4706             for(i = 0; i < n; i++) {
4707                 if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) ||
4708                     unlikely(sh1->prop[i].flags != sh->prop[i].flags))
4709                     goto next;
4710             }
4711             if (unlikely(sh1->prop[n].atom != atom) ||
4712                 unlikely(sh1->prop[n].flags != prop_flags))
4713                 goto next;
4714             return sh1;
4715         }
4716     next: ;
4717     }
4718     return NULL;
4719 }
4720 
JS_DumpShape(JSRuntime * rt,int i,JSShape * sh)4721 static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
4722 {
4723     char atom_buf[ATOM_GET_STR_BUF_SIZE];
4724     int j;
4725 
4726     /* XXX: should output readable class prototype */
4727     printf("%5d %3d%c %14p %5d %5d", i,
4728            sh->header.ref_count, " *"[sh->is_hashed],
4729            (void *)sh->proto, sh->prop_size, sh->prop_count);
4730     for(j = 0; j < sh->prop_count; j++) {
4731         printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf),
4732                                       sh->prop[j].atom));
4733     }
4734     printf("\n");
4735 }
4736 
JS_DumpShapes(JSRuntime * rt)4737 static __maybe_unused void JS_DumpShapes(JSRuntime *rt)
4738 {
4739     int i;
4740     JSShape *sh;
4741     struct list_head *el;
4742     JSObject *p;
4743     JSGCObjectHeader *gp;
4744 
4745     printf("JSShapes: {\n");
4746     printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS");
4747     for(i = 0; i < rt->shape_hash_size; i++) {
4748         for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
4749             JS_DumpShape(rt, i, sh);
4750             assert(sh->is_hashed);
4751         }
4752     }
4753     /* dump non-hashed shapes */
4754     list_for_each(el, &rt->gc_obj_list) {
4755         gp = list_entry(el, JSGCObjectHeader, link);
4756         if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
4757             p = (JSObject *)gp;
4758             if (!p->shape->is_hashed) {
4759                 JS_DumpShape(rt, -1, p->shape);
4760             }
4761         }
4762     }
4763     printf("}\n");
4764 }
4765 
JS_NewObjectFromShape(JSContext * ctx,JSShape * sh,JSClassID class_id)4766 static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id)
4767 {
4768     JSObject *p;
4769 
4770     js_trigger_gc(ctx->rt, sizeof(JSObject));
4771     p = js_malloc(ctx, sizeof(JSObject));
4772     if (unlikely(!p))
4773         goto fail;
4774     p->class_id = class_id;
4775     p->extensible = TRUE;
4776     p->free_mark = 0;
4777     p->is_exotic = 0;
4778     p->fast_array = 0;
4779     p->is_constructor = 0;
4780     p->is_uncatchable_error = 0;
4781     p->tmp_mark = 0;
4782     p->is_HTMLDDA = 0;
4783     p->first_weak_ref = NULL;
4784     p->u.opaque = NULL;
4785     p->shape = sh;
4786     p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);
4787     if (unlikely(!p->prop)) {
4788         js_free(ctx, p);
4789     fail:
4790         js_free_shape(ctx->rt, sh);
4791         return JS_EXCEPTION;
4792     }
4793 
4794     switch(class_id) {
4795     case JS_CLASS_OBJECT:
4796         break;
4797     case JS_CLASS_ARRAY:
4798         {
4799             JSProperty *pr;
4800             p->is_exotic = 1;
4801             p->fast_array = 1;
4802             p->u.array.u.values = NULL;
4803             p->u.array.count = 0;
4804             p->u.array.u1.size = 0;
4805             /* the length property is always the first one */
4806             if (likely(sh == ctx->array_shape)) {
4807                 pr = &p->prop[0];
4808             } else {
4809                 /* only used for the first array */
4810                 /* cannot fail */
4811                 pr = add_property(ctx, p, JS_ATOM_length,
4812                                   JS_PROP_WRITABLE | JS_PROP_LENGTH);
4813             }
4814             pr->u.value = JS_NewInt32(ctx, 0);
4815         }
4816         break;
4817     case JS_CLASS_C_FUNCTION:
4818         p->prop[0].u.value = JS_UNDEFINED;
4819         break;
4820     case JS_CLASS_ARGUMENTS:
4821     case JS_CLASS_UINT8C_ARRAY:
4822     case JS_CLASS_INT8_ARRAY:
4823     case JS_CLASS_UINT8_ARRAY:
4824     case JS_CLASS_INT16_ARRAY:
4825     case JS_CLASS_UINT16_ARRAY:
4826     case JS_CLASS_INT32_ARRAY:
4827     case JS_CLASS_UINT32_ARRAY:
4828 #ifdef CONFIG_BIGNUM
4829     case JS_CLASS_BIG_INT64_ARRAY:
4830     case JS_CLASS_BIG_UINT64_ARRAY:
4831 #endif
4832     case JS_CLASS_FLOAT32_ARRAY:
4833     case JS_CLASS_FLOAT64_ARRAY:
4834         p->is_exotic = 1;
4835         p->fast_array = 1;
4836         p->u.array.u.ptr = NULL;
4837         p->u.array.count = 0;
4838         break;
4839     case JS_CLASS_DATAVIEW:
4840         p->u.array.u.ptr = NULL;
4841         p->u.array.count = 0;
4842         break;
4843     case JS_CLASS_NUMBER:
4844     case JS_CLASS_STRING:
4845     case JS_CLASS_BOOLEAN:
4846     case JS_CLASS_SYMBOL:
4847     case JS_CLASS_DATE:
4848 #ifdef CONFIG_BIGNUM
4849     case JS_CLASS_BIG_INT:
4850     case JS_CLASS_BIG_FLOAT:
4851     case JS_CLASS_BIG_DECIMAL:
4852 #endif
4853         p->u.object_data = JS_UNDEFINED;
4854         goto set_exotic;
4855     case JS_CLASS_REGEXP:
4856         p->u.regexp.pattern = NULL;
4857         p->u.regexp.bytecode = NULL;
4858         goto set_exotic;
4859     default:
4860     set_exotic:
4861         if (ctx->rt->class_array[class_id].exotic) {
4862             p->is_exotic = 1;
4863         }
4864         break;
4865     }
4866     p->header.ref_count = 1;
4867     add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT);
4868     return JS_MKPTR(JS_TAG_OBJECT, p);
4869 }
4870 
get_proto_obj(JSValueConst proto_val)4871 static JSObject *get_proto_obj(JSValueConst proto_val)
4872 {
4873     if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT)
4874         return NULL;
4875     else
4876         return JS_VALUE_GET_OBJ(proto_val);
4877 }
4878 
4879 /* WARNING: proto must be an object or JS_NULL */
JS_NewObjectProtoClass(JSContext * ctx,JSValueConst proto_val,JSClassID class_id)4880 JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val,
4881                                JSClassID class_id)
4882 {
4883     JSShape *sh;
4884     JSObject *proto;
4885 
4886     proto = get_proto_obj(proto_val);
4887     sh = find_hashed_shape_proto(ctx->rt, proto);
4888     if (likely(sh)) {
4889         sh = js_dup_shape(sh);
4890     } else {
4891         sh = js_new_shape(ctx, proto);
4892         if (!sh)
4893             return JS_EXCEPTION;
4894     }
4895     return JS_NewObjectFromShape(ctx, sh, class_id);
4896 }
4897 
4898 #if 0
4899 static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj)
4900 {
4901     JSObject *p;
4902 
4903     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
4904         p = JS_VALUE_GET_OBJ(obj);
4905         switch(p->class_id) {
4906         case JS_CLASS_NUMBER:
4907         case JS_CLASS_STRING:
4908         case JS_CLASS_BOOLEAN:
4909         case JS_CLASS_SYMBOL:
4910         case JS_CLASS_DATE:
4911 #ifdef CONFIG_BIGNUM
4912         case JS_CLASS_BIG_INT:
4913         case JS_CLASS_BIG_FLOAT:
4914         case JS_CLASS_BIG_DECIMAL:
4915 #endif
4916             return JS_DupValue(ctx, p->u.object_data);
4917         }
4918     }
4919     return JS_UNDEFINED;
4920 }
4921 #endif
4922 
JS_SetObjectData(JSContext * ctx,JSValueConst obj,JSValue val)4923 static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val)
4924 {
4925     JSObject *p;
4926 
4927     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
4928         p = JS_VALUE_GET_OBJ(obj);
4929         switch(p->class_id) {
4930         case JS_CLASS_NUMBER:
4931         case JS_CLASS_STRING:
4932         case JS_CLASS_BOOLEAN:
4933         case JS_CLASS_SYMBOL:
4934         case JS_CLASS_DATE:
4935 #ifdef CONFIG_BIGNUM
4936         case JS_CLASS_BIG_INT:
4937         case JS_CLASS_BIG_FLOAT:
4938         case JS_CLASS_BIG_DECIMAL:
4939 #endif
4940             JS_FreeValue(ctx, p->u.object_data);
4941             p->u.object_data = val;
4942             return 0;
4943         }
4944     }
4945     JS_FreeValue(ctx, val);
4946     if (!JS_IsException(obj))
4947         JS_ThrowTypeError(ctx, "invalid object type");
4948     return -1;
4949 }
4950 
JS_NewObjectClass(JSContext * ctx,int class_id)4951 JSValue JS_NewObjectClass(JSContext *ctx, int class_id)
4952 {
4953     return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id);
4954 }
4955 
JS_NewObjectProto(JSContext * ctx,JSValueConst proto)4956 JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto)
4957 {
4958     return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT);
4959 }
4960 
JS_NewArray(JSContext * ctx)4961 JSValue JS_NewArray(JSContext *ctx)
4962 {
4963     return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape),
4964                                  JS_CLASS_ARRAY);
4965 }
4966 
JS_NewObject(JSContext * ctx)4967 JSValue JS_NewObject(JSContext *ctx)
4968 {
4969     /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */
4970     return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT);
4971 }
4972 
js_function_set_properties(JSContext * ctx,JSValueConst func_obj,JSAtom name,int len)4973 static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj,
4974                                        JSAtom name, int len)
4975 {
4976     /* ES6 feature non compatible with ES5.1: length is configurable */
4977     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len),
4978                            JS_PROP_CONFIGURABLE);
4979     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name,
4980                            JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE);
4981 }
4982 
js_class_has_bytecode(JSClassID class_id)4983 static BOOL js_class_has_bytecode(JSClassID class_id)
4984 {
4985     return (class_id == JS_CLASS_BYTECODE_FUNCTION ||
4986             class_id == JS_CLASS_GENERATOR_FUNCTION ||
4987             class_id == JS_CLASS_ASYNC_FUNCTION ||
4988             class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION);
4989 }
4990 
4991 /* return NULL without exception if not a function or no bytecode */
JS_GetFunctionBytecode(JSValueConst val)4992 static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val)
4993 {
4994     JSObject *p;
4995     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
4996         return NULL;
4997     p = JS_VALUE_GET_OBJ(val);
4998     if (!js_class_has_bytecode(p->class_id))
4999         return NULL;
5000     return p->u.func.function_bytecode;
5001 }
5002 
js_method_set_home_object(JSContext * ctx,JSValueConst func_obj,JSValueConst home_obj)5003 static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj,
5004                                       JSValueConst home_obj)
5005 {
5006     JSObject *p, *p1;
5007     JSFunctionBytecode *b;
5008 
5009     if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
5010         return;
5011     p = JS_VALUE_GET_OBJ(func_obj);
5012     if (!js_class_has_bytecode(p->class_id))
5013         return;
5014     b = p->u.func.function_bytecode;
5015     if (b->need_home_object) {
5016         p1 = p->u.func.home_object;
5017         if (p1) {
5018             JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
5019         }
5020         if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT)
5021             p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj));
5022         else
5023             p1 = NULL;
5024         p->u.func.home_object = p1;
5025     }
5026 }
5027 
js_get_function_name(JSContext * ctx,JSAtom name)5028 static JSValue js_get_function_name(JSContext *ctx, JSAtom name)
5029 {
5030     JSValue name_str;
5031 
5032     name_str = JS_AtomToString(ctx, name);
5033     if (JS_AtomSymbolHasDescription(ctx, name)) {
5034         name_str = JS_ConcatString3(ctx, "[", name_str, "]");
5035     }
5036     return name_str;
5037 }
5038 
5039 /* Modify the name of a method according to the atom and
5040    'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and
5041    JS_PROP_HAS_SET. Also set the home object of the method.
5042    Return < 0 if exception. */
js_method_set_properties(JSContext * ctx,JSValueConst func_obj,JSAtom name,int flags,JSValueConst home_obj)5043 static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj,
5044                                     JSAtom name, int flags, JSValueConst home_obj)
5045 {
5046     JSValue name_str;
5047 
5048     name_str = js_get_function_name(ctx, name);
5049     if (flags & JS_PROP_HAS_GET) {
5050         name_str = JS_ConcatString3(ctx, "get ", name_str, "");
5051     } else if (flags & JS_PROP_HAS_SET) {
5052         name_str = JS_ConcatString3(ctx, "set ", name_str, "");
5053     }
5054     if (JS_IsException(name_str))
5055         return -1;
5056     if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str,
5057                                JS_PROP_CONFIGURABLE) < 0)
5058         return -1;
5059     js_method_set_home_object(ctx, func_obj, home_obj);
5060     return 0;
5061 }
5062 
5063 /* Note: at least 'length' arguments will be readable in 'argv' */
JS_NewCFunction3(JSContext * ctx,JSCFunction * func,const char * name,int length,JSCFunctionEnum cproto,int magic,JSValueConst proto_val)5064 static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func,
5065                                 const char *name,
5066                                 int length, JSCFunctionEnum cproto, int magic,
5067                                 JSValueConst proto_val)
5068 {
5069     JSValue func_obj;
5070     JSObject *p;
5071     JSAtom name_atom;
5072 
5073     func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION);
5074     if (JS_IsException(func_obj))
5075         return func_obj;
5076     p = JS_VALUE_GET_OBJ(func_obj);
5077     p->u.cfunc.realm = JS_DupContext(ctx);
5078     p->u.cfunc.c_function.generic = func;
5079     p->u.cfunc.length = length;
5080     p->u.cfunc.cproto = cproto;
5081     p->u.cfunc.magic = magic;
5082     p->is_constructor = (cproto == JS_CFUNC_constructor ||
5083                          cproto == JS_CFUNC_constructor_magic ||
5084                          cproto == JS_CFUNC_constructor_or_func ||
5085                          cproto == JS_CFUNC_constructor_or_func_magic);
5086     if (!name)
5087         name = "";
5088     name_atom = JS_NewAtom(ctx, name);
5089     js_function_set_properties(ctx, func_obj, name_atom, length);
5090     JS_FreeAtom(ctx, name_atom);
5091     return func_obj;
5092 }
5093 
5094 /* Note: at least 'length' arguments will be readable in 'argv' */
JS_NewCFunction2(JSContext * ctx,JSCFunction * func,const char * name,int length,JSCFunctionEnum cproto,int magic)5095 JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,
5096                          const char *name,
5097                          int length, JSCFunctionEnum cproto, int magic)
5098 {
5099     return JS_NewCFunction3(ctx, func, name, length, cproto, magic,
5100                             ctx->function_proto);
5101 }
5102 
5103 typedef struct JSCFunctionDataRecord {
5104     JSCFunctionData *func;
5105     uint8_t length;
5106     uint8_t data_len;
5107     uint16_t magic;
5108     JSValue data[0];
5109 } JSCFunctionDataRecord;
5110 
js_c_function_data_finalizer(JSRuntime * rt,JSValue val)5111 static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val)
5112 {
5113     JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
5114     int i;
5115 
5116     if (s) {
5117         for(i = 0; i < s->data_len; i++) {
5118             JS_FreeValueRT(rt, s->data[i]);
5119         }
5120         js_free_rt(rt, s);
5121     }
5122 }
5123 
js_c_function_data_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5124 static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
5125                                     JS_MarkFunc *mark_func)
5126 {
5127     JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
5128     int i;
5129 
5130     if (s) {
5131         for(i = 0; i < s->data_len; i++) {
5132             JS_MarkValue(rt, s->data[i], mark_func);
5133         }
5134     }
5135 }
5136 
js_c_function_data_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_val,int argc,JSValueConst * argv,int flags)5137 static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
5138                                        JSValueConst this_val,
5139                                        int argc, JSValueConst *argv, int flags)
5140 {
5141     JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA);
5142     JSValueConst *arg_buf;
5143     int i;
5144 
5145     /* XXX: could add the function on the stack for debug */
5146     if (unlikely(argc < s->length)) {
5147         arg_buf = alloca(sizeof(arg_buf[0]) * s->length);
5148         for(i = 0; i < argc; i++)
5149             arg_buf[i] = argv[i];
5150         for(i = argc; i < s->length; i++)
5151             arg_buf[i] = JS_UNDEFINED;
5152     } else {
5153         arg_buf = argv;
5154     }
5155 
5156     return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data);
5157 }
5158 
JS_NewCFunctionData(JSContext * ctx,JSCFunctionData * func,int length,int magic,int data_len,JSValueConst * data)5159 JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,
5160                             int length, int magic, int data_len,
5161                             JSValueConst *data)
5162 {
5163     JSCFunctionDataRecord *s;
5164     JSValue func_obj;
5165     int i;
5166 
5167     func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
5168                                       JS_CLASS_C_FUNCTION_DATA);
5169     if (JS_IsException(func_obj))
5170         return func_obj;
5171     s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue));
5172     if (!s) {
5173         JS_FreeValue(ctx, func_obj);
5174         return JS_EXCEPTION;
5175     }
5176     s->func = func;
5177     s->length = length;
5178     s->data_len = data_len;
5179     s->magic = magic;
5180     for(i = 0; i < data_len; i++)
5181         s->data[i] = JS_DupValue(ctx, data[i]);
5182     JS_SetOpaque(func_obj, s);
5183     js_function_set_properties(ctx, func_obj,
5184                                JS_ATOM_empty_string, length);
5185     return func_obj;
5186 }
5187 
js_autoinit_get_realm(JSProperty * pr)5188 static JSContext *js_autoinit_get_realm(JSProperty *pr)
5189 {
5190     return (JSContext *)(pr->u.init.realm_and_id & ~3);
5191 }
5192 
js_autoinit_get_id(JSProperty * pr)5193 static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr)
5194 {
5195     return pr->u.init.realm_and_id & 3;
5196 }
5197 
js_autoinit_free(JSRuntime * rt,JSProperty * pr)5198 static void js_autoinit_free(JSRuntime *rt, JSProperty *pr)
5199 {
5200     JS_FreeContext(js_autoinit_get_realm(pr));
5201 }
5202 
js_autoinit_mark(JSRuntime * rt,JSProperty * pr,JS_MarkFunc * mark_func)5203 static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,
5204                              JS_MarkFunc *mark_func)
5205 {
5206     mark_func(rt, &js_autoinit_get_realm(pr)->header);
5207 }
5208 
free_property(JSRuntime * rt,JSProperty * pr,int prop_flags)5209 static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)
5210 {
5211     if (unlikely(prop_flags & JS_PROP_TMASK)) {
5212         if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
5213             if (pr->u.getset.getter)
5214                 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
5215             if (pr->u.getset.setter)
5216                 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
5217         } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
5218             free_var_ref(rt, pr->u.var_ref);
5219         } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
5220             js_autoinit_free(rt, pr);
5221         }
5222     } else {
5223         JS_FreeValueRT(rt, pr->u.value);
5224     }
5225 }
5226 
find_own_property1(JSObject * p,JSAtom atom)5227 static force_inline JSShapeProperty *find_own_property1(JSObject *p,
5228                                                         JSAtom atom)
5229 {
5230     JSShape *sh;
5231     JSShapeProperty *pr, *prop;
5232     intptr_t h;
5233     sh = p->shape;
5234     h = (uintptr_t)atom & sh->prop_hash_mask;
5235     h = prop_hash_end(sh)[-h - 1];
5236     prop = get_shape_prop(sh);
5237     while (h) {
5238         pr = &prop[h - 1];
5239         if (likely(pr->atom == atom)) {
5240             return pr;
5241         }
5242         h = pr->hash_next;
5243     }
5244     return NULL;
5245 }
5246 
find_own_property(JSProperty ** ppr,JSObject * p,JSAtom atom)5247 static force_inline JSShapeProperty *find_own_property(JSProperty **ppr,
5248                                                        JSObject *p,
5249                                                        JSAtom atom)
5250 {
5251     JSShape *sh;
5252     JSShapeProperty *pr, *prop;
5253     intptr_t h;
5254     sh = p->shape;
5255     h = (uintptr_t)atom & sh->prop_hash_mask;
5256     h = prop_hash_end(sh)[-h - 1];
5257     prop = get_shape_prop(sh);
5258     while (h) {
5259         pr = &prop[h - 1];
5260         if (likely(pr->atom == atom)) {
5261             *ppr = &p->prop[h - 1];
5262             /* the compiler should be able to assume that pr != NULL here */
5263             return pr;
5264         }
5265         h = pr->hash_next;
5266     }
5267     *ppr = NULL;
5268     return NULL;
5269 }
5270 
5271 /* indicate that the object may be part of a function prototype cycle */
set_cycle_flag(JSContext * ctx,JSValueConst obj)5272 static void set_cycle_flag(JSContext *ctx, JSValueConst obj)
5273 {
5274 }
5275 
free_var_ref(JSRuntime * rt,JSVarRef * var_ref)5276 static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)
5277 {
5278     if (var_ref) {
5279         assert(var_ref->header.ref_count > 0);
5280         if (--var_ref->header.ref_count == 0) {
5281             if (var_ref->is_detached) {
5282                 JS_FreeValueRT(rt, var_ref->value);
5283                 remove_gc_object(&var_ref->header);
5284             } else {
5285                 list_del(&var_ref->header.link); /* still on the stack */
5286             }
5287             js_free_rt(rt, var_ref);
5288         }
5289     }
5290 }
5291 
js_array_finalizer(JSRuntime * rt,JSValue val)5292 static void js_array_finalizer(JSRuntime *rt, JSValue val)
5293 {
5294     JSObject *p = JS_VALUE_GET_OBJ(val);
5295     int i;
5296 
5297     for(i = 0; i < p->u.array.count; i++) {
5298         JS_FreeValueRT(rt, p->u.array.u.values[i]);
5299     }
5300     js_free_rt(rt, p->u.array.u.values);
5301 }
5302 
js_array_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5303 static void js_array_mark(JSRuntime *rt, JSValueConst val,
5304                           JS_MarkFunc *mark_func)
5305 {
5306     JSObject *p = JS_VALUE_GET_OBJ(val);
5307     int i;
5308 
5309     for(i = 0; i < p->u.array.count; i++) {
5310         JS_MarkValue(rt, p->u.array.u.values[i], mark_func);
5311     }
5312 }
5313 
js_object_data_finalizer(JSRuntime * rt,JSValue val)5314 static void js_object_data_finalizer(JSRuntime *rt, JSValue val)
5315 {
5316     JSObject *p = JS_VALUE_GET_OBJ(val);
5317     JS_FreeValueRT(rt, p->u.object_data);
5318     p->u.object_data = JS_UNDEFINED;
5319 }
5320 
js_object_data_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5321 static void js_object_data_mark(JSRuntime *rt, JSValueConst val,
5322                                 JS_MarkFunc *mark_func)
5323 {
5324     JSObject *p = JS_VALUE_GET_OBJ(val);
5325     JS_MarkValue(rt, p->u.object_data, mark_func);
5326 }
5327 
js_c_function_finalizer(JSRuntime * rt,JSValue val)5328 static void js_c_function_finalizer(JSRuntime *rt, JSValue val)
5329 {
5330     JSObject *p = JS_VALUE_GET_OBJ(val);
5331 
5332     if (p->u.cfunc.realm)
5333         JS_FreeContext(p->u.cfunc.realm);
5334 }
5335 
js_c_function_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5336 static void js_c_function_mark(JSRuntime *rt, JSValueConst val,
5337                                JS_MarkFunc *mark_func)
5338 {
5339     JSObject *p = JS_VALUE_GET_OBJ(val);
5340 
5341     if (p->u.cfunc.realm)
5342         mark_func(rt, &p->u.cfunc.realm->header);
5343 }
5344 
js_bytecode_function_finalizer(JSRuntime * rt,JSValue val)5345 static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val)
5346 {
5347     JSObject *p1, *p = JS_VALUE_GET_OBJ(val);
5348     JSFunctionBytecode *b;
5349     JSVarRef **var_refs;
5350     int i;
5351 
5352     p1 = p->u.func.home_object;
5353     if (p1) {
5354         JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1));
5355     }
5356     b = p->u.func.function_bytecode;
5357     if (b) {
5358         var_refs = p->u.func.var_refs;
5359         if (var_refs) {
5360             for(i = 0; i < b->closure_var_count; i++)
5361                 free_var_ref(rt, var_refs[i]);
5362             js_free_rt(rt, var_refs);
5363         }
5364         JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b));
5365     }
5366 }
5367 
js_bytecode_function_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5368 static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
5369                                       JS_MarkFunc *mark_func)
5370 {
5371     JSObject *p = JS_VALUE_GET_OBJ(val);
5372     JSVarRef **var_refs = p->u.func.var_refs;
5373     JSFunctionBytecode *b = p->u.func.function_bytecode;
5374     int i;
5375 
5376     if (p->u.func.home_object) {
5377         JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object),
5378                      mark_func);
5379     }
5380     if (b) {
5381         if (var_refs) {
5382             for(i = 0; i < b->closure_var_count; i++) {
5383                 JSVarRef *var_ref = var_refs[i];
5384                 if (var_ref && var_ref->is_detached) {
5385                     mark_func(rt, &var_ref->header);
5386                 }
5387             }
5388         }
5389         /* must mark the function bytecode because template objects may be
5390            part of a cycle */
5391         JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func);
5392     }
5393 }
5394 
js_bound_function_finalizer(JSRuntime * rt,JSValue val)5395 static void js_bound_function_finalizer(JSRuntime *rt, JSValue val)
5396 {
5397     JSObject *p = JS_VALUE_GET_OBJ(val);
5398     JSBoundFunction *bf = p->u.bound_function;
5399     int i;
5400 
5401     JS_FreeValueRT(rt, bf->func_obj);
5402     JS_FreeValueRT(rt, bf->this_val);
5403     for(i = 0; i < bf->argc; i++) {
5404         JS_FreeValueRT(rt, bf->argv[i]);
5405     }
5406     js_free_rt(rt, bf);
5407 }
5408 
js_bound_function_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5409 static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
5410                                 JS_MarkFunc *mark_func)
5411 {
5412     JSObject *p = JS_VALUE_GET_OBJ(val);
5413     JSBoundFunction *bf = p->u.bound_function;
5414     int i;
5415 
5416     JS_MarkValue(rt, bf->func_obj, mark_func);
5417     JS_MarkValue(rt, bf->this_val, mark_func);
5418     for(i = 0; i < bf->argc; i++)
5419         JS_MarkValue(rt, bf->argv[i], mark_func);
5420 }
5421 
js_for_in_iterator_finalizer(JSRuntime * rt,JSValue val)5422 static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val)
5423 {
5424     JSObject *p = JS_VALUE_GET_OBJ(val);
5425     JSForInIterator *it = p->u.for_in_iterator;
5426     JS_FreeValueRT(rt, it->obj);
5427     js_free_rt(rt, it);
5428 }
5429 
js_for_in_iterator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5430 static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
5431                                 JS_MarkFunc *mark_func)
5432 {
5433     JSObject *p = JS_VALUE_GET_OBJ(val);
5434     JSForInIterator *it = p->u.for_in_iterator;
5435     JS_MarkValue(rt, it->obj, mark_func);
5436 }
5437 
free_object(JSRuntime * rt,JSObject * p)5438 static void free_object(JSRuntime *rt, JSObject *p)
5439 {
5440     int i;
5441     JSClassFinalizer *finalizer;
5442     JSShape *sh;
5443     JSShapeProperty *pr;
5444 
5445     p->free_mark = 1; /* used to tell the object is invalid when
5446                          freeing cycles */
5447     /* free all the fields */
5448     sh = p->shape;
5449     pr = get_shape_prop(sh);
5450     for(i = 0; i < sh->prop_count; i++) {
5451         free_property(rt, &p->prop[i], pr->flags);
5452         pr++;
5453     }
5454     js_free_rt(rt, p->prop);
5455     /* as an optimization we destroy the shape immediately without
5456        putting it in gc_zero_ref_count_list */
5457     js_free_shape(rt, sh);
5458 
5459     /* fail safe */
5460     p->shape = NULL;
5461     p->prop = NULL;
5462 
5463     if (unlikely(p->first_weak_ref)) {
5464         reset_weak_ref(rt, p);
5465     }
5466 
5467     finalizer = rt->class_array[p->class_id].finalizer;
5468     if (finalizer)
5469         (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p));
5470 
5471     /* fail safe */
5472     p->class_id = 0;
5473     p->u.opaque = NULL;
5474     p->u.func.var_refs = NULL;
5475     p->u.func.home_object = NULL;
5476 
5477     remove_gc_object(&p->header);
5478     if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) {
5479         list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);
5480     } else {
5481         js_free_rt(rt, p);
5482     }
5483 }
5484 
free_gc_object(JSRuntime * rt,JSGCObjectHeader * gp)5485 static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)
5486 {
5487     switch(gp->gc_obj_type) {
5488     case JS_GC_OBJ_TYPE_JS_OBJECT:
5489         free_object(rt, (JSObject *)gp);
5490         break;
5491     case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
5492         free_function_bytecode(rt, (JSFunctionBytecode *)gp);
5493         break;
5494     default:
5495         abort();
5496     }
5497 }
5498 
free_zero_refcount(JSRuntime * rt)5499 static void free_zero_refcount(JSRuntime *rt)
5500 {
5501     struct list_head *el;
5502     JSGCObjectHeader *p;
5503 
5504     rt->gc_phase = JS_GC_PHASE_DECREF;
5505     for(;;) {
5506         el = rt->gc_zero_ref_count_list.next;
5507         if (el == &rt->gc_zero_ref_count_list)
5508             break;
5509         p = list_entry(el, JSGCObjectHeader, link);
5510         assert(p->ref_count == 0);
5511         free_gc_object(rt, p);
5512     }
5513     rt->gc_phase = JS_GC_PHASE_NONE;
5514 }
5515 
5516 /* called with the ref_count of 'v' reaches zero. */
__JS_FreeValueRT(JSRuntime * rt,JSValue v)5517 void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
5518 {
5519     uint32_t tag = JS_VALUE_GET_TAG(v);
5520 
5521 #ifdef DUMP_FREE
5522     {
5523         printf("Freeing ");
5524         if (tag == JS_TAG_OBJECT) {
5525             JS_DumpObject(rt, JS_VALUE_GET_OBJ(v));
5526         } else {
5527             JS_DumpValueShort(rt, v);
5528             printf("\n");
5529         }
5530     }
5531 #endif
5532 
5533     switch(tag) {
5534     case JS_TAG_STRING:
5535         {
5536             JSString *p = JS_VALUE_GET_STRING(v);
5537             if (p->atom_type) {
5538                 JS_FreeAtomStruct(rt, p);
5539             } else {
5540 #ifdef DUMP_LEAKS
5541                 list_del(&p->link);
5542 #endif
5543                 js_free_rt(rt, p);
5544             }
5545         }
5546         break;
5547     case JS_TAG_OBJECT:
5548     case JS_TAG_FUNCTION_BYTECODE:
5549         {
5550             JSGCObjectHeader *p = JS_VALUE_GET_PTR(v);
5551             if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
5552                 list_del(&p->link);
5553                 list_add(&p->link, &rt->gc_zero_ref_count_list);
5554                 if (rt->gc_phase == JS_GC_PHASE_NONE) {
5555                     free_zero_refcount(rt);
5556                 }
5557             }
5558         }
5559         break;
5560     case JS_TAG_MODULE:
5561         abort(); /* never freed here */
5562         break;
5563 #ifdef CONFIG_BIGNUM
5564     case JS_TAG_BIG_INT:
5565     case JS_TAG_BIG_FLOAT:
5566         {
5567             JSBigFloat *bf = JS_VALUE_GET_PTR(v);
5568             bf_delete(&bf->num);
5569             js_free_rt(rt, bf);
5570         }
5571         break;
5572     case JS_TAG_BIG_DECIMAL:
5573         {
5574             JSBigDecimal *bf = JS_VALUE_GET_PTR(v);
5575             bfdec_delete(&bf->num);
5576             js_free_rt(rt, bf);
5577         }
5578         break;
5579 #endif
5580     case JS_TAG_SYMBOL:
5581         {
5582             JSAtomStruct *p = JS_VALUE_GET_PTR(v);
5583             JS_FreeAtomStruct(rt, p);
5584         }
5585         break;
5586     default:
5587         printf("__JS_FreeValue: unknown tag=%d\n", tag);
5588         abort();
5589     }
5590 }
5591 
__JS_FreeValue(JSContext * ctx,JSValue v)5592 void __JS_FreeValue(JSContext *ctx, JSValue v)
5593 {
5594     __JS_FreeValueRT(ctx->rt, v);
5595 }
5596 
5597 /* garbage collection */
5598 
add_gc_object(JSRuntime * rt,JSGCObjectHeader * h,JSGCObjectTypeEnum type)5599 static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
5600                           JSGCObjectTypeEnum type)
5601 {
5602     h->mark = 0;
5603     h->gc_obj_type = type;
5604     list_add_tail(&h->link, &rt->gc_obj_list);
5605 }
5606 
remove_gc_object(JSGCObjectHeader * h)5607 static void remove_gc_object(JSGCObjectHeader *h)
5608 {
5609     list_del(&h->link);
5610 }
5611 
JS_MarkValue(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5612 void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
5613 {
5614     if (JS_VALUE_HAS_REF_COUNT(val)) {
5615         switch(JS_VALUE_GET_TAG(val)) {
5616         case JS_TAG_OBJECT:
5617         case JS_TAG_FUNCTION_BYTECODE:
5618             mark_func(rt, JS_VALUE_GET_PTR(val));
5619             break;
5620         default:
5621             break;
5622         }
5623     }
5624 }
5625 
mark_children(JSRuntime * rt,JSGCObjectHeader * gp,JS_MarkFunc * mark_func)5626 static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
5627                           JS_MarkFunc *mark_func)
5628 {
5629     switch(gp->gc_obj_type) {
5630     case JS_GC_OBJ_TYPE_JS_OBJECT:
5631         {
5632             JSObject *p = (JSObject *)gp;
5633             JSShapeProperty *prs;
5634             JSShape *sh;
5635             int i;
5636             sh = p->shape;
5637             mark_func(rt, &sh->header);
5638             /* mark all the fields */
5639             prs = get_shape_prop(sh);
5640             for(i = 0; i < sh->prop_count; i++) {
5641                 JSProperty *pr = &p->prop[i];
5642                 if (prs->atom != JS_ATOM_NULL) {
5643                     if (prs->flags & JS_PROP_TMASK) {
5644                         if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
5645                             if (pr->u.getset.getter)
5646                                 mark_func(rt, &pr->u.getset.getter->header);
5647                             if (pr->u.getset.setter)
5648                                 mark_func(rt, &pr->u.getset.setter->header);
5649                         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
5650                             if (pr->u.var_ref->is_detached) {
5651                                 /* Note: the tag does not matter
5652                                    provided it is a GC object */
5653                                 mark_func(rt, &pr->u.var_ref->header);
5654                             }
5655                         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
5656                             js_autoinit_mark(rt, pr, mark_func);
5657                         }
5658                     } else {
5659                         JS_MarkValue(rt, pr->u.value, mark_func);
5660                     }
5661                 }
5662                 prs++;
5663             }
5664 
5665             if (p->class_id != JS_CLASS_OBJECT) {
5666                 JSClassGCMark *gc_mark;
5667                 gc_mark = rt->class_array[p->class_id].gc_mark;
5668                 if (gc_mark)
5669                     gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func);
5670             }
5671         }
5672         break;
5673     case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
5674         /* the template objects can be part of a cycle */
5675         {
5676             JSFunctionBytecode *b = (JSFunctionBytecode *)gp;
5677             int i;
5678             for(i = 0; i < b->cpool_count; i++) {
5679                 JS_MarkValue(rt, b->cpool[i], mark_func);
5680             }
5681             if (b->realm)
5682                 mark_func(rt, &b->realm->header);
5683         }
5684         break;
5685     case JS_GC_OBJ_TYPE_VAR_REF:
5686         {
5687             JSVarRef *var_ref = (JSVarRef *)gp;
5688             /* only detached variable referenced are taken into account */
5689             assert(var_ref->is_detached);
5690             JS_MarkValue(rt, *var_ref->pvalue, mark_func);
5691         }
5692         break;
5693     case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
5694         {
5695             JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp;
5696             if (s->is_active)
5697                 async_func_mark(rt, &s->func_state, mark_func);
5698             JS_MarkValue(rt, s->resolving_funcs[0], mark_func);
5699             JS_MarkValue(rt, s->resolving_funcs[1], mark_func);
5700         }
5701         break;
5702     case JS_GC_OBJ_TYPE_SHAPE:
5703         {
5704             JSShape *sh = (JSShape *)gp;
5705             if (sh->proto != NULL) {
5706                 mark_func(rt, &sh->proto->header);
5707             }
5708         }
5709         break;
5710     case JS_GC_OBJ_TYPE_JS_CONTEXT:
5711         {
5712             JSContext *ctx = (JSContext *)gp;
5713             JS_MarkContext(rt, ctx, mark_func);
5714         }
5715         break;
5716     default:
5717         abort();
5718     }
5719 }
5720 
gc_decref_child(JSRuntime * rt,JSGCObjectHeader * p)5721 static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p)
5722 {
5723     assert(p->ref_count > 0);
5724     p->ref_count--;
5725     if (p->ref_count == 0 && p->mark == 1) {
5726         list_del(&p->link);
5727         list_add_tail(&p->link, &rt->tmp_obj_list);
5728     }
5729 }
5730 
gc_decref(JSRuntime * rt)5731 static void gc_decref(JSRuntime *rt)
5732 {
5733     struct list_head *el, *el1;
5734     JSGCObjectHeader *p;
5735 
5736     init_list_head(&rt->tmp_obj_list);
5737 
5738     /* decrement the refcount of all the children of all the GC
5739        objects and move the GC objects with zero refcount to
5740        tmp_obj_list */
5741     list_for_each_safe(el, el1, &rt->gc_obj_list) {
5742         p = list_entry(el, JSGCObjectHeader, link);
5743         assert(p->mark == 0);
5744         mark_children(rt, p, gc_decref_child);
5745         p->mark = 1;
5746         if (p->ref_count == 0) {
5747             list_del(&p->link);
5748             list_add_tail(&p->link, &rt->tmp_obj_list);
5749         }
5750     }
5751 }
5752 
gc_scan_incref_child(JSRuntime * rt,JSGCObjectHeader * p)5753 static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p)
5754 {
5755     p->ref_count++;
5756     if (p->ref_count == 1) {
5757         /* ref_count was 0: remove from tmp_obj_list and add at the
5758            end of gc_obj_list */
5759         list_del(&p->link);
5760         list_add_tail(&p->link, &rt->gc_obj_list);
5761         p->mark = 0; /* reset the mark for the next GC call */
5762     }
5763 }
5764 
gc_scan_incref_child2(JSRuntime * rt,JSGCObjectHeader * p)5765 static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p)
5766 {
5767     p->ref_count++;
5768 }
5769 
gc_scan(JSRuntime * rt)5770 static void gc_scan(JSRuntime *rt)
5771 {
5772     struct list_head *el;
5773     JSGCObjectHeader *p;
5774 
5775     /* keep the objects with a refcount > 0 and their children. */
5776     list_for_each(el, &rt->gc_obj_list) {
5777         p = list_entry(el, JSGCObjectHeader, link);
5778         assert(p->ref_count > 0);
5779         p->mark = 0; /* reset the mark for the next GC call */
5780         mark_children(rt, p, gc_scan_incref_child);
5781     }
5782 
5783     /* restore the refcount of the objects to be deleted. */
5784     list_for_each(el, &rt->tmp_obj_list) {
5785         p = list_entry(el, JSGCObjectHeader, link);
5786         mark_children(rt, p, gc_scan_incref_child2);
5787     }
5788 }
5789 
gc_free_cycles(JSRuntime * rt)5790 static void gc_free_cycles(JSRuntime *rt)
5791 {
5792     struct list_head *el, *el1;
5793     JSGCObjectHeader *p;
5794 #ifdef DUMP_GC_FREE
5795     BOOL header_done = FALSE;
5796 #endif
5797 
5798     rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES;
5799 
5800     for(;;) {
5801         el = rt->tmp_obj_list.next;
5802         if (el == &rt->tmp_obj_list)
5803             break;
5804         p = list_entry(el, JSGCObjectHeader, link);
5805         /* Only need to free the GC object associated with JS
5806            values. The rest will be automatically removed because they
5807            must be referenced by them. */
5808         switch(p->gc_obj_type) {
5809         case JS_GC_OBJ_TYPE_JS_OBJECT:
5810         case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
5811 #ifdef DUMP_GC_FREE
5812             if (!header_done) {
5813                 printf("Freeing cycles:\n");
5814                 JS_DumpObjectHeader(rt);
5815                 header_done = TRUE;
5816             }
5817             JS_DumpGCObject(rt, p);
5818 #endif
5819             free_gc_object(rt, p);
5820             break;
5821         default:
5822             list_del(&p->link);
5823             list_add_tail(&p->link, &rt->gc_zero_ref_count_list);
5824             break;
5825         }
5826     }
5827     rt->gc_phase = JS_GC_PHASE_NONE;
5828 
5829     list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {
5830         p = list_entry(el, JSGCObjectHeader, link);
5831         assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
5832                p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
5833         js_free_rt(rt, p);
5834     }
5835 
5836     init_list_head(&rt->gc_zero_ref_count_list);
5837 }
5838 
JS_RunGC(JSRuntime * rt)5839 void JS_RunGC(JSRuntime *rt)
5840 {
5841     /* decrement the reference of the children of each object. mark =
5842        1 after this pass. */
5843     gc_decref(rt);
5844 
5845     /* keep the GC objects with a non zero refcount and their childs */
5846     gc_scan(rt);
5847 
5848     /* free the GC objects in a cycle */
5849     gc_free_cycles(rt);
5850 }
5851 
5852 /* Return false if not an object or if the object has already been
5853    freed (zombie objects are visible in finalizers when freeing
5854    cycles). */
JS_IsLiveObject(JSRuntime * rt,JSValueConst obj)5855 BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj)
5856 {
5857     JSObject *p;
5858     if (!JS_IsObject(obj))
5859         return FALSE;
5860     p = JS_VALUE_GET_OBJ(obj);
5861     return !p->free_mark;
5862 }
5863 
5864 /* Compute memory used by various object types */
5865 /* XXX: poor man's approach to handling multiply referenced objects */
5866 typedef struct JSMemoryUsage_helper {
5867     double memory_used_count;
5868     double str_count;
5869     double str_size;
5870     int64_t js_func_count;
5871     double js_func_size;
5872     int64_t js_func_code_size;
5873     int64_t js_func_pc2line_count;
5874     int64_t js_func_pc2line_size;
5875 } JSMemoryUsage_helper;
5876 
5877 static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp);
5878 
compute_jsstring_size(JSString * str,JSMemoryUsage_helper * hp)5879 static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp)
5880 {
5881     if (!str->atom_type) {  /* atoms are handled separately */
5882         double s_ref_count = str->header.ref_count;
5883         hp->str_count += 1 / s_ref_count;
5884         hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) +
5885                           1 - str->is_wide_char) / s_ref_count);
5886     }
5887 }
5888 
compute_bytecode_size(JSFunctionBytecode * b,JSMemoryUsage_helper * hp)5889 static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp)
5890 {
5891     int memory_used_count, js_func_size, i;
5892 
5893     memory_used_count = 0;
5894     js_func_size = offsetof(JSFunctionBytecode, debug);
5895     if (b->vardefs) {
5896         js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs);
5897     }
5898     if (b->cpool) {
5899         js_func_size += b->cpool_count * sizeof(*b->cpool);
5900         for (i = 0; i < b->cpool_count; i++) {
5901             JSValueConst val = b->cpool[i];
5902             compute_value_size(val, hp);
5903         }
5904     }
5905     if (b->closure_var) {
5906         js_func_size += b->closure_var_count * sizeof(*b->closure_var);
5907     }
5908     if (!b->read_only_bytecode && b->byte_code_buf) {
5909         hp->js_func_code_size += b->byte_code_len;
5910     }
5911     if (b->has_debug) {
5912         js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug);
5913         if (b->debug.source) {
5914             memory_used_count++;
5915             js_func_size += b->debug.source_len + 1;
5916         }
5917         if (b->debug.pc2line_len) {
5918             memory_used_count++;
5919             hp->js_func_pc2line_count += 1;
5920             hp->js_func_pc2line_size += b->debug.pc2line_len;
5921         }
5922     }
5923     hp->js_func_size += js_func_size;
5924     hp->js_func_count += 1;
5925     hp->memory_used_count += memory_used_count;
5926 }
5927 
compute_value_size(JSValueConst val,JSMemoryUsage_helper * hp)5928 static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp)
5929 {
5930     switch(JS_VALUE_GET_TAG(val)) {
5931     case JS_TAG_STRING:
5932         compute_jsstring_size(JS_VALUE_GET_STRING(val), hp);
5933         break;
5934 #ifdef CONFIG_BIGNUM
5935     case JS_TAG_BIG_INT:
5936     case JS_TAG_BIG_FLOAT:
5937     case JS_TAG_BIG_DECIMAL:
5938         /* should track JSBigFloat usage */
5939         break;
5940 #endif
5941     }
5942 }
5943 
JS_ComputeMemoryUsage(JSRuntime * rt,JSMemoryUsage * s)5944 void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
5945 {
5946     struct list_head *el, *el1;
5947     int i;
5948     JSMemoryUsage_helper mem = { 0 }, *hp = &mem;
5949 
5950     memset(s, 0, sizeof(*s));
5951     s->malloc_count = rt->malloc_state.malloc_count;
5952     s->malloc_size = rt->malloc_state.malloc_size;
5953     s->malloc_limit = rt->malloc_state.malloc_limit;
5954 
5955     s->memory_used_count = 2; /* rt + rt->class_array */
5956     s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;
5957 
5958     list_for_each(el, &rt->context_list) {
5959         JSContext *ctx = list_entry(el, JSContext, link);
5960         JSShape *sh = ctx->array_shape;
5961         s->memory_used_count += 2; /* ctx + ctx->class_proto */
5962         s->memory_used_size += sizeof(JSContext) +
5963             sizeof(JSValue) * rt->class_count;
5964         s->binary_object_count += ctx->binary_object_count;
5965         s->binary_object_size += ctx->binary_object_size;
5966 
5967         /* the hashed shapes are counted separately */
5968         if (sh && !sh->is_hashed) {
5969             int hash_size = sh->prop_hash_mask + 1;
5970             s->shape_count++;
5971             s->shape_size += get_shape_size(hash_size, sh->prop_size);
5972         }
5973         list_for_each(el1, &ctx->loaded_modules) {
5974             JSModuleDef *m = list_entry(el1, JSModuleDef, link);
5975             s->memory_used_count += 1;
5976             s->memory_used_size += sizeof(*m);
5977             if (m->req_module_entries) {
5978                 s->memory_used_count += 1;
5979                 s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries);
5980             }
5981             if (m->export_entries) {
5982                 s->memory_used_count += 1;
5983                 s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries);
5984                 for (i = 0; i < m->export_entries_count; i++) {
5985                     JSExportEntry *me = &m->export_entries[i];
5986                     if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) {
5987                         /* potential multiple count */
5988                         s->memory_used_count += 1;
5989                         compute_value_size(me->u.local.var_ref->value, hp);
5990                     }
5991                 }
5992             }
5993             if (m->star_export_entries) {
5994                 s->memory_used_count += 1;
5995                 s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries);
5996             }
5997             if (m->import_entries) {
5998                 s->memory_used_count += 1;
5999                 s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries);
6000             }
6001             compute_value_size(m->module_ns, hp);
6002             compute_value_size(m->func_obj, hp);
6003         }
6004     }
6005 
6006     list_for_each(el, &rt->gc_obj_list) {
6007         JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
6008         JSObject *p;
6009         JSShape *sh;
6010         JSShapeProperty *prs;
6011 
6012         /* XXX: could count the other GC object types too */
6013         if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {
6014             compute_bytecode_size((JSFunctionBytecode *)gp, hp);
6015             continue;
6016         } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) {
6017             continue;
6018         }
6019         p = (JSObject *)gp;
6020         sh = p->shape;
6021         s->obj_count++;
6022         if (p->prop) {
6023             s->memory_used_count++;
6024             s->prop_size += sh->prop_size * sizeof(*p->prop);
6025             s->prop_count += sh->prop_count;
6026             prs = get_shape_prop(sh);
6027             for(i = 0; i < sh->prop_count; i++) {
6028                 JSProperty *pr = &p->prop[i];
6029                 if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) {
6030                     compute_value_size(pr->u.value, hp);
6031                 }
6032                 prs++;
6033             }
6034         }
6035         /* the hashed shapes are counted separately */
6036         if (!sh->is_hashed) {
6037             int hash_size = sh->prop_hash_mask + 1;
6038             s->shape_count++;
6039             s->shape_size += get_shape_size(hash_size, sh->prop_size);
6040         }
6041 
6042         switch(p->class_id) {
6043         case JS_CLASS_ARRAY:             /* u.array | length */
6044         case JS_CLASS_ARGUMENTS:         /* u.array | length */
6045             s->array_count++;
6046             if (p->fast_array) {
6047                 s->fast_array_count++;
6048                 if (p->u.array.u.values) {
6049                     s->memory_used_count++;
6050                     s->memory_used_size += p->u.array.count *
6051                         sizeof(*p->u.array.u.values);
6052                     s->fast_array_elements += p->u.array.count;
6053                     for (i = 0; i < p->u.array.count; i++) {
6054                         compute_value_size(p->u.array.u.values[i], hp);
6055                     }
6056                 }
6057             }
6058             break;
6059         case JS_CLASS_NUMBER:            /* u.object_data */
6060         case JS_CLASS_STRING:            /* u.object_data */
6061         case JS_CLASS_BOOLEAN:           /* u.object_data */
6062         case JS_CLASS_SYMBOL:            /* u.object_data */
6063         case JS_CLASS_DATE:              /* u.object_data */
6064 #ifdef CONFIG_BIGNUM
6065         case JS_CLASS_BIG_INT:           /* u.object_data */
6066         case JS_CLASS_BIG_FLOAT:         /* u.object_data */
6067         case JS_CLASS_BIG_DECIMAL:         /* u.object_data */
6068 #endif
6069             compute_value_size(p->u.object_data, hp);
6070             break;
6071         case JS_CLASS_C_FUNCTION:        /* u.cfunc */
6072             s->c_func_count++;
6073             break;
6074         case JS_CLASS_BYTECODE_FUNCTION: /* u.func */
6075             {
6076                 JSFunctionBytecode *b = p->u.func.function_bytecode;
6077                 JSVarRef **var_refs = p->u.func.var_refs;
6078                 /* home_object: object will be accounted for in list scan */
6079                 if (var_refs) {
6080                     s->memory_used_count++;
6081                     s->js_func_size += b->closure_var_count * sizeof(*var_refs);
6082                     for (i = 0; i < b->closure_var_count; i++) {
6083                         if (var_refs[i]) {
6084                             double ref_count = var_refs[i]->header.ref_count;
6085                             s->memory_used_count += 1 / ref_count;
6086                             s->js_func_size += sizeof(*var_refs[i]) / ref_count;
6087                             /* handle non object closed values */
6088                             if (var_refs[i]->pvalue == &var_refs[i]->value) {
6089                                 /* potential multiple count */
6090                                 compute_value_size(var_refs[i]->value, hp);
6091                             }
6092                         }
6093                     }
6094                 }
6095             }
6096             break;
6097         case JS_CLASS_BOUND_FUNCTION:    /* u.bound_function */
6098             {
6099                 JSBoundFunction *bf = p->u.bound_function;
6100                 /* func_obj and this_val are objects */
6101                 for (i = 0; i < bf->argc; i++) {
6102                     compute_value_size(bf->argv[i], hp);
6103                 }
6104                 s->memory_used_count += 1;
6105                 s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv);
6106             }
6107             break;
6108         case JS_CLASS_C_FUNCTION_DATA:   /* u.c_function_data_record */
6109             {
6110                 JSCFunctionDataRecord *fd = p->u.c_function_data_record;
6111                 if (fd) {
6112                     for (i = 0; i < fd->data_len; i++) {
6113                         compute_value_size(fd->data[i], hp);
6114                     }
6115                     s->memory_used_count += 1;
6116                     s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data);
6117                 }
6118             }
6119             break;
6120         case JS_CLASS_REGEXP:            /* u.regexp */
6121             compute_jsstring_size(p->u.regexp.pattern, hp);
6122             compute_jsstring_size(p->u.regexp.bytecode, hp);
6123             break;
6124 
6125         case JS_CLASS_FOR_IN_ITERATOR:   /* u.for_in_iterator */
6126             {
6127                 JSForInIterator *it = p->u.for_in_iterator;
6128                 if (it) {
6129                     compute_value_size(it->obj, hp);
6130                     s->memory_used_count += 1;
6131                     s->memory_used_size += sizeof(*it);
6132                 }
6133             }
6134             break;
6135         case JS_CLASS_ARRAY_BUFFER:      /* u.array_buffer */
6136         case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */
6137             {
6138                 JSArrayBuffer *abuf = p->u.array_buffer;
6139                 if (abuf) {
6140                     s->memory_used_count += 1;
6141                     s->memory_used_size += sizeof(*abuf);
6142                     if (abuf->data) {
6143                         s->memory_used_count += 1;
6144                         s->memory_used_size += abuf->byte_length;
6145                     }
6146                 }
6147             }
6148             break;
6149         case JS_CLASS_GENERATOR:         /* u.generator_data */
6150         case JS_CLASS_UINT8C_ARRAY:      /* u.typed_array / u.array */
6151         case JS_CLASS_INT8_ARRAY:        /* u.typed_array / u.array */
6152         case JS_CLASS_UINT8_ARRAY:       /* u.typed_array / u.array */
6153         case JS_CLASS_INT16_ARRAY:       /* u.typed_array / u.array */
6154         case JS_CLASS_UINT16_ARRAY:      /* u.typed_array / u.array */
6155         case JS_CLASS_INT32_ARRAY:       /* u.typed_array / u.array */
6156         case JS_CLASS_UINT32_ARRAY:      /* u.typed_array / u.array */
6157 #ifdef CONFIG_BIGNUM
6158         case JS_CLASS_BIG_INT64_ARRAY:   /* u.typed_array / u.array */
6159         case JS_CLASS_BIG_UINT64_ARRAY:  /* u.typed_array / u.array */
6160 #endif
6161         case JS_CLASS_FLOAT32_ARRAY:     /* u.typed_array / u.array */
6162         case JS_CLASS_FLOAT64_ARRAY:     /* u.typed_array / u.array */
6163         case JS_CLASS_DATAVIEW:          /* u.typed_array */
6164 #ifdef CONFIG_BIGNUM
6165         case JS_CLASS_FLOAT_ENV:         /* u.float_env */
6166 #endif
6167         case JS_CLASS_MAP:               /* u.map_state */
6168         case JS_CLASS_SET:               /* u.map_state */
6169         case JS_CLASS_WEAKMAP:           /* u.map_state */
6170         case JS_CLASS_WEAKSET:           /* u.map_state */
6171         case JS_CLASS_MAP_ITERATOR:      /* u.map_iterator_data */
6172         case JS_CLASS_SET_ITERATOR:      /* u.map_iterator_data */
6173         case JS_CLASS_ARRAY_ITERATOR:    /* u.array_iterator_data */
6174         case JS_CLASS_STRING_ITERATOR:   /* u.array_iterator_data */
6175         case JS_CLASS_PROXY:             /* u.proxy_data */
6176         case JS_CLASS_PROMISE:           /* u.promise_data */
6177         case JS_CLASS_PROMISE_RESOLVE_FUNCTION:  /* u.promise_function_data */
6178         case JS_CLASS_PROMISE_REJECT_FUNCTION:   /* u.promise_function_data */
6179         case JS_CLASS_ASYNC_FUNCTION_RESOLVE:    /* u.async_function_data */
6180         case JS_CLASS_ASYNC_FUNCTION_REJECT:     /* u.async_function_data */
6181         case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR:  /* u.async_from_sync_iterator_data */
6182         case JS_CLASS_ASYNC_GENERATOR:   /* u.async_generator_data */
6183             /* TODO */
6184         default:
6185             /* XXX: class definition should have an opaque block size */
6186             if (p->u.opaque) {
6187                 s->memory_used_count += 1;
6188             }
6189             break;
6190         }
6191     }
6192     s->obj_size += s->obj_count * sizeof(JSObject);
6193 
6194     /* hashed shapes */
6195     s->memory_used_count++; /* rt->shape_hash */
6196     s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size;
6197     for(i = 0; i < rt->shape_hash_size; i++) {
6198         JSShape *sh;
6199         for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
6200             int hash_size = sh->prop_hash_mask + 1;
6201             s->shape_count++;
6202             s->shape_size += get_shape_size(hash_size, sh->prop_size);
6203         }
6204     }
6205 
6206     /* atoms */
6207     s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */
6208     s->atom_count = rt->atom_count;
6209     s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size +
6210         sizeof(rt->atom_hash[0]) * rt->atom_hash_size;
6211     for(i = 0; i < rt->atom_size; i++) {
6212         JSAtomStruct *p = rt->atom_array[i];
6213         if (!atom_is_free(p)) {
6214             s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) +
6215                              1 - p->is_wide_char);
6216         }
6217     }
6218     s->str_count = round(mem.str_count);
6219     s->str_size = round(mem.str_size);
6220     s->js_func_count = mem.js_func_count;
6221     s->js_func_size = round(mem.js_func_size);
6222     s->js_func_code_size = mem.js_func_code_size;
6223     s->js_func_pc2line_count = mem.js_func_pc2line_count;
6224     s->js_func_pc2line_size = mem.js_func_pc2line_size;
6225     s->memory_used_count += round(mem.memory_used_count) +
6226         s->atom_count + s->str_count +
6227         s->obj_count + s->shape_count +
6228         s->js_func_count + s->js_func_pc2line_count;
6229     s->memory_used_size += s->atom_size + s->str_size +
6230         s->obj_size + s->prop_size + s->shape_size +
6231         s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size;
6232 }
6233 
JS_DumpMemoryUsage(FILE * fp,const JSMemoryUsage * s,JSRuntime * rt)6234 void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
6235 {
6236     fprintf(fp, "QuickJS memory usage -- "
6237 #ifdef CONFIG_BIGNUM
6238             "BigNum "
6239 #endif
6240             CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n",
6241             (int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit);
6242 #if 1
6243     if (rt) {
6244         static const struct {
6245             const char *name;
6246             size_t size;
6247         } object_types[] = {
6248             { "JSRuntime", sizeof(JSRuntime) },
6249             { "JSContext", sizeof(JSContext) },
6250             { "JSObject", sizeof(JSObject) },
6251             { "JSString", sizeof(JSString) },
6252             { "JSFunctionBytecode", sizeof(JSFunctionBytecode) },
6253         };
6254         int i, usage_size_ok = 0;
6255         for(i = 0; i < countof(object_types); i++) {
6256             unsigned int size = object_types[i].size;
6257             void *p = js_malloc_rt(rt, size);
6258             if (p) {
6259                 unsigned int size1 = js_malloc_usable_size_rt(rt, p);
6260                 if (size1 >= size) {
6261                     usage_size_ok = 1;
6262                     fprintf(fp, "  %3u + %-2u  %s\n",
6263                             size, size1 - size, object_types[i].name);
6264                 }
6265                 js_free_rt(rt, p);
6266             }
6267         }
6268         if (!usage_size_ok) {
6269             fprintf(fp, "  malloc_usable_size unavailable\n");
6270         }
6271         {
6272             int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 };
6273             int class_id;
6274             struct list_head *el;
6275             list_for_each(el, &rt->gc_obj_list) {
6276                 JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
6277                 JSObject *p;
6278                 if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
6279                     p = (JSObject *)gp;
6280                     obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++;
6281                 }
6282             }
6283             fprintf(fp, "\n" "JSObject classes\n");
6284             if (obj_classes[0])
6285                 fprintf(fp, "  %5d  %2.0d %s\n", obj_classes[0], 0, "none");
6286             for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) {
6287                 if (obj_classes[class_id]) {
6288                     char buf[ATOM_GET_STR_BUF_SIZE];
6289                     fprintf(fp, "  %5d  %2.0d %s\n", obj_classes[class_id], class_id,
6290                             JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name));
6291                 }
6292             }
6293             if (obj_classes[JS_CLASS_INIT_COUNT])
6294                 fprintf(fp, "  %5d  %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other");
6295         }
6296         fprintf(fp, "\n");
6297     }
6298 #endif
6299     fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE");
6300 
6301     if (s->malloc_count) {
6302         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per block)\n",
6303                 "memory allocated", s->malloc_count, s->malloc_size,
6304                 (double)s->malloc_size / s->malloc_count);
6305         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%d overhead, %0.1f average slack)\n",
6306                 "memory used", s->memory_used_count, s->memory_used_size,
6307                 MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) /
6308                                   s->memory_used_count));
6309     }
6310     if (s->atom_count) {
6311         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per atom)\n",
6312                 "atoms", s->atom_count, s->atom_size,
6313                 (double)s->atom_size / s->atom_count);
6314     }
6315     if (s->str_count) {
6316         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per string)\n",
6317                 "strings", s->str_count, s->str_size,
6318                 (double)s->str_size / s->str_count);
6319     }
6320     if (s->obj_count) {
6321         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per object)\n",
6322                 "objects", s->obj_count, s->obj_size,
6323                 (double)s->obj_size / s->obj_count);
6324         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per object)\n",
6325                 "  properties", s->prop_count, s->prop_size,
6326                 (double)s->prop_count / s->obj_count);
6327         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per shape)\n",
6328                 "  shapes", s->shape_count, s->shape_size,
6329                 (double)s->shape_size / s->shape_count);
6330     }
6331     if (s->js_func_count) {
6332         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
6333                 "bytecode functions", s->js_func_count, s->js_func_size);
6334         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per function)\n",
6335                 "  bytecode", s->js_func_count, s->js_func_code_size,
6336                 (double)s->js_func_code_size / s->js_func_count);
6337         if (s->js_func_pc2line_count) {
6338             fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per function)\n",
6339                     "  pc2line", s->js_func_pc2line_count,
6340                     s->js_func_pc2line_size,
6341                     (double)s->js_func_pc2line_size / s->js_func_pc2line_count);
6342         }
6343     }
6344     if (s->c_func_count) {
6345         fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);
6346     }
6347     if (s->array_count) {
6348         fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count);
6349         if (s->fast_array_count) {
6350             fprintf(fp, "%-20s %8"PRId64"\n", "  fast arrays", s->fast_array_count);
6351             fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per fast array)\n",
6352                     "  elements", s->fast_array_elements,
6353                     s->fast_array_elements * (int)sizeof(JSValue),
6354                     (double)s->fast_array_elements / s->fast_array_count);
6355         }
6356     }
6357     if (s->binary_object_count) {
6358         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
6359                 "binary objects", s->binary_object_count, s->binary_object_size);
6360     }
6361 }
6362 
JS_GetGlobalObject(JSContext * ctx)6363 JSValue JS_GetGlobalObject(JSContext *ctx)
6364 {
6365     return JS_DupValue(ctx, ctx->global_obj);
6366 }
6367 
6368 /* WARNING: obj is freed */
JS_Throw(JSContext * ctx,JSValue obj)6369 JSValue JS_Throw(JSContext *ctx, JSValue obj)
6370 {
6371     JSRuntime *rt = ctx->rt;
6372     JS_FreeValue(ctx, rt->current_exception);
6373     rt->current_exception = obj;
6374     return JS_EXCEPTION;
6375 }
6376 
6377 /* return the pending exception (cannot be called twice). */
JS_GetException(JSContext * ctx)6378 JSValue JS_GetException(JSContext *ctx)
6379 {
6380     JSValue val;
6381     JSRuntime *rt = ctx->rt;
6382     val = rt->current_exception;
6383     rt->current_exception = JS_NULL;
6384     return val;
6385 }
6386 
dbuf_put_leb128(DynBuf * s,uint32_t v)6387 static void dbuf_put_leb128(DynBuf *s, uint32_t v)
6388 {
6389     uint32_t a;
6390     for(;;) {
6391         a = v & 0x7f;
6392         v >>= 7;
6393         if (v != 0) {
6394             dbuf_putc(s, a | 0x80);
6395         } else {
6396             dbuf_putc(s, a);
6397             break;
6398         }
6399     }
6400 }
6401 
dbuf_put_sleb128(DynBuf * s,int32_t v1)6402 static void dbuf_put_sleb128(DynBuf *s, int32_t v1)
6403 {
6404     uint32_t v = v1;
6405     dbuf_put_leb128(s, (2 * v) ^ -(v >> 31));
6406 }
6407 
get_leb128(uint32_t * pval,const uint8_t * buf,const uint8_t * buf_end)6408 static int get_leb128(uint32_t *pval, const uint8_t *buf,
6409                       const uint8_t *buf_end)
6410 {
6411     const uint8_t *ptr = buf;
6412     uint32_t v, a, i;
6413     v = 0;
6414     for(i = 0; i < 5; i++) {
6415         if (unlikely(ptr >= buf_end))
6416             break;
6417         a = *ptr++;
6418         v |= (a & 0x7f) << (i * 7);
6419         if (!(a & 0x80)) {
6420             *pval = v;
6421             return ptr - buf;
6422         }
6423     }
6424     *pval = 0;
6425     return -1;
6426 }
6427 
get_sleb128(int32_t * pval,const uint8_t * buf,const uint8_t * buf_end)6428 static int get_sleb128(int32_t *pval, const uint8_t *buf,
6429                        const uint8_t *buf_end)
6430 {
6431     int ret;
6432     uint32_t val;
6433     ret = get_leb128(&val, buf, buf_end);
6434     if (ret < 0) {
6435         *pval = 0;
6436         return -1;
6437     }
6438     *pval = (val >> 1) ^ -(val & 1);
6439     return ret;
6440 }
6441 
find_line_num(JSContext * ctx,JSFunctionBytecode * b,uint32_t pc_value)6442 static int find_line_num(JSContext *ctx, JSFunctionBytecode *b,
6443                          uint32_t pc_value)
6444 {
6445     const uint8_t *p_end, *p;
6446     int new_line_num, line_num, pc, v, ret;
6447     unsigned int op;
6448 
6449     if (!b->has_debug || !b->debug.pc2line_buf) {
6450         /* function was stripped */
6451         return -1;
6452     }
6453 
6454     p = b->debug.pc2line_buf;
6455     p_end = p + b->debug.pc2line_len;
6456     pc = 0;
6457     line_num = b->debug.line_num;
6458     while (p < p_end) {
6459         op = *p++;
6460         if (op == 0) {
6461             uint32_t val;
6462             ret = get_leb128(&val, p, p_end);
6463             if (ret < 0)
6464                 goto fail;
6465             pc += val;
6466             p += ret;
6467             ret = get_sleb128(&v, p, p_end);
6468             if (ret < 0) {
6469             fail:
6470                 /* should never happen */
6471                 return b->debug.line_num;
6472             }
6473             p += ret;
6474             new_line_num = line_num + v;
6475         } else {
6476             op -= PC2LINE_OP_FIRST;
6477             pc += (op / PC2LINE_RANGE);
6478             new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE;
6479         }
6480         if (pc_value < pc)
6481             return line_num;
6482         line_num = new_line_num;
6483     }
6484     return line_num;
6485 }
6486 
6487 /* in order to avoid executing arbitrary code during the stack trace
6488    generation, we only look at simple 'name' properties containing a
6489    string. */
get_func_name(JSContext * ctx,JSValueConst func)6490 static const char *get_func_name(JSContext *ctx, JSValueConst func)
6491 {
6492     JSProperty *pr;
6493     JSShapeProperty *prs;
6494     JSValueConst val;
6495 
6496     if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)
6497         return NULL;
6498     prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name);
6499     if (!prs)
6500         return NULL;
6501     if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
6502         return NULL;
6503     val = pr->u.value;
6504     if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
6505         return NULL;
6506     return JS_ToCString(ctx, val);
6507 }
6508 
6509 #define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)
6510 /* only taken into account if filename is provided */
6511 #define JS_BACKTRACE_FLAG_SINGLE_LEVEL     (1 << 1)
6512 
6513 /* if filename != NULL, an additional level is added with the filename
6514    and line number information (used for parse error). */
build_backtrace(JSContext * ctx,JSValueConst error_obj,const char * filename,int line_num,int backtrace_flags)6515 static void build_backtrace(JSContext *ctx, JSValueConst error_obj,
6516                             const char *filename, int line_num,
6517                             int backtrace_flags)
6518 {
6519     JSStackFrame *sf;
6520     JSValue str;
6521     DynBuf dbuf;
6522     const char *func_name_str;
6523     const char *str1;
6524     JSObject *p;
6525     BOOL backtrace_barrier;
6526 
6527     js_dbuf_init(ctx, &dbuf);
6528     if (filename) {
6529         dbuf_printf(&dbuf, "    at %s", filename);
6530         if (line_num != -1)
6531             dbuf_printf(&dbuf, ":%d", line_num);
6532         dbuf_putc(&dbuf, '\n');
6533         str = JS_NewString(ctx, filename);
6534         JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str,
6535                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6536         JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num),
6537                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6538         if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)
6539             goto done;
6540     }
6541     for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {
6542         if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {
6543             backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
6544             continue;
6545         }
6546         func_name_str = get_func_name(ctx, sf->cur_func);
6547         if (!func_name_str || func_name_str[0] == '\0')
6548             str1 = "<anonymous>";
6549         else
6550             str1 = func_name_str;
6551         dbuf_printf(&dbuf, "    at %s", str1);
6552         JS_FreeCString(ctx, func_name_str);
6553 
6554         p = JS_VALUE_GET_OBJ(sf->cur_func);
6555         backtrace_barrier = FALSE;
6556         if (js_class_has_bytecode(p->class_id)) {
6557             JSFunctionBytecode *b;
6558             const char *atom_str;
6559             int line_num1;
6560 
6561             b = p->u.func.function_bytecode;
6562             backtrace_barrier = b->backtrace_barrier;
6563             if (b->has_debug) {
6564                 line_num1 = find_line_num(ctx, b,
6565                                           sf->cur_pc - b->byte_code_buf - 1);
6566                 atom_str = JS_AtomToCString(ctx, b->debug.filename);
6567                 dbuf_printf(&dbuf, " (%s",
6568                             atom_str ? atom_str : "<null>");
6569                 JS_FreeCString(ctx, atom_str);
6570                 if (line_num1 != -1)
6571                     dbuf_printf(&dbuf, ":%d", line_num1);
6572                 dbuf_putc(&dbuf, ')');
6573             }
6574         } else {
6575             dbuf_printf(&dbuf, " (native)");
6576         }
6577         dbuf_putc(&dbuf, '\n');
6578         /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */
6579         if (backtrace_barrier)
6580             break;
6581     }
6582  done:
6583     dbuf_putc(&dbuf, '\0');
6584     if (dbuf_error(&dbuf))
6585         str = JS_NULL;
6586     else
6587         str = JS_NewString(ctx, (char *)dbuf.buf);
6588     dbuf_free(&dbuf);
6589     JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str,
6590                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6591 }
6592 
6593 /* Note: it is important that no exception is returned by this function */
is_backtrace_needed(JSContext * ctx,JSValueConst obj)6594 static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj)
6595 {
6596     JSObject *p;
6597     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
6598         return FALSE;
6599     p = JS_VALUE_GET_OBJ(obj);
6600     if (p->class_id != JS_CLASS_ERROR)
6601         return FALSE;
6602     if (find_own_property1(p, JS_ATOM_stack))
6603         return FALSE;
6604     return TRUE;
6605 }
6606 
JS_NewError(JSContext * ctx)6607 JSValue JS_NewError(JSContext *ctx)
6608 {
6609     return JS_NewObjectClass(ctx, JS_CLASS_ERROR);
6610 }
6611 
JS_ThrowError2(JSContext * ctx,JSErrorEnum error_num,const char * fmt,va_list ap,BOOL add_backtrace)6612 static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num,
6613                               const char *fmt, va_list ap, BOOL add_backtrace)
6614 {
6615     char buf[256];
6616     JSValue obj, ret;
6617 
6618     vsnprintf(buf, sizeof(buf), fmt, ap);
6619     obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num],
6620                                  JS_CLASS_ERROR);
6621     if (unlikely(JS_IsException(obj))) {
6622         /* out of memory: throw JS_NULL to avoid recursing */
6623         obj = JS_NULL;
6624     } else {
6625         JS_DefinePropertyValue(ctx, obj, JS_ATOM_message,
6626                                JS_NewString(ctx, buf),
6627                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6628     }
6629     if (add_backtrace) {
6630         build_backtrace(ctx, obj, NULL, 0, 0);
6631     }
6632     ret = JS_Throw(ctx, obj);
6633     return ret;
6634 }
6635 
JS_ThrowError(JSContext * ctx,JSErrorEnum error_num,const char * fmt,va_list ap)6636 static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num,
6637                              const char *fmt, va_list ap)
6638 {
6639     JSRuntime *rt = ctx->rt;
6640     JSStackFrame *sf;
6641     BOOL add_backtrace;
6642 
6643     /* the backtrace is added later if called from a bytecode function */
6644     sf = rt->current_stack_frame;
6645     add_backtrace = !rt->in_out_of_memory &&
6646         (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL));
6647     return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace);
6648 }
6649 
JS_ThrowSyntaxError(JSContext * ctx,const char * fmt,...)6650 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)
6651 {
6652     JSValue val;
6653     va_list ap;
6654 
6655     va_start(ap, fmt);
6656     val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap);
6657     va_end(ap);
6658     return val;
6659 }
6660 
JS_ThrowTypeError(JSContext * ctx,const char * fmt,...)6661 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)
6662 {
6663     JSValue val;
6664     va_list ap;
6665 
6666     va_start(ap, fmt);
6667     val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
6668     va_end(ap);
6669     return val;
6670 }
6671 
JS_ThrowTypeErrorOrFalse(JSContext * ctx,int flags,const char * fmt,...)6672 static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)
6673 {
6674     va_list ap;
6675 
6676     if ((flags & JS_PROP_THROW) ||
6677         ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
6678         va_start(ap, fmt);
6679         JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
6680         va_end(ap);
6681         return -1;
6682     } else {
6683         return FALSE;
6684     }
6685 }
6686 
6687 /* never use it directly */
__JS_ThrowTypeErrorAtom(JSContext * ctx,JSAtom atom,const char * fmt,...)6688 static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
6689 {
6690     char buf[ATOM_GET_STR_BUF_SIZE];
6691     return JS_ThrowTypeError(ctx, fmt,
6692                              JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
6693 }
6694 
6695 /* never use it directly */
__JS_ThrowSyntaxErrorAtom(JSContext * ctx,JSAtom atom,const char * fmt,...)6696 static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
6697 {
6698     char buf[ATOM_GET_STR_BUF_SIZE];
6699     return JS_ThrowSyntaxError(ctx, fmt,
6700                              JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
6701 }
6702 
6703 /* %s is replaced by 'atom'. The macro is used so that gcc can check
6704     the format string. */
6705 #define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "")
6706 #define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "")
6707 
JS_ThrowTypeErrorReadOnly(JSContext * ctx,int flags,JSAtom atom)6708 static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)
6709 {
6710     if ((flags & JS_PROP_THROW) ||
6711         ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
6712         JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom);
6713         return -1;
6714     } else {
6715         return FALSE;
6716     }
6717 }
6718 
JS_ThrowReferenceError(JSContext * ctx,const char * fmt,...)6719 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)
6720 {
6721     JSValue val;
6722     va_list ap;
6723 
6724     va_start(ap, fmt);
6725     val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap);
6726     va_end(ap);
6727     return val;
6728 }
6729 
JS_ThrowRangeError(JSContext * ctx,const char * fmt,...)6730 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)
6731 {
6732     JSValue val;
6733     va_list ap;
6734 
6735     va_start(ap, fmt);
6736     val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap);
6737     va_end(ap);
6738     return val;
6739 }
6740 
JS_ThrowInternalError(JSContext * ctx,const char * fmt,...)6741 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)
6742 {
6743     JSValue val;
6744     va_list ap;
6745 
6746     va_start(ap, fmt);
6747     val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap);
6748     va_end(ap);
6749     return val;
6750 }
6751 
JS_ThrowOutOfMemory(JSContext * ctx)6752 JSValue JS_ThrowOutOfMemory(JSContext *ctx)
6753 {
6754     JSRuntime *rt = ctx->rt;
6755     if (!rt->in_out_of_memory) {
6756         rt->in_out_of_memory = TRUE;
6757         JS_ThrowInternalError(ctx, "out of memory");
6758         rt->in_out_of_memory = FALSE;
6759     }
6760     return JS_EXCEPTION;
6761 }
6762 
JS_ThrowStackOverflow(JSContext * ctx)6763 static JSValue JS_ThrowStackOverflow(JSContext *ctx)
6764 {
6765     return JS_ThrowInternalError(ctx, "stack overflow");
6766 }
6767 
JS_ThrowTypeErrorNotAnObject(JSContext * ctx)6768 static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx)
6769 {
6770     return JS_ThrowTypeError(ctx, "not an object");
6771 }
6772 
JS_ThrowTypeErrorNotASymbol(JSContext * ctx)6773 static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx)
6774 {
6775     return JS_ThrowTypeError(ctx, "not a symbol");
6776 }
6777 
JS_ThrowReferenceErrorNotDefined(JSContext * ctx,JSAtom name)6778 static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name)
6779 {
6780     char buf[ATOM_GET_STR_BUF_SIZE];
6781     return JS_ThrowReferenceError(ctx, "'%s' is not defined",
6782                                   JS_AtomGetStr(ctx, buf, sizeof(buf), name));
6783 }
6784 
JS_ThrowReferenceErrorUninitialized(JSContext * ctx,JSAtom name)6785 static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name)
6786 {
6787     char buf[ATOM_GET_STR_BUF_SIZE];
6788     return JS_ThrowReferenceError(ctx, "%s is not initialized",
6789                                   name == JS_ATOM_NULL ? "lexical variable" :
6790                                   JS_AtomGetStr(ctx, buf, sizeof(buf), name));
6791 }
6792 
JS_ThrowReferenceErrorUninitialized2(JSContext * ctx,JSFunctionBytecode * b,int idx,BOOL is_ref)6793 static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx,
6794                                                     JSFunctionBytecode *b,
6795                                                     int idx, BOOL is_ref)
6796 {
6797     JSAtom atom = JS_ATOM_NULL;
6798     if (is_ref) {
6799         atom = b->closure_var[idx].var_name;
6800     } else {
6801         /* not present if the function is stripped and contains no eval() */
6802         if (b->vardefs)
6803             atom = b->vardefs[b->arg_count + idx].var_name;
6804     }
6805     return JS_ThrowReferenceErrorUninitialized(ctx, atom);
6806 }
6807 
JS_ThrowTypeErrorInvalidClass(JSContext * ctx,int class_id)6808 static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)
6809 {
6810     JSRuntime *rt = ctx->rt;
6811     JSAtom name;
6812     name = rt->class_array[class_id].class_name;
6813     return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);
6814 }
6815 
__js_poll_interrupts(JSContext * ctx)6816 static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
6817 {
6818     JSRuntime *rt = ctx->rt;
6819     ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;
6820     if (rt->interrupt_handler) {
6821         if (rt->interrupt_handler(rt, rt->interrupt_opaque)) {
6822             /* XXX: should set a specific flag to avoid catching */
6823             JS_ThrowInternalError(ctx, "interrupted");
6824             JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE);
6825             return -1;
6826         }
6827     }
6828     return 0;
6829 }
6830 
js_poll_interrupts(JSContext * ctx)6831 static inline __exception int js_poll_interrupts(JSContext *ctx)
6832 {
6833     if (unlikely(--ctx->interrupt_counter <= 0)) {
6834         return __js_poll_interrupts(ctx);
6835     } else {
6836         return 0;
6837     }
6838 }
6839 
6840 /* return -1 (exception) or TRUE/FALSE */
JS_SetPrototypeInternal(JSContext * ctx,JSValueConst obj,JSValueConst proto_val,BOOL throw_flag)6841 static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj,
6842                                    JSValueConst proto_val,
6843                                    BOOL throw_flag)
6844 {
6845     JSObject *proto, *p, *p1;
6846     JSShape *sh;
6847 
6848     if (throw_flag) {
6849         if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL ||
6850             JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED)
6851             goto not_obj;
6852     } else {
6853         if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
6854             goto not_obj;
6855     }
6856     p = JS_VALUE_GET_OBJ(obj);
6857     if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) {
6858         if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) {
6859         not_obj:
6860             JS_ThrowTypeErrorNotAnObject(ctx);
6861             return -1;
6862         }
6863         proto = NULL;
6864     } else {
6865         proto = JS_VALUE_GET_OBJ(proto_val);
6866     }
6867 
6868     if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
6869         return TRUE;
6870 
6871     if (unlikely(p->class_id == JS_CLASS_PROXY))
6872         return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag);
6873     sh = p->shape;
6874     if (sh->proto == proto)
6875         return TRUE;
6876     if (!p->extensible) {
6877         if (throw_flag) {
6878             JS_ThrowTypeError(ctx, "object is not extensible");
6879             return -1;
6880         } else {
6881             return FALSE;
6882         }
6883     }
6884     if (proto) {
6885         /* check if there is a cycle */
6886         p1 = proto;
6887         do {
6888             if (p1 == p) {
6889                 if (throw_flag) {
6890                     JS_ThrowTypeError(ctx, "circular prototype chain");
6891                     return -1;
6892                 } else {
6893                     return FALSE;
6894                 }
6895             }
6896             /* Note: for Proxy objects, proto is NULL */
6897             p1 = p1->shape->proto;
6898         } while (p1 != NULL);
6899         JS_DupValue(ctx, proto_val);
6900     }
6901 
6902     if (js_shape_prepare_update(ctx, p, NULL))
6903         return -1;
6904     sh = p->shape;
6905     if (sh->proto)
6906         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
6907     sh->proto = proto;
6908     return TRUE;
6909 }
6910 
6911 /* return -1 (exception) or TRUE/FALSE */
JS_SetPrototype(JSContext * ctx,JSValueConst obj,JSValueConst proto_val)6912 int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val)
6913 {
6914     return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE);
6915 }
6916 
6917 /* Only works for primitive types, otherwise return JS_NULL. */
JS_GetPrototypePrimitive(JSContext * ctx,JSValueConst val)6918 static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)
6919 {
6920     switch(JS_VALUE_GET_NORM_TAG(val)) {
6921 #ifdef CONFIG_BIGNUM
6922     case JS_TAG_BIG_INT:
6923         val = ctx->class_proto[JS_CLASS_BIG_INT];
6924         break;
6925     case JS_TAG_BIG_FLOAT:
6926         val = ctx->class_proto[JS_CLASS_BIG_FLOAT];
6927         break;
6928     case JS_TAG_BIG_DECIMAL:
6929         val = ctx->class_proto[JS_CLASS_BIG_DECIMAL];
6930         break;
6931 #endif
6932     case JS_TAG_INT:
6933     case JS_TAG_FLOAT64:
6934         val = ctx->class_proto[JS_CLASS_NUMBER];
6935         break;
6936     case JS_TAG_BOOL:
6937         val = ctx->class_proto[JS_CLASS_BOOLEAN];
6938         break;
6939     case JS_TAG_STRING:
6940         val = ctx->class_proto[JS_CLASS_STRING];
6941         break;
6942     case JS_TAG_SYMBOL:
6943         val = ctx->class_proto[JS_CLASS_SYMBOL];
6944         break;
6945     case JS_TAG_OBJECT:
6946     case JS_TAG_NULL:
6947     case JS_TAG_UNDEFINED:
6948     default:
6949         val = JS_NULL;
6950         break;
6951     }
6952     return val;
6953 }
6954 
6955 /* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */
JS_GetPrototype(JSContext * ctx,JSValueConst obj)6956 JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj)
6957 {
6958     JSValue val;
6959     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
6960         JSObject *p;
6961         p = JS_VALUE_GET_OBJ(obj);
6962         if (unlikely(p->class_id == JS_CLASS_PROXY)) {
6963             val = js_proxy_getPrototypeOf(ctx, obj);
6964         } else {
6965             p = p->shape->proto;
6966             if (!p)
6967                 val = JS_NULL;
6968             else
6969                 val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
6970         }
6971     } else {
6972         val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj));
6973     }
6974     return val;
6975 }
6976 
JS_GetPrototypeFree(JSContext * ctx,JSValue obj)6977 static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj)
6978 {
6979     JSValue obj1;
6980     obj1 = JS_GetPrototype(ctx, obj);
6981     JS_FreeValue(ctx, obj);
6982     return obj1;
6983 }
6984 
6985 /* return TRUE, FALSE or (-1) in case of exception */
JS_OrdinaryIsInstanceOf(JSContext * ctx,JSValueConst val,JSValueConst obj)6986 static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,
6987                                    JSValueConst obj)
6988 {
6989     JSValue obj_proto;
6990     JSObject *proto;
6991     const JSObject *p, *proto1;
6992     BOOL ret;
6993 
6994     if (!JS_IsFunction(ctx, obj))
6995         return FALSE;
6996     p = JS_VALUE_GET_OBJ(obj);
6997     if (p->class_id == JS_CLASS_BOUND_FUNCTION) {
6998         JSBoundFunction *s = p->u.bound_function;
6999         return JS_IsInstanceOf(ctx, val, s->func_obj);
7000     }
7001 
7002     /* Only explicitly boxed values are instances of constructors */
7003     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
7004         return FALSE;
7005     obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype);
7006     if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) {
7007         if (!JS_IsException(obj_proto))
7008             JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object");
7009         ret = -1;
7010         goto done;
7011     }
7012     proto = JS_VALUE_GET_OBJ(obj_proto);
7013     p = JS_VALUE_GET_OBJ(val);
7014     for(;;) {
7015         proto1 = p->shape->proto;
7016         if (!proto1) {
7017             /* slow case if proxy in the prototype chain */
7018             if (unlikely(p->class_id == JS_CLASS_PROXY)) {
7019                 JSValue obj1;
7020                 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p));
7021                 for(;;) {
7022                     obj1 = JS_GetPrototypeFree(ctx, obj1);
7023                     if (JS_IsException(obj1)) {
7024                         ret = -1;
7025                         break;
7026                     }
7027                     if (JS_IsNull(obj1)) {
7028                         ret = FALSE;
7029                         break;
7030                     }
7031                     if (proto == JS_VALUE_GET_OBJ(obj1)) {
7032                         JS_FreeValue(ctx, obj1);
7033                         ret = TRUE;
7034                         break;
7035                     }
7036                     /* must check for timeout to avoid infinite loop */
7037                     if (js_poll_interrupts(ctx)) {
7038                         JS_FreeValue(ctx, obj1);
7039                         ret = -1;
7040                         break;
7041                     }
7042                 }
7043             } else {
7044                 ret = FALSE;
7045             }
7046             break;
7047         }
7048         p = proto1;
7049         if (proto == p) {
7050             ret = TRUE;
7051             break;
7052         }
7053     }
7054 done:
7055     JS_FreeValue(ctx, obj_proto);
7056     return ret;
7057 }
7058 
7059 /* return TRUE, FALSE or (-1) in case of exception */
JS_IsInstanceOf(JSContext * ctx,JSValueConst val,JSValueConst obj)7060 int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj)
7061 {
7062     JSValue method;
7063 
7064     if (!JS_IsObject(obj))
7065         goto fail;
7066     method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance);
7067     if (JS_IsException(method))
7068         return -1;
7069     if (!JS_IsNull(method) && !JS_IsUndefined(method)) {
7070         JSValue ret;
7071         ret = JS_CallFree(ctx, method, obj, 1, &val);
7072         return JS_ToBoolFree(ctx, ret);
7073     }
7074 
7075     /* legacy case */
7076     if (!JS_IsFunction(ctx, obj)) {
7077     fail:
7078         JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand");
7079         return -1;
7080     }
7081     return JS_OrdinaryIsInstanceOf(ctx, val, obj);
7082 }
7083 
7084 /* return the value associated to the autoinit property or an exception */
7085 typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
7086 
7087 static JSAutoInitFunc *js_autoinit_func_table[] = {
7088     js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */
7089     js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */
7090     JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */
7091 };
7092 
7093 /* warning: 'prs' is reallocated after it */
JS_AutoInitProperty(JSContext * ctx,JSObject * p,JSAtom prop,JSProperty * pr,JSShapeProperty * prs)7094 static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,
7095                                JSProperty *pr, JSShapeProperty *prs)
7096 {
7097     JSValue val;
7098     JSContext *realm;
7099     JSAutoInitFunc *func;
7100 
7101     if (js_shape_prepare_update(ctx, p, &prs))
7102         return -1;
7103 
7104     realm = js_autoinit_get_realm(pr);
7105     func = js_autoinit_func_table[js_autoinit_get_id(pr)];
7106     /* 'func' shall not modify the object properties 'pr' */
7107     val = func(realm, p, prop, pr->u.init.opaque);
7108     js_autoinit_free(ctx->rt, pr);
7109     prs->flags &= ~JS_PROP_TMASK;
7110     pr->u.value = JS_UNDEFINED;
7111     if (JS_IsException(val))
7112         return -1;
7113     pr->u.value = val;
7114     return 0;
7115 }
7116 
JS_GetPropertyInternal(JSContext * ctx,JSValueConst obj,JSAtom prop,JSValueConst this_obj,BOOL throw_ref_error)7117 JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
7118                                JSAtom prop, JSValueConst this_obj,
7119                                BOOL throw_ref_error)
7120 {
7121     JSObject *p;
7122     JSProperty *pr;
7123     JSShapeProperty *prs;
7124     uint32_t tag;
7125 
7126     tag = JS_VALUE_GET_TAG(obj);
7127     if (unlikely(tag != JS_TAG_OBJECT)) {
7128         switch(tag) {
7129         case JS_TAG_NULL:
7130             return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop);
7131         case JS_TAG_UNDEFINED:
7132             return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop);
7133         case JS_TAG_EXCEPTION:
7134             return JS_EXCEPTION;
7135         case JS_TAG_STRING:
7136             {
7137                 JSString *p1 = JS_VALUE_GET_STRING(obj);
7138                 if (__JS_AtomIsTaggedInt(prop)) {
7139                     uint32_t idx, ch;
7140                     idx = __JS_AtomToUInt32(prop);
7141                     if (idx < p1->len) {
7142                         if (p1->is_wide_char)
7143                             ch = p1->u.str16[idx];
7144                         else
7145                             ch = p1->u.str8[idx];
7146                         return js_new_string_char(ctx, ch);
7147                     }
7148                 } else if (prop == JS_ATOM_length) {
7149                     return JS_NewInt32(ctx, p1->len);
7150                 }
7151             }
7152             break;
7153         default:
7154             break;
7155         }
7156         /* cannot raise an exception */
7157         p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));
7158         if (!p)
7159             return JS_UNDEFINED;
7160     } else {
7161         p = JS_VALUE_GET_OBJ(obj);
7162     }
7163 
7164     for(;;) {
7165         prs = find_own_property(&pr, p, prop);
7166         if (prs) {
7167             /* found */
7168             if (unlikely(prs->flags & JS_PROP_TMASK)) {
7169                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
7170                     if (unlikely(!pr->u.getset.getter)) {
7171                         return JS_UNDEFINED;
7172                     } else {
7173                         JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter);
7174                         /* Note: the field could be removed in the getter */
7175                         func = JS_DupValue(ctx, func);
7176                         return JS_CallFree(ctx, func, this_obj, 0, NULL);
7177                     }
7178                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
7179                     JSValue val = *pr->u.var_ref->pvalue;
7180                     if (unlikely(JS_IsUninitialized(val)))
7181                         return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
7182                     return JS_DupValue(ctx, val);
7183                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
7184                     /* Instantiate property and retry */
7185                     if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
7186                         return JS_EXCEPTION;
7187                     continue;
7188                 }
7189             } else {
7190                 return JS_DupValue(ctx, pr->u.value);
7191             }
7192         }
7193         if (unlikely(p->is_exotic)) {
7194             /* exotic behaviors */
7195             if (p->fast_array) {
7196                 if (__JS_AtomIsTaggedInt(prop)) {
7197                     uint32_t idx = __JS_AtomToUInt32(prop);
7198                     if (idx < p->u.array.count) {
7199                         /* we avoid duplicating the code */
7200                         return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
7201                     } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
7202                                p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
7203                         return JS_UNDEFINED;
7204                     }
7205                 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
7206                            p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
7207                     int ret;
7208                     ret = JS_AtomIsNumericIndex(ctx, prop);
7209                     if (ret != 0) {
7210                         if (ret < 0)
7211                             return JS_EXCEPTION;
7212                         return JS_UNDEFINED;
7213                     }
7214                 }
7215             } else {
7216                 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7217                 if (em) {
7218                     if (em->get_property) {
7219                         JSValue obj1, retval;
7220                         /* XXX: should pass throw_ref_error */
7221                         /* Note: if 'p' is a prototype, it can be
7222                            freed in the called function */
7223                         obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7224                         retval = em->get_property(ctx, obj1, prop, this_obj);
7225                         JS_FreeValue(ctx, obj1);
7226                         return retval;
7227                     }
7228                     if (em->get_own_property) {
7229                         JSPropertyDescriptor desc;
7230                         int ret;
7231                         JSValue obj1;
7232 
7233                         /* Note: if 'p' is a prototype, it can be
7234                            freed in the called function */
7235                         obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7236                         ret = em->get_own_property(ctx, &desc, obj1, prop);
7237                         JS_FreeValue(ctx, obj1);
7238                         if (ret < 0)
7239                             return JS_EXCEPTION;
7240                         if (ret) {
7241                             if (desc.flags & JS_PROP_GETSET) {
7242                                 JS_FreeValue(ctx, desc.setter);
7243                                 return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL);
7244                             } else {
7245                                 return desc.value;
7246                             }
7247                         }
7248                     }
7249                 }
7250             }
7251         }
7252         p = p->shape->proto;
7253         if (!p)
7254             break;
7255     }
7256     if (unlikely(throw_ref_error)) {
7257         return JS_ThrowReferenceErrorNotDefined(ctx, prop);
7258     } else {
7259         return JS_UNDEFINED;
7260     }
7261 }
7262 
JS_ThrowTypeErrorPrivateNotFound(JSContext * ctx,JSAtom atom)7263 static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom)
7264 {
7265     return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist",
7266                                  atom);
7267 }
7268 
7269 /* Private fields can be added even on non extensible objects or
7270    Proxies */
JS_DefinePrivateField(JSContext * ctx,JSValueConst obj,JSValueConst name,JSValue val)7271 static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,
7272                                  JSValueConst name, JSValue val)
7273 {
7274     JSObject *p;
7275     JSShapeProperty *prs;
7276     JSProperty *pr;
7277     JSAtom prop;
7278 
7279     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
7280         JS_ThrowTypeErrorNotAnObject(ctx);
7281         goto fail;
7282     }
7283     /* safety check */
7284     if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
7285         JS_ThrowTypeErrorNotASymbol(ctx);
7286         goto fail;
7287     }
7288     prop = js_symbol_to_atom(ctx, JS_VALUE_CONST_CAST(name));
7289     p = JS_VALUE_GET_OBJ(obj);
7290     prs = find_own_property(&pr, p, prop);
7291     if (prs) {
7292         JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists",
7293                               prop);
7294         goto fail;
7295     }
7296     pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
7297     if (unlikely(!pr)) {
7298     fail:
7299         JS_FreeValue(ctx, val);
7300         return -1;
7301     }
7302     pr->u.value = val;
7303     return 0;
7304 }
7305 
JS_GetPrivateField(JSContext * ctx,JSValueConst obj,JSValueConst name)7306 static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj,
7307                                   JSValueConst name)
7308 {
7309     JSObject *p;
7310     JSShapeProperty *prs;
7311     JSProperty *pr;
7312     JSAtom prop;
7313 
7314     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
7315         return JS_ThrowTypeErrorNotAnObject(ctx);
7316     /* safety check */
7317     if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL))
7318         return JS_ThrowTypeErrorNotASymbol(ctx);
7319     prop = js_symbol_to_atom(ctx, JS_VALUE_CONST_CAST(name));
7320     p = JS_VALUE_GET_OBJ(obj);
7321     prs = find_own_property(&pr, p, prop);
7322     if (!prs) {
7323         JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
7324         return JS_EXCEPTION;
7325     }
7326     return JS_DupValue(ctx, pr->u.value);
7327 }
7328 
JS_SetPrivateField(JSContext * ctx,JSValueConst obj,JSValueConst name,JSValue val)7329 static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,
7330                               JSValueConst name, JSValue val)
7331 {
7332     JSObject *p;
7333     JSShapeProperty *prs;
7334     JSProperty *pr;
7335     JSAtom prop;
7336 
7337     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
7338         JS_ThrowTypeErrorNotAnObject(ctx);
7339         goto fail;
7340     }
7341     /* safety check */
7342     if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
7343         JS_ThrowTypeErrorNotASymbol(ctx);
7344         goto fail;
7345     }
7346     prop = js_symbol_to_atom(ctx, JS_VALUE_CONST_CAST(name));
7347     p = JS_VALUE_GET_OBJ(obj);
7348     prs = find_own_property(&pr, p, prop);
7349     if (!prs) {
7350         JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
7351     fail:
7352         JS_FreeValue(ctx, val);
7353         return -1;
7354     }
7355     set_value(ctx, &pr->u.value, val);
7356     return 0;
7357 }
7358 
JS_AddBrand(JSContext * ctx,JSValueConst obj,JSValueConst home_obj)7359 static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
7360 {
7361     JSObject *p, *p1;
7362     JSShapeProperty *prs;
7363     JSProperty *pr;
7364     JSValue brand;
7365     JSAtom brand_atom;
7366 
7367     if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) {
7368         JS_ThrowTypeErrorNotAnObject(ctx);
7369         return -1;
7370     }
7371     p = JS_VALUE_GET_OBJ(home_obj);
7372     prs = find_own_property(&pr, p, JS_ATOM_Private_brand);
7373     if (!prs) {
7374         brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE);
7375         if (JS_IsException(brand))
7376             return -1;
7377         /* if the brand is not present, add it */
7378         pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E);
7379         if (!pr) {
7380             JS_FreeValue(ctx, brand);
7381             return -1;
7382         }
7383         pr->u.value = JS_DupValue(ctx, brand);
7384     } else {
7385         brand = JS_DupValue(ctx, pr->u.value);
7386     }
7387     brand_atom = js_symbol_to_atom(ctx, brand);
7388 
7389     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
7390         JS_ThrowTypeErrorNotAnObject(ctx);
7391         JS_FreeAtom(ctx, brand_atom);
7392         return -1;
7393     }
7394     p1 = JS_VALUE_GET_OBJ(obj);
7395     pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);
7396     JS_FreeAtom(ctx, brand_atom);
7397     if (!pr)
7398         return -1;
7399     pr->u.value = JS_UNDEFINED;
7400     return 0;
7401 }
7402 
JS_CheckBrand(JSContext * ctx,JSValueConst obj,JSValueConst func)7403 static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
7404 {
7405     JSObject *p, *p1, *home_obj;
7406     JSShapeProperty *prs;
7407     JSProperty *pr;
7408     JSValueConst brand;
7409 
7410     /* get the home object of 'func' */
7411     if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) {
7412     not_obj:
7413         JS_ThrowTypeErrorNotAnObject(ctx);
7414         return -1;
7415     }
7416     p1 = JS_VALUE_GET_OBJ(func);
7417     if (!js_class_has_bytecode(p1->class_id))
7418         goto not_obj;
7419     home_obj = p1->u.func.home_object;
7420     if (!home_obj)
7421         goto not_obj;
7422     prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand);
7423     if (!prs) {
7424         JS_ThrowTypeError(ctx, "expecting <brand> private field");
7425         return -1;
7426     }
7427     brand = pr->u.value;
7428     /* safety check */
7429     if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL))
7430         goto not_obj;
7431 
7432     /* get the brand array of 'obj' */
7433     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
7434         goto not_obj;
7435     p = JS_VALUE_GET_OBJ(obj);
7436     prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, JS_VALUE_CONST_CAST(brand)));
7437     if (!prs) {
7438         JS_ThrowTypeError(ctx, "invalid brand on object");
7439         return -1;
7440     }
7441     return 0;
7442 }
7443 
js_string_obj_get_length(JSContext * ctx,JSValueConst obj)7444 static uint32_t js_string_obj_get_length(JSContext *ctx,
7445                                          JSValueConst obj)
7446 {
7447     JSObject *p;
7448     JSString *p1;
7449     uint32_t len = 0;
7450 
7451     /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
7452     p = JS_VALUE_GET_OBJ(obj);
7453     if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
7454         p1 = JS_VALUE_GET_STRING(p->u.object_data);
7455         len = p1->len;
7456     }
7457     return len;
7458 }
7459 
num_keys_cmp(const void * p1,const void * p2,void * opaque)7460 static int num_keys_cmp(const void *p1, const void *p2, void *opaque)
7461 {
7462     JSContext *ctx = opaque;
7463     JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom;
7464     JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom;
7465     uint32_t v1, v2;
7466     BOOL atom1_is_integer, atom2_is_integer;
7467 
7468     atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1);
7469     atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2);
7470     assert(atom1_is_integer && atom2_is_integer);
7471     if (v1 < v2)
7472         return -1;
7473     else if (v1 == v2)
7474         return 0;
7475     else
7476         return 1;
7477 }
7478 
js_free_prop_enum(JSContext * ctx,JSPropertyEnum * tab,uint32_t len)7479 static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len)
7480 {
7481     uint32_t i;
7482     if (tab) {
7483         for(i = 0; i < len; i++)
7484             JS_FreeAtom(ctx, tab[i].atom);
7485         js_free(ctx, tab);
7486     }
7487 }
7488 
7489 /* return < 0 in case if exception, 0 if OK. ptab and its atoms must
7490    be freed by the user. */
JS_GetOwnPropertyNamesInternal(JSContext * ctx,JSPropertyEnum ** ptab,uint32_t * plen,JSObject * p,int flags)7491 static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,
7492                                                       JSPropertyEnum **ptab,
7493                                                       uint32_t *plen,
7494                                                       JSObject *p, int flags)
7495 {
7496     int i, j;
7497     JSShape *sh;
7498     JSShapeProperty *prs;
7499     JSPropertyEnum *tab_atom, *tab_exotic;
7500     JSAtom atom;
7501     uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count;
7502     uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count;
7503     BOOL is_enumerable, num_sorted;
7504     uint32_t num_key;
7505     JSAtomKindEnum kind;
7506 
7507     /* clear pointer for consistency in case of failure */
7508     *ptab = NULL;
7509     *plen = 0;
7510 
7511     /* compute the number of returned properties */
7512     num_keys_count = 0;
7513     str_keys_count = 0;
7514     sym_keys_count = 0;
7515     exotic_keys_count = 0;
7516     exotic_count = 0;
7517     tab_exotic = NULL;
7518     sh = p->shape;
7519     for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
7520         atom = prs->atom;
7521         if (atom != JS_ATOM_NULL) {
7522             is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
7523             kind = JS_AtomGetKind(ctx, atom);
7524             if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
7525                 ((flags >> kind) & 1) != 0) {
7526                 /* need to raise an exception in case of the module
7527                    name space (implicit GetOwnProperty) */
7528                 if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) &&
7529                     (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) {
7530                     JSVarRef *var_ref = p->prop[i].u.var_ref;
7531                     if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) {
7532                         JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
7533                         return -1;
7534                     }
7535                 }
7536                 if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
7537                     num_keys_count++;
7538                 } else if (kind == JS_ATOM_KIND_STRING) {
7539                     str_keys_count++;
7540                 } else {
7541                     sym_keys_count++;
7542                 }
7543             }
7544         }
7545     }
7546 
7547     if (p->is_exotic) {
7548         if (p->fast_array) {
7549             if (flags & JS_GPN_STRING_MASK) {
7550                 num_keys_count += p->u.array.count;
7551             }
7552         } else if (p->class_id == JS_CLASS_STRING) {
7553             if (flags & JS_GPN_STRING_MASK) {
7554                 num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7555             }
7556         } else {
7557             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7558             if (em && em->get_own_property_names) {
7559                 if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count,
7560                                                JS_MKPTR(JS_TAG_OBJECT, p)))
7561                     return -1;
7562                 for(i = 0; i < exotic_count; i++) {
7563                     atom = tab_exotic[i].atom;
7564                     kind = JS_AtomGetKind(ctx, atom);
7565                     if (((flags >> kind) & 1) != 0) {
7566                         is_enumerable = FALSE;
7567                         if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) {
7568                             JSPropertyDescriptor desc;
7569                             int res;
7570                             /* set the "is_enumerable" field if necessary */
7571                             res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
7572                             if (res < 0) {
7573                                 js_free_prop_enum(ctx, tab_exotic, exotic_count);
7574                                 return -1;
7575                             }
7576                             if (res) {
7577                                 is_enumerable =
7578                                     ((desc.flags & JS_PROP_ENUMERABLE) != 0);
7579                                 js_free_desc(ctx, &desc);
7580                             }
7581                             tab_exotic[i].is_enumerable = is_enumerable;
7582                         }
7583                         if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) {
7584                             exotic_keys_count++;
7585                         }
7586                     }
7587                 }
7588             }
7589         }
7590     }
7591 
7592     /* fill them */
7593 
7594     atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count;
7595     /* avoid allocating 0 bytes */
7596     tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1));
7597     if (!tab_atom) {
7598         js_free_prop_enum(ctx, tab_exotic, exotic_count);
7599         return -1;
7600     }
7601 
7602     num_index = 0;
7603     str_index = num_keys_count;
7604     sym_index = str_index + str_keys_count;
7605 
7606     num_sorted = TRUE;
7607     sh = p->shape;
7608     for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
7609         atom = prs->atom;
7610         if (atom != JS_ATOM_NULL) {
7611             is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
7612             kind = JS_AtomGetKind(ctx, atom);
7613             if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
7614                 ((flags >> kind) & 1) != 0) {
7615                 if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
7616                     j = num_index++;
7617                     num_sorted = FALSE;
7618                 } else if (kind == JS_ATOM_KIND_STRING) {
7619                     j = str_index++;
7620                 } else {
7621                     j = sym_index++;
7622                 }
7623                 tab_atom[j].atom = JS_DupAtom(ctx, atom);
7624                 tab_atom[j].is_enumerable = is_enumerable;
7625             }
7626         }
7627     }
7628 
7629     if (p->is_exotic) {
7630         int len;
7631         if (p->fast_array) {
7632             if (flags & JS_GPN_STRING_MASK) {
7633                 len = p->u.array.count;
7634                 goto add_array_keys;
7635             }
7636         } else if (p->class_id == JS_CLASS_STRING) {
7637             if (flags & JS_GPN_STRING_MASK) {
7638                 len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7639             add_array_keys:
7640                 for(i = 0; i < len; i++) {
7641                     tab_atom[num_index].atom = __JS_AtomFromUInt32(i);
7642                     if (tab_atom[num_index].atom == JS_ATOM_NULL) {
7643                         js_free_prop_enum(ctx, tab_atom, num_index);
7644                         return -1;
7645                     }
7646                     tab_atom[num_index].is_enumerable = TRUE;
7647                     num_index++;
7648                 }
7649             }
7650         } else {
7651             /* Note: exotic keys are not reordered and comes after the object own properties. */
7652             for(i = 0; i < exotic_count; i++) {
7653                 atom = tab_exotic[i].atom;
7654                 is_enumerable = tab_exotic[i].is_enumerable;
7655                 kind = JS_AtomGetKind(ctx, atom);
7656                 if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
7657                     ((flags >> kind) & 1) != 0) {
7658                     tab_atom[sym_index].atom = atom;
7659                     tab_atom[sym_index].is_enumerable = is_enumerable;
7660                     sym_index++;
7661                 } else {
7662                     JS_FreeAtom(ctx, atom);
7663                 }
7664             }
7665             js_free(ctx, tab_exotic);
7666         }
7667     }
7668 
7669     assert(num_index == num_keys_count);
7670     assert(str_index == num_keys_count + str_keys_count);
7671     assert(sym_index == atom_count);
7672 
7673     if (num_keys_count != 0 && !num_sorted) {
7674         rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp,
7675                ctx);
7676     }
7677     *ptab = tab_atom;
7678     *plen = atom_count;
7679     return 0;
7680 }
7681 
JS_GetOwnPropertyNames(JSContext * ctx,JSPropertyEnum ** ptab,uint32_t * plen,JSValueConst obj,int flags)7682 int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
7683                            uint32_t *plen, JSValueConst obj, int flags)
7684 {
7685     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
7686         JS_ThrowTypeErrorNotAnObject(ctx);
7687         return -1;
7688     }
7689     return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
7690                                           JS_VALUE_GET_OBJ(obj), flags);
7691 }
7692 
7693 /* Return -1 if exception,
7694    FALSE if the property does not exist, TRUE if it exists. If TRUE is
7695    returned, the property descriptor 'desc' is filled present. */
JS_GetOwnPropertyInternal(JSContext * ctx,JSPropertyDescriptor * desc,JSObject * p,JSAtom prop)7696 static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
7697                                      JSObject *p, JSAtom prop)
7698 {
7699     JSShapeProperty *prs;
7700     JSProperty *pr;
7701 
7702 retry:
7703     prs = find_own_property(&pr, p, prop);
7704     if (prs) {
7705         if (desc) {
7706             desc->flags = prs->flags & JS_PROP_C_W_E;
7707             desc->getter = JS_UNDEFINED;
7708             desc->setter = JS_UNDEFINED;
7709             desc->value = JS_UNDEFINED;
7710             if (unlikely(prs->flags & JS_PROP_TMASK)) {
7711                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
7712                     desc->flags |= JS_PROP_GETSET;
7713                     if (pr->u.getset.getter)
7714                         desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
7715                     if (pr->u.getset.setter)
7716                         desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
7717                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
7718                     JSValue val = *pr->u.var_ref->pvalue;
7719                     if (unlikely(JS_IsUninitialized(val))) {
7720                         JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
7721                         return -1;
7722                     }
7723                     desc->value = JS_DupValue(ctx, val);
7724                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
7725                     /* Instantiate property and retry */
7726                     if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
7727                         return -1;
7728                     goto retry;
7729                 }
7730             } else {
7731                 desc->value = JS_DupValue(ctx, pr->u.value);
7732             }
7733         } else {
7734             /* for consistency, send the exception even if desc is NULL */
7735             if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) {
7736                 if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) {
7737                     JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
7738                     return -1;
7739                 }
7740             } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
7741                 /* nothing to do: delay instantiation until actual value and/or attributes are read */
7742             }
7743         }
7744         return TRUE;
7745     }
7746     if (p->is_exotic) {
7747         if (p->fast_array) {
7748             /* specific case for fast arrays */
7749             if (__JS_AtomIsTaggedInt(prop)) {
7750                 uint32_t idx;
7751                 idx = __JS_AtomToUInt32(prop);
7752                 if (idx < p->u.array.count) {
7753                     if (desc) {
7754                         desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE |
7755                             JS_PROP_CONFIGURABLE;
7756                         desc->getter = JS_UNDEFINED;
7757                         desc->setter = JS_UNDEFINED;
7758                         desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
7759                     }
7760                     return TRUE;
7761                 }
7762             }
7763         } else {
7764             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7765             if (em && em->get_own_property) {
7766                 return em->get_own_property(ctx, desc,
7767                                             JS_MKPTR(JS_TAG_OBJECT, p), prop);
7768             }
7769         }
7770     }
7771     return FALSE;
7772 }
7773 
JS_GetOwnProperty(JSContext * ctx,JSPropertyDescriptor * desc,JSValueConst obj,JSAtom prop)7774 int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,
7775                       JSValueConst obj, JSAtom prop)
7776 {
7777     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
7778         JS_ThrowTypeErrorNotAnObject(ctx);
7779         return -1;
7780     }
7781     return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop);
7782 }
7783 
7784 /* return -1 if exception (Proxy object only) or TRUE/FALSE */
JS_IsExtensible(JSContext * ctx,JSValueConst obj)7785 int JS_IsExtensible(JSContext *ctx, JSValueConst obj)
7786 {
7787     JSObject *p;
7788 
7789     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
7790         return FALSE;
7791     p = JS_VALUE_GET_OBJ(obj);
7792     if (unlikely(p->class_id == JS_CLASS_PROXY))
7793         return js_proxy_isExtensible(ctx, obj);
7794     else
7795         return p->extensible;
7796 }
7797 
7798 /* return -1 if exception (Proxy object only) or TRUE/FALSE */
JS_PreventExtensions(JSContext * ctx,JSValueConst obj)7799 int JS_PreventExtensions(JSContext *ctx, JSValueConst obj)
7800 {
7801     JSObject *p;
7802 
7803     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
7804         return FALSE;
7805     p = JS_VALUE_GET_OBJ(obj);
7806     if (unlikely(p->class_id == JS_CLASS_PROXY))
7807         return js_proxy_preventExtensions(ctx, obj);
7808     p->extensible = FALSE;
7809     return TRUE;
7810 }
7811 
7812 /* return -1 if exception otherwise TRUE or FALSE */
JS_HasProperty(JSContext * ctx,JSValueConst obj,JSAtom prop)7813 int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop)
7814 {
7815     JSObject *p;
7816     int ret;
7817     JSValue obj1;
7818 
7819     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
7820         return FALSE;
7821     p = JS_VALUE_GET_OBJ(obj);
7822     for(;;) {
7823         if (p->is_exotic) {
7824             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7825             if (em && em->has_property) {
7826                 /* has_property can free the prototype */
7827                 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7828                 ret = em->has_property(ctx, obj1, prop);
7829                 JS_FreeValue(ctx, obj1);
7830                 return ret;
7831             }
7832         }
7833         /* JS_GetOwnPropertyInternal can free the prototype */
7834         JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7835         ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop);
7836         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7837         if (ret != 0)
7838             return ret;
7839         if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
7840             p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
7841             ret = JS_AtomIsNumericIndex(ctx, prop);
7842             if (ret != 0) {
7843                 if (ret < 0)
7844                     return -1;
7845                 return FALSE;
7846             }
7847         }
7848         p = p->shape->proto;
7849         if (!p)
7850             break;
7851     }
7852     return FALSE;
7853 }
7854 
7855 /* val must be a symbol */
js_symbol_to_atom(JSContext * ctx,JSValue val)7856 static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val)
7857 {
7858     JSAtomStruct *p = JS_VALUE_GET_PTR(val);
7859     return js_get_atom_index(ctx->rt, p);
7860 }
7861 
7862 /* return JS_ATOM_NULL in case of exception */
JS_ValueToAtom(JSContext * ctx,JSValueConst val)7863 JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)
7864 {
7865     JSAtom atom;
7866     uint32_t tag;
7867     tag = JS_VALUE_GET_TAG(val);
7868     if (tag == JS_TAG_INT &&
7869         (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) {
7870         /* fast path for integer values */
7871         atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val));
7872     } else if (tag == JS_TAG_SYMBOL) {
7873         JSAtomStruct *p = JS_VALUE_GET_PTR(val);
7874         atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p));
7875     } else {
7876         JSValue str;
7877         str = JS_ToPropertyKey(ctx, val);
7878         if (JS_IsException(str))
7879             return JS_ATOM_NULL;
7880         if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) {
7881             atom = js_symbol_to_atom(ctx, str);
7882         } else {
7883             atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str));
7884         }
7885     }
7886     return atom;
7887 }
7888 
JS_GetPropertyValue(JSContext * ctx,JSValueConst this_obj,JSValue prop)7889 static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
7890                                    JSValue prop)
7891 {
7892     JSAtom atom;
7893     JSValue ret;
7894 
7895     if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
7896                JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
7897         JSObject *p;
7898         uint32_t idx, len;
7899         /* fast path for array access */
7900         p = JS_VALUE_GET_OBJ(this_obj);
7901         idx = JS_VALUE_GET_INT(prop);
7902         len = (uint32_t)p->u.array.count;
7903         if (unlikely(idx >= len))
7904             goto slow_path;
7905         switch(p->class_id) {
7906         case JS_CLASS_ARRAY:
7907         case JS_CLASS_ARGUMENTS:
7908             return JS_DupValue(ctx, p->u.array.u.values[idx]);
7909         case JS_CLASS_INT8_ARRAY:
7910             return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]);
7911         case JS_CLASS_UINT8C_ARRAY:
7912         case JS_CLASS_UINT8_ARRAY:
7913             return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]);
7914         case JS_CLASS_INT16_ARRAY:
7915             return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]);
7916         case JS_CLASS_UINT16_ARRAY:
7917             return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]);
7918         case JS_CLASS_INT32_ARRAY:
7919             return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]);
7920         case JS_CLASS_UINT32_ARRAY:
7921             return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]);
7922 #ifdef CONFIG_BIGNUM
7923         case JS_CLASS_BIG_INT64_ARRAY:
7924             return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]);
7925         case JS_CLASS_BIG_UINT64_ARRAY:
7926             return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);
7927 #endif
7928         case JS_CLASS_FLOAT32_ARRAY:
7929             return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]);
7930         case JS_CLASS_FLOAT64_ARRAY:
7931             return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]);
7932         default:
7933             goto slow_path;
7934         }
7935     } else {
7936     slow_path:
7937         atom = JS_ValueToAtom(ctx, prop);
7938         JS_FreeValue(ctx, prop);
7939         if (unlikely(atom == JS_ATOM_NULL))
7940             return JS_EXCEPTION;
7941         ret = JS_GetProperty(ctx, this_obj, atom);
7942         JS_FreeAtom(ctx, atom);
7943         return ret;
7944     }
7945 }
7946 
JS_GetPropertyUint32(JSContext * ctx,JSValueConst this_obj,uint32_t idx)7947 JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
7948                              uint32_t idx)
7949 {
7950     return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx));
7951 }
7952 
7953 /* Check if an object has a generalized numeric property. Return value:
7954    -1 for exception,
7955    TRUE if property exists, stored into *pval,
7956    FALSE if proprty does not exist.
7957  */
JS_TryGetPropertyInt64(JSContext * ctx,JSValueConst obj,int64_t idx,JSValue * pval)7958 static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval)
7959 {
7960     JSValue val = JS_UNDEFINED;
7961     JSAtom prop;
7962     int present;
7963 
7964     if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) {
7965         /* fast path */
7966         present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx));
7967         if (present > 0) {
7968             val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
7969             if (unlikely(JS_IsException(val)))
7970                 present = -1;
7971         }
7972     } else {
7973         prop = JS_NewAtomInt64(ctx, idx);
7974         present = -1;
7975         if (likely(prop != JS_ATOM_NULL)) {
7976             present = JS_HasProperty(ctx, obj, prop);
7977             if (present > 0) {
7978                 val = JS_GetProperty(ctx, obj, prop);
7979                 if (unlikely(JS_IsException(val)))
7980                     present = -1;
7981             }
7982             JS_FreeAtom(ctx, prop);
7983         }
7984     }
7985     *pval = val;
7986     return present;
7987 }
7988 
JS_GetPropertyInt64(JSContext * ctx,JSValueConst obj,int64_t idx)7989 static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx)
7990 {
7991     JSAtom prop;
7992     JSValue val;
7993 
7994     if ((uint64_t)idx <= INT32_MAX) {
7995         /* fast path for fast arrays */
7996         return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
7997     }
7998     prop = JS_NewAtomInt64(ctx, idx);
7999     if (prop == JS_ATOM_NULL)
8000         return JS_EXCEPTION;
8001 
8002     val = JS_GetProperty(ctx, obj, prop);
8003     JS_FreeAtom(ctx, prop);
8004     return val;
8005 }
8006 
JS_GetPropertyStr(JSContext * ctx,JSValueConst this_obj,const char * prop)8007 JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,
8008                           const char *prop)
8009 {
8010     JSAtom atom;
8011     JSValue ret;
8012     atom = JS_NewAtom(ctx, prop);
8013     ret = JS_GetProperty(ctx, this_obj, atom);
8014     JS_FreeAtom(ctx, atom);
8015     return ret;
8016 }
8017 
8018 /* Note: the property value is not initialized. Return NULL if memory
8019    error. */
add_property(JSContext * ctx,JSObject * p,JSAtom prop,int prop_flags)8020 static JSProperty *add_property(JSContext *ctx,
8021                                 JSObject *p, JSAtom prop, int prop_flags)
8022 {
8023     JSShape *sh, *new_sh;
8024 
8025     sh = p->shape;
8026     if (sh->is_hashed) {
8027         /* try to find an existing shape */
8028         new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags);
8029         if (new_sh) {
8030             /* matching shape found: use it */
8031             /*  the property array may need to be resized */
8032             if (new_sh->prop_size != sh->prop_size) {
8033                 JSProperty *new_prop;
8034                 new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) *
8035                                       new_sh->prop_size);
8036                 if (!new_prop)
8037                     return NULL;
8038                 p->prop = new_prop;
8039             }
8040             p->shape = js_dup_shape(new_sh);
8041             js_free_shape(ctx->rt, sh);
8042             return &p->prop[new_sh->prop_count - 1];
8043         } else if (sh->header.ref_count != 1) {
8044             /* if the shape is shared, clone it */
8045             new_sh = js_clone_shape(ctx, sh);
8046             if (!new_sh)
8047                 return NULL;
8048             /* hash the cloned shape */
8049             new_sh->is_hashed = TRUE;
8050             js_shape_hash_link(ctx->rt, new_sh);
8051             js_free_shape(ctx->rt, p->shape);
8052             p->shape = new_sh;
8053         }
8054     }
8055     assert(p->shape->header.ref_count == 1);
8056     if (add_shape_property(ctx, &p->shape, p, prop, prop_flags))
8057         return NULL;
8058     return &p->prop[p->shape->prop_count - 1];
8059 }
8060 
8061 /* can be called on Array or Arguments objects. return < 0 if
8062    memory alloc error. */
convert_fast_array_to_array(JSContext * ctx,JSObject * p)8063 static no_inline __exception int convert_fast_array_to_array(JSContext *ctx,
8064                                                              JSObject *p)
8065 {
8066     JSProperty *pr;
8067     JSShape *sh;
8068     JSValue *tab;
8069     uint32_t i, len, new_count;
8070 
8071     if (js_shape_prepare_update(ctx, p, NULL))
8072         return -1;
8073     len = p->u.array.count;
8074     /* resize the properties once to simplify the error handling */
8075     sh = p->shape;
8076     new_count = sh->prop_count + len;
8077     if (new_count > sh->prop_size) {
8078         if (resize_properties(ctx, &p->shape, p, new_count))
8079             return -1;
8080     }
8081 
8082     tab = p->u.array.u.values;
8083     for(i = 0; i < len; i++) {
8084         /* add_property cannot fail here but
8085            __JS_AtomFromUInt32(i) fails for i > INT32_MAX */
8086         pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E);
8087         pr->u.value = *tab++;
8088     }
8089     js_free(ctx, p->u.array.u.values);
8090     p->u.array.count = 0;
8091     p->u.array.u.values = NULL; /* fail safe */
8092     p->u.array.u1.size = 0;
8093     p->fast_array = 0;
8094     return 0;
8095 }
8096 
delete_property(JSContext * ctx,JSObject * p,JSAtom atom)8097 static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
8098 {
8099     JSShape *sh;
8100     JSShapeProperty *pr, *lpr, *prop;
8101     JSProperty *pr1;
8102     uint32_t lpr_idx;
8103     intptr_t h, h1;
8104 
8105  redo:
8106     sh = p->shape;
8107     h1 = atom & sh->prop_hash_mask;
8108     h = prop_hash_end(sh)[-h1 - 1];
8109     prop = get_shape_prop(sh);
8110     lpr = NULL;
8111     lpr_idx = 0;   /* prevent warning */
8112     while (h != 0) {
8113         pr = &prop[h - 1];
8114         if (likely(pr->atom == atom)) {
8115             /* found ! */
8116             if (!(pr->flags & JS_PROP_CONFIGURABLE))
8117                 return FALSE;
8118             /* realloc the shape if needed */
8119             if (lpr)
8120                 lpr_idx = lpr - get_shape_prop(sh);
8121             if (js_shape_prepare_update(ctx, p, &pr))
8122                 return -1;
8123             sh = p->shape;
8124             /* remove property */
8125             if (lpr) {
8126                 lpr = get_shape_prop(sh) + lpr_idx;
8127                 lpr->hash_next = pr->hash_next;
8128             } else {
8129                 prop_hash_end(sh)[-h1 - 1] = pr->hash_next;
8130             }
8131             sh->deleted_prop_count++;
8132             /* free the entry */
8133             pr1 = &p->prop[h - 1];
8134             free_property(ctx->rt, pr1, pr->flags);
8135             JS_FreeAtom(ctx, pr->atom);
8136             /* put default values */
8137             pr->flags = 0;
8138             pr->atom = JS_ATOM_NULL;
8139             pr1->u.value = JS_UNDEFINED;
8140 
8141             /* compact the properties if too many deleted properties */
8142             if (sh->deleted_prop_count >= 8 &&
8143                 sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) {
8144                 compact_properties(ctx, p);
8145             }
8146             return TRUE;
8147         }
8148         lpr = pr;
8149         h = pr->hash_next;
8150     }
8151 
8152     if (p->is_exotic) {
8153         if (p->fast_array) {
8154             uint32_t idx;
8155             if (JS_AtomIsArrayIndex(ctx, &idx, atom) &&
8156                 idx < p->u.array.count) {
8157                 if (p->class_id == JS_CLASS_ARRAY ||
8158                     p->class_id == JS_CLASS_ARGUMENTS) {
8159                     /* Special case deleting the last element of a fast Array */
8160                     if (idx == p->u.array.count - 1) {
8161                         JS_FreeValue(ctx, p->u.array.u.values[idx]);
8162                         p->u.array.count = idx;
8163                         return TRUE;
8164                     }
8165                     if (convert_fast_array_to_array(ctx, p))
8166                         return -1;
8167                     goto redo;
8168                 } else {
8169                     return FALSE;
8170                 }
8171             }
8172         } else {
8173             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
8174             if (em && em->delete_property) {
8175                 return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom);
8176             }
8177         }
8178     }
8179     /* not found */
8180     return TRUE;
8181 }
8182 
call_setter(JSContext * ctx,JSObject * setter,JSValueConst this_obj,JSValue val,int flags)8183 static int call_setter(JSContext *ctx, JSObject *setter,
8184                        JSValueConst this_obj, JSValue val, int flags)
8185 {
8186     JSValue ret, func;
8187     if (likely(setter)) {
8188         func = JS_MKPTR(JS_TAG_OBJECT, setter);
8189         /* Note: the field could be removed in the setter */
8190         func = JS_DupValue(ctx, func);
8191         ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val);
8192         JS_FreeValue(ctx, val);
8193         if (JS_IsException(ret))
8194             return -1;
8195         JS_FreeValue(ctx, ret);
8196         return TRUE;
8197     } else {
8198         JS_FreeValue(ctx, val);
8199         if ((flags & JS_PROP_THROW) ||
8200             ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
8201             JS_ThrowTypeError(ctx, "no setter for property");
8202             return -1;
8203         }
8204         return FALSE;
8205     }
8206 }
8207 
8208 /* set the array length and remove the array elements if necessary. */
set_array_length(JSContext * ctx,JSObject * p,JSValue val,int flags)8209 static int set_array_length(JSContext *ctx, JSObject *p, JSValue val,
8210                             int flags)
8211 {
8212     uint32_t len, idx, cur_len;
8213     int i, ret;
8214 
8215     /* Note: this call can reallocate the properties of 'p' */
8216     ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE);
8217     if (ret)
8218         return -1;
8219     /* JS_ToArrayLengthFree() must be done before the read-only test */
8220     if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE)))
8221         return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
8222 
8223     if (likely(p->fast_array)) {
8224         uint32_t old_len = p->u.array.count;
8225         if (len < old_len) {
8226             for(i = len; i < old_len; i++) {
8227                 JS_FreeValue(ctx, p->u.array.u.values[i]);
8228             }
8229             p->u.array.count = len;
8230         }
8231         p->prop[0].u.value = JS_NewUint32(ctx, len);
8232     } else {
8233         /* Note: length is always a uint32 because the object is an
8234            array */
8235         JS_ToUint32(ctx, &cur_len, p->prop[0].u.value);
8236         if (len < cur_len) {
8237             uint32_t d;
8238             JSShape *sh;
8239             JSShapeProperty *pr;
8240 
8241             d = cur_len - len;
8242             sh = p->shape;
8243             if (d <= sh->prop_count) {
8244                 JSAtom atom;
8245 
8246                 /* faster to iterate */
8247                 while (cur_len > len) {
8248                     atom = JS_NewAtomUInt32(ctx, cur_len - 1);
8249                     ret = delete_property(ctx, p, atom);
8250                     JS_FreeAtom(ctx, atom);
8251                     if (unlikely(!ret)) {
8252                         /* unlikely case: property is not
8253                            configurable */
8254                         break;
8255                     }
8256                     cur_len--;
8257                 }
8258             } else {
8259                 /* faster to iterate thru all the properties. Need two
8260                    passes in case one of the property is not
8261                    configurable */
8262                 cur_len = len;
8263                 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
8264                     i++, pr++) {
8265                     if (pr->atom != JS_ATOM_NULL &&
8266                         JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
8267                         if (idx >= cur_len &&
8268                             !(pr->flags & JS_PROP_CONFIGURABLE)) {
8269                             cur_len = idx + 1;
8270                         }
8271                     }
8272                 }
8273 
8274                 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
8275                     i++, pr++) {
8276                     if (pr->atom != JS_ATOM_NULL &&
8277                         JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
8278                         if (idx >= cur_len) {
8279                             /* remove the property */
8280                             delete_property(ctx, p, pr->atom);
8281                             /* WARNING: the shape may have been modified */
8282                             sh = p->shape;
8283                             pr = get_shape_prop(sh) + i;
8284                         }
8285                     }
8286                 }
8287             }
8288         } else {
8289             cur_len = len;
8290         }
8291         set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len));
8292         if (unlikely(cur_len > len)) {
8293             return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable");
8294         }
8295     }
8296     return TRUE;
8297 }
8298 
8299 /* return -1 if exception */
expand_fast_array(JSContext * ctx,JSObject * p,uint32_t new_len)8300 static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len)
8301 {
8302     uint32_t new_size;
8303     size_t slack;
8304     JSValue *new_array_prop;
8305     /* XXX: potential arithmetic overflow */
8306     new_size = max_int(new_len, p->u.array.u1.size * 3 / 2);
8307     new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack);
8308     if (!new_array_prop)
8309         return -1;
8310     new_size += slack / sizeof(*new_array_prop);
8311     p->u.array.u.values = new_array_prop;
8312     p->u.array.u1.size = new_size;
8313     return 0;
8314 }
8315 
8316 /* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array =
8317    TRUE and p->extensible = TRUE */
add_fast_array_element(JSContext * ctx,JSObject * p,JSValue val,int flags)8318 static int add_fast_array_element(JSContext *ctx, JSObject *p,
8319                                   JSValue val, int flags)
8320 {
8321     uint32_t new_len, array_len;
8322     /* extend the array by one */
8323     /* XXX: convert to slow array if new_len > 2^31-1 elements */
8324     new_len = p->u.array.count + 1;
8325     /* update the length if necessary. We assume that if the length is
8326        not an integer, then if it >= 2^31.  */
8327     if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) {
8328         array_len = JS_VALUE_GET_INT(p->prop[0].u.value);
8329         if (new_len > array_len) {
8330             if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) {
8331                 JS_FreeValue(ctx, val);
8332                 return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
8333             }
8334             p->prop[0].u.value = JS_NewInt32(ctx, new_len);
8335         }
8336     }
8337     if (unlikely(new_len > p->u.array.u1.size)) {
8338         if (expand_fast_array(ctx, p, new_len)) {
8339             JS_FreeValue(ctx, val);
8340             return -1;
8341         }
8342     }
8343     p->u.array.u.values[new_len - 1] = val;
8344     p->u.array.count = new_len;
8345     return TRUE;
8346 }
8347 
js_free_desc(JSContext * ctx,JSPropertyDescriptor * desc)8348 static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)
8349 {
8350     JS_FreeValue(ctx, desc->getter);
8351     JS_FreeValue(ctx, desc->setter);
8352     JS_FreeValue(ctx, desc->value);
8353 }
8354 
8355 /* generic (and slower) version of JS_SetProperty() for
8356  * Reflect.set(). 'obj' must be an object.  */
JS_SetPropertyGeneric(JSContext * ctx,JSValueConst obj,JSAtom prop,JSValue val,JSValueConst this_obj,int flags)8357 static int JS_SetPropertyGeneric(JSContext *ctx,
8358                                  JSValueConst obj, JSAtom prop,
8359                                  JSValue val, JSValueConst this_obj,
8360                                  int flags)
8361 {
8362     int ret;
8363     JSPropertyDescriptor desc;
8364     JSValue obj1;
8365     JSObject *p;
8366 
8367     obj1 = JS_DupValue(ctx, obj);
8368     for(;;) {
8369         p = JS_VALUE_GET_OBJ(obj1);
8370         if (p->is_exotic) {
8371             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
8372             if (em && em->set_property) {
8373                 ret = em->set_property(ctx, obj1, prop,
8374                                        val, this_obj, flags);
8375                 JS_FreeValue(ctx, obj1);
8376                 JS_FreeValue(ctx, val);
8377                 return ret;
8378             }
8379         }
8380 
8381         ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
8382         if (ret < 0) {
8383             JS_FreeValue(ctx, obj1);
8384             JS_FreeValue(ctx, val);
8385             return ret;
8386         }
8387         if (ret) {
8388             if (desc.flags & JS_PROP_GETSET) {
8389                 JSObject *setter;
8390                 if (JS_IsUndefined(desc.setter))
8391                     setter = NULL;
8392                 else
8393                     setter = JS_VALUE_GET_OBJ(desc.setter);
8394                 ret = call_setter(ctx, setter, this_obj, val, flags);
8395                 JS_FreeValue(ctx, desc.getter);
8396                 JS_FreeValue(ctx, desc.setter);
8397                 JS_FreeValue(ctx, obj1);
8398                 return ret;
8399             } else {
8400                 JS_FreeValue(ctx, desc.value);
8401                 if (!(desc.flags & JS_PROP_WRITABLE)) {
8402                     JS_FreeValue(ctx, obj1);
8403                     goto read_only_error;
8404                 }
8405             }
8406             break;
8407         }
8408         /* Note: at this point 'obj1' cannot be a proxy. XXX: may have
8409            to check recursion */
8410         obj1 = JS_GetPrototypeFree(ctx, obj1);
8411         if (JS_IsNull(obj1))
8412             break;
8413     }
8414     JS_FreeValue(ctx, obj1);
8415 
8416     if (!JS_IsObject(this_obj)) {
8417         JS_FreeValue(ctx, val);
8418         return JS_ThrowTypeErrorOrFalse(ctx, flags, "receiver is not an object");
8419     }
8420 
8421     p = JS_VALUE_GET_OBJ(this_obj);
8422 
8423     /* modify the property in this_obj if it already exists */
8424     ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
8425     if (ret < 0) {
8426         JS_FreeValue(ctx, val);
8427         return ret;
8428     }
8429     if (ret) {
8430         if (desc.flags & JS_PROP_GETSET) {
8431             JS_FreeValue(ctx, desc.getter);
8432             JS_FreeValue(ctx, desc.setter);
8433             JS_FreeValue(ctx, val);
8434             return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden");
8435         } else {
8436             JS_FreeValue(ctx, desc.value);
8437             if (!(desc.flags & JS_PROP_WRITABLE) ||
8438                 p->class_id == JS_CLASS_MODULE_NS) {
8439             read_only_error:
8440                 JS_FreeValue(ctx, val);
8441                 return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
8442             }
8443         }
8444         ret = JS_DefineProperty(ctx, this_obj, prop, val,
8445                                 JS_UNDEFINED, JS_UNDEFINED,
8446                                 JS_PROP_HAS_VALUE);
8447         JS_FreeValue(ctx, val);
8448         return ret;
8449     }
8450 
8451     ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
8452                             flags |
8453                             JS_PROP_HAS_VALUE |
8454                             JS_PROP_HAS_ENUMERABLE |
8455                             JS_PROP_HAS_WRITABLE |
8456                             JS_PROP_HAS_CONFIGURABLE |
8457                             JS_PROP_C_W_E);
8458     JS_FreeValue(ctx, val);
8459     return ret;
8460 }
8461 
8462 /* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is
8463    freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD,
8464    JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set,
8465    the new property is not added and an error is raised. */
JS_SetPropertyInternal(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSValue val,int flags)8466 int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,
8467                            JSAtom prop, JSValue val, int flags)
8468 {
8469     JSObject *p, *p1;
8470     JSShapeProperty *prs;
8471     JSProperty *pr;
8472     uint32_t tag;
8473     JSPropertyDescriptor desc;
8474     int ret;
8475 #if 0
8476     printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n");
8477 #endif
8478     tag = JS_VALUE_GET_TAG(this_obj);
8479     if (unlikely(tag != JS_TAG_OBJECT)) {
8480         switch(tag) {
8481         case JS_TAG_NULL:
8482             JS_FreeValue(ctx, val);
8483             JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop);
8484             return -1;
8485         case JS_TAG_UNDEFINED:
8486             JS_FreeValue(ctx, val);
8487             JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop);
8488             return -1;
8489         default:
8490             /* even on a primitive type we can have setters on the prototype */
8491             p = NULL;
8492             p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj));
8493             goto prototype_lookup;
8494         }
8495     }
8496     p = JS_VALUE_GET_OBJ(this_obj);
8497 retry:
8498     prs = find_own_property(&pr, p, prop);
8499     if (prs) {
8500         if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
8501                                   JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
8502             /* fast case */
8503             set_value(ctx, &pr->u.value, val);
8504             return TRUE;
8505         } else if (prs->flags & JS_PROP_LENGTH) {
8506             assert(p->class_id == JS_CLASS_ARRAY);
8507             assert(prop == JS_ATOM_length);
8508             return set_array_length(ctx, p, val, flags);
8509         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
8510             return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
8511         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
8512             /* JS_PROP_WRITABLE is always true for variable
8513                references, but they are write protected in module name
8514                spaces. */
8515             if (p->class_id == JS_CLASS_MODULE_NS)
8516                 goto read_only_prop;
8517             set_value(ctx, pr->u.var_ref->pvalue, val);
8518             return TRUE;
8519         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
8520             /* Instantiate property and retry (potentially useless) */
8521             if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) {
8522                 JS_FreeValue(ctx, val);
8523                 return -1;
8524             }
8525             goto retry;
8526         } else {
8527             goto read_only_prop;
8528         }
8529     }
8530 
8531     p1 = p;
8532     for(;;) {
8533         if (p1->is_exotic) {
8534             if (p1->fast_array) {
8535                 if (__JS_AtomIsTaggedInt(prop)) {
8536                     uint32_t idx = __JS_AtomToUInt32(prop);
8537                     if (idx < p1->u.array.count) {
8538                         if (unlikely(p == p1))
8539                             return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags);
8540                         else
8541                             break;
8542                     } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
8543                                p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
8544                         goto typed_array_oob;
8545                     }
8546                 } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
8547                            p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
8548                     ret = JS_AtomIsNumericIndex(ctx, prop);
8549                     if (ret != 0) {
8550                         if (ret < 0) {
8551                             JS_FreeValue(ctx, val);
8552                             return -1;
8553                         }
8554                     typed_array_oob:
8555                         val = JS_ToNumberFree(ctx, val);
8556                         JS_FreeValue(ctx, val);
8557                         if (JS_IsException(val))
8558                             return -1;
8559                         return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");
8560                     }
8561                 }
8562             } else {
8563                 const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic;
8564                 if (em) {
8565                     JSValue obj1;
8566                     if (em->set_property) {
8567                         /* set_property can free the prototype */
8568                         obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
8569                         ret = em->set_property(ctx, obj1, prop,
8570                                                val, this_obj, flags);
8571                         JS_FreeValue(ctx, obj1);
8572                         JS_FreeValue(ctx, val);
8573                         return ret;
8574                     }
8575                     if (em->get_own_property) {
8576                         /* get_own_property can free the prototype */
8577                         obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
8578                         ret = em->get_own_property(ctx, &desc,
8579                                                    obj1, prop);
8580                         JS_FreeValue(ctx, obj1);
8581                         if (ret < 0) {
8582                             JS_FreeValue(ctx, val);
8583                             return ret;
8584                         }
8585                         if (ret) {
8586                             if (desc.flags & JS_PROP_GETSET) {
8587                                 JSObject *setter;
8588                                 if (JS_IsUndefined(desc.setter))
8589                                     setter = NULL;
8590                                 else
8591                                     setter = JS_VALUE_GET_OBJ(desc.setter);
8592                                 ret = call_setter(ctx, setter, this_obj, val, flags);
8593                                 JS_FreeValue(ctx, desc.getter);
8594                                 JS_FreeValue(ctx, desc.setter);
8595                                 return ret;
8596                             } else {
8597                                 JS_FreeValue(ctx, desc.value);
8598                                 if (!(desc.flags & JS_PROP_WRITABLE))
8599                                     goto read_only_prop;
8600                                 if (likely(p == p1)) {
8601                                     ret = JS_DefineProperty(ctx, this_obj, prop, val,
8602                                                             JS_UNDEFINED, JS_UNDEFINED,
8603                                                             JS_PROP_HAS_VALUE);
8604                                     JS_FreeValue(ctx, val);
8605                                     return ret;
8606                                 } else {
8607                                     break;
8608                                 }
8609                             }
8610                         }
8611                     }
8612                 }
8613             }
8614         }
8615         p1 = p1->shape->proto;
8616     prototype_lookup:
8617         if (!p1)
8618             break;
8619 
8620     retry2:
8621         prs = find_own_property(&pr, p1, prop);
8622         if (prs) {
8623             if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
8624                 return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
8625             } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
8626                 /* Instantiate property and retry (potentially useless) */
8627                 if (JS_AutoInitProperty(ctx, p1, prop, pr, prs))
8628                     return -1;
8629                 goto retry2;
8630             } else if (!(prs->flags & JS_PROP_WRITABLE)) {
8631             read_only_prop:
8632                 JS_FreeValue(ctx, val);
8633                 return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
8634             }
8635         }
8636     }
8637 
8638     if (unlikely(flags & JS_PROP_NO_ADD)) {
8639         JS_FreeValue(ctx, val);
8640         JS_ThrowReferenceErrorNotDefined(ctx, prop);
8641         return -1;
8642     }
8643 
8644     if (unlikely(!p)) {
8645         JS_FreeValue(ctx, val);
8646         return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object");
8647     }
8648 
8649     if (unlikely(!p->extensible)) {
8650         JS_FreeValue(ctx, val);
8651         return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
8652     }
8653 
8654     if (p->is_exotic) {
8655         if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&
8656             __JS_AtomIsTaggedInt(prop)) {
8657             uint32_t idx = __JS_AtomToUInt32(prop);
8658             if (idx == p->u.array.count) {
8659                 /* fast case */
8660                 return add_fast_array_element(ctx, p, val, flags);
8661             } else {
8662                 goto generic_create_prop;
8663             }
8664         } else {
8665         generic_create_prop:
8666             ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
8667                                     flags |
8668                                     JS_PROP_HAS_VALUE |
8669                                     JS_PROP_HAS_ENUMERABLE |
8670                                     JS_PROP_HAS_WRITABLE |
8671                                     JS_PROP_HAS_CONFIGURABLE |
8672                                     JS_PROP_C_W_E);
8673             JS_FreeValue(ctx, val);
8674             return ret;
8675         }
8676     }
8677 
8678     pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
8679     if (unlikely(!pr)) {
8680         JS_FreeValue(ctx, val);
8681         return -1;
8682     }
8683     pr->u.value = val;
8684     return TRUE;
8685 }
8686 
8687 /* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */
JS_SetPropertyValue(JSContext * ctx,JSValueConst this_obj,JSValue prop,JSValue val,int flags)8688 static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
8689                                JSValue prop, JSValue val, int flags)
8690 {
8691     if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
8692                JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
8693         JSObject *p;
8694         uint32_t idx;
8695         double d;
8696         int32_t v;
8697 
8698         /* fast path for array access */
8699         p = JS_VALUE_GET_OBJ(this_obj);
8700         idx = JS_VALUE_GET_INT(prop);
8701         switch(p->class_id) {
8702         case JS_CLASS_ARRAY:
8703             if (unlikely(idx >= (uint32_t)p->u.array.count)) {
8704                 JSObject *p1;
8705                 JSShape *sh1;
8706 
8707                 /* fast path to add an element to the array */
8708                 if (idx != (uint32_t)p->u.array.count ||
8709                     !p->fast_array || !p->extensible)
8710                     goto slow_path;
8711                 /* check if prototype chain has a numeric property */
8712                 p1 = p->shape->proto;
8713                 while (p1 != NULL) {
8714                     sh1 = p1->shape;
8715                     if (p1->class_id == JS_CLASS_ARRAY) {
8716                         if (unlikely(!p1->fast_array))
8717                             goto slow_path;
8718                     } else if (p1->class_id == JS_CLASS_OBJECT) {
8719                         if (unlikely(sh1->has_small_array_index))
8720                             goto slow_path;
8721                     } else {
8722                         goto slow_path;
8723                     }
8724                     p1 = sh1->proto;
8725                 }
8726                 /* add element */
8727                 return add_fast_array_element(ctx, p, val, flags);
8728             }
8729             set_value(ctx, &p->u.array.u.values[idx], val);
8730             break;
8731         case JS_CLASS_ARGUMENTS:
8732             if (unlikely(idx >= (uint32_t)p->u.array.count))
8733                 goto slow_path;
8734             set_value(ctx, &p->u.array.u.values[idx], val);
8735             break;
8736         case JS_CLASS_UINT8C_ARRAY:
8737             if (JS_ToUint8ClampFree(ctx, &v, val))
8738                 return -1;
8739             /* Note: the conversion can detach the typed array, so the
8740                array bound check must be done after */
8741             if (unlikely(idx >= (uint32_t)p->u.array.count))
8742                 goto ta_out_of_bound;
8743             p->u.array.u.uint8_ptr[idx] = v;
8744             break;
8745         case JS_CLASS_INT8_ARRAY:
8746         case JS_CLASS_UINT8_ARRAY:
8747             if (JS_ToInt32Free(ctx, &v, val))
8748                 return -1;
8749             if (unlikely(idx >= (uint32_t)p->u.array.count))
8750                 goto ta_out_of_bound;
8751             p->u.array.u.uint8_ptr[idx] = v;
8752             break;
8753         case JS_CLASS_INT16_ARRAY:
8754         case JS_CLASS_UINT16_ARRAY:
8755             if (JS_ToInt32Free(ctx, &v, val))
8756                 return -1;
8757             if (unlikely(idx >= (uint32_t)p->u.array.count))
8758                 goto ta_out_of_bound;
8759             p->u.array.u.uint16_ptr[idx] = v;
8760             break;
8761         case JS_CLASS_INT32_ARRAY:
8762         case JS_CLASS_UINT32_ARRAY:
8763             if (JS_ToInt32Free(ctx, &v, val))
8764                 return -1;
8765             if (unlikely(idx >= (uint32_t)p->u.array.count))
8766                 goto ta_out_of_bound;
8767             p->u.array.u.uint32_ptr[idx] = v;
8768             break;
8769 #ifdef CONFIG_BIGNUM
8770         case JS_CLASS_BIG_INT64_ARRAY:
8771         case JS_CLASS_BIG_UINT64_ARRAY:
8772             /* XXX: need specific conversion function */
8773             {
8774                 int64_t v;
8775                 if (JS_ToBigInt64Free(ctx, &v, val))
8776                     return -1;
8777                 if (unlikely(idx >= (uint32_t)p->u.array.count))
8778                     goto ta_out_of_bound;
8779                 p->u.array.u.uint64_ptr[idx] = v;
8780             }
8781             break;
8782 #endif
8783         case JS_CLASS_FLOAT32_ARRAY:
8784             if (JS_ToFloat64Free(ctx, &d, val))
8785                 return -1;
8786             if (unlikely(idx >= (uint32_t)p->u.array.count))
8787                 goto ta_out_of_bound;
8788             p->u.array.u.float_ptr[idx] = d;
8789             break;
8790         case JS_CLASS_FLOAT64_ARRAY:
8791             if (JS_ToFloat64Free(ctx, &d, val))
8792                 return -1;
8793             if (unlikely(idx >= (uint32_t)p->u.array.count)) {
8794             ta_out_of_bound:
8795                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");
8796             }
8797             p->u.array.u.double_ptr[idx] = d;
8798             break;
8799         default:
8800             goto slow_path;
8801         }
8802         return TRUE;
8803     } else {
8804         JSAtom atom;
8805         int ret;
8806     slow_path:
8807         atom = JS_ValueToAtom(ctx, prop);
8808         JS_FreeValue(ctx, prop);
8809         if (unlikely(atom == JS_ATOM_NULL)) {
8810             JS_FreeValue(ctx, val);
8811             return -1;
8812         }
8813         ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags);
8814         JS_FreeAtom(ctx, atom);
8815         return ret;
8816     }
8817 }
8818 
JS_SetPropertyUint32(JSContext * ctx,JSValueConst this_obj,uint32_t idx,JSValue val)8819 int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
8820                          uint32_t idx, JSValue val)
8821 {
8822     return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val,
8823                                JS_PROP_THROW);
8824 }
8825 
JS_SetPropertyInt64(JSContext * ctx,JSValueConst this_obj,int64_t idx,JSValue val)8826 int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,
8827                         int64_t idx, JSValue val)
8828 {
8829     JSAtom prop;
8830     int res;
8831 
8832     if ((uint64_t)idx <= INT32_MAX) {
8833         /* fast path for fast arrays */
8834         return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val,
8835                                    JS_PROP_THROW);
8836     }
8837     prop = JS_NewAtomInt64(ctx, idx);
8838     if (prop == JS_ATOM_NULL) {
8839         JS_FreeValue(ctx, val);
8840         return -1;
8841     }
8842     res = JS_SetProperty(ctx, this_obj, prop, val);
8843     JS_FreeAtom(ctx, prop);
8844     return res;
8845 }
8846 
JS_SetPropertyStr(JSContext * ctx,JSValueConst this_obj,const char * prop,JSValue val)8847 int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,
8848                       const char *prop, JSValue val)
8849 {
8850     JSAtom atom;
8851     int ret;
8852     atom = JS_NewAtom(ctx, prop);
8853     ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW);
8854     JS_FreeAtom(ctx, atom);
8855     return ret;
8856 }
8857 
8858 /* compute the property flags. For each flag: (JS_PROP_HAS_x forces
8859    it, otherwise def_flags is used)
8860    Note: makes assumption about the bit pattern of the flags
8861 */
get_prop_flags(int flags,int def_flags)8862 static int get_prop_flags(int flags, int def_flags)
8863 {
8864     int mask;
8865     mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E;
8866     return (flags & mask) | (def_flags & ~mask);
8867 }
8868 
JS_CreateProperty(JSContext * ctx,JSObject * p,JSAtom prop,JSValueConst val,JSValueConst getter,JSValueConst setter,int flags)8869 static int JS_CreateProperty(JSContext *ctx, JSObject *p,
8870                              JSAtom prop, JSValueConst val,
8871                              JSValueConst getter, JSValueConst setter,
8872                              int flags)
8873 {
8874     JSProperty *pr;
8875     int ret, prop_flags;
8876 
8877     /* add a new property or modify an existing exotic one */
8878     if (p->is_exotic) {
8879         if (p->class_id == JS_CLASS_ARRAY) {
8880             uint32_t idx, len;
8881 
8882             if (p->fast_array) {
8883                 if (__JS_AtomIsTaggedInt(prop)) {
8884                     idx = __JS_AtomToUInt32(prop);
8885                     if (idx == p->u.array.count) {
8886                         if (!p->extensible)
8887                             goto not_extensible;
8888                         if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET))
8889                             goto convert_to_array;
8890                         prop_flags = get_prop_flags(flags, 0);
8891                         if (prop_flags != JS_PROP_C_W_E)
8892                             goto convert_to_array;
8893                         return add_fast_array_element(ctx, p,
8894                                                       JS_DupValue(ctx, val), flags);
8895                     } else {
8896                         goto convert_to_array;
8897                     }
8898                 } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
8899                     /* convert the fast array to normal array */
8900                 convert_to_array:
8901                     if (convert_fast_array_to_array(ctx, p))
8902                         return -1;
8903                     goto generic_array;
8904                 }
8905             } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
8906                 JSProperty *plen;
8907                 JSShapeProperty *pslen;
8908             generic_array:
8909                 /* update the length field */
8910                 plen = &p->prop[0];
8911                 JS_ToUint32(ctx, &len, plen->u.value);
8912                 if ((idx + 1) > len) {
8913                     pslen = get_shape_prop(p->shape);
8914                     if (unlikely(!(pslen->flags & JS_PROP_WRITABLE)))
8915                         return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
8916                     /* XXX: should update the length after defining
8917                        the property */
8918                     len = idx + 1;
8919                     set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len));
8920                 }
8921             }
8922         } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
8923                    p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
8924             ret = JS_AtomIsNumericIndex(ctx, prop);
8925             if (ret != 0) {
8926                 if (ret < 0)
8927                     return -1;
8928                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array");
8929             }
8930         } else if (!(flags & JS_PROP_NO_EXOTIC)) {
8931             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
8932             if (em) {
8933                 if (em->define_own_property) {
8934                     return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p),
8935                                                    prop, val, getter, setter, flags);
8936                 }
8937                 ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
8938                 if (ret < 0)
8939                     return -1;
8940                 if (!ret)
8941                     goto not_extensible;
8942             }
8943         }
8944     }
8945 
8946     if (!p->extensible) {
8947     not_extensible:
8948         return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
8949     }
8950 
8951     if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
8952         prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
8953             JS_PROP_GETSET;
8954     } else {
8955         prop_flags = flags & JS_PROP_C_W_E;
8956     }
8957     pr = add_property(ctx, p, prop, prop_flags);
8958     if (unlikely(!pr))
8959         return -1;
8960     if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
8961         pr->u.getset.getter = NULL;
8962         if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) {
8963             pr->u.getset.getter =
8964                 JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter));
8965         }
8966         pr->u.getset.setter = NULL;
8967         if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) {
8968             pr->u.getset.setter =
8969                 JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter));
8970         }
8971     } else {
8972         if (flags & JS_PROP_HAS_VALUE) {
8973             pr->u.value = JS_DupValue(ctx, val);
8974         } else {
8975             pr->u.value = JS_UNDEFINED;
8976         }
8977     }
8978     return TRUE;
8979 }
8980 
8981 /* return FALSE if not OK */
check_define_prop_flags(int prop_flags,int flags)8982 static BOOL check_define_prop_flags(int prop_flags, int flags)
8983 {
8984     BOOL has_accessor, is_getset;
8985 
8986     if (!(prop_flags & JS_PROP_CONFIGURABLE)) {
8987         if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) ==
8988             (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) {
8989             return FALSE;
8990         }
8991         if ((flags & JS_PROP_HAS_ENUMERABLE) &&
8992             (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE))
8993             return FALSE;
8994     }
8995     if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
8996                  JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
8997         if (!(prop_flags & JS_PROP_CONFIGURABLE)) {
8998             has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0);
8999             is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET);
9000             if (has_accessor != is_getset)
9001                 return FALSE;
9002             if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) {
9003                 /* not writable: cannot set the writable bit */
9004                 if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
9005                     (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE))
9006                     return FALSE;
9007             }
9008         }
9009     }
9010     return TRUE;
9011 }
9012 
9013 /* ensure that the shape can be safely modified */
js_shape_prepare_update(JSContext * ctx,JSObject * p,JSShapeProperty ** pprs)9014 static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
9015                                    JSShapeProperty **pprs)
9016 {
9017     JSShape *sh;
9018     uint32_t idx = 0;    /* prevent warning */
9019 
9020     sh = p->shape;
9021     if (sh->is_hashed) {
9022         if (sh->header.ref_count != 1) {
9023             if (pprs)
9024                 idx = *pprs - get_shape_prop(sh);
9025             /* clone the shape (the resulting one is no longer hashed) */
9026             sh = js_clone_shape(ctx, sh);
9027             if (!sh)
9028                 return -1;
9029             js_free_shape(ctx->rt, p->shape);
9030             p->shape = sh;
9031             if (pprs)
9032                 *pprs = get_shape_prop(sh) + idx;
9033         } else {
9034             js_shape_hash_unlink(ctx->rt, sh);
9035             sh->is_hashed = FALSE;
9036         }
9037     }
9038     return 0;
9039 }
9040 
js_update_property_flags(JSContext * ctx,JSObject * p,JSShapeProperty ** pprs,int flags)9041 static int js_update_property_flags(JSContext *ctx, JSObject *p,
9042                                     JSShapeProperty **pprs, int flags)
9043 {
9044     if (flags != (*pprs)->flags) {
9045         if (js_shape_prepare_update(ctx, p, pprs))
9046             return -1;
9047         (*pprs)->flags = flags;
9048     }
9049     return 0;
9050 }
9051 
9052 /* allowed flags:
9053    JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE
9054    JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE,
9055    JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE,
9056    JS_PROP_THROW, JS_PROP_NO_EXOTIC.
9057    If JS_PROP_THROW is set, return an exception instead of FALSE.
9058    if JS_PROP_NO_EXOTIC is set, do not call the exotic
9059    define_own_property callback.
9060    return -1 (exception), FALSE or TRUE.
9061 */
JS_DefineProperty(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSValueConst val,JSValueConst getter,JSValueConst setter,int flags)9062 int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
9063                       JSAtom prop, JSValueConst val,
9064                       JSValueConst getter, JSValueConst setter, int flags)
9065 {
9066     JSObject *p;
9067     JSShapeProperty *prs;
9068     JSProperty *pr;
9069     int mask, res;
9070 
9071     if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) {
9072         JS_ThrowTypeErrorNotAnObject(ctx);
9073         return -1;
9074     }
9075     p = JS_VALUE_GET_OBJ(this_obj);
9076 
9077  redo_prop_update:
9078     prs = find_own_property(&pr, p, prop);
9079     if (prs) {
9080         /* the range of the Array length property is always tested before */
9081         if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) {
9082             uint32_t array_length;
9083             if (JS_ToArrayLengthFree(ctx, &array_length,
9084                                      JS_DupValue(ctx, val), FALSE)) {
9085                 return -1;
9086             }
9087             /* this code relies on the fact that Uint32 are never allocated */
9088             val = JS_VALUE_MAKE_CONST(JS_NewUint32(ctx, array_length));
9089             /* prs may have been modified */
9090             prs = find_own_property(&pr, p, prop);
9091             assert(prs != NULL);
9092         }
9093         /* property already exists */
9094         if (!check_define_prop_flags(prs->flags, flags)) {
9095         not_configurable:
9096             return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
9097         }
9098 
9099         if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
9100             /* Instantiate property and retry */
9101             if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
9102                 return -1;
9103             goto redo_prop_update;
9104         }
9105 
9106         if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
9107                      JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
9108             if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
9109                 JSObject *new_getter, *new_setter;
9110 
9111                 if (JS_IsFunction(ctx, getter)) {
9112                     new_getter = JS_VALUE_GET_OBJ(getter);
9113                 } else {
9114                     new_getter = NULL;
9115                 }
9116                 if (JS_IsFunction(ctx, setter)) {
9117                     new_setter = JS_VALUE_GET_OBJ(setter);
9118                 } else {
9119                     new_setter = NULL;
9120                 }
9121 
9122                 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) {
9123                     if (js_shape_prepare_update(ctx, p, &prs))
9124                         return -1;
9125                     /* convert to getset */
9126                     if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
9127                         free_var_ref(ctx->rt, pr->u.var_ref);
9128                     } else {
9129                         JS_FreeValue(ctx, pr->u.value);
9130                     }
9131                     prs->flags = (prs->flags &
9132                                   (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
9133                         JS_PROP_GETSET;
9134                     pr->u.getset.getter = NULL;
9135                     pr->u.getset.setter = NULL;
9136                 } else {
9137                     if (!(prs->flags & JS_PROP_CONFIGURABLE)) {
9138                         if ((flags & JS_PROP_HAS_GET) &&
9139                             new_getter != pr->u.getset.getter) {
9140                             goto not_configurable;
9141                         }
9142                         if ((flags & JS_PROP_HAS_SET) &&
9143                             new_setter != pr->u.getset.setter) {
9144                             goto not_configurable;
9145                         }
9146                     }
9147                 }
9148                 if (flags & JS_PROP_HAS_GET) {
9149                     if (pr->u.getset.getter)
9150                         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
9151                     if (new_getter)
9152                         JS_DupValue(ctx, getter);
9153                     pr->u.getset.getter = new_getter;
9154                 }
9155                 if (flags & JS_PROP_HAS_SET) {
9156                     if (pr->u.getset.setter)
9157                         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
9158                     if (new_setter)
9159                         JS_DupValue(ctx, setter);
9160                     pr->u.getset.setter = new_setter;
9161                 }
9162             } else {
9163                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
9164                     /* convert to data descriptor */
9165                     if (js_shape_prepare_update(ctx, p, &prs))
9166                         return -1;
9167                     if (pr->u.getset.getter)
9168                         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
9169                     if (pr->u.getset.setter)
9170                         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
9171                     prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
9172                     pr->u.value = JS_UNDEFINED;
9173                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
9174                     /* Note: JS_PROP_VARREF is always writable */
9175                 } else {
9176                     if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&
9177                         (flags & JS_PROP_HAS_VALUE)) {
9178                         if (!js_same_value(ctx, val, pr->u.value)) {
9179                             goto not_configurable;
9180                         } else {
9181                             return TRUE;
9182                         }
9183                     }
9184                 }
9185                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
9186                     if (flags & JS_PROP_HAS_VALUE) {
9187                         if (p->class_id == JS_CLASS_MODULE_NS) {
9188                             /* JS_PROP_WRITABLE is always true for variable
9189                                references, but they are write protected in module name
9190                                spaces. */
9191                             if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue))
9192                                 goto not_configurable;
9193                         }
9194                         /* update the reference */
9195                         set_value(ctx, pr->u.var_ref->pvalue,
9196                                   JS_DupValue(ctx, val));
9197                     }
9198                     /* if writable is set to false, no longer a
9199                        reference (for mapped arguments) */
9200                     if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) {
9201                         JSValue val1;
9202                         if (js_shape_prepare_update(ctx, p, &prs))
9203                             return -1;
9204                         val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue);
9205                         free_var_ref(ctx->rt, pr->u.var_ref);
9206                         pr->u.value = val1;
9207                         prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
9208                     }
9209                 } else if (prs->flags & JS_PROP_LENGTH) {
9210                     if (flags & JS_PROP_HAS_VALUE) {
9211                         /* Note: no JS code is executable because
9212                            'val' is guaranted to be a Uint32 */
9213                         res = set_array_length(ctx, p, JS_DupValue(ctx, val),
9214                                                flags);
9215                     } else {
9216                         res = TRUE;
9217                     }
9218                     /* still need to reset the writable flag if
9219                        needed.  The JS_PROP_LENGTH is kept because the
9220                        Uint32 test is still done if the length
9221                        property is read-only. */
9222                     if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
9223                         JS_PROP_HAS_WRITABLE) {
9224                         prs = get_shape_prop(p->shape);
9225                         if (js_update_property_flags(ctx, p, &prs,
9226                                                      prs->flags & ~JS_PROP_WRITABLE))
9227                             return -1;
9228                     }
9229                     return res;
9230                 } else {
9231                     if (flags & JS_PROP_HAS_VALUE) {
9232                         JS_FreeValue(ctx, pr->u.value);
9233                         pr->u.value = JS_DupValue(ctx, val);
9234                     }
9235                     if (flags & JS_PROP_HAS_WRITABLE) {
9236                         if (js_update_property_flags(ctx, p, &prs,
9237                                                      (prs->flags & ~JS_PROP_WRITABLE) |
9238                                                      (flags & JS_PROP_WRITABLE)))
9239                             return -1;
9240                     }
9241                 }
9242             }
9243         }
9244         mask = 0;
9245         if (flags & JS_PROP_HAS_CONFIGURABLE)
9246             mask |= JS_PROP_CONFIGURABLE;
9247         if (flags & JS_PROP_HAS_ENUMERABLE)
9248             mask |= JS_PROP_ENUMERABLE;
9249         if (js_update_property_flags(ctx, p, &prs,
9250                                      (prs->flags & ~mask) | (flags & mask)))
9251             return -1;
9252         return TRUE;
9253     }
9254 
9255     /* handle modification of fast array elements */
9256     if (p->fast_array) {
9257         uint32_t idx;
9258         uint32_t prop_flags;
9259         if (p->class_id == JS_CLASS_ARRAY) {
9260             if (__JS_AtomIsTaggedInt(prop)) {
9261                 idx = __JS_AtomToUInt32(prop);
9262                 if (idx < p->u.array.count) {
9263                     prop_flags = get_prop_flags(flags, JS_PROP_C_W_E);
9264                     if (prop_flags != JS_PROP_C_W_E)
9265                         goto convert_to_slow_array;
9266                     if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
9267                     convert_to_slow_array:
9268                         if (convert_fast_array_to_array(ctx, p))
9269                             return -1;
9270                         else
9271                             goto redo_prop_update;
9272                     }
9273                     if (flags & JS_PROP_HAS_VALUE) {
9274                         set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val));
9275                     }
9276                     return TRUE;
9277                 }
9278             }
9279         } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
9280                    p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
9281             JSValue num;
9282             int ret;
9283 
9284             if (!__JS_AtomIsTaggedInt(prop)) {
9285                 /* slow path with to handle all numeric indexes */
9286                 num = JS_AtomIsNumericIndex1(ctx, prop);
9287                 if (JS_IsUndefined(num))
9288                     goto typed_array_done;
9289                 if (JS_IsException(num))
9290                     return -1;
9291                 ret = JS_NumberIsInteger(ctx, num);
9292                 if (ret < 0) {
9293                     JS_FreeValue(ctx, num);
9294                     return -1;
9295                 }
9296                 if (!ret) {
9297                     JS_FreeValue(ctx, num);
9298                     return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array");
9299                 }
9300                 ret = JS_NumberIsNegativeOrMinusZero(ctx, num);
9301                 JS_FreeValue(ctx, num);
9302                 if (ret) {
9303                     return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array");
9304                 }
9305                 if (!__JS_AtomIsTaggedInt(prop))
9306                     goto typed_array_oob;
9307             }
9308             idx = __JS_AtomToUInt32(prop);
9309             /* if the typed array is detached, p->u.array.count = 0 */
9310             if (idx >= typed_array_get_length(ctx, p)) {
9311             typed_array_oob:
9312                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array");
9313             }
9314             prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
9315             if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) ||
9316                 prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) {
9317                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags");
9318             }
9319             if (flags & JS_PROP_HAS_VALUE) {
9320                 return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags);
9321             }
9322             return TRUE;
9323         typed_array_done: ;
9324         }
9325     }
9326 
9327     return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags);
9328 }
9329 
JS_DefineAutoInitProperty(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSAutoInitIDEnum id,void * opaque,int flags)9330 static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj,
9331                                      JSAtom prop, JSAutoInitIDEnum id,
9332                                      void *opaque, int flags)
9333 {
9334     JSObject *p;
9335     JSProperty *pr;
9336 
9337     if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT)
9338         return FALSE;
9339 
9340     p = JS_VALUE_GET_OBJ(this_obj);
9341 
9342     if (find_own_property(&pr, p, prop)) {
9343         /* property already exists */
9344         abort();
9345         return FALSE;
9346     }
9347 
9348     /* Specialized CreateProperty */
9349     pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT);
9350     if (unlikely(!pr))
9351         return -1;
9352     pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx);
9353     assert((pr->u.init.realm_and_id & 3) == 0);
9354     assert(id <= 3);
9355     pr->u.init.realm_and_id |= id;
9356     pr->u.init.opaque = opaque;
9357     return TRUE;
9358 }
9359 
9360 /* shortcut to add or redefine a new property value */
JS_DefinePropertyValue(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSValue val,int flags)9361 int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,
9362                            JSAtom prop, JSValue val, int flags)
9363 {
9364     int ret;
9365     ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED,
9366                             flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE);
9367     JS_FreeValue(ctx, val);
9368     return ret;
9369 }
9370 
JS_DefinePropertyValueValue(JSContext * ctx,JSValueConst this_obj,JSValue prop,JSValue val,int flags)9371 int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj,
9372                                 JSValue prop, JSValue val, int flags)
9373 {
9374     JSAtom atom;
9375     int ret;
9376     atom = JS_ValueToAtom(ctx, prop);
9377     JS_FreeValue(ctx, prop);
9378     if (unlikely(atom == JS_ATOM_NULL)) {
9379         JS_FreeValue(ctx, val);
9380         return -1;
9381     }
9382     ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
9383     JS_FreeAtom(ctx, atom);
9384     return ret;
9385 }
9386 
JS_DefinePropertyValueUint32(JSContext * ctx,JSValueConst this_obj,uint32_t idx,JSValue val,int flags)9387 int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,
9388                                  uint32_t idx, JSValue val, int flags)
9389 {
9390     return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx),
9391                                        val, flags);
9392 }
9393 
JS_DefinePropertyValueInt64(JSContext * ctx,JSValueConst this_obj,int64_t idx,JSValue val,int flags)9394 int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj,
9395                                 int64_t idx, JSValue val, int flags)
9396 {
9397     return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
9398                                        val, flags);
9399 }
9400 
JS_DefinePropertyValueStr(JSContext * ctx,JSValueConst this_obj,const char * prop,JSValue val,int flags)9401 int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,
9402                               const char *prop, JSValue val, int flags)
9403 {
9404     JSAtom atom;
9405     int ret;
9406     atom = JS_NewAtom(ctx, prop);
9407     ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
9408     JS_FreeAtom(ctx, atom);
9409     return ret;
9410 }
9411 
9412 /* shortcut to add getter & setter */
JS_DefinePropertyGetSet(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSValue getter,JSValue setter,int flags)9413 int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,
9414                             JSAtom prop, JSValue getter, JSValue setter,
9415                             int flags)
9416 {
9417     int ret;
9418     ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter,
9419                             flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET |
9420                             JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE);
9421     JS_FreeValue(ctx, getter);
9422     JS_FreeValue(ctx, setter);
9423     return ret;
9424 }
9425 
JS_CreateDataPropertyUint32(JSContext * ctx,JSValueConst this_obj,int64_t idx,JSValue val,int flags)9426 static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj,
9427                                        int64_t idx, JSValue val, int flags)
9428 {
9429     return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
9430                                        val, flags | JS_PROP_CONFIGURABLE |
9431                                        JS_PROP_ENUMERABLE | JS_PROP_WRITABLE);
9432 }
9433 
9434 
9435 /* return TRUE if 'obj' has a non empty 'name' string */
js_object_has_name(JSContext * ctx,JSValueConst obj)9436 static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj)
9437 {
9438     JSProperty *pr;
9439     JSShapeProperty *prs;
9440     JSValueConst val;
9441     JSString *p;
9442 
9443     prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name);
9444     if (!prs)
9445         return FALSE;
9446     if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
9447         return TRUE;
9448     val = pr->u.value;
9449     if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
9450         return TRUE;
9451     p = JS_VALUE_GET_STRING(val);
9452     return (p->len != 0);
9453 }
9454 
JS_DefineObjectName(JSContext * ctx,JSValueConst obj,JSAtom name,int flags)9455 static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj,
9456                                JSAtom name, int flags)
9457 {
9458     if (name != JS_ATOM_NULL
9459     &&  JS_IsObject(obj)
9460     &&  !js_object_has_name(ctx, obj)
9461     &&  JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) {
9462         return -1;
9463     }
9464     return 0;
9465 }
9466 
JS_DefineObjectNameComputed(JSContext * ctx,JSValueConst obj,JSValueConst str,int flags)9467 static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj,
9468                                        JSValueConst str, int flags)
9469 {
9470     if (JS_IsObject(obj) &&
9471         !js_object_has_name(ctx, obj)) {
9472         JSAtom prop;
9473         JSValue name_str;
9474         prop = JS_ValueToAtom(ctx, str);
9475         if (prop == JS_ATOM_NULL)
9476             return -1;
9477         name_str = js_get_function_name(ctx, prop);
9478         JS_FreeAtom(ctx, prop);
9479         if (JS_IsException(name_str))
9480             return -1;
9481         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0)
9482             return -1;
9483     }
9484     return 0;
9485 }
9486 
9487 #define DEFINE_GLOBAL_LEX_VAR (1 << 7)
9488 #define DEFINE_GLOBAL_FUNC_VAR (1 << 6)
9489 
JS_ThrowSyntaxErrorVarRedeclaration(JSContext * ctx,JSAtom prop)9490 static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop)
9491 {
9492     return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop);
9493 }
9494 
9495 /* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */
9496 /* XXX: could support exotic global object. */
JS_CheckDefineGlobalVar(JSContext * ctx,JSAtom prop,int flags)9497 static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags)
9498 {
9499     JSObject *p;
9500     JSShapeProperty *prs;
9501 
9502     p = JS_VALUE_GET_OBJ(ctx->global_obj);
9503     prs = find_own_property1(p, prop);
9504     /* XXX: should handle JS_PROP_AUTOINIT */
9505     if (flags & DEFINE_GLOBAL_LEX_VAR) {
9506         if (prs && !(prs->flags & JS_PROP_CONFIGURABLE))
9507             goto fail_redeclaration;
9508     } else {
9509         if (!prs && !p->extensible)
9510             goto define_error;
9511         if (flags & DEFINE_GLOBAL_FUNC_VAR) {
9512             if (prs) {
9513                 if (!(prs->flags & JS_PROP_CONFIGURABLE) &&
9514                     ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET ||
9515                      ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) !=
9516                       (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) {
9517                 define_error:
9518                     JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'",
9519                                           prop);
9520                     return -1;
9521                 }
9522             }
9523         }
9524     }
9525     /* check if there already is a lexical declaration */
9526     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9527     prs = find_own_property1(p, prop);
9528     if (prs) {
9529     fail_redeclaration:
9530         JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop);
9531         return -1;
9532     }
9533     return 0;
9534 }
9535 
9536 /* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) |
9537    JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */
9538 /* XXX: could support exotic global object. */
JS_DefineGlobalVar(JSContext * ctx,JSAtom prop,int def_flags)9539 static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags)
9540 {
9541     JSObject *p;
9542     JSShapeProperty *prs;
9543     JSProperty *pr;
9544     JSValue val;
9545     int flags;
9546 
9547     if (def_flags & DEFINE_GLOBAL_LEX_VAR) {
9548         p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9549         flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) |
9550             JS_PROP_CONFIGURABLE;
9551         val = JS_UNINITIALIZED;
9552     } else {
9553         p = JS_VALUE_GET_OBJ(ctx->global_obj);
9554         flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
9555             (def_flags & JS_PROP_CONFIGURABLE);
9556         val = JS_UNDEFINED;
9557     }
9558     prs = find_own_property1(p, prop);
9559     if (prs)
9560         return 0;
9561     if (!p->extensible)
9562         return 0;
9563     pr = add_property(ctx, p, prop, flags);
9564     if (unlikely(!pr))
9565         return -1;
9566     pr->u.value = val;
9567     return 0;
9568 }
9569 
9570 /* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */
9571 /* XXX: could support exotic global object. */
JS_DefineGlobalFunction(JSContext * ctx,JSAtom prop,JSValueConst func,int def_flags)9572 static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,
9573                                    JSValueConst func, int def_flags)
9574 {
9575 
9576     JSObject *p;
9577     JSShapeProperty *prs;
9578     int flags;
9579 
9580     p = JS_VALUE_GET_OBJ(ctx->global_obj);
9581     prs = find_own_property1(p, prop);
9582     flags = JS_PROP_HAS_VALUE | JS_PROP_THROW;
9583     if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) {
9584         flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags |
9585             JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE;
9586     }
9587     if (JS_DefineProperty(ctx, ctx->global_obj, prop, func,
9588                           JS_UNDEFINED, JS_UNDEFINED, flags) < 0)
9589         return -1;
9590     return 0;
9591 }
9592 
JS_GetGlobalVar(JSContext * ctx,JSAtom prop,BOOL throw_ref_error)9593 static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
9594                                BOOL throw_ref_error)
9595 {
9596     JSObject *p;
9597     JSShapeProperty *prs;
9598     JSProperty *pr;
9599 
9600     /* no exotic behavior is possible in global_var_obj */
9601     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9602     prs = find_own_property(&pr, p, prop);
9603     if (prs) {
9604         /* XXX: should handle JS_PROP_TMASK properties */
9605         if (unlikely(JS_IsUninitialized(pr->u.value)))
9606             return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
9607         return JS_DupValue(ctx, pr->u.value);
9608     }
9609     return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,
9610                                  ctx->global_obj, throw_ref_error);
9611 }
9612 
9613 /* construct a reference to a global variable */
JS_GetGlobalVarRef(JSContext * ctx,JSAtom prop,JSValue * sp)9614 static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)
9615 {
9616     JSObject *p;
9617     JSShapeProperty *prs;
9618     JSProperty *pr;
9619 
9620     /* no exotic behavior is possible in global_var_obj */
9621     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9622     prs = find_own_property(&pr, p, prop);
9623     if (prs) {
9624         /* XXX: should handle JS_PROP_AUTOINIT properties? */
9625         /* XXX: conformance: do these tests in
9626            OP_put_var_ref/OP_get_var_ref ? */
9627         if (unlikely(JS_IsUninitialized(pr->u.value))) {
9628             JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
9629             return -1;
9630         }
9631         if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
9632             return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
9633         }
9634         sp[0] = JS_DupValue(ctx, ctx->global_var_obj);
9635     } else {
9636         int ret;
9637         ret = JS_HasProperty(ctx, ctx->global_obj, prop);
9638         if (ret < 0)
9639             return -1;
9640         if (ret) {
9641             sp[0] = JS_DupValue(ctx, ctx->global_obj);
9642         } else {
9643             sp[0] = JS_UNDEFINED;
9644         }
9645     }
9646     sp[1] = JS_AtomToValue(ctx, prop);
9647     return 0;
9648 }
9649 
9650 /* use for strict variable access: test if the variable exists */
JS_CheckGlobalVar(JSContext * ctx,JSAtom prop)9651 static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop)
9652 {
9653     JSObject *p;
9654     JSShapeProperty *prs;
9655     int ret;
9656 
9657     /* no exotic behavior is possible in global_var_obj */
9658     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9659     prs = find_own_property1(p, prop);
9660     if (prs) {
9661         ret = TRUE;
9662     } else {
9663         ret = JS_HasProperty(ctx, ctx->global_obj, prop);
9664         if (ret < 0)
9665             return -1;
9666     }
9667     return ret;
9668 }
9669 
9670 /* flag = 0: normal variable write
9671    flag = 1: initialize lexical variable
9672    flag = 2: normal variable write, strict check was done before
9673 */
JS_SetGlobalVar(JSContext * ctx,JSAtom prop,JSValue val,int flag)9674 static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
9675                            int flag)
9676 {
9677     JSObject *p;
9678     JSShapeProperty *prs;
9679     JSProperty *pr;
9680     int flags;
9681 
9682     /* no exotic behavior is possible in global_var_obj */
9683     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9684     prs = find_own_property(&pr, p, prop);
9685     if (prs) {
9686         /* XXX: should handle JS_PROP_AUTOINIT properties? */
9687         if (flag != 1) {
9688             if (unlikely(JS_IsUninitialized(pr->u.value))) {
9689                 JS_FreeValue(ctx, val);
9690                 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
9691                 return -1;
9692             }
9693             if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
9694                 JS_FreeValue(ctx, val);
9695                 return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
9696             }
9697         }
9698         set_value(ctx, &pr->u.value, val);
9699         return 0;
9700     }
9701     flags = JS_PROP_THROW_STRICT;
9702     if (is_strict_mode(ctx))
9703         flags |= JS_PROP_NO_ADD;
9704     return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags);
9705 }
9706 
9707 /* return -1, FALSE or TRUE. return FALSE if not configurable or
9708    invalid object. return -1 in case of exception.
9709    flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */
JS_DeleteProperty(JSContext * ctx,JSValueConst obj,JSAtom prop,int flags)9710 int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags)
9711 {
9712     JSValue obj1;
9713     JSObject *p;
9714     int res;
9715 
9716     obj1 = JS_ToObject(ctx, obj);
9717     if (JS_IsException(obj1))
9718         return -1;
9719     p = JS_VALUE_GET_OBJ(obj1);
9720     res = delete_property(ctx, p, prop);
9721     JS_FreeValue(ctx, obj1);
9722     if (res != FALSE)
9723         return res;
9724     if ((flags & JS_PROP_THROW) ||
9725         ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
9726         JS_ThrowTypeError(ctx, "could not delete property");
9727         return -1;
9728     }
9729     return FALSE;
9730 }
9731 
JS_DeletePropertyInt64(JSContext * ctx,JSValueConst obj,int64_t idx,int flags)9732 int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags)
9733 {
9734     JSAtom prop;
9735     int res;
9736 
9737     if ((uint64_t)idx <= JS_ATOM_MAX_INT) {
9738         /* fast path for fast arrays */
9739         return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags);
9740     }
9741     prop = JS_NewAtomInt64(ctx, idx);
9742     if (prop == JS_ATOM_NULL)
9743         return -1;
9744     res = JS_DeleteProperty(ctx, obj, prop, flags);
9745     JS_FreeAtom(ctx, prop);
9746     return res;
9747 }
9748 
JS_IsFunction(JSContext * ctx,JSValueConst val)9749 BOOL JS_IsFunction(JSContext *ctx, JSValueConst val)
9750 {
9751     JSObject *p;
9752     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9753         return FALSE;
9754     p = JS_VALUE_GET_OBJ(val);
9755     switch(p->class_id) {
9756     case JS_CLASS_BYTECODE_FUNCTION:
9757         return TRUE;
9758     case JS_CLASS_PROXY:
9759         return p->u.proxy_data->is_func;
9760     default:
9761         return (ctx->rt->class_array[p->class_id].call != NULL);
9762     }
9763 }
9764 
JS_IsCFunction(JSContext * ctx,JSValueConst val,JSCFunction * func,int magic)9765 BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic)
9766 {
9767     JSObject *p;
9768     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9769         return FALSE;
9770     p = JS_VALUE_GET_OBJ(val);
9771     if (p->class_id == JS_CLASS_C_FUNCTION)
9772         return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic);
9773     else
9774         return FALSE;
9775 }
9776 
JS_IsConstructor(JSContext * ctx,JSValueConst val)9777 BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val)
9778 {
9779     JSObject *p;
9780     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9781         return FALSE;
9782     p = JS_VALUE_GET_OBJ(val);
9783     return p->is_constructor;
9784 }
9785 
JS_SetConstructorBit(JSContext * ctx,JSValueConst func_obj,BOOL val)9786 BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val)
9787 {
9788     JSObject *p;
9789     if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
9790         return FALSE;
9791     p = JS_VALUE_GET_OBJ(func_obj);
9792     p->is_constructor = val;
9793     return TRUE;
9794 }
9795 
JS_IsError(JSContext * ctx,JSValueConst val)9796 BOOL JS_IsError(JSContext *ctx, JSValueConst val)
9797 {
9798     JSObject *p;
9799     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9800         return FALSE;
9801     p = JS_VALUE_GET_OBJ(val);
9802     return (p->class_id == JS_CLASS_ERROR);
9803 }
9804 
9805 /* used to avoid catching interrupt exceptions */
JS_IsUncatchableError(JSContext * ctx,JSValueConst val)9806 BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val)
9807 {
9808     JSObject *p;
9809     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9810         return FALSE;
9811     p = JS_VALUE_GET_OBJ(val);
9812     return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error;
9813 }
9814 
JS_SetUncatchableError(JSContext * ctx,JSValueConst val,BOOL flag)9815 void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag)
9816 {
9817     JSObject *p;
9818     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9819         return;
9820     p = JS_VALUE_GET_OBJ(val);
9821     if (p->class_id == JS_CLASS_ERROR)
9822         p->is_uncatchable_error = flag;
9823 }
9824 
JS_ResetUncatchableError(JSContext * ctx)9825 void JS_ResetUncatchableError(JSContext *ctx)
9826 {
9827     JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE);
9828 }
9829 
JS_SetOpaque(JSValue obj,void * opaque)9830 void JS_SetOpaque(JSValue obj, void *opaque)
9831 {
9832    JSObject *p;
9833     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
9834         p = JS_VALUE_GET_OBJ(obj);
9835         p->u.opaque = opaque;
9836     }
9837 }
9838 
9839 /* return NULL if not an object of class class_id */
JS_GetOpaque(JSValueConst obj,JSClassID class_id)9840 void *JS_GetOpaque(JSValueConst obj, JSClassID class_id)
9841 {
9842     JSObject *p;
9843     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
9844         return NULL;
9845     p = JS_VALUE_GET_OBJ(obj);
9846     if (p->class_id != class_id)
9847         return NULL;
9848     return p->u.opaque;
9849 }
9850 
JS_GetOpaque2(JSContext * ctx,JSValueConst obj,JSClassID class_id)9851 void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id)
9852 {
9853     void *p = JS_GetOpaque(obj, class_id);
9854     if (unlikely(!p)) {
9855         JS_ThrowTypeErrorInvalidClass(ctx, class_id);
9856     }
9857     return p;
9858 }
9859 
9860 #define HINT_STRING  0
9861 #define HINT_NUMBER  1
9862 #define HINT_NONE    2
9863 /* don't try Symbol.toPrimitive */
9864 #define HINT_FORCE_ORDINARY (1 << 4)
9865 
JS_ToPrimitiveFree(JSContext * ctx,JSValue val,int hint)9866 static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
9867 {
9868     int i;
9869     BOOL force_ordinary;
9870 
9871     JSAtom method_name;
9872     JSValue method, ret;
9873     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9874         return val;
9875     force_ordinary = hint & HINT_FORCE_ORDINARY;
9876     hint &= ~HINT_FORCE_ORDINARY;
9877     if (!force_ordinary) {
9878         method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive);
9879         if (JS_IsException(method))
9880             goto exception;
9881         /* ECMA says *If exoticToPrim is not undefined* but tests in
9882            test262 use null as a non callable converter */
9883         if (!JS_IsUndefined(method) && !JS_IsNull(method)) {
9884             JSAtom atom;
9885             JSValue arg;
9886             switch(hint) {
9887             case HINT_STRING:
9888                 atom = JS_ATOM_string;
9889                 break;
9890             case HINT_NUMBER:
9891                 atom = JS_ATOM_number;
9892                 break;
9893             default:
9894             case HINT_NONE:
9895                 atom = JS_ATOM_default;
9896                 break;
9897             }
9898             arg = JS_AtomToString(ctx, atom);
9899             ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg);
9900             JS_FreeValue(ctx, arg);
9901             if (JS_IsException(ret))
9902                 goto exception;
9903             JS_FreeValue(ctx, val);
9904             if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT)
9905                 return ret;
9906             JS_FreeValue(ctx, ret);
9907             return JS_ThrowTypeError(ctx, "toPrimitive");
9908         }
9909     }
9910     if (hint != HINT_STRING)
9911         hint = HINT_NUMBER;
9912     for(i = 0; i < 2; i++) {
9913         if ((i ^ hint) == 0) {
9914             method_name = JS_ATOM_toString;
9915         } else {
9916             method_name = JS_ATOM_valueOf;
9917         }
9918         method = JS_GetProperty(ctx, val, method_name);
9919         if (JS_IsException(method))
9920             goto exception;
9921         if (JS_IsFunction(ctx, method)) {
9922             ret = JS_CallFree(ctx, method, val, 0, NULL);
9923             if (JS_IsException(ret))
9924                 goto exception;
9925             if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
9926                 JS_FreeValue(ctx, val);
9927                 return ret;
9928             }
9929             JS_FreeValue(ctx, ret);
9930         } else {
9931             JS_FreeValue(ctx, method);
9932         }
9933     }
9934     JS_ThrowTypeError(ctx, "toPrimitive");
9935 exception:
9936     JS_FreeValue(ctx, val);
9937     return JS_EXCEPTION;
9938 }
9939 
JS_ToPrimitive(JSContext * ctx,JSValueConst val,int hint)9940 static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint)
9941 {
9942     return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint);
9943 }
9944 
JS_SetIsHTMLDDA(JSContext * ctx,JSValueConst obj)9945 void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj)
9946 {
9947     JSObject *p;
9948     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
9949         return;
9950     p = JS_VALUE_GET_OBJ(obj);
9951     p->is_HTMLDDA = TRUE;
9952 }
9953 
JS_IsHTMLDDA(JSContext * ctx,JSValueConst obj)9954 static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj)
9955 {
9956     JSObject *p;
9957     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
9958         return FALSE;
9959     p = JS_VALUE_GET_OBJ(obj);
9960     return p->is_HTMLDDA;
9961 }
9962 
JS_ToBoolFree(JSContext * ctx,JSValue val)9963 static int JS_ToBoolFree(JSContext *ctx, JSValue val)
9964 {
9965     uint32_t tag = JS_VALUE_GET_TAG(val);
9966     switch(tag) {
9967     case JS_TAG_INT:
9968         return JS_VALUE_GET_INT(val) != 0;
9969     case JS_TAG_BOOL:
9970     case JS_TAG_NULL:
9971     case JS_TAG_UNDEFINED:
9972         return JS_VALUE_GET_INT(val);
9973     case JS_TAG_EXCEPTION:
9974         return -1;
9975     case JS_TAG_STRING:
9976         {
9977             BOOL ret = JS_VALUE_GET_STRING(val)->len != 0;
9978             JS_FreeValue(ctx, val);
9979             return ret;
9980         }
9981 #ifdef CONFIG_BIGNUM
9982     case JS_TAG_BIG_INT:
9983     case JS_TAG_BIG_FLOAT:
9984         {
9985             JSBigFloat *p = JS_VALUE_GET_PTR(val);
9986             BOOL ret;
9987             ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
9988             JS_FreeValue(ctx, val);
9989             return ret;
9990         }
9991     case JS_TAG_BIG_DECIMAL:
9992         {
9993             JSBigDecimal *p = JS_VALUE_GET_PTR(val);
9994             BOOL ret;
9995             ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
9996             JS_FreeValue(ctx, val);
9997             return ret;
9998         }
9999 #endif
10000     case JS_TAG_OBJECT:
10001         {
10002             JSObject *p = JS_VALUE_GET_OBJ(val);
10003             BOOL ret;
10004             ret = !p->is_HTMLDDA;
10005             JS_FreeValue(ctx, val);
10006             return ret;
10007         }
10008         break;
10009     default:
10010         if (JS_TAG_IS_FLOAT64(tag)) {
10011             double d = JS_VALUE_GET_FLOAT64(val);
10012             return !isnan(d) && d != 0;
10013         } else {
10014             JS_FreeValue(ctx, val);
10015             return TRUE;
10016         }
10017     }
10018 }
10019 
JS_ToBool(JSContext * ctx,JSValueConst val)10020 int JS_ToBool(JSContext *ctx, JSValueConst val)
10021 {
10022     return JS_ToBoolFree(ctx, JS_DupValue(ctx, val));
10023 }
10024 
skip_spaces(const char * pc)10025 static int skip_spaces(const char *pc)
10026 {
10027     const uint8_t *p, *p_next, *p_start;
10028     uint32_t c;
10029 
10030     p = p_start = (const uint8_t *)pc;
10031     for (;;) {
10032         c = *p;
10033         if (c < 128) {
10034             if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20)))
10035                 break;
10036             p++;
10037         } else {
10038             c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
10039             if (!lre_is_space(c))
10040                 break;
10041             p = p_next;
10042         }
10043     }
10044     return p - p_start;
10045 }
10046 
to_digit(int c)10047 static inline int to_digit(int c)
10048 {
10049     if (c >= '0' && c <= '9')
10050         return c - '0';
10051     else if (c >= 'A' && c <= 'Z')
10052         return c - 'A' + 10;
10053     else if (c >= 'a' && c <= 'z')
10054         return c - 'a' + 10;
10055     else
10056         return 36;
10057 }
10058 
10059 /* XXX: remove */
js_strtod(const char * p,int radix,BOOL is_float)10060 static double js_strtod(const char *p, int radix, BOOL is_float)
10061 {
10062     double d;
10063     int c;
10064 
10065     if (!is_float || radix != 10) {
10066         uint64_t n_max, n;
10067         int int_exp, is_neg;
10068 
10069         is_neg = 0;
10070         if (*p == '-') {
10071             is_neg = 1;
10072             p++;
10073         }
10074 
10075         /* skip leading zeros */
10076         while (*p == '0')
10077             p++;
10078         n = 0;
10079         if (radix == 10)
10080             n_max = ((uint64_t)-1 - 9) / 10; /* most common case */
10081         else
10082             n_max = ((uint64_t)-1 - (radix - 1)) / radix;
10083         /* XXX: could be more precise */
10084         int_exp = 0;
10085         while (*p != '\0') {
10086             c = to_digit((uint8_t)*p);
10087             if (c >= radix)
10088                 break;
10089             if (n <= n_max) {
10090                 n = n * radix + c;
10091             } else {
10092                 int_exp++;
10093             }
10094             p++;
10095         }
10096         d = n;
10097         if (int_exp != 0) {
10098             d *= pow(radix, int_exp);
10099         }
10100         if (is_neg)
10101             d = -d;
10102     } else {
10103         d = strtod(p, NULL);
10104     }
10105     return d;
10106 }
10107 
10108 #define ATOD_INT_ONLY        (1 << 0)
10109 /* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
10110 #define ATOD_ACCEPT_BIN_OCT  (1 << 2)
10111 /* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
10112 #define ATOD_ACCEPT_LEGACY_OCTAL  (1 << 4)
10113 /* accept _ between digits as a digit separator */
10114 #define ATOD_ACCEPT_UNDERSCORES  (1 << 5)
10115 /* allow a suffix to override the type */
10116 #define ATOD_ACCEPT_SUFFIX    (1 << 6)
10117 /* default type */
10118 #define ATOD_TYPE_MASK        (3 << 7)
10119 #define ATOD_TYPE_FLOAT64     (0 << 7)
10120 #define ATOD_TYPE_BIG_INT     (1 << 7)
10121 #define ATOD_TYPE_BIG_FLOAT   (2 << 7)
10122 #define ATOD_TYPE_BIG_DECIMAL (3 << 7)
10123 /* assume bigint mode: floats are parsed as integers if no decimal
10124    point nor exponent */
10125 #define ATOD_MODE_BIGINT      (1 << 9)
10126 /* accept -0x1 */
10127 #define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)
10128 
10129 #ifdef CONFIG_BIGNUM
js_string_to_bigint(JSContext * ctx,const char * buf,int radix,int flags,slimb_t * pexponent)10130 static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,
10131                                    int radix, int flags, slimb_t *pexponent)
10132 {
10133     bf_t a_s, *a = &a_s;
10134     int ret;
10135     JSValue val;
10136     val = JS_NewBigInt(ctx);
10137     if (JS_IsException(val))
10138         return val;
10139     a = JS_GetBigInt(val);
10140     ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ);
10141     if (ret & BF_ST_MEM_ERROR) {
10142         JS_FreeValue(ctx, val);
10143         return JS_ThrowOutOfMemory(ctx);
10144     }
10145     val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);
10146     return val;
10147 }
10148 
js_string_to_bigfloat(JSContext * ctx,const char * buf,int radix,int flags,slimb_t * pexponent)10149 static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf,
10150                                      int radix, int flags, slimb_t *pexponent)
10151 {
10152     bf_t *a;
10153     int ret;
10154     JSValue val;
10155 
10156     val = JS_NewBigFloat(ctx);
10157     if (JS_IsException(val))
10158         return val;
10159     a = JS_GetBigFloat(val);
10160     if (flags & ATOD_ACCEPT_SUFFIX) {
10161         /* return the exponent to get infinite precision */
10162         ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF,
10163                        BF_RNDZ | BF_ATOF_EXPONENT);
10164     } else {
10165         ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec,
10166                       ctx->fp_env.flags);
10167     }
10168     if (ret & BF_ST_MEM_ERROR) {
10169         JS_FreeValue(ctx, val);
10170         return JS_ThrowOutOfMemory(ctx);
10171     }
10172     return val;
10173 }
10174 
js_string_to_bigdecimal(JSContext * ctx,const char * buf,int radix,int flags,slimb_t * pexponent)10175 static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf,
10176                                        int radix, int flags, slimb_t *pexponent)
10177 {
10178     bfdec_t *a;
10179     int ret;
10180     JSValue val;
10181 
10182     val = JS_NewBigDecimal(ctx);
10183     if (JS_IsException(val))
10184         return val;
10185     a = JS_GetBigDecimal(val);
10186     ret = bfdec_atof(a, buf, NULL, BF_PREC_INF,
10187                      BF_RNDZ | BF_ATOF_NO_NAN_INF);
10188     if (ret & BF_ST_MEM_ERROR) {
10189         JS_FreeValue(ctx, val);
10190         return JS_ThrowOutOfMemory(ctx);
10191     }
10192     return val;
10193 }
10194 
10195 #endif
10196 
10197 /* return an exception in case of memory error. Return JS_NAN if
10198    invalid syntax */
10199 #ifdef CONFIG_BIGNUM
js_atof2(JSContext * ctx,const char * str,const char ** pp,int radix,int flags,slimb_t * pexponent)10200 static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp,
10201                         int radix, int flags, slimb_t *pexponent)
10202 #else
10203 static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
10204                        int radix, int flags)
10205 #endif
10206 {
10207     const char *p, *p_start;
10208     int sep, is_neg;
10209     BOOL is_float, has_legacy_octal;
10210     int atod_type = flags & ATOD_TYPE_MASK;
10211     char buf1[64], *buf;
10212     int i, j, len;
10213     BOOL buf_allocated = FALSE;
10214     JSValue val;
10215 
10216     /* optional separator between digits */
10217     sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
10218     has_legacy_octal = FALSE;
10219 
10220     p = str;
10221     p_start = p;
10222     is_neg = 0;
10223     if (p[0] == '+') {
10224         p++;
10225         p_start++;
10226         if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
10227             goto no_radix_prefix;
10228     } else if (p[0] == '-') {
10229         p++;
10230         p_start++;
10231         is_neg = 1;
10232         if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
10233             goto no_radix_prefix;
10234     }
10235     if (p[0] == '0') {
10236         if ((p[1] == 'x' || p[1] == 'X') &&
10237             (radix == 0 || radix == 16)) {
10238             p += 2;
10239             radix = 16;
10240         } else if ((p[1] == 'o' || p[1] == 'O') &&
10241                    radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
10242             p += 2;
10243             radix = 8;
10244         } else if ((p[1] == 'b' || p[1] == 'B') &&
10245                    radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
10246             p += 2;
10247             radix = 2;
10248         } else if ((p[1] >= '0' && p[1] <= '9') &&
10249                    radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {
10250             int i;
10251             has_legacy_octal = TRUE;
10252             sep = 256;
10253             for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
10254                 continue;
10255             if (p[i] == '8' || p[i] == '9')
10256                 goto no_prefix;
10257             p += 1;
10258             radix = 8;
10259         } else {
10260             goto no_prefix;
10261         }
10262         /* there must be a digit after the prefix */
10263         if (to_digit((uint8_t)*p) >= radix)
10264             goto fail;
10265     no_prefix: ;
10266     } else {
10267  no_radix_prefix:
10268         if (!(flags & ATOD_INT_ONLY) &&
10269             (atod_type == ATOD_TYPE_FLOAT64 ||
10270              atod_type == ATOD_TYPE_BIG_FLOAT) &&
10271             strstart(p, "Infinity", &p)) {
10272 #ifdef CONFIG_BIGNUM
10273             if (atod_type == ATOD_TYPE_BIG_FLOAT) {
10274                 bf_t *a;
10275                 val = JS_NewBigFloat(ctx);
10276                 if (JS_IsException(val))
10277                     goto done;
10278                 a = JS_GetBigFloat(val);
10279                 bf_set_inf(a, is_neg);
10280             } else
10281 #endif
10282             {
10283 #if defined(HUGE_VAL) || defined(_MSC_VER)
10284                 double d = HUGE_VAL;
10285 #else
10286                 double d = 1.0 / 0.0;
10287 #endif
10288                 if (is_neg)
10289                     d = -d;
10290                 val = JS_NewFloat64(ctx, d);
10291             }
10292             goto done;
10293         }
10294     }
10295     if (radix == 0)
10296         radix = 10;
10297     is_float = FALSE;
10298     p_start = p;
10299     while (to_digit((uint8_t)*p) < radix
10300            ||  (*p == sep && (radix != 10 ||
10301                               p != p_start + 1 || p[-1] != '0') &&
10302                 to_digit((uint8_t)p[1]) < radix)) {
10303         p++;
10304     }
10305     if (!(flags & ATOD_INT_ONLY)) {
10306         if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {
10307             is_float = TRUE;
10308             p++;
10309             if (*p == sep)
10310                 goto fail;
10311             while (to_digit((uint8_t)*p) < radix ||
10312                    (*p == sep && to_digit((uint8_t)p[1]) < radix))
10313                 p++;
10314         }
10315         if (p > p_start &&
10316             (((*p == 'e' || *p == 'E') && radix == 10) ||
10317              ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
10318             const char *p1 = p + 1;
10319             is_float = TRUE;
10320             if (*p1 == '+') {
10321                 p1++;
10322             } else if (*p1 == '-') {
10323                 p1++;
10324             }
10325             if (is_digit((uint8_t)*p1)) {
10326                 p = p1 + 1;
10327                 while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))
10328                     p++;
10329             }
10330         }
10331     }
10332     if (p == p_start)
10333         goto fail;
10334 
10335     buf = buf1;
10336     buf_allocated = FALSE;
10337     len = p - p_start;
10338     if (unlikely((len + 2) > sizeof(buf1))) {
10339         buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */
10340         if (!buf)
10341             goto mem_error;
10342         buf_allocated = TRUE;
10343     }
10344     /* remove the separators and the radix prefixes */
10345     j = 0;
10346     if (is_neg)
10347         buf[j++] = '-';
10348     for (i = 0; i < len; i++) {
10349         if (p_start[i] != '_')
10350             buf[j++] = p_start[i];
10351     }
10352     buf[j] = '\0';
10353 
10354 #ifdef CONFIG_BIGNUM
10355     if (flags & ATOD_ACCEPT_SUFFIX) {
10356         if (*p == 'n') {
10357             p++;
10358             atod_type = ATOD_TYPE_BIG_INT;
10359         } else if (*p == 'l') {
10360             p++;
10361             atod_type = ATOD_TYPE_BIG_FLOAT;
10362         } else if (*p == 'm') {
10363             p++;
10364             atod_type = ATOD_TYPE_BIG_DECIMAL;
10365         } else {
10366             if (flags & ATOD_MODE_BIGINT) {
10367                 if (!is_float)
10368                     atod_type = ATOD_TYPE_BIG_INT;
10369                 if (has_legacy_octal)
10370                     goto fail;
10371             } else {
10372                 if (is_float && radix != 10)
10373                     goto fail;
10374             }
10375         }
10376     } else {
10377         if (atod_type == ATOD_TYPE_FLOAT64) {
10378             if (flags & ATOD_MODE_BIGINT) {
10379                 if (!is_float)
10380                     atod_type = ATOD_TYPE_BIG_INT;
10381                 if (has_legacy_octal)
10382                     goto fail;
10383             } else {
10384                 if (is_float && radix != 10)
10385                     goto fail;
10386             }
10387         }
10388     }
10389 
10390     switch(atod_type) {
10391     case ATOD_TYPE_FLOAT64:
10392         {
10393             double d;
10394             d = js_strtod(buf, radix, is_float);
10395             /* return int or float64 */
10396             val = JS_NewFloat64(ctx, d);
10397         }
10398         break;
10399     case ATOD_TYPE_BIG_INT:
10400         if (has_legacy_octal || is_float)
10401             goto fail;
10402         val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL);
10403         break;
10404     case ATOD_TYPE_BIG_FLOAT:
10405         if (has_legacy_octal)
10406             goto fail;
10407         val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags,
10408                                                 pexponent);
10409         break;
10410     case ATOD_TYPE_BIG_DECIMAL:
10411         if (radix != 10)
10412             goto fail;
10413         val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL);
10414         break;
10415     default:
10416         abort();
10417     }
10418 #else
10419     {
10420         double d;
10421         (void)has_legacy_octal;
10422         if (is_float && radix != 10)
10423             goto fail;
10424         d = js_strtod(buf, radix, is_float);
10425         val = JS_NewFloat64(ctx, d);
10426     }
10427 #endif
10428 
10429 done:
10430     if (buf_allocated)
10431         js_free_rt(ctx->rt, buf);
10432     if (pp)
10433         *pp = p;
10434     return val;
10435  fail:
10436     val = JS_NAN;
10437     goto done;
10438  mem_error:
10439     val = JS_ThrowOutOfMemory(ctx);
10440     goto done;
10441 }
10442 
10443 #ifdef CONFIG_BIGNUM
js_atof(JSContext * ctx,const char * str,const char ** pp,int radix,int flags)10444 static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
10445                        int radix, int flags)
10446 {
10447     return js_atof2(ctx, str, pp, radix, flags, NULL);
10448 }
10449 #endif
10450 
10451 typedef enum JSToNumberHintEnum {
10452     TON_FLAG_NUMBER,
10453     TON_FLAG_NUMERIC,
10454 } JSToNumberHintEnum;
10455 
JS_ToNumberHintFree(JSContext * ctx,JSValue val,JSToNumberHintEnum flag)10456 static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
10457                                    JSToNumberHintEnum flag)
10458 {
10459     uint32_t tag;
10460     JSValue ret;
10461 
10462  redo:
10463     tag = JS_VALUE_GET_NORM_TAG(val);
10464     switch(tag) {
10465 #ifdef CONFIG_BIGNUM
10466     case JS_TAG_BIG_DECIMAL:
10467         if (flag != TON_FLAG_NUMERIC) {
10468             JS_FreeValue(ctx, val);
10469             return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number");
10470         }
10471         ret = val;
10472         break;
10473     case JS_TAG_BIG_INT:
10474         if (flag != TON_FLAG_NUMERIC) {
10475             JS_FreeValue(ctx, val);
10476             return JS_ThrowTypeError(ctx, "cannot convert bigint to number");
10477         }
10478         ret = val;
10479         break;
10480     case JS_TAG_BIG_FLOAT:
10481         if (flag != TON_FLAG_NUMERIC) {
10482             JS_FreeValue(ctx, val);
10483             return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number");
10484         }
10485         ret = val;
10486         break;
10487 #endif
10488     case JS_TAG_FLOAT64:
10489     case JS_TAG_INT:
10490     case JS_TAG_EXCEPTION:
10491         ret = val;
10492         break;
10493     case JS_TAG_BOOL:
10494     case JS_TAG_NULL:
10495         ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
10496         break;
10497     case JS_TAG_UNDEFINED:
10498         ret = JS_NAN;
10499         break;
10500     case JS_TAG_OBJECT:
10501         val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
10502         if (JS_IsException(val))
10503             return JS_EXCEPTION;
10504         goto redo;
10505     case JS_TAG_STRING:
10506         {
10507             const char *str;
10508             const char *p;
10509             size_t len;
10510 
10511             str = JS_ToCStringLen(ctx, &len, val);
10512             JS_FreeValue(ctx, val);
10513             if (!str)
10514                 return JS_EXCEPTION;
10515             p = str;
10516             p += skip_spaces(p);
10517             if ((p - str) == len) {
10518                 ret = JS_NewInt32(ctx, 0);
10519             } else {
10520                 int flags = ATOD_ACCEPT_BIN_OCT;
10521                 ret = js_atof(ctx, p, &p, 0, flags);
10522                 if (!JS_IsException(ret)) {
10523                     p += skip_spaces(p);
10524                     if ((p - str) != len) {
10525                         JS_FreeValue(ctx, ret);
10526                         ret = JS_NAN;
10527                     }
10528                 }
10529             }
10530             JS_FreeCString(ctx, str);
10531         }
10532         break;
10533     case JS_TAG_SYMBOL:
10534         JS_FreeValue(ctx, val);
10535         return JS_ThrowTypeError(ctx, "cannot convert symbol to number");
10536     default:
10537         JS_FreeValue(ctx, val);
10538         ret = JS_NAN;
10539         break;
10540     }
10541     return ret;
10542 }
10543 
JS_ToNumberFree(JSContext * ctx,JSValue val)10544 static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
10545 {
10546     return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);
10547 }
10548 
JS_ToNumericFree(JSContext * ctx,JSValue val)10549 static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)
10550 {
10551     return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);
10552 }
10553 
JS_ToNumeric(JSContext * ctx,JSValueConst val)10554 static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
10555 {
10556     return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
10557 }
10558 
__JS_ToFloat64Free(JSContext * ctx,double * pres,JSValue val)10559 static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,
10560                                           JSValue val)
10561 {
10562     double d;
10563     uint32_t tag;
10564 
10565     val = JS_ToNumberFree(ctx, val);
10566     if (JS_IsException(val)) {
10567         *pres = JS_FLOAT64_NAN;
10568         return -1;
10569     }
10570     tag = JS_VALUE_GET_NORM_TAG(val);
10571     switch(tag) {
10572     case JS_TAG_INT:
10573         d = JS_VALUE_GET_INT(val);
10574         break;
10575     case JS_TAG_FLOAT64:
10576         d = JS_VALUE_GET_FLOAT64(val);
10577         break;
10578 #ifdef CONFIG_BIGNUM
10579     case JS_TAG_BIG_INT:
10580     case JS_TAG_BIG_FLOAT:
10581         {
10582             JSBigFloat *p = JS_VALUE_GET_PTR(val);
10583             /* XXX: there can be a double rounding issue with some
10584                primitives (such as JS_ToUint8ClampFree()), but it is
10585                not critical to fix it. */
10586             bf_get_float64(&p->num, &d, BF_RNDN);
10587             JS_FreeValue(ctx, val);
10588         }
10589         break;
10590 #endif
10591     default:
10592         abort();
10593     }
10594     *pres = d;
10595     return 0;
10596 }
10597 
JS_ToFloat64Free(JSContext * ctx,double * pres,JSValue val)10598 static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
10599 {
10600     uint32_t tag;
10601 
10602     tag = JS_VALUE_GET_TAG(val);
10603     if (tag <= JS_TAG_NULL) {
10604         *pres = JS_VALUE_GET_INT(val);
10605         return 0;
10606     } else if (JS_TAG_IS_FLOAT64(tag)) {
10607         *pres = JS_VALUE_GET_FLOAT64(val);
10608         return 0;
10609     } else {
10610         return __JS_ToFloat64Free(ctx, pres, val);
10611     }
10612 }
10613 
JS_ToFloat64(JSContext * ctx,double * pres,JSValueConst val)10614 int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)
10615 {
10616     return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));
10617 }
10618 
JS_ToNumber(JSContext * ctx,JSValueConst val)10619 static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
10620 {
10621     return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
10622 }
10623 
10624 /* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
JS_ToIntegerFree(JSContext * ctx,JSValue val)10625 static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
10626 {
10627     uint32_t tag;
10628     JSValue ret;
10629 
10630  redo:
10631     tag = JS_VALUE_GET_NORM_TAG(val);
10632     switch(tag) {
10633     case JS_TAG_INT:
10634     case JS_TAG_BOOL:
10635     case JS_TAG_NULL:
10636     case JS_TAG_UNDEFINED:
10637         ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
10638         break;
10639     case JS_TAG_FLOAT64:
10640         {
10641             double d = JS_VALUE_GET_FLOAT64(val);
10642             if (isnan(d)) {
10643                 ret = JS_NewInt32(ctx, 0);
10644             } else {
10645                 /* convert -0 to +0 */
10646                 d = trunc(d) + 0.0;
10647                 ret = JS_NewFloat64(ctx, d);
10648             }
10649         }
10650         break;
10651 #ifdef CONFIG_BIGNUM
10652     case JS_TAG_BIG_FLOAT:
10653         {
10654             bf_t a_s, *a, r_s, *r = &r_s;
10655             BOOL is_nan;
10656 
10657             a = JS_ToBigFloat(ctx, &a_s, val);
10658             if (!bf_is_finite(a)) {
10659                 is_nan = bf_is_nan(a);
10660                 if (is_nan)
10661                     ret = JS_NewInt32(ctx, 0);
10662                 else
10663                     ret = JS_DupValue(ctx, val);
10664             } else {
10665                 ret = JS_NewBigInt(ctx);
10666                 if (!JS_IsException(ret)) {
10667                     r = JS_GetBigInt(ret);
10668                     bf_set(r, a);
10669                     bf_rint(r, BF_RNDZ);
10670                     ret = JS_CompactBigInt(ctx, ret);
10671                 }
10672             }
10673             if (a == &a_s)
10674                 bf_delete(a);
10675             JS_FreeValue(ctx, val);
10676         }
10677         break;
10678 #endif
10679     default:
10680         val = JS_ToNumberFree(ctx, val);
10681         if (JS_IsException(val))
10682             return val;
10683         goto redo;
10684     }
10685     return ret;
10686 }
10687 
10688 /* Note: the integer value is satured to 32 bits */
JS_ToInt32SatFree(JSContext * ctx,int * pres,JSValue val)10689 static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
10690 {
10691     uint32_t tag;
10692     int ret;
10693 
10694  redo:
10695     tag = JS_VALUE_GET_NORM_TAG(val);
10696     switch(tag) {
10697     case JS_TAG_INT:
10698     case JS_TAG_BOOL:
10699     case JS_TAG_NULL:
10700     case JS_TAG_UNDEFINED:
10701         ret = JS_VALUE_GET_INT(val);
10702         break;
10703     case JS_TAG_EXCEPTION:
10704         *pres = 0;
10705         return -1;
10706     case JS_TAG_FLOAT64:
10707         {
10708             double d = JS_VALUE_GET_FLOAT64(val);
10709             if (isnan(d)) {
10710                 ret = 0;
10711             } else {
10712                 if (d < INT32_MIN)
10713                     ret = INT32_MIN;
10714                 else if (d > INT32_MAX)
10715                     ret = INT32_MAX;
10716                 else
10717                     ret = (int)d;
10718             }
10719         }
10720         break;
10721 #ifdef CONFIG_BIGNUM
10722     case JS_TAG_BIG_FLOAT:
10723         {
10724             JSBigFloat *p = JS_VALUE_GET_PTR(val);
10725             bf_get_int32(&ret, &p->num, 0);
10726             JS_FreeValue(ctx, val);
10727         }
10728         break;
10729 #endif
10730     default:
10731         val = JS_ToNumberFree(ctx, val);
10732         if (JS_IsException(val)) {
10733             *pres = 0;
10734             return -1;
10735         }
10736         goto redo;
10737     }
10738     *pres = ret;
10739     return 0;
10740 }
10741 
JS_ToInt32Sat(JSContext * ctx,int * pres,JSValueConst val)10742 int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)
10743 {
10744     return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
10745 }
10746 
JS_ToInt32Clamp(JSContext * ctx,int * pres,JSValueConst val,int min,int max,int min_offset)10747 int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,
10748                     int min, int max, int min_offset)
10749 {
10750     int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
10751     if (res == 0) {
10752         if (*pres < min) {
10753             *pres += min_offset;
10754             if (*pres < min)
10755                 *pres = min;
10756         } else {
10757             if (*pres > max)
10758                 *pres = max;
10759         }
10760     }
10761     return res;
10762 }
10763 
JS_ToInt64SatFree(JSContext * ctx,int64_t * pres,JSValue val)10764 static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)
10765 {
10766     uint32_t tag;
10767 
10768  redo:
10769     tag = JS_VALUE_GET_NORM_TAG(val);
10770     switch(tag) {
10771     case JS_TAG_INT:
10772     case JS_TAG_BOOL:
10773     case JS_TAG_NULL:
10774     case JS_TAG_UNDEFINED:
10775         *pres = JS_VALUE_GET_INT(val);
10776         return 0;
10777     case JS_TAG_EXCEPTION:
10778         *pres = 0;
10779         return -1;
10780     case JS_TAG_FLOAT64:
10781         {
10782             double d = JS_VALUE_GET_FLOAT64(val);
10783             if (isnan(d)) {
10784                 *pres = 0;
10785             } else {
10786                 if (d < INT64_MIN)
10787                     *pres = INT64_MIN;
10788                 else if (d > INT64_MAX)
10789                     *pres = INT64_MAX;
10790                 else
10791                     *pres = (int64_t)d;
10792             }
10793         }
10794         return 0;
10795 #ifdef CONFIG_BIGNUM
10796     case JS_TAG_BIG_FLOAT:
10797         {
10798             JSBigFloat *p = JS_VALUE_GET_PTR(val);
10799             bf_get_int64(pres, &p->num, 0);
10800             JS_FreeValue(ctx, val);
10801         }
10802         return 0;
10803 #endif
10804     default:
10805         val = JS_ToNumberFree(ctx, val);
10806         if (JS_IsException(val)) {
10807             *pres = 0;
10808             return -1;
10809         }
10810         goto redo;
10811     }
10812 }
10813 
JS_ToInt64Sat(JSContext * ctx,int64_t * pres,JSValueConst val)10814 int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)
10815 {
10816     return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
10817 }
10818 
JS_ToInt64Clamp(JSContext * ctx,int64_t * pres,JSValueConst val,int64_t min,int64_t max,int64_t neg_offset)10819 int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,
10820                     int64_t min, int64_t max, int64_t neg_offset)
10821 {
10822     int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
10823     if (res == 0) {
10824         if (*pres < 0)
10825             *pres += neg_offset;
10826         if (*pres < min)
10827             *pres = min;
10828         else if (*pres > max)
10829             *pres = max;
10830     }
10831     return res;
10832 }
10833 
10834 /* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)
10835    in case of exception */
JS_ToInt64Free(JSContext * ctx,int64_t * pres,JSValue val)10836 static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
10837 {
10838     uint32_t tag;
10839     int64_t ret;
10840 
10841  redo:
10842     tag = JS_VALUE_GET_NORM_TAG(val);
10843     switch(tag) {
10844     case JS_TAG_INT:
10845     case JS_TAG_BOOL:
10846     case JS_TAG_NULL:
10847     case JS_TAG_UNDEFINED:
10848         ret = JS_VALUE_GET_INT(val);
10849         break;
10850     case JS_TAG_FLOAT64:
10851         {
10852             JSFloat64Union u;
10853             double d;
10854             int e;
10855             d = JS_VALUE_GET_FLOAT64(val);
10856             u.d = d;
10857             /* we avoid doing fmod(x, 2^64) */
10858             e = (u.u64 >> 52) & 0x7ff;
10859             if (likely(e <= (1023 + 62))) {
10860                 /* fast case */
10861                 ret = (int64_t)d;
10862             } else if (e <= (1023 + 62 + 53)) {
10863                 uint64_t v;
10864                 /* remainder modulo 2^64 */
10865                 v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
10866                 ret = v << ((e - 1023) - 52);
10867                 /* take the sign into account */
10868                 if (u.u64 >> 63)
10869                     ret = -ret;
10870             } else {
10871                 ret = 0; /* also handles NaN and +inf */
10872             }
10873         }
10874         break;
10875 #ifdef CONFIG_BIGNUM
10876     case JS_TAG_BIG_FLOAT:
10877         {
10878             JSBigFloat *p = JS_VALUE_GET_PTR(val);
10879             bf_get_int64(&ret, &p->num, BF_GET_INT_MOD);
10880             JS_FreeValue(ctx, val);
10881         }
10882         break;
10883 #endif
10884     default:
10885         val = JS_ToNumberFree(ctx, val);
10886         if (JS_IsException(val)) {
10887             *pres = 0;
10888             return -1;
10889         }
10890         goto redo;
10891     }
10892     *pres = ret;
10893     return 0;
10894 }
10895 
JS_ToInt64(JSContext * ctx,int64_t * pres,JSValueConst val)10896 int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
10897 {
10898     return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));
10899 }
10900 
JS_ToInt64Ext(JSContext * ctx,int64_t * pres,JSValueConst val)10901 int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)
10902 {
10903     if (JS_IsBigInt(ctx, val))
10904         return JS_ToBigInt64(ctx, pres, val);
10905     else
10906         return JS_ToInt64(ctx, pres, val);
10907 }
10908 
10909 /* return (<0, 0) in case of exception */
JS_ToInt32Free(JSContext * ctx,int32_t * pres,JSValue val)10910 static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
10911 {
10912     uint32_t tag;
10913     int32_t ret;
10914 
10915  redo:
10916     tag = JS_VALUE_GET_NORM_TAG(val);
10917     switch(tag) {
10918     case JS_TAG_INT:
10919     case JS_TAG_BOOL:
10920     case JS_TAG_NULL:
10921     case JS_TAG_UNDEFINED:
10922         ret = JS_VALUE_GET_INT(val);
10923         break;
10924     case JS_TAG_FLOAT64:
10925         {
10926             JSFloat64Union u;
10927             double d;
10928             int e;
10929             d = JS_VALUE_GET_FLOAT64(val);
10930             u.d = d;
10931             /* we avoid doing fmod(x, 2^32) */
10932             e = (u.u64 >> 52) & 0x7ff;
10933             if (likely(e <= (1023 + 30))) {
10934                 /* fast case */
10935                 ret = (int32_t)d;
10936             } else if (e <= (1023 + 30 + 53)) {
10937                 uint64_t v;
10938                 /* remainder modulo 2^32 */
10939                 v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
10940                 v = v << ((e - 1023) - 52 + 32);
10941                 ret = v >> 32;
10942                 /* take the sign into account */
10943                 if (u.u64 >> 63)
10944                     ret = -ret;
10945             } else {
10946                 ret = 0; /* also handles NaN and +inf */
10947             }
10948         }
10949         break;
10950 #ifdef CONFIG_BIGNUM
10951     case JS_TAG_BIG_FLOAT:
10952         {
10953             JSBigFloat *p = JS_VALUE_GET_PTR(val);
10954             bf_get_int32(&ret, &p->num, BF_GET_INT_MOD);
10955             JS_FreeValue(ctx, val);
10956         }
10957         break;
10958 #endif
10959     default:
10960         val = JS_ToNumberFree(ctx, val);
10961         if (JS_IsException(val)) {
10962             *pres = 0;
10963             return -1;
10964         }
10965         goto redo;
10966     }
10967     *pres = ret;
10968     return 0;
10969 }
10970 
JS_ToInt32(JSContext * ctx,int32_t * pres,JSValueConst val)10971 int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)
10972 {
10973     return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));
10974 }
10975 
JS_ToUint32Free(JSContext * ctx,uint32_t * pres,JSValue val)10976 static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)
10977 {
10978     return JS_ToInt32Free(ctx, (int32_t *)pres, val);
10979 }
10980 
JS_ToUint8ClampFree(JSContext * ctx,int32_t * pres,JSValue val)10981 static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
10982 {
10983     uint32_t tag;
10984     int res;
10985 
10986  redo:
10987     tag = JS_VALUE_GET_NORM_TAG(val);
10988     switch(tag) {
10989     case JS_TAG_INT:
10990     case JS_TAG_BOOL:
10991     case JS_TAG_NULL:
10992     case JS_TAG_UNDEFINED:
10993         res = JS_VALUE_GET_INT(val);
10994 #ifdef CONFIG_BIGNUM
10995     int_clamp:
10996 #endif
10997         res = max_int(0, min_int(255, res));
10998         break;
10999     case JS_TAG_FLOAT64:
11000         {
11001             double d = JS_VALUE_GET_FLOAT64(val);
11002             if (isnan(d)) {
11003                 res = 0;
11004             } else {
11005                 if (d < 0)
11006                     res = 0;
11007                 else if (d > 255)
11008                     res = 255;
11009                 else
11010                     res = lrint(d);
11011             }
11012         }
11013         break;
11014 #ifdef CONFIG_BIGNUM
11015     case JS_TAG_BIG_FLOAT:
11016         {
11017             JSBigFloat *p = JS_VALUE_GET_PTR(val);
11018             bf_t r_s, *r = &r_s;
11019             bf_init(ctx->bf_ctx, r);
11020             bf_set(r, &p->num);
11021             bf_rint(r, BF_RNDN);
11022             bf_get_int32(&res, r, 0);
11023             bf_delete(r);
11024             JS_FreeValue(ctx, val);
11025         }
11026         goto int_clamp;
11027 #endif
11028     default:
11029         val = JS_ToNumberFree(ctx, val);
11030         if (JS_IsException(val)) {
11031             *pres = 0;
11032             return -1;
11033         }
11034         goto redo;
11035     }
11036     *pres = res;
11037     return 0;
11038 }
11039 
JS_ToArrayLengthFree(JSContext * ctx,uint32_t * plen,JSValue val,BOOL is_array_ctor)11040 static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
11041                                             JSValue val, BOOL is_array_ctor)
11042 {
11043     uint32_t tag, len;
11044 
11045     tag = JS_VALUE_GET_TAG(val);
11046     switch(tag) {
11047     case JS_TAG_INT:
11048     case JS_TAG_BOOL:
11049     case JS_TAG_NULL:
11050         {
11051             int v;
11052             v = JS_VALUE_GET_INT(val);
11053             if (v < 0)
11054                 goto fail;
11055             len = v;
11056         }
11057         break;
11058 #ifdef CONFIG_BIGNUM
11059     case JS_TAG_BIG_INT:
11060     case JS_TAG_BIG_FLOAT:
11061         {
11062             JSBigFloat *p = JS_VALUE_GET_PTR(val);
11063             bf_t a;
11064             BOOL res;
11065             bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD);
11066             bf_init(ctx->bf_ctx, &a);
11067             bf_set_ui(&a, len);
11068             res = bf_cmp_eq(&a, &p->num);
11069             bf_delete(&a);
11070             JS_FreeValue(ctx, val);
11071             if (!res)
11072                 goto fail;
11073         }
11074         break;
11075 #endif
11076     default:
11077         if (JS_TAG_IS_FLOAT64(tag)) {
11078             double d;
11079             d = JS_VALUE_GET_FLOAT64(val);
11080             len = (uint32_t)d;
11081             if (len != d)
11082                 goto fail;
11083         } else {
11084             uint32_t len1;
11085 
11086             if (is_array_ctor) {
11087                 val = JS_ToNumberFree(ctx, val);
11088                 if (JS_IsException(val))
11089                     return -1;
11090                 /* cannot recurse because val is a number */
11091                 if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))
11092                     return -1;
11093             } else {
11094                 /* legacy behavior: must do the conversion twice and compare */
11095                 if (JS_ToUint32(ctx, &len, val)) {
11096                     JS_FreeValue(ctx, val);
11097                     return -1;
11098                 }
11099                 val = JS_ToNumberFree(ctx, val);
11100                 if (JS_IsException(val))
11101                     return -1;
11102                 /* cannot recurse because val is a number */
11103                 if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))
11104                     return -1;
11105                 if (len1 != len) {
11106                 fail:
11107                     JS_ThrowRangeError(ctx, "invalid array length");
11108                     return -1;
11109                 }
11110             }
11111         }
11112         break;
11113     }
11114     *plen = len;
11115     return 0;
11116 }
11117 
11118 #define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
11119 
is_safe_integer(double d)11120 static BOOL is_safe_integer(double d)
11121 {
11122     return isfinite(d) && floor(d) == d &&
11123         fabs(d) <= (double)MAX_SAFE_INTEGER;
11124 }
11125 
JS_ToIndex(JSContext * ctx,uint64_t * plen,JSValueConst val)11126 int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
11127 {
11128     int64_t v;
11129     if (JS_ToInt64Sat(ctx, &v, val))
11130         return -1;
11131     if (v < 0 || v > MAX_SAFE_INTEGER) {
11132         JS_ThrowRangeError(ctx, "invalid array index");
11133         *plen = 0;
11134         return -1;
11135     }
11136     *plen = v;
11137     return 0;
11138 }
11139 
11140 /* convert a value to a length between 0 and MAX_SAFE_INTEGER.
11141    return -1 for exception */
JS_ToLengthFree(JSContext * ctx,int64_t * plen,JSValue val)11142 static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
11143                                        JSValue val)
11144 {
11145     int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);
11146     JS_FreeValue(ctx, val);
11147     return res;
11148 }
11149 
11150 /* Note: can return an exception */
JS_NumberIsInteger(JSContext * ctx,JSValueConst val)11151 static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)
11152 {
11153     double d;
11154     if (!JS_IsNumber(val))
11155         return FALSE;
11156     if (unlikely(JS_ToFloat64(ctx, &d, val)))
11157         return -1;
11158     return isfinite(d) && floor(d) == d;
11159 }
11160 
JS_NumberIsNegativeOrMinusZero(JSContext * ctx,JSValueConst val)11161 static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)
11162 {
11163     uint32_t tag;
11164 
11165     tag = JS_VALUE_GET_NORM_TAG(val);
11166     switch(tag) {
11167     case JS_TAG_INT:
11168         {
11169             int v;
11170             v = JS_VALUE_GET_INT(val);
11171             return (v < 0);
11172         }
11173     case JS_TAG_FLOAT64:
11174         {
11175             JSFloat64Union u;
11176             u.d = JS_VALUE_GET_FLOAT64(val);
11177             return (u.u64 >> 63);
11178         }
11179 #ifdef CONFIG_BIGNUM
11180     case JS_TAG_BIG_INT:
11181         {
11182             JSBigFloat *p = JS_VALUE_GET_PTR(val);
11183             /* Note: integer zeros are not necessarily positive */
11184             return p->num.sign && !bf_is_zero(&p->num);
11185         }
11186     case JS_TAG_BIG_FLOAT:
11187         {
11188             JSBigFloat *p = JS_VALUE_GET_PTR(val);
11189             return p->num.sign;
11190         }
11191         break;
11192     case JS_TAG_BIG_DECIMAL:
11193         {
11194             JSBigDecimal *p = JS_VALUE_GET_PTR(val);
11195             return p->num.sign;
11196         }
11197         break;
11198 #endif
11199     default:
11200         return FALSE;
11201     }
11202 }
11203 
11204 #ifdef CONFIG_BIGNUM
11205 
js_bigint_to_string1(JSContext * ctx,JSValueConst val,int radix)11206 static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)
11207 {
11208     JSValue ret;
11209     bf_t a_s, *a;
11210     char *str;
11211     int saved_sign;
11212 
11213     a = JS_ToBigInt(ctx, &a_s, val);
11214     if (!a)
11215         return JS_EXCEPTION;
11216     saved_sign = a->sign;
11217     if (a->expn == BF_EXP_ZERO)
11218         a->sign = 0;
11219     str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC |
11220                   BF_FTOA_JS_QUIRKS);
11221     a->sign = saved_sign;
11222     JS_FreeBigInt(ctx, a, &a_s);
11223     if (!str)
11224         return JS_ThrowOutOfMemory(ctx);
11225     ret = JS_NewString(ctx, str);
11226     bf_free(ctx->bf_ctx, str);
11227     return ret;
11228 }
11229 
js_bigint_to_string(JSContext * ctx,JSValueConst val)11230 static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)
11231 {
11232     return js_bigint_to_string1(ctx, val, 10);
11233 }
11234 
js_ftoa(JSContext * ctx,JSValueConst val1,int radix,limb_t prec,bf_flags_t flags)11235 static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix,
11236                        limb_t prec, bf_flags_t flags)
11237 {
11238     JSValue val, ret;
11239     bf_t a_s, *a;
11240     char *str;
11241     int saved_sign;
11242 
11243     val = JS_ToNumeric(ctx, val1);
11244     if (JS_IsException(val))
11245         return val;
11246     a = JS_ToBigFloat(ctx, &a_s, val);
11247     saved_sign = a->sign;
11248     if (a->expn == BF_EXP_ZERO)
11249         a->sign = 0;
11250     flags |= BF_FTOA_JS_QUIRKS;
11251     if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) {
11252         /* Note: for floating point numbers with a radix which is not
11253            a power of two, the current precision is used to compute
11254            the number of digits. */
11255         if ((radix & (radix - 1)) != 0) {
11256             bf_t r_s, *r = &r_s;
11257             int prec, flags1;
11258             /* must round first */
11259             if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
11260                 prec = ctx->fp_env.prec;
11261                 flags1 = ctx->fp_env.flags &
11262                     (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT));
11263             } else {
11264                 prec = 53;
11265                 flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL;
11266             }
11267             bf_init(ctx->bf_ctx, r);
11268             bf_set(r, a);
11269             bf_round(r, prec, flags1 | BF_RNDN);
11270             str = bf_ftoa(NULL, r, radix, prec, flags1 | flags);
11271             bf_delete(r);
11272         } else {
11273             str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags);
11274         }
11275     } else {
11276         str = bf_ftoa(NULL, a, radix, prec, flags);
11277     }
11278     a->sign = saved_sign;
11279     if (a == &a_s)
11280         bf_delete(a);
11281     JS_FreeValue(ctx, val);
11282     if (!str)
11283         return JS_ThrowOutOfMemory(ctx);
11284     ret = JS_NewString(ctx, str);
11285     bf_free(ctx->bf_ctx, str);
11286     return ret;
11287 }
11288 
js_bigfloat_to_string(JSContext * ctx,JSValueConst val)11289 static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val)
11290 {
11291     return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
11292 }
11293 
js_bigdecimal_to_string1(JSContext * ctx,JSValueConst val,limb_t prec,int flags)11294 static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val,
11295                                         limb_t prec, int flags)
11296 {
11297     JSValue ret;
11298     bfdec_t *a;
11299     char *str;
11300     int saved_sign;
11301 
11302     a = JS_ToBigDecimal(ctx, val);
11303     saved_sign = a->sign;
11304     if (a->expn == BF_EXP_ZERO)
11305         a->sign = 0;
11306     str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS);
11307     a->sign = saved_sign;
11308     if (!str)
11309         return JS_ThrowOutOfMemory(ctx);
11310     ret = JS_NewString(ctx, str);
11311     bf_free(ctx->bf_ctx, str);
11312     return ret;
11313 }
11314 
js_bigdecimal_to_string(JSContext * ctx,JSValueConst val)11315 static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)
11316 {
11317     return js_bigdecimal_to_string1(ctx, val, 0,
11318                                     BF_RNDZ | BF_FTOA_FORMAT_FREE);
11319 }
11320 
11321 #endif /* CONFIG_BIGNUM */
11322 
11323 /* 2 <= base <= 36 */
i64toa(char * buf_end,int64_t n,unsigned int base)11324 static char *i64toa(char *buf_end, int64_t n, unsigned int base)
11325 {
11326     char *q = buf_end;
11327     int digit, is_neg;
11328 
11329     is_neg = 0;
11330     if (n < 0) {
11331         is_neg = 1;
11332         n = -n;
11333     }
11334     *--q = '\0';
11335     do {
11336         digit = (uint64_t)n % base;
11337         n = (uint64_t)n / base;
11338         if (digit < 10)
11339             digit += '0';
11340         else
11341             digit += 'a' - 10;
11342         *--q = digit;
11343     } while (n != 0);
11344     if (is_neg)
11345         *--q = '-';
11346     return q;
11347 }
11348 
11349 /* buf1 contains the printf result */
js_ecvt1(double d,int n_digits,int * decpt,int * sign,char * buf,int rounding_mode,char * buf1,int buf1_size)11350 static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,
11351                      int rounding_mode, char *buf1, int buf1_size)
11352 {
11353     if (rounding_mode != FE_TONEAREST)
11354         fesetround(rounding_mode);
11355     snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);
11356     if (rounding_mode != FE_TONEAREST)
11357         fesetround(FE_TONEAREST);
11358     *sign = (buf1[0] == '-');
11359     /* mantissa */
11360     buf[0] = buf1[1];
11361     if (n_digits > 1)
11362         memcpy(buf + 1, buf1 + 3, n_digits - 1);
11363     buf[n_digits] = '\0';
11364     /* exponent */
11365     *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;
11366 }
11367 
11368 /* maximum buffer size for js_dtoa */
11369 #define JS_DTOA_BUF_SIZE 128
11370 
11371 /* needed because ecvt usually limits the number of digits to
11372    17. Return the number of digits. */
js_ecvt(double d,int n_digits,int * decpt,int * sign,char * buf,BOOL is_fixed)11373 static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,
11374                    BOOL is_fixed)
11375 {
11376     int rounding_mode;
11377     char buf_tmp[JS_DTOA_BUF_SIZE];
11378 
11379     if (!is_fixed) {
11380         unsigned int n_digits_min, n_digits_max;
11381         /* find the minimum amount of digits (XXX: inefficient but simple) */
11382         n_digits_min = 1;
11383         n_digits_max = 17;
11384         while (n_digits_min < n_digits_max) {
11385             n_digits = (n_digits_min + n_digits_max) / 2;
11386             js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,
11387                      buf_tmp, sizeof(buf_tmp));
11388             if (strtod(buf_tmp, NULL) == d) {
11389                 /* no need to keep the trailing zeros */
11390                 while (n_digits >= 2 && buf[n_digits - 1] == '0')
11391                     n_digits--;
11392                 n_digits_max = n_digits;
11393             } else {
11394                 n_digits_min = n_digits + 1;
11395             }
11396         }
11397         n_digits = n_digits_max;
11398         rounding_mode = FE_TONEAREST;
11399     } else {
11400         rounding_mode = FE_TONEAREST;
11401 #ifdef CONFIG_PRINTF_RNDN
11402         {
11403             char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];
11404             int decpt1, sign1, decpt2, sign2;
11405             /* The JS rounding is specified as round to nearest ties away
11406                from zero (RNDNA), but in printf the "ties" case is not
11407                specified (for example it is RNDN for glibc, RNDNA for
11408                Windows), so we must round manually. */
11409             js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,
11410                      buf_tmp, sizeof(buf_tmp));
11411             /* XXX: could use 2 digits to reduce the average running time */
11412             if (buf1[n_digits] == '5') {
11413                 js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,
11414                          buf_tmp, sizeof(buf_tmp));
11415                 js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,
11416                          buf_tmp, sizeof(buf_tmp));
11417                 if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {
11418                     /* exact result: round away from zero */
11419                     if (sign1)
11420                         rounding_mode = FE_DOWNWARD;
11421                     else
11422                         rounding_mode = FE_UPWARD;
11423                 }
11424             }
11425         }
11426 #endif /* CONFIG_PRINTF_RNDN */
11427     }
11428     js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,
11429              buf_tmp, sizeof(buf_tmp));
11430     return n_digits;
11431 }
11432 
js_fcvt1(char * buf,int buf_size,double d,int n_digits,int rounding_mode)11433 static int js_fcvt1(char *buf, int buf_size, double d, int n_digits,
11434                     int rounding_mode)
11435 {
11436     int n;
11437     if (rounding_mode != FE_TONEAREST)
11438         fesetround(rounding_mode);
11439     n = snprintf(buf, buf_size, "%.*f", n_digits, d);
11440     if (rounding_mode != FE_TONEAREST)
11441         fesetround(FE_TONEAREST);
11442     assert(n < buf_size);
11443     return n;
11444 }
11445 
js_fcvt(char * buf,int buf_size,double d,int n_digits)11446 static void js_fcvt(char *buf, int buf_size, double d, int n_digits)
11447 {
11448     int rounding_mode;
11449     rounding_mode = FE_TONEAREST;
11450 #ifdef CONFIG_PRINTF_RNDN
11451     {
11452         int n1, n2;
11453         char buf1[JS_DTOA_BUF_SIZE];
11454         char buf2[JS_DTOA_BUF_SIZE];
11455 
11456         /* The JS rounding is specified as round to nearest ties away from
11457            zero (RNDNA), but in printf the "ties" case is not specified
11458            (for example it is RNDN for glibc, RNDNA for Windows), so we
11459            must round manually. */
11460         n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST);
11461         rounding_mode = FE_TONEAREST;
11462         /* XXX: could use 2 digits to reduce the average running time */
11463         if (buf1[n1 - 1] == '5') {
11464             n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD);
11465             n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD);
11466             if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {
11467                 /* exact result: round away from zero */
11468                 if (buf1[0] == '-')
11469                     rounding_mode = FE_DOWNWARD;
11470                 else
11471                     rounding_mode = FE_UPWARD;
11472             }
11473         }
11474     }
11475 #endif /* CONFIG_PRINTF_RNDN */
11476     js_fcvt1(buf, buf_size, d, n_digits, rounding_mode);
11477 }
11478 
11479 /* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */
11480 /* use as many digits as necessary */
11481 #define JS_DTOA_VAR_FORMAT   (0 << 0)
11482 /* use n_digits significant digits (1 <= n_digits <= 101) */
11483 #define JS_DTOA_FIXED_FORMAT (1 << 0)
11484 /* force fractional format: [-]dd.dd with n_digits fractional digits */
11485 #define JS_DTOA_FRAC_FORMAT  (2 << 0)
11486 /* force exponential notation either in fixed or variable format */
11487 #define JS_DTOA_FORCE_EXP    (1 << 2)
11488 
11489 /* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.
11490    XXX: radix != 10 is only supported for small integers
11491 */
js_dtoa1(char * buf,double d,int radix,int n_digits,int flags)11492 static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags)
11493 {
11494     char *q;
11495 
11496     if (!isfinite(d)) {
11497         if (isnan(d)) {
11498             strcpy(buf, "NaN");
11499         } else {
11500             q = buf;
11501             if (d < 0)
11502                 *q++ = '-';
11503             strcpy(q, "Infinity");
11504         }
11505     } else if (flags == JS_DTOA_VAR_FORMAT) {
11506         int64_t i64;
11507         char buf1[70], *ptr;
11508         i64 = (int64_t)d;
11509         if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER)
11510             goto generic_conv;
11511         /* fast path for integers */
11512         ptr = i64toa(buf1 + sizeof(buf1), i64, radix);
11513         strcpy(buf, ptr);
11514     } else {
11515         if (d == 0.0)
11516             d = 0.0; /* convert -0 to 0 */
11517         if (flags == JS_DTOA_FRAC_FORMAT) {
11518             js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits);
11519         } else {
11520             char buf1[JS_DTOA_BUF_SIZE];
11521             int sign, decpt, k, n, i, p, n_max;
11522             BOOL is_fixed;
11523         generic_conv:
11524             is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);
11525             if (is_fixed) {
11526                 n_max = n_digits;
11527             } else {
11528                 n_max = 21;
11529             }
11530             /* the number has k digits (k >= 1) */
11531             k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);
11532             n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */
11533             q = buf;
11534             if (sign)
11535                 *q++ = '-';
11536             if (flags & JS_DTOA_FORCE_EXP)
11537                 goto force_exp;
11538             if (n >= 1 && n <= n_max) {
11539                 if (k <= n) {
11540                     memcpy(q, buf1, k);
11541                     q += k;
11542                     for(i = 0; i < (n - k); i++)
11543                         *q++ = '0';
11544                     *q = '\0';
11545                 } else {
11546                     /* k > n */
11547                     memcpy(q, buf1, n);
11548                     q += n;
11549                     *q++ = '.';
11550                     for(i = 0; i < (k - n); i++)
11551                         *q++ = buf1[n + i];
11552                     *q = '\0';
11553                 }
11554             } else if (n >= -5 && n <= 0) {
11555                 *q++ = '0';
11556                 *q++ = '.';
11557                 for(i = 0; i < -n; i++)
11558                     *q++ = '0';
11559                 memcpy(q, buf1, k);
11560                 q += k;
11561                 *q = '\0';
11562             } else {
11563             force_exp:
11564                 /* exponential notation */
11565                 *q++ = buf1[0];
11566                 if (k > 1) {
11567                     *q++ = '.';
11568                     for(i = 1; i < k; i++)
11569                         *q++ = buf1[i];
11570                 }
11571                 *q++ = 'e';
11572                 p = n - 1;
11573                 if (p >= 0)
11574                     *q++ = '+';
11575                 sprintf(q, "%d", p);
11576             }
11577         }
11578     }
11579 }
11580 
js_dtoa(JSContext * ctx,double d,int radix,int n_digits,int flags)11581 static JSValue js_dtoa(JSContext *ctx,
11582                        double d, int radix, int n_digits, int flags)
11583 {
11584     char buf[JS_DTOA_BUF_SIZE];
11585     js_dtoa1(buf, d, radix, n_digits, flags);
11586     return JS_NewString(ctx, buf);
11587 }
11588 
JS_ToStringInternal(JSContext * ctx,JSValueConst val,BOOL is_ToPropertyKey)11589 JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)
11590 {
11591     uint32_t tag;
11592     const char *str;
11593     char buf[32];
11594 
11595     tag = JS_VALUE_GET_NORM_TAG(val);
11596     switch(tag) {
11597     case JS_TAG_STRING:
11598         return JS_DupValue(ctx, val);
11599     case JS_TAG_INT:
11600         snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));
11601         str = buf;
11602         goto new_string;
11603     case JS_TAG_BOOL:
11604         return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
11605                           JS_ATOM_true : JS_ATOM_false);
11606     case JS_TAG_NULL:
11607         return JS_AtomToString(ctx, JS_ATOM_null);
11608     case JS_TAG_UNDEFINED:
11609         return JS_AtomToString(ctx, JS_ATOM_undefined);
11610     case JS_TAG_EXCEPTION:
11611         return JS_EXCEPTION;
11612     case JS_TAG_OBJECT:
11613         {
11614             JSValue val1, ret;
11615             val1 = JS_ToPrimitive(ctx, val, HINT_STRING);
11616             if (JS_IsException(val1))
11617                 return val1;
11618             ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);
11619             JS_FreeValue(ctx, val1);
11620             return ret;
11621         }
11622         break;
11623     case JS_TAG_FUNCTION_BYTECODE:
11624         str = "[function bytecode]";
11625         goto new_string;
11626     case JS_TAG_SYMBOL:
11627         if (is_ToPropertyKey) {
11628             return JS_DupValue(ctx, val);
11629         } else {
11630             return JS_ThrowTypeError(ctx, "cannot convert symbol to string");
11631         }
11632     case JS_TAG_FLOAT64:
11633         return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
11634                        JS_DTOA_VAR_FORMAT);
11635 #ifdef CONFIG_BIGNUM
11636     case JS_TAG_BIG_INT:
11637         return ctx->rt->bigint_ops.to_string(ctx, val);
11638     case JS_TAG_BIG_FLOAT:
11639         return ctx->rt->bigfloat_ops.to_string(ctx, val);
11640     case JS_TAG_BIG_DECIMAL:
11641         return ctx->rt->bigdecimal_ops.to_string(ctx, val);
11642 #endif
11643     default:
11644         str = "[unsupported type]";
11645     new_string:
11646         return JS_NewString(ctx, str);
11647     }
11648 }
11649 
JS_ToString(JSContext * ctx,JSValueConst val)11650 JSValue JS_ToString(JSContext *ctx, JSValueConst val)
11651 {
11652     return JS_ToStringInternal(ctx, val, FALSE);
11653 }
11654 
JS_ToStringFree(JSContext * ctx,JSValue val)11655 static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)
11656 {
11657     JSValue ret;
11658     ret = JS_ToString(ctx, val);
11659     JS_FreeValue(ctx, val);
11660     return ret;
11661 }
11662 
JS_ToLocaleStringFree(JSContext * ctx,JSValue val)11663 static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)
11664 {
11665     if (JS_IsUndefined(val) || JS_IsNull(val))
11666         return JS_ToStringFree(ctx, val);
11667     return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);
11668 }
11669 
JS_ToPropertyKey(JSContext * ctx,JSValueConst val)11670 JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
11671 {
11672     return JS_ToStringInternal(ctx, val, TRUE);
11673 }
11674 
JS_ToStringCheckObject(JSContext * ctx,JSValueConst val)11675 static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)
11676 {
11677     uint32_t tag = JS_VALUE_GET_TAG(val);
11678     if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
11679         return JS_ThrowTypeError(ctx, "null or undefined are forbidden");
11680     return JS_ToString(ctx, val);
11681 }
11682 
JS_ToQuotedString(JSContext * ctx,JSValueConst val1)11683 static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
11684 {
11685     JSValue val;
11686     JSString *p;
11687     int i;
11688     uint32_t c;
11689     StringBuffer b_s, *b = &b_s;
11690     char buf[16];
11691 
11692     val = JS_ToStringCheckObject(ctx, val1);
11693     if (JS_IsException(val))
11694         return val;
11695     p = JS_VALUE_GET_STRING(val);
11696 
11697     if (string_buffer_init(ctx, b, p->len + 2))
11698         goto fail;
11699 
11700     if (string_buffer_putc8(b, '\"'))
11701         goto fail;
11702     for(i = 0; i < p->len; ) {
11703         c = string_getc(p, &i);
11704         switch(c) {
11705         case '\t':
11706             c = 't';
11707             goto quote;
11708         case '\r':
11709             c = 'r';
11710             goto quote;
11711         case '\n':
11712             c = 'n';
11713             goto quote;
11714         case '\b':
11715             c = 'b';
11716             goto quote;
11717         case '\f':
11718             c = 'f';
11719             goto quote;
11720         case '\"':
11721         case '\\':
11722         quote:
11723             if (string_buffer_putc8(b, '\\'))
11724                 goto fail;
11725             if (string_buffer_putc8(b, c))
11726                 goto fail;
11727             break;
11728         default:
11729             if (c < 32 || (c >= 0xd800 && c < 0xe000)) {
11730                 snprintf(buf, sizeof(buf), "\\u%04x", c);
11731                 if (string_buffer_puts8(b, buf))
11732                     goto fail;
11733             } else {
11734                 if (string_buffer_putc(b, c))
11735                     goto fail;
11736             }
11737             break;
11738         }
11739     }
11740     if (string_buffer_putc8(b, '\"'))
11741         goto fail;
11742     JS_FreeValue(ctx, val);
11743     return string_buffer_end(b);
11744  fail:
11745     JS_FreeValue(ctx, val);
11746     string_buffer_free(b);
11747     return JS_EXCEPTION;
11748 }
11749 
JS_DumpObjectHeader(JSRuntime * rt)11750 static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
11751 {
11752     printf("%14s %4s %4s %14s %10s %s\n",
11753            "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
11754 }
11755 
11756 /* for debug only: dump an object without side effect */
JS_DumpObject(JSRuntime * rt,JSObject * p)11757 static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
11758 {
11759     uint32_t i;
11760     char atom_buf[ATOM_GET_STR_BUF_SIZE];
11761     JSShape *sh;
11762     JSShapeProperty *prs;
11763     JSProperty *pr;
11764     BOOL is_first = TRUE;
11765 
11766     /* XXX: should encode atoms with special characters */
11767     sh = p->shape; /* the shape can be NULL while freeing an object */
11768     printf("%14p %4d ",
11769            (void *)p,
11770            p->header.ref_count);
11771     if (sh) {
11772         printf("%3d%c %14p ",
11773                sh->header.ref_count,
11774                " *"[sh->is_hashed],
11775                (void *)sh->proto);
11776     } else {
11777         printf("%3s  %14s ", "-", "-");
11778     }
11779     printf("%10s ",
11780            JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));
11781     if (p->is_exotic && p->fast_array) {
11782         printf("[ ");
11783         for(i = 0; i < p->u.array.count; i++) {
11784             if (i != 0)
11785                 printf(", ");
11786             switch (p->class_id) {
11787             case JS_CLASS_ARRAY:
11788             case JS_CLASS_ARGUMENTS:
11789                 JS_DumpValueShort(rt, p->u.array.u.values[i]);
11790                 break;
11791             case JS_CLASS_UINT8C_ARRAY:
11792             case JS_CLASS_INT8_ARRAY:
11793             case JS_CLASS_UINT8_ARRAY:
11794             case JS_CLASS_INT16_ARRAY:
11795             case JS_CLASS_UINT16_ARRAY:
11796             case JS_CLASS_INT32_ARRAY:
11797             case JS_CLASS_UINT32_ARRAY:
11798 #ifdef CONFIG_BIGNUM
11799             case JS_CLASS_BIG_INT64_ARRAY:
11800             case JS_CLASS_BIG_UINT64_ARRAY:
11801 #endif
11802             case JS_CLASS_FLOAT32_ARRAY:
11803             case JS_CLASS_FLOAT64_ARRAY:
11804                 {
11805                     int size = 1 << typed_array_size_log2(p->class_id);
11806                     const uint8_t *b = p->u.array.u.uint8_ptr + i * size;
11807                     while (size-- > 0)
11808                         printf("%02X", *b++);
11809                 }
11810                 break;
11811             }
11812         }
11813         printf(" ] ");
11814     }
11815 
11816     if (sh) {
11817         printf("{ ");
11818         for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
11819             if (prs->atom != JS_ATOM_NULL) {
11820                 pr = &p->prop[i];
11821                 if (!is_first)
11822                     printf(", ");
11823                 printf("%s: ",
11824                        JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));
11825                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
11826                     printf("[getset %p %p]", (void *)pr->u.getset.getter,
11827                            (void *)pr->u.getset.setter);
11828                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
11829                     printf("[varref %p]", (void *)pr->u.var_ref);
11830                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
11831                     printf("[autoinit %p %d %p]",
11832                            (void *)js_autoinit_get_realm(pr),
11833                            js_autoinit_get_id(pr),
11834                            (void *)pr->u.init.opaque);
11835                 } else {
11836                     JS_DumpValueShort(rt, pr->u.value);
11837                 }
11838                 is_first = FALSE;
11839             }
11840         }
11841         printf(" }");
11842     }
11843 
11844     if (js_class_has_bytecode(p->class_id)) {
11845         JSFunctionBytecode *b = p->u.func.function_bytecode;
11846         JSVarRef **var_refs;
11847         if (b->closure_var_count) {
11848             var_refs = p->u.func.var_refs;
11849             printf(" Closure:");
11850             for(i = 0; i < b->closure_var_count; i++) {
11851                 printf(" ");
11852                 JS_DumpValueShort(rt, var_refs[i]->value);
11853             }
11854             if (p->u.func.home_object) {
11855                 printf(" HomeObject: ");
11856                 JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
11857             }
11858         }
11859     }
11860     printf("\n");
11861 }
11862 
JS_DumpGCObject(JSRuntime * rt,JSGCObjectHeader * p)11863 static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
11864 {
11865     if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
11866         JS_DumpObject(rt, (JSObject *)p);
11867     } else {
11868         printf("%14p %4d ",
11869                (void *)p,
11870                p->ref_count);
11871         switch(p->gc_obj_type) {
11872         case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
11873             printf("[function bytecode]");
11874             break;
11875         case JS_GC_OBJ_TYPE_SHAPE:
11876             printf("[shape]");
11877             break;
11878         case JS_GC_OBJ_TYPE_VAR_REF:
11879             printf("[var_ref]");
11880             break;
11881         case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
11882             printf("[async_function]");
11883             break;
11884         case JS_GC_OBJ_TYPE_JS_CONTEXT:
11885             printf("[js_context]");
11886             break;
11887         default:
11888             printf("[unknown %d]", p->gc_obj_type);
11889             break;
11890         }
11891         printf("\n");
11892     }
11893 }
11894 
JS_DumpValueShort(JSRuntime * rt,JSValueConst val)11895 static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
11896                                                       JSValueConst val)
11897 {
11898     uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
11899     const char *str;
11900 
11901     switch(tag) {
11902     case JS_TAG_INT:
11903         printf("%d", JS_VALUE_GET_INT(val));
11904         break;
11905     case JS_TAG_BOOL:
11906         if (JS_VALUE_GET_BOOL(val))
11907             str = "true";
11908         else
11909             str = "false";
11910         goto print_str;
11911     case JS_TAG_NULL:
11912         str = "null";
11913         goto print_str;
11914     case JS_TAG_EXCEPTION:
11915         str = "exception";
11916         goto print_str;
11917     case JS_TAG_UNINITIALIZED:
11918         str = "uninitialized";
11919         goto print_str;
11920     case JS_TAG_UNDEFINED:
11921         str = "undefined";
11922     print_str:
11923         printf("%s", str);
11924         break;
11925     case JS_TAG_FLOAT64:
11926         printf("%.14g", JS_VALUE_GET_FLOAT64(val));
11927         break;
11928 #ifdef CONFIG_BIGNUM
11929     case JS_TAG_BIG_INT:
11930         {
11931             JSBigFloat *p = JS_VALUE_GET_PTR(val);
11932             char *str;
11933             str = bf_ftoa(NULL, &p->num, 10, 0,
11934                           BF_RNDZ | BF_FTOA_FORMAT_FRAC);
11935             printf("%sn", str);
11936             bf_realloc(&rt->bf_ctx, str, 0);
11937         }
11938         break;
11939     case JS_TAG_BIG_FLOAT:
11940         {
11941             JSBigFloat *p = JS_VALUE_GET_PTR(val);
11942             char *str;
11943             str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF,
11944                           BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX);
11945             printf("%sl", str);
11946             bf_free(&rt->bf_ctx, str);
11947         }
11948         break;
11949     case JS_TAG_BIG_DECIMAL:
11950         {
11951             JSBigDecimal *p = JS_VALUE_GET_PTR(val);
11952             char *str;
11953             str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF,
11954                              BF_RNDZ | BF_FTOA_FORMAT_FREE);
11955             printf("%sm", str);
11956             bf_free(&rt->bf_ctx, str);
11957         }
11958         break;
11959 #endif
11960     case JS_TAG_STRING:
11961         {
11962             JSString *p;
11963             p = JS_VALUE_GET_STRING(val);
11964             JS_DumpString(rt, p);
11965         }
11966         break;
11967     case JS_TAG_FUNCTION_BYTECODE:
11968         {
11969             JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);
11970             char buf[ATOM_GET_STR_BUF_SIZE];
11971             printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
11972         }
11973         break;
11974     case JS_TAG_OBJECT:
11975         {
11976             JSObject *p = JS_VALUE_GET_OBJ(val);
11977             JSAtom atom = rt->class_array[p->class_id].class_name;
11978             char atom_buf[ATOM_GET_STR_BUF_SIZE];
11979             printf("[%s %p]",
11980                    JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);
11981         }
11982         break;
11983     case JS_TAG_SYMBOL:
11984         {
11985             JSAtomStruct *p = JS_VALUE_GET_PTR(val);
11986             char atom_buf[ATOM_GET_STR_BUF_SIZE];
11987             printf("Symbol(%s)",
11988                    JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));
11989         }
11990         break;
11991     case JS_TAG_MODULE:
11992         printf("[module]");
11993         break;
11994     default:
11995         printf("[unknown tag %d]", tag);
11996         break;
11997     }
11998 }
11999 
JS_DumpValue(JSContext * ctx,JSValueConst val)12000 static __maybe_unused void JS_DumpValue(JSContext *ctx,
12001                                                  JSValueConst val)
12002 {
12003     JS_DumpValueShort(ctx->rt, val);
12004 }
12005 
JS_PrintValue(JSContext * ctx,const char * str,JSValueConst val)12006 static __maybe_unused void JS_PrintValue(JSContext *ctx,
12007                                                   const char *str,
12008                                                   JSValueConst val)
12009 {
12010     printf("%s=", str);
12011     JS_DumpValueShort(ctx->rt, val);
12012     printf("\n");
12013 }
12014 
12015 /* return -1 if exception (proxy case) or TRUE/FALSE */
JS_IsArray(JSContext * ctx,JSValueConst val)12016 int JS_IsArray(JSContext *ctx, JSValueConst val)
12017 {
12018     JSObject *p;
12019     if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
12020         p = JS_VALUE_GET_OBJ(val);
12021         if (unlikely(p->class_id == JS_CLASS_PROXY))
12022             return js_proxy_isArray(ctx, val);
12023         else
12024             return p->class_id == JS_CLASS_ARRAY;
12025     } else {
12026         return FALSE;
12027     }
12028 }
12029 
js_pow(double a,double b)12030 static double js_pow(double a, double b)
12031 {
12032     if (unlikely(!isfinite(b)) && fabs(a) == 1) {
12033         /* not compatible with IEEE 754 */
12034         return JS_FLOAT64_NAN;
12035     } else {
12036         return pow(a, b);
12037     }
12038 }
12039 
12040 #ifdef CONFIG_BIGNUM
12041 
JS_NewBigInt64_1(JSContext * ctx,int64_t v)12042 JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v)
12043 {
12044     JSValue val;
12045     bf_t *a;
12046     val = JS_NewBigInt(ctx);
12047     if (JS_IsException(val))
12048         return val;
12049     a = JS_GetBigInt(val);
12050     if (bf_set_si(a, v)) {
12051         JS_FreeValue(ctx, val);
12052         return JS_ThrowOutOfMemory(ctx);
12053     }
12054     return val;
12055 }
12056 
JS_NewBigInt64(JSContext * ctx,int64_t v)12057 JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
12058 {
12059     if (is_math_mode(ctx) &&
12060         v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
12061         return JS_NewInt64(ctx, v);
12062     } else {
12063         return JS_NewBigInt64_1(ctx, v);
12064     }
12065 }
12066 
JS_NewBigUint64(JSContext * ctx,uint64_t v)12067 JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
12068 {
12069     JSValue val;
12070     if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) {
12071         val = JS_NewInt64(ctx, v);
12072     } else {
12073         bf_t *a;
12074         val = JS_NewBigInt(ctx);
12075         if (JS_IsException(val))
12076             return val;
12077         a = JS_GetBigInt(val);
12078         if (bf_set_ui(a, v)) {
12079             JS_FreeValue(ctx, val);
12080             return JS_ThrowOutOfMemory(ctx);
12081         }
12082     }
12083     return val;
12084 }
12085 
12086 /* if the returned bigfloat is allocated it is equal to
12087    'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return
12088    NULL in case of error. */
JS_ToBigFloat(JSContext * ctx,bf_t * buf,JSValueConst val)12089 static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val)
12090 {
12091     uint32_t tag;
12092     bf_t *r;
12093     JSBigFloat *p;
12094 
12095     tag = JS_VALUE_GET_NORM_TAG(val);
12096     switch(tag) {
12097     case JS_TAG_INT:
12098     case JS_TAG_BOOL:
12099     case JS_TAG_NULL:
12100         r = buf;
12101         bf_init(ctx->bf_ctx, r);
12102         if (bf_set_si(r, JS_VALUE_GET_INT(val)))
12103             goto fail;
12104         break;
12105     case JS_TAG_FLOAT64:
12106         r = buf;
12107         bf_init(ctx->bf_ctx, r);
12108         if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) {
12109         fail:
12110             bf_delete(r);
12111             return NULL;
12112         }
12113         break;
12114     case JS_TAG_BIG_INT:
12115     case JS_TAG_BIG_FLOAT:
12116         p = JS_VALUE_GET_PTR(val);
12117         r = &p->num;
12118         break;
12119     case JS_TAG_UNDEFINED:
12120     default:
12121         r = buf;
12122         bf_init(ctx->bf_ctx, r);
12123         bf_set_nan(r);
12124         break;
12125     }
12126     return r;
12127 }
12128 
12129 /* return NULL if invalid type */
JS_ToBigDecimal(JSContext * ctx,JSValueConst val)12130 static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val)
12131 {
12132     uint32_t tag;
12133     JSBigDecimal *p;
12134     bfdec_t *r;
12135 
12136     tag = JS_VALUE_GET_NORM_TAG(val);
12137     switch(tag) {
12138     case JS_TAG_BIG_DECIMAL:
12139         p = JS_VALUE_GET_PTR(val);
12140         r = &p->num;
12141         break;
12142     default:
12143         JS_ThrowTypeError(ctx, "bigdecimal expected");
12144         r = NULL;
12145         break;
12146     }
12147     return r;
12148 }
12149 
12150 /* return NaN if bad bigint literal */
JS_StringToBigInt(JSContext * ctx,JSValue val)12151 static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
12152 {
12153     const char *str, *p;
12154     size_t len;
12155     int flags;
12156 
12157     str = JS_ToCStringLen(ctx, &len, val);
12158     JS_FreeValue(ctx, val);
12159     if (!str)
12160         return JS_EXCEPTION;
12161     p = str;
12162     p += skip_spaces(p);
12163     if ((p - str) == len) {
12164         val = JS_NewBigInt64(ctx, 0);
12165     } else {
12166         flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
12167         if (is_math_mode(ctx))
12168             flags |= ATOD_MODE_BIGINT;
12169         val = js_atof(ctx, p, &p, 0, flags);
12170         p += skip_spaces(p);
12171         if (!JS_IsException(val)) {
12172             if ((p - str) != len) {
12173                 JS_FreeValue(ctx, val);
12174                 val = JS_NAN;
12175             }
12176         }
12177     }
12178     JS_FreeCString(ctx, str);
12179     return val;
12180 }
12181 
JS_StringToBigIntErr(JSContext * ctx,JSValue val)12182 static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)
12183 {
12184     val = JS_StringToBigInt(ctx, val);
12185     if (JS_VALUE_IS_NAN(val))
12186         return JS_ThrowSyntaxError(ctx, "invalid bigint literal");
12187     return val;
12188 }
12189 
12190 /* if the returned bigfloat is allocated it is equal to
12191    'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */
JS_ToBigIntFree(JSContext * ctx,bf_t * buf,JSValue val)12192 static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
12193 {
12194     uint32_t tag;
12195     bf_t *r;
12196     JSBigFloat *p;
12197 
12198  redo:
12199     tag = JS_VALUE_GET_NORM_TAG(val);
12200     switch(tag) {
12201     case JS_TAG_INT:
12202     case JS_TAG_NULL:
12203     case JS_TAG_UNDEFINED:
12204         if (!is_math_mode(ctx))
12205             goto fail;
12206         /* fall tru */
12207     case JS_TAG_BOOL:
12208         r = buf;
12209         bf_init(ctx->bf_ctx, r);
12210         bf_set_si(r, JS_VALUE_GET_INT(val));
12211         break;
12212     case JS_TAG_FLOAT64:
12213         {
12214             double d = JS_VALUE_GET_FLOAT64(val);
12215             if (!is_math_mode(ctx))
12216                 goto fail;
12217             if (!isfinite(d))
12218                 goto fail;
12219             r = buf;
12220             bf_init(ctx->bf_ctx, r);
12221             d = trunc(d);
12222             bf_set_float64(r, d);
12223         }
12224         break;
12225     case JS_TAG_BIG_INT:
12226         p = JS_VALUE_GET_PTR(val);
12227         r = &p->num;
12228         break;
12229     case JS_TAG_BIG_FLOAT:
12230         if (!is_math_mode(ctx))
12231             goto fail;
12232         p = JS_VALUE_GET_PTR(val);
12233         if (!bf_is_finite(&p->num))
12234             goto fail;
12235         r = buf;
12236         bf_init(ctx->bf_ctx, r);
12237         bf_set(r, &p->num);
12238         bf_rint(r, BF_RNDZ);
12239         JS_FreeValue(ctx, val);
12240         break;
12241     case JS_TAG_STRING:
12242         val = JS_StringToBigIntErr(ctx, val);
12243         if (JS_IsException(val))
12244             return NULL;
12245         goto redo;
12246     case JS_TAG_OBJECT:
12247         val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
12248         if (JS_IsException(val))
12249             return NULL;
12250         goto redo;
12251     default:
12252     fail:
12253         JS_FreeValue(ctx, val);
12254         JS_ThrowTypeError(ctx, "cannot convert to bigint");
12255         return NULL;
12256     }
12257     return r;
12258 }
12259 
JS_ToBigInt(JSContext * ctx,bf_t * buf,JSValueConst val)12260 static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val)
12261 {
12262     return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val));
12263 }
12264 
JS_ToBigIntValueFree(JSContext * ctx,JSValue val)12265 static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val)
12266 {
12267     if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) {
12268         return val;
12269     } else {
12270         bf_t a_s, *a, *r;
12271         int ret;
12272         JSValue res;
12273 
12274         res = JS_NewBigInt(ctx);
12275         if (JS_IsException(res))
12276             return JS_EXCEPTION;
12277         a = JS_ToBigIntFree(ctx, &a_s, val);
12278         if (!a) {
12279             JS_FreeValue(ctx, res);
12280             return JS_EXCEPTION;
12281         }
12282         r = JS_GetBigInt(res);
12283         ret = bf_set(r, a);
12284         JS_FreeBigInt(ctx, a, &a_s);
12285         if (ret) {
12286             JS_FreeValue(ctx, res);
12287             return JS_ThrowOutOfMemory(ctx);
12288         }
12289         return JS_CompactBigInt(ctx, res);
12290     }
12291 }
12292 
12293 /* free the bf_t allocated by JS_ToBigInt */
JS_FreeBigInt(JSContext * ctx,bf_t * a,bf_t * buf)12294 static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf)
12295 {
12296     if (a == buf) {
12297         bf_delete(a);
12298     } else {
12299         JSBigFloat *p = (JSBigFloat *)((uint8_t *)a -
12300                                        offsetof(JSBigFloat, num));
12301         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_FLOAT, p));
12302     }
12303 }
12304 
12305 /* XXX: merge with JS_ToInt64Free with a specific flag */
JS_ToBigInt64Free(JSContext * ctx,int64_t * pres,JSValue val)12306 static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
12307 {
12308     bf_t a_s, *a;
12309 
12310     a = JS_ToBigIntFree(ctx, &a_s, val);
12311     if (!a) {
12312         *pres = 0;
12313         return -1;
12314     }
12315     bf_get_int64(pres, a, BF_GET_INT_MOD);
12316     JS_FreeBigInt(ctx, a, &a_s);
12317     return 0;
12318 }
12319 
JS_ToBigInt64(JSContext * ctx,int64_t * pres,JSValueConst val)12320 int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
12321 {
12322     return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));
12323 }
12324 
js_new_bf(JSContext * ctx)12325 static JSBigFloat *js_new_bf(JSContext *ctx)
12326 {
12327     JSBigFloat *p;
12328     p = js_malloc(ctx, sizeof(*p));
12329     if (!p)
12330         return NULL;
12331     p->header.ref_count = 1;
12332     bf_init(ctx->bf_ctx, &p->num);
12333     return p;
12334 }
12335 
JS_NewBigFloat(JSContext * ctx)12336 static JSValue JS_NewBigFloat(JSContext *ctx)
12337 {
12338     JSBigFloat *p;
12339     p = js_malloc(ctx, sizeof(*p));
12340     if (!p)
12341         return JS_EXCEPTION;
12342     p->header.ref_count = 1;
12343     bf_init(ctx->bf_ctx, &p->num);
12344     return JS_MKPTR(JS_TAG_BIG_FLOAT, p);
12345 }
12346 
JS_NewBigDecimal(JSContext * ctx)12347 static JSValue JS_NewBigDecimal(JSContext *ctx)
12348 {
12349     JSBigDecimal *p;
12350     p = js_malloc(ctx, sizeof(*p));
12351     if (!p)
12352         return JS_EXCEPTION;
12353     p->header.ref_count = 1;
12354     bfdec_init(ctx->bf_ctx, &p->num);
12355     return JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
12356 }
12357 
JS_NewBigInt(JSContext * ctx)12358 static JSValue JS_NewBigInt(JSContext *ctx)
12359 {
12360     JSBigFloat *p;
12361     p = js_malloc(ctx, sizeof(*p));
12362     if (!p)
12363         return JS_EXCEPTION;
12364     p->header.ref_count = 1;
12365     bf_init(ctx->bf_ctx, &p->num);
12366     return JS_MKPTR(JS_TAG_BIG_INT, p);
12367 }
12368 
JS_CompactBigInt1(JSContext * ctx,JSValue val,BOOL convert_to_safe_integer)12369 static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
12370                                  BOOL convert_to_safe_integer)
12371 {
12372     int64_t v;
12373     bf_t *a;
12374 
12375     if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT)
12376         return val; /* fail safe */
12377     a = JS_GetBigInt(val);
12378     if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 &&
12379         v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
12380         JS_FreeValue(ctx, val);
12381         return JS_NewInt64(ctx, v);
12382     } else if (a->expn == BF_EXP_ZERO && a->sign) {
12383         JSBigFloat *p = JS_VALUE_GET_PTR(val);
12384         assert(p->header.ref_count == 1);
12385         a->sign = 0;
12386     }
12387     return val;
12388 }
12389 
12390 /* Convert the big int to a safe integer if in math mode. normalize
12391    the zero representation. Could also be used to convert the bigint
12392    to a short bigint value. The reference count of the value must be
12393    1. Cannot fail */
JS_CompactBigInt(JSContext * ctx,JSValue val)12394 static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val)
12395 {
12396     return JS_CompactBigInt1(ctx, val, is_math_mode(ctx));
12397 }
12398 
12399 /* must be kept in sync with JSOverloadableOperatorEnum */
12400 /* XXX: use atoms ? */
12401 static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = {
12402     "+",
12403     "-",
12404     "*",
12405     "/",
12406     "%",
12407     "**",
12408     "|",
12409     "&",
12410     "^",
12411     "<<",
12412     ">>",
12413     ">>>",
12414     "==",
12415     "<",
12416     "pos",
12417     "neg",
12418     "++",
12419     "--",
12420     "~",
12421 };
12422 
get_ovop_from_opcode(OPCodeEnum op)12423 static int get_ovop_from_opcode(OPCodeEnum op)
12424 {
12425     switch(op) {
12426     case OP_add:
12427         return JS_OVOP_ADD;
12428     case OP_sub:
12429         return JS_OVOP_SUB;
12430     case OP_mul:
12431         return JS_OVOP_MUL;
12432     case OP_div:
12433         return JS_OVOP_DIV;
12434     case OP_mod:
12435     case OP_math_mod:
12436         return JS_OVOP_MOD;
12437     case OP_pow:
12438         return JS_OVOP_POW;
12439     case OP_or:
12440         return JS_OVOP_OR;
12441     case OP_and:
12442         return JS_OVOP_AND;
12443     case OP_xor:
12444         return JS_OVOP_XOR;
12445     case OP_shl:
12446         return JS_OVOP_SHL;
12447     case OP_sar:
12448         return JS_OVOP_SAR;
12449     case OP_shr:
12450         return JS_OVOP_SHR;
12451     case OP_eq:
12452     case OP_neq:
12453         return JS_OVOP_EQ;
12454     case OP_lt:
12455     case OP_lte:
12456     case OP_gt:
12457     case OP_gte:
12458         return JS_OVOP_LESS;
12459     case OP_plus:
12460         return JS_OVOP_POS;
12461     case OP_neg:
12462         return JS_OVOP_NEG;
12463     case OP_inc:
12464         return JS_OVOP_INC;
12465     case OP_dec:
12466         return JS_OVOP_DEC;
12467     default:
12468         abort();
12469     }
12470 }
12471 
12472 /* return NULL if not present */
find_binary_op(JSBinaryOperatorDef * def,uint32_t operator_index,JSOverloadableOperatorEnum op)12473 static JSObject *find_binary_op(JSBinaryOperatorDef *def,
12474                                 uint32_t operator_index,
12475                                 JSOverloadableOperatorEnum op)
12476 {
12477     JSBinaryOperatorDefEntry *ent;
12478     int i;
12479     for(i = 0; i < def->count; i++) {
12480         ent = &def->tab[i];
12481         if (ent->operator_index == operator_index)
12482             return ent->ops[op];
12483     }
12484     return NULL;
12485 }
12486 
12487 /* return -1 if exception, 0 if no operator overloading, 1 if
12488    overloaded operator called */
js_call_binary_op_fallback(JSContext * ctx,JSValue * pret,JSValueConst op1,JSValueConst op2,OPCodeEnum op,BOOL is_numeric,int hint)12489 static __exception int js_call_binary_op_fallback(JSContext *ctx,
12490                                                   JSValue *pret,
12491                                                   JSValueConst op1,
12492                                                   JSValueConst op2,
12493                                                   OPCodeEnum op,
12494                                                   BOOL is_numeric,
12495                                                   int hint)
12496 {
12497     JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2;
12498     JSOperatorSetData *opset1, *opset2;
12499     JSOverloadableOperatorEnum ovop;
12500     JSObject *p;
12501     JSValueConst args[2];
12502 
12503     if (!ctx->allow_operator_overloading)
12504         return 0;
12505 
12506     opset2_obj = JS_UNDEFINED;
12507     opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
12508     if (JS_IsException(opset1_obj))
12509         goto exception;
12510     if (JS_IsUndefined(opset1_obj))
12511         return 0;
12512     opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
12513     if (!opset1)
12514         goto exception;
12515 
12516     opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet);
12517     if (JS_IsException(opset2_obj))
12518         goto exception;
12519     if (JS_IsUndefined(opset2_obj)) {
12520         JS_FreeValue(ctx, opset1_obj);
12521         return 0;
12522     }
12523     opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET);
12524     if (!opset2)
12525         goto exception;
12526 
12527     if (opset1->is_primitive && opset2->is_primitive) {
12528         JS_FreeValue(ctx, opset1_obj);
12529         JS_FreeValue(ctx, opset2_obj);
12530         return 0;
12531     }
12532 
12533     ovop = get_ovop_from_opcode(op);
12534 
12535     if (opset1->operator_counter == opset2->operator_counter) {
12536         p = opset1->self_ops[ovop];
12537     } else if (opset1->operator_counter > opset2->operator_counter) {
12538         p = find_binary_op(&opset1->left, opset2->operator_counter, ovop);
12539     } else {
12540         p = find_binary_op(&opset2->right, opset1->operator_counter, ovop);
12541     }
12542     if (!p) {
12543         JS_ThrowTypeError(ctx, "operator %s: no function defined",
12544                           js_overloadable_operator_names[ovop]);
12545         goto exception;
12546     }
12547 
12548     if (opset1->is_primitive) {
12549         if (is_numeric) {
12550             new_op1 = JS_ToNumeric(ctx, op1);
12551         } else {
12552             new_op1 = JS_ToPrimitive(ctx, op1, hint);
12553         }
12554         if (JS_IsException(new_op1))
12555             goto exception;
12556     } else {
12557         new_op1 = JS_DupValue(ctx, op1);
12558     }
12559 
12560     if (opset2->is_primitive) {
12561         if (is_numeric) {
12562             new_op2 = JS_ToNumeric(ctx, op2);
12563         } else {
12564             new_op2 = JS_ToPrimitive(ctx, op2, hint);
12565         }
12566         if (JS_IsException(new_op2)) {
12567             JS_FreeValue(ctx, new_op1);
12568             goto exception;
12569         }
12570     } else {
12571         new_op2 = JS_DupValue(ctx, op2);
12572     }
12573 
12574     /* XXX: could apply JS_ToPrimitive() if primitive type so that the
12575        operator function does not get a value object */
12576 
12577     method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
12578     if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) {
12579         args[0] = new_op2;
12580         args[1] = new_op1;
12581     } else {
12582         args[0] = new_op1;
12583         args[1] = new_op2;
12584     }
12585     ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
12586     JS_FreeValue(ctx, new_op1);
12587     JS_FreeValue(ctx, new_op2);
12588     if (JS_IsException(ret))
12589         goto exception;
12590     if (ovop == JS_OVOP_EQ) {
12591         BOOL res = JS_ToBoolFree(ctx, ret);
12592         if (op == OP_neq)
12593             res ^= 1;
12594         ret = JS_NewBool(ctx, res);
12595     } else if (ovop == JS_OVOP_LESS) {
12596         if (JS_IsUndefined(ret)) {
12597             ret = JS_FALSE;
12598         } else {
12599             BOOL res = JS_ToBoolFree(ctx, ret);
12600             if (op == OP_lte || op == OP_gte)
12601                 res ^= 1;
12602             ret = JS_NewBool(ctx, res);
12603         }
12604     }
12605     JS_FreeValue(ctx, opset1_obj);
12606     JS_FreeValue(ctx, opset2_obj);
12607     *pret = ret;
12608     return 1;
12609  exception:
12610     JS_FreeValue(ctx, opset1_obj);
12611     JS_FreeValue(ctx, opset2_obj);
12612     *pret = JS_UNDEFINED;
12613     return -1;
12614 }
12615 
12616 /* try to call the operation on the operatorSet field of 'obj'. Only
12617    used for "/" and "**" on the BigInt prototype in math mode */
js_call_binary_op_simple(JSContext * ctx,JSValue * pret,JSValueConst obj,JSValueConst op1,JSValueConst op2,OPCodeEnum op)12618 static __exception int js_call_binary_op_simple(JSContext *ctx,
12619                                                 JSValue *pret,
12620                                                 JSValueConst obj,
12621                                                 JSValueConst op1,
12622                                                 JSValueConst op2,
12623                                                 OPCodeEnum op)
12624 {
12625     JSValue opset1_obj, method, ret, new_op1, new_op2;
12626     JSOperatorSetData *opset1;
12627     JSOverloadableOperatorEnum ovop;
12628     JSObject *p;
12629     JSValueConst args[2];
12630 
12631     opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
12632     if (JS_IsException(opset1_obj))
12633         goto exception;
12634     if (JS_IsUndefined(opset1_obj))
12635         return 0;
12636     opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
12637     if (!opset1)
12638         goto exception;
12639     ovop = get_ovop_from_opcode(op);
12640 
12641     p = opset1->self_ops[ovop];
12642     if (!p) {
12643         JS_FreeValue(ctx, opset1_obj);
12644         return 0;
12645     }
12646 
12647     new_op1 = JS_ToNumeric(ctx, op1);
12648     if (JS_IsException(new_op1))
12649         goto exception;
12650     new_op2 = JS_ToNumeric(ctx, op2);
12651     if (JS_IsException(new_op2)) {
12652         JS_FreeValue(ctx, new_op1);
12653         goto exception;
12654     }
12655 
12656     method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
12657     args[0] = new_op1;
12658     args[1] = new_op2;
12659     ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
12660     JS_FreeValue(ctx, new_op1);
12661     JS_FreeValue(ctx, new_op2);
12662     if (JS_IsException(ret))
12663         goto exception;
12664     JS_FreeValue(ctx, opset1_obj);
12665     *pret = ret;
12666     return 1;
12667  exception:
12668     JS_FreeValue(ctx, opset1_obj);
12669     *pret = JS_UNDEFINED;
12670     return -1;
12671 }
12672 
12673 /* return -1 if exception, 0 if no operator overloading, 1 if
12674    overloaded operator called */
js_call_unary_op_fallback(JSContext * ctx,JSValue * pret,JSValueConst op1,OPCodeEnum op)12675 static __exception int js_call_unary_op_fallback(JSContext *ctx,
12676                                                  JSValue *pret,
12677                                                  JSValueConst op1,
12678                                                  OPCodeEnum op)
12679 {
12680     JSValue opset1_obj, method, ret;
12681     JSOperatorSetData *opset1;
12682     JSOverloadableOperatorEnum ovop;
12683     JSObject *p;
12684 
12685     if (!ctx->allow_operator_overloading)
12686         return 0;
12687 
12688     opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
12689     if (JS_IsException(opset1_obj))
12690         goto exception;
12691     if (JS_IsUndefined(opset1_obj))
12692         return 0;
12693     opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
12694     if (!opset1)
12695         goto exception;
12696     if (opset1->is_primitive) {
12697         JS_FreeValue(ctx, opset1_obj);
12698         return 0;
12699     }
12700 
12701     ovop = get_ovop_from_opcode(op);
12702 
12703     p = opset1->self_ops[ovop];
12704     if (!p) {
12705         JS_ThrowTypeError(ctx, "no overloaded operator %s",
12706                           js_overloadable_operator_names[ovop]);
12707         goto exception;
12708     }
12709     method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
12710     ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1);
12711     if (JS_IsException(ret))
12712         goto exception;
12713     JS_FreeValue(ctx, opset1_obj);
12714     *pret = ret;
12715     return 1;
12716  exception:
12717     JS_FreeValue(ctx, opset1_obj);
12718     *pret = JS_UNDEFINED;
12719     return -1;
12720 }
12721 
throw_bf_exception(JSContext * ctx,int status)12722 static JSValue throw_bf_exception(JSContext *ctx, int status)
12723 {
12724     const char *str;
12725     if (status & BF_ST_MEM_ERROR)
12726         return JS_ThrowOutOfMemory(ctx);
12727     if (status & BF_ST_DIVIDE_ZERO) {
12728         str = "division by zero";
12729     } else if (status & BF_ST_INVALID_OP) {
12730         str = "invalid operation";
12731     } else {
12732         str = "integer overflow";
12733     }
12734     return JS_ThrowRangeError(ctx, "%s", str);
12735 }
12736 
js_unary_arith_bigint(JSContext * ctx,JSValue * pres,OPCodeEnum op,JSValue op1)12737 static int js_unary_arith_bigint(JSContext *ctx,
12738                                  JSValue *pres, OPCodeEnum op, JSValue op1)
12739 {
12740     bf_t a_s, *r, *a;
12741     int ret, v;
12742     JSValue res;
12743 
12744     if (op == OP_plus && !is_math_mode(ctx)) {
12745         JS_ThrowTypeError(ctx, "bigint argument with unary +");
12746         JS_FreeValue(ctx, op1);
12747         return -1;
12748     }
12749     res = JS_NewBigInt(ctx);
12750     if (JS_IsException(res)) {
12751         JS_FreeValue(ctx, op1);
12752         return -1;
12753     }
12754     r = JS_GetBigInt(res);
12755     a = JS_ToBigInt(ctx, &a_s, op1);
12756     ret = 0;
12757     switch(op) {
12758     case OP_inc:
12759     case OP_dec:
12760         v = 2 * (op - OP_dec) - 1;
12761         ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
12762         break;
12763     case OP_plus:
12764         ret = bf_set(r, a);
12765         break;
12766     case OP_neg:
12767         ret = bf_set(r, a);
12768         bf_neg(r);
12769         break;
12770     case OP_not:
12771         ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ);
12772         bf_neg(r);
12773         break;
12774     default:
12775         abort();
12776     }
12777     JS_FreeBigInt(ctx, a, &a_s);
12778     JS_FreeValue(ctx, op1);
12779     if (unlikely(ret)) {
12780         JS_FreeValue(ctx, res);
12781         throw_bf_exception(ctx, ret);
12782         return -1;
12783     }
12784     res = JS_CompactBigInt(ctx, res);
12785     *pres = res;
12786     return 0;
12787 }
12788 
js_unary_arith_bigfloat(JSContext * ctx,JSValue * pres,OPCodeEnum op,JSValue op1)12789 static int js_unary_arith_bigfloat(JSContext *ctx,
12790                                    JSValue *pres, OPCodeEnum op, JSValue op1)
12791 {
12792     bf_t a_s, *r, *a;
12793     int ret, v;
12794     JSValue res;
12795 
12796     if (op == OP_plus && !is_math_mode(ctx)) {
12797         JS_ThrowTypeError(ctx, "bigfloat argument with unary +");
12798         JS_FreeValue(ctx, op1);
12799         return -1;
12800     }
12801 
12802     res = JS_NewBigFloat(ctx);
12803     if (JS_IsException(res)) {
12804         JS_FreeValue(ctx, op1);
12805         return -1;
12806     }
12807     r = JS_GetBigFloat(res);
12808     a = JS_ToBigFloat(ctx, &a_s, op1);
12809     ret = 0;
12810     switch(op) {
12811     case OP_inc:
12812     case OP_dec:
12813         v = 2 * (op - OP_dec) - 1;
12814         ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags);
12815         break;
12816     case OP_plus:
12817         ret = bf_set(r, a);
12818         break;
12819     case OP_neg:
12820         ret = bf_set(r, a);
12821         bf_neg(r);
12822         break;
12823     default:
12824         abort();
12825     }
12826     if (a == &a_s)
12827         bf_delete(a);
12828     JS_FreeValue(ctx, op1);
12829     if (unlikely(ret & BF_ST_MEM_ERROR)) {
12830         JS_FreeValue(ctx, res);
12831         throw_bf_exception(ctx, ret);
12832         return -1;
12833     }
12834     *pres = res;
12835     return 0;
12836 }
12837 
js_unary_arith_bigdecimal(JSContext * ctx,JSValue * pres,OPCodeEnum op,JSValue op1)12838 static int js_unary_arith_bigdecimal(JSContext *ctx,
12839                                      JSValue *pres, OPCodeEnum op, JSValue op1)
12840 {
12841     bfdec_t *r, *a;
12842     int ret, v;
12843     JSValue res;
12844 
12845     if (op == OP_plus && !is_math_mode(ctx)) {
12846         JS_ThrowTypeError(ctx, "bigdecimal argument with unary +");
12847         JS_FreeValue(ctx, op1);
12848         return -1;
12849     }
12850 
12851     res = JS_NewBigDecimal(ctx);
12852     if (JS_IsException(res)) {
12853         JS_FreeValue(ctx, op1);
12854         return -1;
12855     }
12856     r = JS_GetBigDecimal(res);
12857     a = JS_ToBigDecimal(ctx, op1);
12858     ret = 0;
12859     switch(op) {
12860     case OP_inc:
12861     case OP_dec:
12862         v = 2 * (op - OP_dec) - 1;
12863         ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
12864         break;
12865     case OP_plus:
12866         ret = bfdec_set(r, a);
12867         break;
12868     case OP_neg:
12869         ret = bfdec_set(r, a);
12870         bfdec_neg(r);
12871         break;
12872     default:
12873         abort();
12874     }
12875     JS_FreeValue(ctx, op1);
12876     if (unlikely(ret)) {
12877         JS_FreeValue(ctx, res);
12878         throw_bf_exception(ctx, ret);
12879         return -1;
12880     }
12881     *pres = res;
12882     return 0;
12883 }
12884 
js_unary_arith_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)12885 static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
12886                                                      JSValue *sp,
12887                                                      OPCodeEnum op)
12888 {
12889     JSValue op1, val;
12890     int v, ret;
12891     uint32_t tag;
12892 
12893     op1 = sp[-1];
12894     /* fast path for float64 */
12895     if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))
12896         goto handle_float64;
12897     if (JS_IsObject(op1)) {
12898         ret = js_call_unary_op_fallback(ctx, &val, op1, op);
12899         if (ret < 0)
12900             return -1;
12901         if (ret) {
12902             JS_FreeValue(ctx, op1);
12903             sp[-1] = val;
12904             return 0;
12905         }
12906     }
12907 
12908     op1 = JS_ToNumericFree(ctx, op1);
12909     if (JS_IsException(op1))
12910         goto exception;
12911     tag = JS_VALUE_GET_TAG(op1);
12912     switch(tag) {
12913     case JS_TAG_INT:
12914         {
12915             int64_t v64;
12916             v64 = JS_VALUE_GET_INT(op1);
12917             switch(op) {
12918             case OP_inc:
12919             case OP_dec:
12920                 v = 2 * (op - OP_dec) - 1;
12921                 v64 += v;
12922                 break;
12923             case OP_plus:
12924                 break;
12925             case OP_neg:
12926                 if (v64 == 0) {
12927                     sp[-1] = __JS_NewFloat64(ctx, -0.0);
12928                     return 0;
12929                 } else {
12930                     v64 = -v64;
12931                 }
12932                 break;
12933             default:
12934                 abort();
12935             }
12936             sp[-1] = JS_NewInt64(ctx, v64);
12937         }
12938         break;
12939     case JS_TAG_BIG_INT:
12940     handle_bigint:
12941         if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1))
12942             goto exception;
12943         break;
12944     case JS_TAG_BIG_FLOAT:
12945         if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1))
12946             goto exception;
12947         break;
12948     case JS_TAG_BIG_DECIMAL:
12949         if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1))
12950             goto exception;
12951         break;
12952     default:
12953     handle_float64:
12954         {
12955             double d;
12956             if (is_math_mode(ctx))
12957                 goto handle_bigint;
12958             d = JS_VALUE_GET_FLOAT64(op1);
12959             switch(op) {
12960             case OP_inc:
12961             case OP_dec:
12962                 v = 2 * (op - OP_dec) - 1;
12963                 d += v;
12964                 break;
12965             case OP_plus:
12966                 break;
12967             case OP_neg:
12968                 d = -d;
12969                 break;
12970             default:
12971                 abort();
12972             }
12973             sp[-1] = __JS_NewFloat64(ctx, d);
12974         }
12975         break;
12976     }
12977     return 0;
12978  exception:
12979     sp[-1] = JS_UNDEFINED;
12980     return -1;
12981 }
12982 
js_post_inc_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)12983 static __exception int js_post_inc_slow(JSContext *ctx,
12984                                         JSValue *sp, OPCodeEnum op)
12985 {
12986     JSValue op1;
12987 
12988     /* XXX: allow custom operators */
12989     op1 = sp[-1];
12990     op1 = JS_ToNumericFree(ctx, op1);
12991     if (JS_IsException(op1)) {
12992         sp[-1] = JS_UNDEFINED;
12993         return -1;
12994     }
12995     sp[-1] = op1;
12996     sp[0] = JS_DupValue(ctx, op1);
12997     return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);
12998 }
12999 
js_not_slow(JSContext * ctx,JSValue * sp)13000 static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
13001 {
13002     JSValue op1, val;
13003     int ret;
13004 
13005     op1 = sp[-1];
13006     if (JS_IsObject(op1)) {
13007         ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not);
13008         if (ret < 0)
13009             return -1;
13010         if (ret) {
13011             JS_FreeValue(ctx, op1);
13012             sp[-1] = val;
13013             return 0;
13014         }
13015     }
13016 
13017     op1 = JS_ToNumericFree(ctx, op1);
13018     if (JS_IsException(op1))
13019         goto exception;
13020     if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
13021         if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1))
13022             goto exception;
13023     } else {
13024         int32_t v1;
13025         if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
13026             goto exception;
13027         sp[-1] = JS_NewInt32(ctx, ~v1);
13028     }
13029     return 0;
13030  exception:
13031     sp[-1] = JS_UNDEFINED;
13032     return -1;
13033 }
13034 
js_binary_arith_bigfloat(JSContext * ctx,OPCodeEnum op,JSValue * pres,JSValue op1,JSValue op2)13035 static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,
13036                                     JSValue *pres, JSValue op1, JSValue op2)
13037 {
13038     bf_t a_s, b_s, *r, *a, *b;
13039     int ret;
13040     JSValue res;
13041 
13042     res = JS_NewBigFloat(ctx);
13043     if (JS_IsException(res)) {
13044         JS_FreeValue(ctx, op1);
13045         JS_FreeValue(ctx, op2);
13046         return -1;
13047     }
13048     r = JS_GetBigFloat(res);
13049     a = JS_ToBigFloat(ctx, &a_s, op1);
13050     b = JS_ToBigFloat(ctx, &b_s, op2);
13051     bf_init(ctx->bf_ctx, r);
13052     switch(op) {
13053     case OP_add:
13054         ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
13055         break;
13056     case OP_sub:
13057         ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
13058         break;
13059     case OP_mul:
13060         ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
13061         break;
13062     case OP_div:
13063         ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
13064         break;
13065     case OP_math_mod:
13066         /* Euclidian remainder */
13067         ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
13068                      BF_DIVREM_EUCLIDIAN);
13069         break;
13070     case OP_mod:
13071         ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
13072                      BF_RNDZ);
13073         break;
13074     case OP_pow:
13075         ret = bf_pow(r, a, b, ctx->fp_env.prec,
13076                      ctx->fp_env.flags | BF_POW_JS_QUIRKS);
13077         break;
13078     default:
13079         abort();
13080     }
13081     if (a == &a_s)
13082         bf_delete(a);
13083     if (b == &b_s)
13084         bf_delete(b);
13085     JS_FreeValue(ctx, op1);
13086     JS_FreeValue(ctx, op2);
13087     if (unlikely(ret & BF_ST_MEM_ERROR)) {
13088         JS_FreeValue(ctx, res);
13089         throw_bf_exception(ctx, ret);
13090         return -1;
13091     }
13092     *pres = res;
13093     return 0;
13094 }
13095 
js_binary_arith_bigint(JSContext * ctx,OPCodeEnum op,JSValue * pres,JSValue op1,JSValue op2)13096 static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,
13097                                   JSValue *pres, JSValue op1, JSValue op2)
13098 {
13099     bf_t a_s, b_s, *r, *a, *b;
13100     int ret;
13101     JSValue res;
13102 
13103     res = JS_NewBigInt(ctx);
13104     if (JS_IsException(res))
13105         goto fail;
13106     a = JS_ToBigInt(ctx, &a_s, op1);
13107     if (!a)
13108         goto fail;
13109     b = JS_ToBigInt(ctx, &b_s, op2);
13110     if (!b) {
13111         JS_FreeBigInt(ctx, a, &a_s);
13112         goto fail;
13113     }
13114     r = JS_GetBigInt(res);
13115     ret = 0;
13116     switch(op) {
13117     case OP_add:
13118         ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ);
13119         break;
13120     case OP_sub:
13121         ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
13122         break;
13123     case OP_mul:
13124         ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
13125         break;
13126     case OP_div:
13127         if (!is_math_mode(ctx)) {
13128             bf_t rem_s, *rem = &rem_s;
13129             bf_init(ctx->bf_ctx, rem);
13130             ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ,
13131                             BF_RNDZ);
13132             bf_delete(rem);
13133         } else {
13134             goto math_mode_div_pow;
13135         }
13136         break;
13137     case OP_math_mod:
13138         /* Euclidian remainder */
13139         ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
13140                      BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP;
13141         break;
13142     case OP_mod:
13143         ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
13144                      BF_RNDZ) & BF_ST_INVALID_OP;
13145         break;
13146     case OP_pow:
13147         if (b->sign) {
13148             if (!is_math_mode(ctx)) {
13149                 ret = BF_ST_INVALID_OP;
13150             } else {
13151             math_mode_div_pow:
13152                 JS_FreeValue(ctx, res);
13153                 ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op);
13154                 if (ret != 0) {
13155                     JS_FreeBigInt(ctx, a, &a_s);
13156                     JS_FreeBigInt(ctx, b, &b_s);
13157                     JS_FreeValue(ctx, op1);
13158                     JS_FreeValue(ctx, op2);
13159                     if (ret < 0) {
13160                         return -1;
13161                     } else {
13162                         *pres = res;
13163                         return 0;
13164                     }
13165                 }
13166                 /* if no BigInt power operator defined, return a
13167                    bigfloat */
13168                 res = JS_NewBigFloat(ctx);
13169                 if (JS_IsException(res)) {
13170                     JS_FreeBigInt(ctx, a, &a_s);
13171                     JS_FreeBigInt(ctx, b, &b_s);
13172                     goto fail;
13173                 }
13174                 r = JS_GetBigFloat(res);
13175                 if (op == OP_div) {
13176                     ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR;
13177                 } else {
13178                     ret = bf_pow(r, a, b, ctx->fp_env.prec,
13179                                  ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR;
13180                 }
13181                 JS_FreeBigInt(ctx, a, &a_s);
13182                 JS_FreeBigInt(ctx, b, &b_s);
13183                 JS_FreeValue(ctx, op1);
13184                 JS_FreeValue(ctx, op2);
13185                 if (unlikely(ret)) {
13186                     JS_FreeValue(ctx, res);
13187                     throw_bf_exception(ctx, ret);
13188                     return -1;
13189                 }
13190                 *pres = res;
13191                 return 0;
13192             }
13193         } else {
13194             ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS);
13195         }
13196         break;
13197 
13198         /* logical operations */
13199     case OP_shl:
13200     case OP_sar:
13201         {
13202             slimb_t v2;
13203 #if LIMB_BITS == 32
13204             bf_get_int32(&v2, b, 0);
13205             if (v2 == INT32_MIN)
13206                 v2 = INT32_MIN + 1;
13207 #else
13208             bf_get_int64(&v2, b, 0);
13209             if (v2 == INT64_MIN)
13210                 v2 = INT64_MIN + 1;
13211 #endif
13212             if (op == OP_sar)
13213                 v2 = -v2;
13214             ret = bf_set(r, a);
13215             ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ);
13216             if (v2 < 0) {
13217                 ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR);
13218             }
13219         }
13220         break;
13221     case OP_and:
13222         ret = bf_logic_and(r, a, b);
13223         break;
13224     case OP_or:
13225         ret = bf_logic_or(r, a, b);
13226         break;
13227     case OP_xor:
13228         ret = bf_logic_xor(r, a, b);
13229         break;
13230     default:
13231         abort();
13232     }
13233     JS_FreeBigInt(ctx, a, &a_s);
13234     JS_FreeBigInt(ctx, b, &b_s);
13235     JS_FreeValue(ctx, op1);
13236     JS_FreeValue(ctx, op2);
13237     if (unlikely(ret)) {
13238         JS_FreeValue(ctx, res);
13239         throw_bf_exception(ctx, ret);
13240         return -1;
13241     }
13242     *pres = JS_CompactBigInt(ctx, res);
13243     return 0;
13244  fail:
13245     JS_FreeValue(ctx, res);
13246     JS_FreeValue(ctx, op1);
13247     JS_FreeValue(ctx, op2);
13248     return -1;
13249 }
13250 
13251 /* b must be a positive integer */
js_bfdec_pow(bfdec_t * r,const bfdec_t * a,const bfdec_t * b)13252 static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b)
13253 {
13254     bfdec_t b1;
13255     int32_t b2;
13256     int ret;
13257 
13258     bfdec_init(b->ctx, &b1);
13259     ret = bfdec_set(&b1, b);
13260     if (ret) {
13261         bfdec_delete(&b1);
13262         return ret;
13263     }
13264     ret = bfdec_rint(&b1, BF_RNDZ);
13265     if (ret) {
13266         bfdec_delete(&b1);
13267         return BF_ST_INVALID_OP; /* must be an integer */
13268     }
13269     ret = bfdec_get_int32(&b2, &b1);
13270     bfdec_delete(&b1);
13271     if (ret)
13272         return ret; /* overflow */
13273     if (b2 < 0)
13274         return BF_ST_INVALID_OP; /* must be positive */
13275     return bfdec_pow_ui(r, a, b2);
13276 }
13277 
js_binary_arith_bigdecimal(JSContext * ctx,OPCodeEnum op,JSValue * pres,JSValue op1,JSValue op2)13278 static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
13279                                       JSValue *pres, JSValue op1, JSValue op2)
13280 {
13281     bfdec_t *r, *a, *b;
13282     int ret;
13283     JSValue res;
13284 
13285     res = JS_NewBigDecimal(ctx);
13286     if (JS_IsException(res))
13287         goto fail;
13288     r = JS_GetBigDecimal(res);
13289 
13290     a = JS_ToBigDecimal(ctx, op1);
13291     if (!a)
13292         goto fail;
13293     b = JS_ToBigDecimal(ctx, op2);
13294     if (!b)
13295         goto fail;
13296     switch(op) {
13297     case OP_add:
13298         ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ);
13299         break;
13300     case OP_sub:
13301         ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
13302         break;
13303     case OP_mul:
13304         ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
13305         break;
13306     case OP_div:
13307         ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ);
13308         break;
13309     case OP_math_mod:
13310         /* Euclidian remainder */
13311         ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN);
13312         break;
13313     case OP_mod:
13314         ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ);
13315         break;
13316     case OP_pow:
13317         ret = js_bfdec_pow(r, a, b);
13318         break;
13319     default:
13320         abort();
13321     }
13322     JS_FreeValue(ctx, op1);
13323     JS_FreeValue(ctx, op2);
13324     if (unlikely(ret)) {
13325         JS_FreeValue(ctx, res);
13326         throw_bf_exception(ctx, ret);
13327         return -1;
13328     }
13329     *pres = res;
13330     return 0;
13331  fail:
13332     JS_FreeValue(ctx, res);
13333     JS_FreeValue(ctx, op1);
13334     JS_FreeValue(ctx, op2);
13335     return -1;
13336 }
13337 
js_binary_arith_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)13338 static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
13339                                                       OPCodeEnum op)
13340 {
13341     JSValue op1, op2, res;
13342     uint32_t tag1, tag2;
13343     int ret;
13344     double d1, d2;
13345 
13346     op1 = sp[-2];
13347     op2 = sp[-1];
13348     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13349     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13350     /* fast path for float operations */
13351     if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
13352         d1 = JS_VALUE_GET_FLOAT64(op1);
13353         d2 = JS_VALUE_GET_FLOAT64(op2);
13354         goto handle_float64;
13355     }
13356 
13357     /* try to call an overloaded operator */
13358     if ((tag1 == JS_TAG_OBJECT &&
13359          (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
13360         (tag2 == JS_TAG_OBJECT &&
13361          (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
13362         ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
13363         if (ret != 0) {
13364             JS_FreeValue(ctx, op1);
13365             JS_FreeValue(ctx, op2);
13366             if (ret < 0) {
13367                 goto exception;
13368             } else {
13369                 sp[-2] = res;
13370                 return 0;
13371             }
13372         }
13373     }
13374 
13375     op1 = JS_ToNumericFree(ctx, op1);
13376     if (JS_IsException(op1)) {
13377         JS_FreeValue(ctx, op2);
13378         goto exception;
13379     }
13380     op2 = JS_ToNumericFree(ctx, op2);
13381     if (JS_IsException(op2)) {
13382         JS_FreeValue(ctx, op1);
13383         goto exception;
13384     }
13385     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13386     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13387 
13388     if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
13389         int32_t v1, v2;
13390         int64_t v;
13391         v1 = JS_VALUE_GET_INT(op1);
13392         v2 = JS_VALUE_GET_INT(op2);
13393         switch(op) {
13394         case OP_sub:
13395             v = (int64_t)v1 - (int64_t)v2;
13396             break;
13397         case OP_mul:
13398             v = (int64_t)v1 * (int64_t)v2;
13399             if (is_math_mode(ctx) &&
13400                 (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER))
13401                 goto handle_bigint;
13402             if (v == 0 && (v1 | v2) < 0) {
13403                 sp[-2] = __JS_NewFloat64(ctx, -0.0);
13404                 return 0;
13405             }
13406             break;
13407         case OP_div:
13408             if (is_math_mode(ctx))
13409                 goto handle_bigint;
13410             sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2);
13411             return 0;
13412         case OP_math_mod:
13413             if (unlikely(v2 == 0)) {
13414                 throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO);
13415                 goto exception;
13416             }
13417             v = (int64_t)v1 % (int64_t)v2;
13418             if (v < 0) {
13419                 if (v2 < 0)
13420                     v -= v2;
13421                 else
13422                     v += v2;
13423             }
13424             break;
13425         case OP_mod:
13426             if (v1 < 0 || v2 <= 0) {
13427                 sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));
13428                 return 0;
13429             } else {
13430                 v = (int64_t)v1 % (int64_t)v2;
13431             }
13432             break;
13433         case OP_pow:
13434             if (!is_math_mode(ctx)) {
13435                 sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
13436                 return 0;
13437             } else {
13438                 goto handle_bigint;
13439             }
13440             break;
13441         default:
13442             abort();
13443         }
13444         sp[-2] = JS_NewInt64(ctx, v);
13445     } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
13446         if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2))
13447             goto exception;
13448     } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
13449         if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2))
13450             goto exception;
13451     } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
13452     handle_bigint:
13453         if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
13454             goto exception;
13455     } else {
13456         double dr;
13457         /* float64 result */
13458         if (JS_ToFloat64Free(ctx, &d1, op1)) {
13459             JS_FreeValue(ctx, op2);
13460             goto exception;
13461         }
13462         if (JS_ToFloat64Free(ctx, &d2, op2))
13463             goto exception;
13464     handle_float64:
13465         if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
13466             goto handle_bigint;
13467         switch(op) {
13468         case OP_sub:
13469             dr = d1 - d2;
13470             break;
13471         case OP_mul:
13472             dr = d1 * d2;
13473             break;
13474         case OP_div:
13475             dr = d1 / d2;
13476             break;
13477         case OP_mod:
13478             dr = fmod(d1, d2);
13479             break;
13480         case OP_math_mod:
13481             d2 = fabs(d2);
13482             dr = fmod(d1, d2);
13483             /* XXX: loss of accuracy if dr < 0 */
13484             if (dr < 0)
13485                 dr += d2;
13486             break;
13487         case OP_pow:
13488             dr = js_pow(d1, d2);
13489             break;
13490         default:
13491             abort();
13492         }
13493         sp[-2] = __JS_NewFloat64(ctx, dr);
13494     }
13495     return 0;
13496  exception:
13497     sp[-2] = JS_UNDEFINED;
13498     sp[-1] = JS_UNDEFINED;
13499     return -1;
13500 }
13501 
js_add_slow(JSContext * ctx,JSValue * sp)13502 static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
13503 {
13504     JSValue op1, op2, res;
13505     uint32_t tag1, tag2;
13506     int ret;
13507 
13508     op1 = sp[-2];
13509     op2 = sp[-1];
13510 
13511     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13512     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13513     /* fast path for float64 */
13514     if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
13515         double d1, d2;
13516         d1 = JS_VALUE_GET_FLOAT64(op1);
13517         d2 = JS_VALUE_GET_FLOAT64(op2);
13518         sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
13519         return 0;
13520     }
13521 
13522     if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {
13523         /* try to call an overloaded operator */
13524         if ((tag1 == JS_TAG_OBJECT &&
13525              (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED &&
13526               tag2 != JS_TAG_STRING)) ||
13527             (tag2 == JS_TAG_OBJECT &&
13528              (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED &&
13529               tag1 != JS_TAG_STRING))) {
13530             ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add,
13531                                              FALSE, HINT_NONE);
13532             if (ret != 0) {
13533                 JS_FreeValue(ctx, op1);
13534                 JS_FreeValue(ctx, op2);
13535                 if (ret < 0) {
13536                     goto exception;
13537                 } else {
13538                     sp[-2] = res;
13539                     return 0;
13540                 }
13541             }
13542         }
13543 
13544         op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
13545         if (JS_IsException(op1)) {
13546             JS_FreeValue(ctx, op2);
13547             goto exception;
13548         }
13549 
13550         op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
13551         if (JS_IsException(op2)) {
13552             JS_FreeValue(ctx, op1);
13553             goto exception;
13554         }
13555         tag1 = JS_VALUE_GET_NORM_TAG(op1);
13556         tag2 = JS_VALUE_GET_NORM_TAG(op2);
13557     }
13558 
13559     if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {
13560         sp[-2] = JS_ConcatString(ctx, op1, op2);
13561         if (JS_IsException(sp[-2]))
13562             goto exception;
13563         return 0;
13564     }
13565 
13566     op1 = JS_ToNumericFree(ctx, op1);
13567     if (JS_IsException(op1)) {
13568         JS_FreeValue(ctx, op2);
13569         goto exception;
13570     }
13571     op2 = JS_ToNumericFree(ctx, op2);
13572     if (JS_IsException(op2)) {
13573         JS_FreeValue(ctx, op1);
13574         goto exception;
13575     }
13576     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13577     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13578 
13579     if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
13580         int32_t v1, v2;
13581         int64_t v;
13582         v1 = JS_VALUE_GET_INT(op1);
13583         v2 = JS_VALUE_GET_INT(op2);
13584         v = (int64_t)v1 + (int64_t)v2;
13585         sp[-2] = JS_NewInt64(ctx, v);
13586     } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
13587         if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
13588             goto exception;
13589     } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
13590         if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
13591             goto exception;
13592     } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
13593     handle_bigint:
13594         if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
13595             goto exception;
13596     } else {
13597         double d1, d2;
13598         /* float64 result */
13599         if (JS_ToFloat64Free(ctx, &d1, op1)) {
13600             JS_FreeValue(ctx, op2);
13601             goto exception;
13602         }
13603         if (JS_ToFloat64Free(ctx, &d2, op2))
13604             goto exception;
13605         if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
13606             goto handle_bigint;
13607         sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
13608     }
13609     return 0;
13610  exception:
13611     sp[-2] = JS_UNDEFINED;
13612     sp[-1] = JS_UNDEFINED;
13613     return -1;
13614 }
13615 
js_binary_logic_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)13616 static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
13617                                                       JSValue *sp,
13618                                                       OPCodeEnum op)
13619 {
13620     JSValue op1, op2, res;
13621     int ret;
13622     uint32_t tag1, tag2;
13623     uint32_t v1, v2, r;
13624 
13625     op1 = sp[-2];
13626     op2 = sp[-1];
13627     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13628     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13629 
13630     /* try to call an overloaded operator */
13631     if ((tag1 == JS_TAG_OBJECT &&
13632          (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
13633         (tag2 == JS_TAG_OBJECT &&
13634          (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
13635         ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
13636         if (ret != 0) {
13637             JS_FreeValue(ctx, op1);
13638             JS_FreeValue(ctx, op2);
13639             if (ret < 0) {
13640                 goto exception;
13641             } else {
13642                 sp[-2] = res;
13643                 return 0;
13644             }
13645         }
13646     }
13647 
13648     op1 = JS_ToNumericFree(ctx, op1);
13649     if (JS_IsException(op1)) {
13650         JS_FreeValue(ctx, op2);
13651         goto exception;
13652     }
13653     op2 = JS_ToNumericFree(ctx, op2);
13654     if (JS_IsException(op2)) {
13655         JS_FreeValue(ctx, op1);
13656         goto exception;
13657     }
13658 
13659     if (is_math_mode(ctx))
13660         goto bigint_op;
13661 
13662     tag1 = JS_VALUE_GET_TAG(op1);
13663     tag2 = JS_VALUE_GET_TAG(op2);
13664     if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
13665         if (tag1 != tag2) {
13666             JS_FreeValue(ctx, op1);
13667             JS_FreeValue(ctx, op2);
13668             JS_ThrowTypeError(ctx, "both operands must be bigint");
13669             goto exception;
13670         } else {
13671         bigint_op:
13672             if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
13673                 goto exception;
13674         }
13675     } else {
13676         if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
13677             JS_FreeValue(ctx, op2);
13678             goto exception;
13679         }
13680         if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
13681             goto exception;
13682         switch(op) {
13683         case OP_shl:
13684             r = v1 << (v2 & 0x1f);
13685             break;
13686         case OP_sar:
13687             r = (int)v1 >> (v2 & 0x1f);
13688             break;
13689         case OP_and:
13690             r = v1 & v2;
13691             break;
13692         case OP_or:
13693             r = v1 | v2;
13694             break;
13695         case OP_xor:
13696             r = v1 ^ v2;
13697             break;
13698         default:
13699             abort();
13700         }
13701         sp[-2] = JS_NewInt32(ctx, r);
13702     }
13703     return 0;
13704  exception:
13705     sp[-2] = JS_UNDEFINED;
13706     sp[-1] = JS_UNDEFINED;
13707     return -1;
13708 }
13709 
13710 /* Note: also used for bigint */
js_compare_bigfloat(JSContext * ctx,OPCodeEnum op,JSValue op1,JSValue op2)13711 static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op,
13712                                JSValue op1, JSValue op2)
13713 {
13714     bf_t a_s, b_s, *a, *b;
13715     int res;
13716 
13717     a = JS_ToBigFloat(ctx, &a_s, op1);
13718     if (!a) {
13719         JS_FreeValue(ctx, op2);
13720         return -1;
13721     }
13722     b = JS_ToBigFloat(ctx, &b_s, op2);
13723     if (!b) {
13724         if (a == &a_s)
13725             bf_delete(a);
13726         JS_FreeValue(ctx, op1);
13727         return -1;
13728     }
13729     switch(op) {
13730     case OP_lt:
13731         res = bf_cmp_lt(a, b); /* if NaN return false */
13732         break;
13733     case OP_lte:
13734         res = bf_cmp_le(a, b); /* if NaN return false */
13735         break;
13736     case OP_gt:
13737         res = bf_cmp_lt(b, a); /* if NaN return false */
13738         break;
13739     case OP_gte:
13740         res = bf_cmp_le(b, a); /* if NaN return false */
13741         break;
13742     case OP_eq:
13743         res = bf_cmp_eq(a, b); /* if NaN return false */
13744         break;
13745     default:
13746         abort();
13747     }
13748     if (a == &a_s)
13749         bf_delete(a);
13750     if (b == &b_s)
13751         bf_delete(b);
13752     JS_FreeValue(ctx, op1);
13753     JS_FreeValue(ctx, op2);
13754     return res;
13755 }
13756 
js_compare_bigdecimal(JSContext * ctx,OPCodeEnum op,JSValue op1,JSValue op2)13757 static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op,
13758                                  JSValue op1, JSValue op2)
13759 {
13760     bfdec_t *a, *b;
13761     int res;
13762 
13763     /* Note: binary floats are converted to bigdecimal with
13764        toString(). It is not mathematically correct but is consistent
13765        with the BigDecimal() constructor behavior */
13766     op1 = JS_ToBigDecimalFree(ctx, op1, TRUE);
13767     if (JS_IsException(op1)) {
13768         JS_FreeValue(ctx, op2);
13769         return -1;
13770     }
13771     op2 = JS_ToBigDecimalFree(ctx, op2, TRUE);
13772     if (JS_IsException(op2)) {
13773         JS_FreeValue(ctx, op1);
13774         return -1;
13775     }
13776     a = JS_ToBigDecimal(ctx, op1);
13777     b = JS_ToBigDecimal(ctx, op2);
13778 
13779     switch(op) {
13780     case OP_lt:
13781         res = bfdec_cmp_lt(a, b); /* if NaN return false */
13782         break;
13783     case OP_lte:
13784         res = bfdec_cmp_le(a, b); /* if NaN return false */
13785         break;
13786     case OP_gt:
13787         res = bfdec_cmp_lt(b, a); /* if NaN return false */
13788         break;
13789     case OP_gte:
13790         res = bfdec_cmp_le(b, a); /* if NaN return false */
13791         break;
13792     case OP_eq:
13793         res = bfdec_cmp_eq(a, b); /* if NaN return false */
13794         break;
13795     default:
13796         abort();
13797     }
13798     JS_FreeValue(ctx, op1);
13799     JS_FreeValue(ctx, op2);
13800     return res;
13801 }
13802 
js_relational_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)13803 static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
13804                                         OPCodeEnum op)
13805 {
13806     JSValue op1, op2, ret;
13807     int res;
13808     uint32_t tag1, tag2;
13809 
13810     op1 = sp[-2];
13811     op2 = sp[-1];
13812     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13813     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13814     /* try to call an overloaded operator */
13815     if ((tag1 == JS_TAG_OBJECT &&
13816          (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
13817         (tag2 == JS_TAG_OBJECT &&
13818          (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
13819         res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op,
13820                                          FALSE, HINT_NUMBER);
13821         if (res != 0) {
13822             JS_FreeValue(ctx, op1);
13823             JS_FreeValue(ctx, op2);
13824             if (res < 0) {
13825                 goto exception;
13826             } else {
13827                 sp[-2] = ret;
13828                 return 0;
13829             }
13830         }
13831     }
13832     op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
13833     if (JS_IsException(op1)) {
13834         JS_FreeValue(ctx, op2);
13835         goto exception;
13836     }
13837     op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);
13838     if (JS_IsException(op2)) {
13839         JS_FreeValue(ctx, op1);
13840         goto exception;
13841     }
13842     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13843     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13844 
13845     if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {
13846         JSString *p1, *p2;
13847         p1 = JS_VALUE_GET_STRING(op1);
13848         p2 = JS_VALUE_GET_STRING(op2);
13849         res = js_string_compare(ctx, p1, p2);
13850         switch(op) {
13851         case OP_lt:
13852             res = (res < 0);
13853             break;
13854         case OP_lte:
13855             res = (res <= 0);
13856             break;
13857         case OP_gt:
13858             res = (res > 0);
13859             break;
13860         default:
13861         case OP_gte:
13862             res = (res >= 0);
13863             break;
13864         }
13865         JS_FreeValue(ctx, op1);
13866         JS_FreeValue(ctx, op2);
13867     } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) &&
13868                (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) {
13869         /* fast path for float64/int */
13870         goto float64_compare;
13871     } else {
13872         if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) ||
13873              (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&
13874             !is_math_mode(ctx)) {
13875             if (tag1 == JS_TAG_STRING) {
13876                 op1 = JS_StringToBigInt(ctx, op1);
13877                 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
13878                     goto invalid_bigint_string;
13879             }
13880             if (tag2 == JS_TAG_STRING) {
13881                 op2 = JS_StringToBigInt(ctx, op2);
13882                 if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
13883                 invalid_bigint_string:
13884                     JS_FreeValue(ctx, op1);
13885                     JS_FreeValue(ctx, op2);
13886                     res = FALSE;
13887                     goto done;
13888                 }
13889             }
13890         } else {
13891             op1 = JS_ToNumericFree(ctx, op1);
13892             if (JS_IsException(op1)) {
13893                 JS_FreeValue(ctx, op2);
13894                 goto exception;
13895             }
13896             op2 = JS_ToNumericFree(ctx, op2);
13897             if (JS_IsException(op2)) {
13898                 JS_FreeValue(ctx, op1);
13899                 goto exception;
13900             }
13901         }
13902 
13903         tag1 = JS_VALUE_GET_NORM_TAG(op1);
13904         tag2 = JS_VALUE_GET_NORM_TAG(op2);
13905 
13906         if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
13907             res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2);
13908             if (res < 0)
13909                 goto exception;
13910         } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
13911             res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2);
13912             if (res < 0)
13913                 goto exception;
13914         } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
13915             res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2);
13916             if (res < 0)
13917                 goto exception;
13918         } else {
13919             double d1, d2;
13920 
13921         float64_compare:
13922             /* can use floating point comparison */
13923             if (tag1 == JS_TAG_FLOAT64) {
13924                 d1 = JS_VALUE_GET_FLOAT64(op1);
13925             } else {
13926                 d1 = JS_VALUE_GET_INT(op1);
13927             }
13928             if (tag2 == JS_TAG_FLOAT64) {
13929                 d2 = JS_VALUE_GET_FLOAT64(op2);
13930             } else {
13931                 d2 = JS_VALUE_GET_INT(op2);
13932             }
13933             switch(op) {
13934             case OP_lt:
13935                 res = (d1 < d2); /* if NaN return false */
13936                 break;
13937             case OP_lte:
13938                 res = (d1 <= d2); /* if NaN return false */
13939                 break;
13940             case OP_gt:
13941                 res = (d1 > d2); /* if NaN return false */
13942                 break;
13943             default:
13944             case OP_gte:
13945                 res = (d1 >= d2); /* if NaN return false */
13946                 break;
13947             }
13948         }
13949     }
13950  done:
13951     sp[-2] = JS_NewBool(ctx, res);
13952     return 0;
13953  exception:
13954     sp[-2] = JS_UNDEFINED;
13955     sp[-1] = JS_UNDEFINED;
13956     return -1;
13957 }
13958 
tag_is_number(uint32_t tag)13959 static BOOL tag_is_number(uint32_t tag)
13960 {
13961     return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT ||
13962             tag == JS_TAG_FLOAT64 || tag == JS_TAG_BIG_FLOAT ||
13963             tag == JS_TAG_BIG_DECIMAL);
13964 }
13965 
js_eq_slow(JSContext * ctx,JSValue * sp,BOOL is_neq)13966 static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
13967                                             BOOL is_neq)
13968 {
13969     JSValue op1, op2, ret;
13970     int res;
13971     uint32_t tag1, tag2;
13972 
13973     op1 = sp[-2];
13974     op2 = sp[-1];
13975  redo:
13976     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13977     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13978     if (tag_is_number(tag1) && tag_is_number(tag2)) {
13979         if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
13980             res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
13981         } else if ((tag1 == JS_TAG_FLOAT64 &&
13982                     (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) ||
13983                    (tag2 == JS_TAG_FLOAT64 &&
13984                     (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) {
13985             double d1, d2;
13986             if (tag1 == JS_TAG_FLOAT64) {
13987                 d1 = JS_VALUE_GET_FLOAT64(op1);
13988             } else {
13989                 d1 = JS_VALUE_GET_INT(op1);
13990             }
13991             if (tag2 == JS_TAG_FLOAT64) {
13992                 d2 = JS_VALUE_GET_FLOAT64(op2);
13993             } else {
13994                 d2 = JS_VALUE_GET_INT(op2);
13995             }
13996             res = (d1 == d2);
13997         } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
13998             res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2);
13999             if (res < 0)
14000                 goto exception;
14001         } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
14002             res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2);
14003             if (res < 0)
14004                 goto exception;
14005         } else {
14006             res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2);
14007             if (res < 0)
14008                 goto exception;
14009         }
14010     } else if (tag1 == tag2) {
14011         if (tag1 == JS_TAG_OBJECT) {
14012             /* try the fallback operator */
14013             res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
14014                                              is_neq ? OP_neq : OP_eq,
14015                                              FALSE, HINT_NONE);
14016             if (res != 0) {
14017                 JS_FreeValue(ctx, op1);
14018                 JS_FreeValue(ctx, op2);
14019                 if (res < 0) {
14020                     goto exception;
14021                 } else {
14022                     sp[-2] = ret;
14023                     return 0;
14024                 }
14025             }
14026         }
14027         res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
14028     } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
14029                (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
14030         res = TRUE;
14031     } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) ||
14032                (tag2 == JS_TAG_STRING && tag_is_number(tag1))) {
14033 
14034         if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) &&
14035             !is_math_mode(ctx)) {
14036             if (tag1 == JS_TAG_STRING) {
14037                 op1 = JS_StringToBigInt(ctx, op1);
14038                 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
14039                     goto invalid_bigint_string;
14040             }
14041             if (tag2 == JS_TAG_STRING) {
14042                 op2 = JS_StringToBigInt(ctx, op2);
14043                 if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
14044                 invalid_bigint_string:
14045                     JS_FreeValue(ctx, op1);
14046                     JS_FreeValue(ctx, op2);
14047                     res = FALSE;
14048                     goto done;
14049                 }
14050             }
14051         } else {
14052             op1 = JS_ToNumericFree(ctx, op1);
14053             if (JS_IsException(op1)) {
14054                 JS_FreeValue(ctx, op2);
14055                 goto exception;
14056             }
14057             op2 = JS_ToNumericFree(ctx, op2);
14058             if (JS_IsException(op2)) {
14059                 JS_FreeValue(ctx, op1);
14060                 goto exception;
14061             }
14062         }
14063         res = js_strict_eq(ctx, op1, op2);
14064     } else if (tag1 == JS_TAG_BOOL) {
14065         op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
14066         goto redo;
14067     } else if (tag2 == JS_TAG_BOOL) {
14068         op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
14069         goto redo;
14070     } else if ((tag1 == JS_TAG_OBJECT &&
14071                 (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) ||
14072                (tag2 == JS_TAG_OBJECT &&
14073                 (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) {
14074 
14075         /* try the fallback operator */
14076         res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
14077                                          is_neq ? OP_neq : OP_eq,
14078                                          FALSE, HINT_NONE);
14079         if (res != 0) {
14080             JS_FreeValue(ctx, op1);
14081             JS_FreeValue(ctx, op2);
14082             if (res < 0) {
14083                 goto exception;
14084             } else {
14085                 sp[-2] = ret;
14086                 return 0;
14087             }
14088         }
14089 
14090         op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
14091         if (JS_IsException(op1)) {
14092             JS_FreeValue(ctx, op2);
14093             goto exception;
14094         }
14095         op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
14096         if (JS_IsException(op2)) {
14097             JS_FreeValue(ctx, op1);
14098             goto exception;
14099         }
14100         goto redo;
14101     } else {
14102         /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
14103         if ((JS_IsHTMLDDA(ctx, op1) &&
14104              (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
14105             (JS_IsHTMLDDA(ctx, op2) &&
14106              (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
14107             res = TRUE;
14108         } else {
14109             res = FALSE;
14110         }
14111         JS_FreeValue(ctx, op1);
14112         JS_FreeValue(ctx, op2);
14113     }
14114  done:
14115     sp[-2] = JS_NewBool(ctx, res ^ is_neq);
14116     return 0;
14117  exception:
14118     sp[-2] = JS_UNDEFINED;
14119     sp[-1] = JS_UNDEFINED;
14120     return -1;
14121 }
14122 
js_shr_slow(JSContext * ctx,JSValue * sp)14123 static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
14124 {
14125     JSValue op1, op2;
14126     uint32_t v1, v2, r;
14127 
14128     op1 = sp[-2];
14129     op2 = sp[-1];
14130     op1 = JS_ToNumericFree(ctx, op1);
14131     if (JS_IsException(op1)) {
14132         JS_FreeValue(ctx, op2);
14133         goto exception;
14134     }
14135     op2 = JS_ToNumericFree(ctx, op2);
14136     if (JS_IsException(op2)) {
14137         JS_FreeValue(ctx, op1);
14138         goto exception;
14139     }
14140     /* XXX: could forbid >>> in bignum mode */
14141     if (!is_math_mode(ctx) &&
14142         (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
14143          JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) {
14144         JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");
14145         JS_FreeValue(ctx, op1);
14146         JS_FreeValue(ctx, op2);
14147         goto exception;
14148     }
14149     /* cannot give an exception */
14150     JS_ToUint32Free(ctx, &v1, op1);
14151     JS_ToUint32Free(ctx, &v2, op2);
14152     r = v1 >> (v2 & 0x1f);
14153     sp[-2] = JS_NewUint32(ctx, r);
14154     return 0;
14155  exception:
14156     sp[-2] = JS_UNDEFINED;
14157     sp[-1] = JS_UNDEFINED;
14158     return -1;
14159 }
14160 
js_mul_pow10_to_float64(JSContext * ctx,const bf_t * a,int64_t exponent)14161 static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
14162                                        int64_t exponent)
14163 {
14164     bf_t r_s, *r = &r_s;
14165     double d;
14166     int ret;
14167 
14168     /* always convert to Float64 */
14169     bf_init(ctx->bf_ctx, r);
14170     ret = bf_mul_pow_radix(r, a, 10, exponent,
14171                            53, bf_set_exp_bits(11) | BF_RNDN |
14172                            BF_FLAG_SUBNORMAL);
14173     bf_get_float64(r, &d, BF_RNDN);
14174     bf_delete(r);
14175     if (ret & BF_ST_MEM_ERROR)
14176         return JS_ThrowOutOfMemory(ctx);
14177     else
14178         return __JS_NewFloat64(ctx, d);
14179 }
14180 
js_mul_pow10(JSContext * ctx,JSValue * sp)14181 static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp)
14182 {
14183     bf_t a_s, *a, *r;
14184     JSValue op1, op2, res;
14185     int64_t e;
14186     int ret;
14187 
14188     res = JS_NewBigFloat(ctx);
14189     if (JS_IsException(res))
14190         return -1;
14191     r = JS_GetBigFloat(res);
14192     op1 = sp[-2];
14193     op2 = sp[-1];
14194     a = JS_ToBigFloat(ctx, &a_s, op1);
14195     if (!a)
14196         return -1;
14197     if (JS_IsBigInt(ctx, op2)) {
14198         ret = JS_ToBigInt64(ctx, &e, op2);
14199     } else {
14200         ret = JS_ToInt64(ctx, &e, op2);
14201     }
14202     if (ret) {
14203         if (a == &a_s)
14204             bf_delete(a);
14205         JS_FreeValue(ctx, res);
14206         return -1;
14207     }
14208 
14209     bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags);
14210     if (a == &a_s)
14211         bf_delete(a);
14212     JS_FreeValue(ctx, op1);
14213     JS_FreeValue(ctx, op2);
14214     sp[-2] = res;
14215     return 0;
14216 }
14217 
14218 #else /* !CONFIG_BIGNUM */
14219 
JS_ThrowUnsupportedBigint(JSContext * ctx)14220 static JSValue JS_ThrowUnsupportedBigint(JSContext *ctx)
14221 {
14222     return JS_ThrowTypeError(ctx, "bigint is not supported");
14223 }
14224 
JS_NewBigInt64(JSContext * ctx,int64_t v)14225 JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
14226 {
14227     return JS_ThrowUnsupportedBigint(ctx);
14228 }
14229 
JS_NewBigUint64(JSContext * ctx,uint64_t v)14230 JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
14231 {
14232     return JS_ThrowUnsupportedBigint(ctx);
14233 }
14234 
JS_ToBigInt64(JSContext * ctx,int64_t * pres,JSValueConst val)14235 int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
14236 {
14237     JS_ThrowUnsupportedBigint(ctx);
14238     *pres = 0;
14239     return -1;
14240 }
14241 
js_unary_arith_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)14242 static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
14243                                                      JSValue *sp,
14244                                                      OPCodeEnum op)
14245 {
14246     JSValue op1;
14247     double d;
14248 
14249     op1 = sp[-1];
14250     if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {
14251         sp[-1] = JS_UNDEFINED;
14252         return -1;
14253     }
14254     switch(op) {
14255     case OP_inc:
14256         d++;
14257         break;
14258     case OP_dec:
14259         d--;
14260         break;
14261     case OP_plus:
14262         break;
14263     case OP_neg:
14264         d = -d;
14265         break;
14266     default:
14267         abort();
14268     }
14269     sp[-1] = JS_NewFloat64(ctx, d);
14270     return 0;
14271 }
14272 
14273 /* specific case necessary for correct return value semantics */
js_post_inc_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)14274 static __exception int js_post_inc_slow(JSContext *ctx,
14275                                         JSValue *sp, OPCodeEnum op)
14276 {
14277     JSValue op1;
14278     double d, r;
14279 
14280     op1 = sp[-1];
14281     if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {
14282         sp[-1] = JS_UNDEFINED;
14283         return -1;
14284     }
14285     r = d + 2 * (op - OP_post_dec) - 1;
14286     sp[0] = JS_NewFloat64(ctx, r);
14287     sp[-1] = JS_NewFloat64(ctx, d);
14288     return 0;
14289 }
14290 
js_binary_arith_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)14291 static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
14292                                                       OPCodeEnum op)
14293 {
14294     JSValue op1, op2;
14295     double d1, d2, r;
14296 
14297     op1 = sp[-2];
14298     op2 = sp[-1];
14299     if (unlikely(JS_ToFloat64Free(ctx, &d1, op1))) {
14300         JS_FreeValue(ctx, op2);
14301         goto exception;
14302     }
14303     if (unlikely(JS_ToFloat64Free(ctx, &d2, op2))) {
14304         goto exception;
14305     }
14306     switch(op) {
14307     case OP_sub:
14308         r = d1 - d2;
14309         break;
14310     case OP_mul:
14311         r = d1 * d2;
14312         break;
14313     case OP_div:
14314         r = d1 / d2;
14315         break;
14316     case OP_mod:
14317         r = fmod(d1, d2);
14318         break;
14319     case OP_pow:
14320         r = js_pow(d1, d2);
14321         break;
14322     default:
14323         abort();
14324     }
14325     sp[-2] = JS_NewFloat64(ctx, r);
14326     return 0;
14327  exception:
14328     sp[-2] = JS_UNDEFINED;
14329     sp[-1] = JS_UNDEFINED;
14330     return -1;
14331 }
14332 
js_add_slow(JSContext * ctx,JSValue * sp)14333 static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
14334 {
14335     JSValue op1, op2;
14336     uint32_t tag1, tag2;
14337 
14338     op1 = sp[-2];
14339     op2 = sp[-1];
14340     tag1 = JS_VALUE_GET_TAG(op1);
14341     tag2 = JS_VALUE_GET_TAG(op2);
14342     if ((tag1 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag1)) &&
14343         (tag2 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag2))) {
14344         goto add_numbers;
14345     } else {
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         tag1 = JS_VALUE_GET_TAG(op1);
14357         tag2 = JS_VALUE_GET_TAG(op2);
14358         if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {
14359             sp[-2] = JS_ConcatString(ctx, op1, op2);
14360             if (JS_IsException(sp[-2]))
14361                 goto exception;
14362         } else {
14363             double d1, d2;
14364         add_numbers:
14365             if (JS_ToFloat64Free(ctx, &d1, op1)) {
14366                 JS_FreeValue(ctx, op2);
14367                 goto exception;
14368             }
14369             if (JS_ToFloat64Free(ctx, &d2, op2))
14370                 goto exception;
14371             sp[-2] = JS_NewFloat64(ctx, d1 + d2);
14372         }
14373     }
14374     return 0;
14375  exception:
14376     sp[-2] = JS_UNDEFINED;
14377     sp[-1] = JS_UNDEFINED;
14378     return -1;
14379 }
14380 
js_binary_logic_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)14381 static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
14382                                                       JSValue *sp,
14383                                                       OPCodeEnum op)
14384 {
14385     JSValue op1, op2;
14386     uint32_t v1, v2, r;
14387 
14388     op1 = sp[-2];
14389     op2 = sp[-1];
14390     if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
14391         JS_FreeValue(ctx, op2);
14392         goto exception;
14393     }
14394     if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
14395         goto exception;
14396     switch(op) {
14397     case OP_shl:
14398         r = v1 << (v2 & 0x1f);
14399         break;
14400     case OP_sar:
14401         r = (int)v1 >> (v2 & 0x1f);
14402         break;
14403     case OP_and:
14404         r = v1 & v2;
14405         break;
14406     case OP_or:
14407         r = v1 | v2;
14408         break;
14409     case OP_xor:
14410         r = v1 ^ v2;
14411         break;
14412     default:
14413         abort();
14414     }
14415     sp[-2] = JS_NewInt32(ctx, r);
14416     return 0;
14417  exception:
14418     sp[-2] = JS_UNDEFINED;
14419     sp[-1] = JS_UNDEFINED;
14420     return -1;
14421 }
14422 
js_not_slow(JSContext * ctx,JSValue * sp)14423 static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
14424 {
14425     int32_t v1;
14426 
14427     if (unlikely(JS_ToInt32Free(ctx, &v1, sp[-1]))) {
14428         sp[-1] = JS_UNDEFINED;
14429         return -1;
14430     }
14431     sp[-1] = JS_NewInt32(ctx, ~v1);
14432     return 0;
14433 }
14434 
js_relational_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)14435 static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
14436                                         OPCodeEnum op)
14437 {
14438     JSValue op1, op2;
14439     int res;
14440 
14441     op1 = sp[-2];
14442     op2 = sp[-1];
14443     op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
14444     if (JS_IsException(op1)) {
14445         JS_FreeValue(ctx, op2);
14446         goto exception;
14447     }
14448     op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);
14449     if (JS_IsException(op2)) {
14450         JS_FreeValue(ctx, op1);
14451         goto exception;
14452     }
14453     if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING &&
14454         JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
14455         JSString *p1, *p2;
14456         p1 = JS_VALUE_GET_STRING(op1);
14457         p2 = JS_VALUE_GET_STRING(op2);
14458         res = js_string_compare(ctx, p1, p2);
14459         JS_FreeValue(ctx, op1);
14460         JS_FreeValue(ctx, op2);
14461         switch(op) {
14462         case OP_lt:
14463             res = (res < 0);
14464             break;
14465         case OP_lte:
14466             res = (res <= 0);
14467             break;
14468         case OP_gt:
14469             res = (res > 0);
14470             break;
14471         default:
14472         case OP_gte:
14473             res = (res >= 0);
14474             break;
14475         }
14476     } else {
14477         double d1, d2;
14478         if (JS_ToFloat64Free(ctx, &d1, op1)) {
14479             JS_FreeValue(ctx, op2);
14480             goto exception;
14481         }
14482         if (JS_ToFloat64Free(ctx, &d2, op2))
14483             goto exception;
14484         switch(op) {
14485         case OP_lt:
14486             res = (d1 < d2); /* if NaN return false */
14487             break;
14488         case OP_lte:
14489             res = (d1 <= d2); /* if NaN return false */
14490             break;
14491         case OP_gt:
14492             res = (d1 > d2); /* if NaN return false */
14493             break;
14494         default:
14495         case OP_gte:
14496             res = (d1 >= d2); /* if NaN return false */
14497             break;
14498         }
14499     }
14500     sp[-2] = JS_NewBool(ctx, res);
14501     return 0;
14502  exception:
14503     sp[-2] = JS_UNDEFINED;
14504     sp[-1] = JS_UNDEFINED;
14505     return -1;
14506 }
14507 
js_eq_slow(JSContext * ctx,JSValue * sp,BOOL is_neq)14508 static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
14509                                             BOOL is_neq)
14510 {
14511     JSValue op1, op2;
14512     int tag1, tag2;
14513     BOOL res;
14514 
14515     op1 = sp[-2];
14516     op2 = sp[-1];
14517  redo:
14518     tag1 = JS_VALUE_GET_NORM_TAG(op1);
14519     tag2 = JS_VALUE_GET_NORM_TAG(op2);
14520     if (tag1 == tag2 ||
14521         (tag1 == JS_TAG_INT && tag2 == JS_TAG_FLOAT64) ||
14522         (tag2 == JS_TAG_INT && tag1 == JS_TAG_FLOAT64)) {
14523         res = js_strict_eq(ctx, op1, op2);
14524     } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
14525                (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
14526         res = TRUE;
14527     } else if ((tag1 == JS_TAG_STRING && (tag2 == JS_TAG_INT ||
14528                                    tag2 == JS_TAG_FLOAT64)) ||
14529         (tag2 == JS_TAG_STRING && (tag1 == JS_TAG_INT ||
14530                                    tag1 == JS_TAG_FLOAT64))) {
14531         double d1;
14532         double d2;
14533         if (JS_ToFloat64Free(ctx, &d1, op1)) {
14534             JS_FreeValue(ctx, op2);
14535             goto exception;
14536         }
14537         if (JS_ToFloat64Free(ctx, &d2, op2))
14538             goto exception;
14539         res = (d1 == d2);
14540     } else if (tag1 == JS_TAG_BOOL) {
14541         op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
14542         goto redo;
14543     } else if (tag2 == JS_TAG_BOOL) {
14544         op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
14545         goto redo;
14546     } else if (tag1 == JS_TAG_OBJECT &&
14547                (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64 || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) {
14548         op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
14549         if (JS_IsException(op1)) {
14550             JS_FreeValue(ctx, op2);
14551             goto exception;
14552         }
14553         goto redo;
14554     } else if (tag2 == JS_TAG_OBJECT &&
14555                (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64 || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL)) {
14556         op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
14557         if (JS_IsException(op2)) {
14558             JS_FreeValue(ctx, op1);
14559             goto exception;
14560         }
14561         goto redo;
14562     } else {
14563         /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
14564         if ((JS_IsHTMLDDA(ctx, op1) &&
14565              (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
14566             (JS_IsHTMLDDA(ctx, op2) &&
14567              (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
14568             res = TRUE;
14569         } else {
14570             res = FALSE;
14571         }
14572         JS_FreeValue(ctx, op1);
14573         JS_FreeValue(ctx, op2);
14574     }
14575     sp[-2] = JS_NewBool(ctx, res ^ is_neq);
14576     return 0;
14577  exception:
14578     sp[-2] = JS_UNDEFINED;
14579     sp[-1] = JS_UNDEFINED;
14580     return -1;
14581 }
14582 
js_shr_slow(JSContext * ctx,JSValue * sp)14583 static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
14584 {
14585     JSValue op1, op2;
14586     uint32_t v1, v2, r;
14587 
14588     op1 = sp[-2];
14589     op2 = sp[-1];
14590     if (unlikely(JS_ToUint32Free(ctx, &v1, op1))) {
14591         JS_FreeValue(ctx, op2);
14592         goto exception;
14593     }
14594     if (unlikely(JS_ToUint32Free(ctx, &v2, op2)))
14595         goto exception;
14596     r = v1 >> (v2 & 0x1f);
14597     sp[-2] = JS_NewUint32(ctx, r);
14598     return 0;
14599  exception:
14600     sp[-2] = JS_UNDEFINED;
14601     sp[-1] = JS_UNDEFINED;
14602     return -1;
14603 }
14604 
14605 #endif /* !CONFIG_BIGNUM */
14606 
14607 /* XXX: Should take JSValueConst arguments */
js_strict_eq2(JSContext * ctx,JSValue op1,JSValue op2,JSStrictEqModeEnum eq_mode)14608 static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
14609                           JSStrictEqModeEnum eq_mode)
14610 {
14611     BOOL res;
14612     int tag1, tag2;
14613     double d1, d2;
14614 
14615     tag1 = JS_VALUE_GET_NORM_TAG(op1);
14616     tag2 = JS_VALUE_GET_NORM_TAG(op2);
14617     switch(tag1) {
14618     case JS_TAG_BOOL:
14619         if (tag1 != tag2) {
14620             res = FALSE;
14621         } else {
14622             res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
14623             goto done_no_free;
14624         }
14625         break;
14626     case JS_TAG_NULL:
14627     case JS_TAG_UNDEFINED:
14628         res = (tag1 == tag2);
14629         break;
14630     case JS_TAG_STRING:
14631         {
14632             JSString *p1, *p2;
14633             if (tag1 != tag2) {
14634                 res = FALSE;
14635             } else {
14636                 p1 = JS_VALUE_GET_STRING(op1);
14637                 p2 = JS_VALUE_GET_STRING(op2);
14638                 res = (js_string_compare(ctx, p1, p2) == 0);
14639             }
14640         }
14641         break;
14642     case JS_TAG_SYMBOL:
14643         {
14644             JSAtomStruct *p1, *p2;
14645             if (tag1 != tag2) {
14646                 res = FALSE;
14647             } else {
14648                 p1 = JS_VALUE_GET_PTR(op1);
14649                 p2 = JS_VALUE_GET_PTR(op2);
14650                 res = (p1 == p2);
14651             }
14652         }
14653         break;
14654     case JS_TAG_OBJECT:
14655         if (tag1 != tag2)
14656             res = FALSE;
14657         else
14658             res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2);
14659         break;
14660     case JS_TAG_INT:
14661         d1 = JS_VALUE_GET_INT(op1);
14662         if (tag2 == JS_TAG_INT) {
14663             d2 = JS_VALUE_GET_INT(op2);
14664             goto number_test;
14665         } else if (tag2 == JS_TAG_FLOAT64) {
14666             d2 = JS_VALUE_GET_FLOAT64(op2);
14667             goto number_test;
14668         } else {
14669             res = FALSE;
14670         }
14671         break;
14672     case JS_TAG_FLOAT64:
14673         d1 = JS_VALUE_GET_FLOAT64(op1);
14674         if (tag2 == JS_TAG_FLOAT64) {
14675             d2 = JS_VALUE_GET_FLOAT64(op2);
14676         } else if (tag2 == JS_TAG_INT) {
14677             d2 = JS_VALUE_GET_INT(op2);
14678         } else {
14679             res = FALSE;
14680             break;
14681         }
14682     number_test:
14683         if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
14684             JSFloat64Union u1, u2;
14685             /* NaN is not always normalized, so this test is necessary */
14686             if (isnan(d1) || isnan(d2)) {
14687                 res = isnan(d1) == isnan(d2);
14688             } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) {
14689                 res = (d1 == d2); /* +0 == -0 */
14690             } else {
14691                 u1.d = d1;
14692                 u2.d = d2;
14693                 res = (u1.u64 == u2.u64); /* +0 != -0 */
14694             }
14695         } else {
14696             res = (d1 == d2); /* if NaN return false and +0 == -0 */
14697         }
14698         goto done_no_free;
14699 #ifdef CONFIG_BIGNUM
14700     case JS_TAG_BIG_INT:
14701         {
14702             bf_t a_s, *a, b_s, *b;
14703             if (tag1 != tag2) {
14704                 res = FALSE;
14705                 break;
14706             }
14707             a = JS_ToBigFloat(ctx, &a_s, op1);
14708             b = JS_ToBigFloat(ctx, &b_s, op2);
14709             res = bf_cmp_eq(a, b);
14710             if (a == &a_s)
14711                 bf_delete(a);
14712             if (b == &b_s)
14713                 bf_delete(b);
14714         }
14715         break;
14716     case JS_TAG_BIG_FLOAT:
14717         {
14718             JSBigFloat *p1, *p2;
14719             const bf_t *a, *b;
14720             if (tag1 != tag2) {
14721                 res = FALSE;
14722                 break;
14723             }
14724             p1 = JS_VALUE_GET_PTR(op1);
14725             p2 = JS_VALUE_GET_PTR(op2);
14726             a = &p1->num;
14727             b = &p2->num;
14728             if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
14729                 if (eq_mode == JS_EQ_SAME_VALUE_ZERO &&
14730                            a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) {
14731                     res = TRUE;
14732                 } else {
14733                     res = (bf_cmp_full(a, b) == 0);
14734                 }
14735             } else {
14736                 res = bf_cmp_eq(a, b);
14737             }
14738         }
14739         break;
14740     case JS_TAG_BIG_DECIMAL:
14741         {
14742             JSBigDecimal *p1, *p2;
14743             const bfdec_t *a, *b;
14744             if (tag1 != tag2) {
14745                 res = FALSE;
14746                 break;
14747             }
14748             p1 = JS_VALUE_GET_PTR(op1);
14749             p2 = JS_VALUE_GET_PTR(op2);
14750             a = &p1->num;
14751             b = &p2->num;
14752             res = bfdec_cmp_eq(a, b);
14753         }
14754         break;
14755 #endif
14756     default:
14757         res = FALSE;
14758         break;
14759     }
14760     JS_FreeValue(ctx, op1);
14761     JS_FreeValue(ctx, op2);
14762  done_no_free:
14763     return res;
14764 }
14765 
js_strict_eq(JSContext * ctx,JSValue op1,JSValue op2)14766 static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2)
14767 {
14768     return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
14769 }
14770 
js_same_value(JSContext * ctx,JSValueConst op1,JSValueConst op2)14771 static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2)
14772 {
14773     return js_strict_eq2(ctx,
14774                          JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
14775                          JS_EQ_SAME_VALUE);
14776 }
14777 
js_same_value_zero(JSContext * ctx,JSValueConst op1,JSValueConst op2)14778 static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2)
14779 {
14780     return js_strict_eq2(ctx,
14781                          JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
14782                          JS_EQ_SAME_VALUE_ZERO);
14783 }
14784 
js_strict_eq_slow(JSContext * ctx,JSValue * sp,BOOL is_neq)14785 static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp,
14786                                        BOOL is_neq)
14787 {
14788     BOOL res;
14789     res = js_strict_eq(ctx, sp[-2], sp[-1]);
14790     sp[-2] = JS_NewBool(ctx, res ^ is_neq);
14791     return 0;
14792 }
14793 
js_operator_in(JSContext * ctx,JSValue * sp)14794 static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
14795 {
14796     JSValue op1, op2;
14797     JSAtom atom;
14798     int ret;
14799 
14800     op1 = sp[-2];
14801     op2 = sp[-1];
14802 
14803     if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) {
14804         JS_ThrowTypeError(ctx, "invalid 'in' operand");
14805         return -1;
14806     }
14807     atom = JS_ValueToAtom(ctx, op1);
14808     if (unlikely(atom == JS_ATOM_NULL))
14809         return -1;
14810     ret = JS_HasProperty(ctx, op2, atom);
14811     JS_FreeAtom(ctx, atom);
14812     if (ret < 0)
14813         return -1;
14814     JS_FreeValue(ctx, op1);
14815     JS_FreeValue(ctx, op2);
14816     sp[-2] = JS_NewBool(ctx, ret);
14817     return 0;
14818 }
14819 
js_has_unscopable(JSContext * ctx,JSValueConst obj,JSAtom atom)14820 static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,
14821                                          JSAtom atom)
14822 {
14823     JSValue arr, val;
14824     int ret;
14825 
14826     arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables);
14827     if (JS_IsException(arr))
14828         return -1;
14829     ret = 0;
14830     if (JS_IsObject(arr)) {
14831         val = JS_GetProperty(ctx, arr, atom);
14832         ret = JS_ToBoolFree(ctx, val);
14833     }
14834     JS_FreeValue(ctx, arr);
14835     return ret;
14836 }
14837 
js_operator_instanceof(JSContext * ctx,JSValue * sp)14838 static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp)
14839 {
14840     JSValue op1, op2;
14841     BOOL ret;
14842 
14843     op1 = sp[-2];
14844     op2 = sp[-1];
14845     ret = JS_IsInstanceOf(ctx, op1, op2);
14846     if (ret < 0)
14847         return ret;
14848     JS_FreeValue(ctx, op1);
14849     JS_FreeValue(ctx, op2);
14850     sp[-2] = JS_NewBool(ctx, ret);
14851     return 0;
14852 }
14853 
js_operator_typeof(JSContext * ctx,JSValueConst op1)14854 static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1)
14855 {
14856     JSAtom atom;
14857     uint32_t tag;
14858 
14859     tag = JS_VALUE_GET_NORM_TAG(op1);
14860     switch(tag) {
14861 #ifdef CONFIG_BIGNUM
14862     case JS_TAG_BIG_INT:
14863         atom = JS_ATOM_bigint;
14864         break;
14865     case JS_TAG_BIG_FLOAT:
14866         atom = JS_ATOM_bigfloat;
14867         break;
14868     case JS_TAG_BIG_DECIMAL:
14869         atom = JS_ATOM_bigdecimal;
14870         break;
14871 #endif
14872     case JS_TAG_INT:
14873     case JS_TAG_FLOAT64:
14874         atom = JS_ATOM_number;
14875         break;
14876     case JS_TAG_UNDEFINED:
14877         atom = JS_ATOM_undefined;
14878         break;
14879     case JS_TAG_BOOL:
14880         atom = JS_ATOM_boolean;
14881         break;
14882     case JS_TAG_STRING:
14883         atom = JS_ATOM_string;
14884         break;
14885     case JS_TAG_OBJECT:
14886         {
14887             JSObject *p;
14888             p = JS_VALUE_GET_OBJ(op1);
14889             if (unlikely(p->is_HTMLDDA))
14890                 atom = JS_ATOM_undefined;
14891             else if (JS_IsFunction(ctx, op1))
14892                 atom = JS_ATOM_function;
14893             else
14894                 goto obj_type;
14895         }
14896         break;
14897     case JS_TAG_NULL:
14898     obj_type:
14899         atom = JS_ATOM_object;
14900         break;
14901     case JS_TAG_SYMBOL:
14902         atom = JS_ATOM_symbol;
14903         break;
14904     default:
14905         atom = JS_ATOM_unknown;
14906         break;
14907     }
14908     return atom;
14909 }
14910 
js_operator_delete(JSContext * ctx,JSValue * sp)14911 static __exception int js_operator_delete(JSContext *ctx, JSValue *sp)
14912 {
14913     JSValue op1, op2;
14914     JSAtom atom;
14915     int ret;
14916 
14917     op1 = sp[-2];
14918     op2 = sp[-1];
14919     atom = JS_ValueToAtom(ctx, op2);
14920     if (unlikely(atom == JS_ATOM_NULL))
14921         return -1;
14922     ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT);
14923     JS_FreeAtom(ctx, atom);
14924     if (unlikely(ret < 0))
14925         return -1;
14926     JS_FreeValue(ctx, op1);
14927     JS_FreeValue(ctx, op2);
14928     sp[-2] = JS_NewBool(ctx, ret);
14929     return 0;
14930 }
14931 
js_throw_type_error(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)14932 static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val,
14933                                    int argc, JSValueConst *argv)
14934 {
14935     return JS_ThrowTypeError(ctx, "invalid property access");
14936 }
14937 
14938 /* XXX: not 100% compatible, but mozilla seems to use a similar
14939    implementation to ensure that caller in non strict mode does not
14940    throw (ES5 compatibility) */
js_function_proto_caller(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)14941 static JSValue js_function_proto_caller(JSContext *ctx, JSValueConst this_val,
14942                                         int argc, JSValueConst *argv)
14943 {
14944     JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
14945     if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) {
14946         return js_throw_type_error(ctx, this_val, 0, NULL);
14947     }
14948     return JS_UNDEFINED;
14949 }
14950 
js_function_proto_fileName(JSContext * ctx,JSValueConst this_val)14951 static JSValue js_function_proto_fileName(JSContext *ctx,
14952                                           JSValueConst this_val)
14953 {
14954     JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
14955     if (b && b->has_debug) {
14956         return JS_AtomToString(ctx, b->debug.filename);
14957     }
14958     return JS_UNDEFINED;
14959 }
14960 
js_function_proto_lineNumber(JSContext * ctx,JSValueConst this_val)14961 static JSValue js_function_proto_lineNumber(JSContext *ctx,
14962                                             JSValueConst this_val)
14963 {
14964     JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
14965     if (b && b->has_debug) {
14966         return JS_NewInt32(ctx, b->debug.line_num);
14967     }
14968     return JS_UNDEFINED;
14969 }
14970 
js_arguments_define_own_property(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSValueConst val,JSValueConst getter,JSValueConst setter,int flags)14971 static int js_arguments_define_own_property(JSContext *ctx,
14972                                             JSValueConst this_obj,
14973                                             JSAtom prop, JSValueConst val,
14974                                             JSValueConst getter, JSValueConst setter, int flags)
14975 {
14976     JSObject *p;
14977     uint32_t idx;
14978     p = JS_VALUE_GET_OBJ(this_obj);
14979     /* convert to normal array when redefining an existing numeric field */
14980     if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) &&
14981         idx < p->u.array.count) {
14982         if (convert_fast_array_to_array(ctx, p))
14983             return -1;
14984     }
14985     /* run the default define own property */
14986     return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
14987                              flags | JS_PROP_NO_EXOTIC);
14988 }
14989 
14990 static const JSClassExoticMethods js_arguments_exotic_methods = {
14991     .define_own_property = js_arguments_define_own_property,
14992 };
14993 
js_build_arguments(JSContext * ctx,int argc,JSValueConst * argv)14994 static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv)
14995 {
14996     JSValue val, *tab;
14997     JSProperty *pr;
14998     JSObject *p;
14999     int i;
15000 
15001     val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
15002                                  JS_CLASS_ARGUMENTS);
15003     if (JS_IsException(val))
15004         return val;
15005     p = JS_VALUE_GET_OBJ(val);
15006 
15007     /* add the length field (cannot fail) */
15008     pr = add_property(ctx, p, JS_ATOM_length,
15009                       JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
15010     pr->u.value = JS_NewInt32(ctx, argc);
15011 
15012     /* initialize the fast array part */
15013     tab = NULL;
15014     if (argc > 0) {
15015         tab = js_malloc(ctx, sizeof(tab[0]) * argc);
15016         if (!tab) {
15017             JS_FreeValue(ctx, val);
15018             return JS_EXCEPTION;
15019         }
15020         for(i = 0; i < argc; i++) {
15021             tab[i] = JS_DupValue(ctx, argv[i]);
15022         }
15023     }
15024     p->u.array.u.values = tab;
15025     p->u.array.count = argc;
15026 
15027     JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
15028                            JS_DupValue(ctx, ctx->array_proto_values),
15029                            JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
15030     /* add callee property to throw a TypeError in strict mode */
15031     JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED,
15032                       ctx->throw_type_error, ctx->throw_type_error,
15033                       JS_PROP_HAS_GET | JS_PROP_HAS_SET);
15034     return val;
15035 }
15036 
15037 #define GLOBAL_VAR_OFFSET 0x40000000
15038 #define ARGUMENT_VAR_OFFSET 0x20000000
15039 
15040 /* legacy arguments object: add references to the function arguments */
js_build_mapped_arguments(JSContext * ctx,int argc,JSValueConst * argv,JSStackFrame * sf,int arg_count)15041 static JSValue js_build_mapped_arguments(JSContext *ctx, int argc,
15042                                          JSValueConst *argv,
15043                                          JSStackFrame *sf, int arg_count)
15044 {
15045     JSValue val;
15046     JSProperty *pr;
15047     JSObject *p;
15048     int i;
15049 
15050     val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
15051                                  JS_CLASS_MAPPED_ARGUMENTS);
15052     if (JS_IsException(val))
15053         return val;
15054     p = JS_VALUE_GET_OBJ(val);
15055 
15056     /* add the length field (cannot fail) */
15057     pr = add_property(ctx, p, JS_ATOM_length,
15058                       JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
15059     pr->u.value = JS_NewInt32(ctx, argc);
15060 
15061     for(i = 0; i < arg_count; i++) {
15062         JSVarRef *var_ref;
15063         var_ref = get_var_ref(ctx, sf, i, TRUE);
15064         if (!var_ref)
15065             goto fail;
15066         pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);
15067         if (!pr) {
15068             free_var_ref(ctx->rt, var_ref);
15069             goto fail;
15070         }
15071         pr->u.var_ref = var_ref;
15072     }
15073 
15074     /* the arguments not mapped to the arguments of the function can
15075        be normal properties */
15076     for(i = arg_count; i < argc; i++) {
15077         if (JS_DefinePropertyValueUint32(ctx, val, i,
15078                                          JS_DupValue(ctx, argv[i]),
15079                                          JS_PROP_C_W_E) < 0)
15080             goto fail;
15081     }
15082 
15083     JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
15084                            JS_DupValue(ctx, ctx->array_proto_values),
15085                            JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
15086     /* callee returns this function in non strict mode */
15087     JS_DefinePropertyValue(ctx, val, JS_ATOM_callee,
15088                            JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func),
15089                            JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
15090     return val;
15091  fail:
15092     JS_FreeValue(ctx, val);
15093     return JS_EXCEPTION;
15094 }
15095 
js_build_rest(JSContext * ctx,int first,int argc,JSValueConst * argv)15096 static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv)
15097 {
15098     JSValue val;
15099     int i, ret;
15100 
15101     val = JS_NewArray(ctx);
15102     if (JS_IsException(val))
15103         return val;
15104     for (i = first; i < argc; i++) {
15105         ret = JS_DefinePropertyValueUint32(ctx, val, i - first,
15106                                            JS_DupValue(ctx, argv[i]),
15107                                            JS_PROP_C_W_E);
15108         if (ret < 0) {
15109             JS_FreeValue(ctx, val);
15110             return JS_EXCEPTION;
15111         }
15112     }
15113     return val;
15114 }
15115 
build_for_in_iterator(JSContext * ctx,JSValue obj)15116 static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
15117 {
15118     JSObject *p;
15119     JSPropertyEnum *tab_atom;
15120     int i;
15121     JSValue enum_obj, obj1;
15122     JSForInIterator *it;
15123     uint32_t tag, tab_atom_count;
15124 
15125     tag = JS_VALUE_GET_TAG(obj);
15126     if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) {
15127         obj = JS_ToObjectFree(ctx, obj);
15128     }
15129 
15130     it = js_malloc(ctx, sizeof(*it));
15131     if (!it) {
15132         JS_FreeValue(ctx, obj);
15133         return JS_EXCEPTION;
15134     }
15135     enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR);
15136     if (JS_IsException(enum_obj)) {
15137         js_free(ctx, it);
15138         JS_FreeValue(ctx, obj);
15139         return JS_EXCEPTION;
15140     }
15141     it->is_array = FALSE;
15142     it->obj = obj;
15143     it->idx = 0;
15144     p = JS_VALUE_GET_OBJ(enum_obj);
15145     p->u.for_in_iterator = it;
15146 
15147     if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
15148         return enum_obj;
15149 
15150     /* fast path: assume no enumerable properties in the prototype chain */
15151     obj1 = JS_DupValue(ctx, obj);
15152     for(;;) {
15153         obj1 = JS_GetPrototypeFree(ctx, obj1);
15154         if (JS_IsNull(obj1))
15155             break;
15156         if (JS_IsException(obj1))
15157             goto fail;
15158         if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
15159                                            JS_VALUE_GET_OBJ(obj1),
15160                                            JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) {
15161             JS_FreeValue(ctx, obj1);
15162             goto fail;
15163         }
15164         js_free_prop_enum(ctx, tab_atom, tab_atom_count);
15165         if (tab_atom_count != 0) {
15166             JS_FreeValue(ctx, obj1);
15167             goto slow_path;
15168         }
15169         /* must check for timeout to avoid infinite loop */
15170         if (js_poll_interrupts(ctx)) {
15171             JS_FreeValue(ctx, obj1);
15172             goto fail;
15173         }
15174     }
15175 
15176     p = JS_VALUE_GET_OBJ(obj);
15177 
15178     if (p->fast_array) {
15179         JSShape *sh;
15180         JSShapeProperty *prs;
15181         /* check that there are no enumerable normal fields */
15182         sh = p->shape;
15183         for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
15184             if (prs->flags & JS_PROP_ENUMERABLE)
15185                 goto normal_case;
15186         }
15187         /* for fast arrays, we only store the number of elements */
15188         it->is_array = TRUE;
15189         it->array_length = p->u.array.count;
15190     } else {
15191     normal_case:
15192         if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
15193                                    JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))
15194             goto fail;
15195         for(i = 0; i < tab_atom_count; i++) {
15196             JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0);
15197         }
15198         js_free_prop_enum(ctx, tab_atom, tab_atom_count);
15199     }
15200     return enum_obj;
15201 
15202  slow_path:
15203     /* non enumerable properties hide the enumerables ones in the
15204        prototype chain */
15205     obj1 = JS_DupValue(ctx, obj);
15206     for(;;) {
15207         if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
15208                                            JS_VALUE_GET_OBJ(obj1),
15209                                            JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
15210             JS_FreeValue(ctx, obj1);
15211             goto fail;
15212         }
15213         for(i = 0; i < tab_atom_count; i++) {
15214             JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL,
15215                                    (tab_atom[i].is_enumerable ?
15216                                     JS_PROP_ENUMERABLE : 0));
15217         }
15218         js_free_prop_enum(ctx, tab_atom, tab_atom_count);
15219         obj1 = JS_GetPrototypeFree(ctx, obj1);
15220         if (JS_IsNull(obj1))
15221             break;
15222         if (JS_IsException(obj1))
15223             goto fail;
15224         /* must check for timeout to avoid infinite loop */
15225         if (js_poll_interrupts(ctx)) {
15226             JS_FreeValue(ctx, obj1);
15227             goto fail;
15228         }
15229     }
15230     return enum_obj;
15231 
15232  fail:
15233     JS_FreeValue(ctx, enum_obj);
15234     return JS_EXCEPTION;
15235 }
15236 
15237 /* obj -> enum_obj */
js_for_in_start(JSContext * ctx,JSValue * sp)15238 static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)
15239 {
15240     sp[-1] = build_for_in_iterator(ctx, sp[-1]);
15241     if (JS_IsException(sp[-1]))
15242         return -1;
15243     return 0;
15244 }
15245 
15246 /* enum_obj -> enum_obj value done */
js_for_in_next(JSContext * ctx,JSValue * sp)15247 static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)
15248 {
15249     JSValueConst enum_obj;
15250     JSObject *p;
15251     JSAtom prop;
15252     JSForInIterator *it;
15253     int ret;
15254 
15255     enum_obj = sp[-1];
15256     /* fail safe */
15257     if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT)
15258         goto done;
15259     p = JS_VALUE_GET_OBJ(enum_obj);
15260     if (p->class_id != JS_CLASS_FOR_IN_ITERATOR)
15261         goto done;
15262     it = p->u.for_in_iterator;
15263 
15264     for(;;) {
15265         if (it->is_array) {
15266             if (it->idx >= it->array_length)
15267                 goto done;
15268             prop = __JS_AtomFromUInt32(it->idx);
15269             it->idx++;
15270         } else {
15271             JSShape *sh = p->shape;
15272             JSShapeProperty *prs;
15273             if (it->idx >= sh->prop_count)
15274                 goto done;
15275             prs = get_shape_prop(sh) + it->idx;
15276             prop = prs->atom;
15277             it->idx++;
15278             if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE))
15279                 continue;
15280         }
15281         /* check if the property was deleted */
15282         ret = JS_HasProperty(ctx, it->obj, prop);
15283         if (ret < 0)
15284             return ret;
15285         if (ret)
15286             break;
15287     }
15288     /* return the property */
15289     sp[0] = JS_AtomToValue(ctx, prop);
15290     sp[1] = JS_FALSE;
15291     return 0;
15292  done:
15293     /* return the end */
15294     sp[0] = JS_UNDEFINED;
15295     sp[1] = JS_TRUE;
15296     return 0;
15297 }
15298 
JS_GetIterator2(JSContext * ctx,JSValueConst obj,JSValueConst method)15299 static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj,
15300                                JSValueConst method)
15301 {
15302     JSValue enum_obj;
15303 
15304     enum_obj = JS_Call(ctx, method, obj, 0, NULL);
15305     if (JS_IsException(enum_obj))
15306         return enum_obj;
15307     if (!JS_IsObject(enum_obj)) {
15308         JS_FreeValue(ctx, enum_obj);
15309         return JS_ThrowTypeErrorNotAnObject(ctx);
15310     }
15311     return enum_obj;
15312 }
15313 
JS_GetIterator(JSContext * ctx,JSValueConst obj,BOOL is_async)15314 static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async)
15315 {
15316     JSValue method, ret, sync_iter;
15317 
15318     if (is_async) {
15319         method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator);
15320         if (JS_IsException(method))
15321             return method;
15322         if (JS_IsUndefined(method) || JS_IsNull(method)) {
15323             method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
15324             if (JS_IsException(method))
15325                 return method;
15326             sync_iter = JS_GetIterator2(ctx, obj, method);
15327             JS_FreeValue(ctx, method);
15328             if (JS_IsException(sync_iter))
15329                 return sync_iter;
15330             ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter);
15331             JS_FreeValue(ctx, sync_iter);
15332             return ret;
15333         }
15334     } else {
15335         method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
15336         if (JS_IsException(method))
15337             return method;
15338     }
15339     if (!JS_IsFunction(ctx, method)) {
15340         JS_FreeValue(ctx, method);
15341         return JS_ThrowTypeError(ctx, "value is not iterable");
15342     }
15343     ret = JS_GetIterator2(ctx, obj, method);
15344     JS_FreeValue(ctx, method);
15345     return ret;
15346 }
15347 
15348 /* return *pdone = 2 if the iterator object is not parsed */
JS_IteratorNext2(JSContext * ctx,JSValueConst enum_obj,JSValueConst method,int argc,JSValueConst * argv,int * pdone)15349 static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj,
15350                                 JSValueConst method,
15351                                 int argc, JSValueConst *argv, int *pdone)
15352 {
15353     JSValue obj;
15354 
15355     /* fast path for the built-in iterators (avoid creating the
15356        intermediate result object) */
15357     if (JS_IsObject(method)) {
15358         JSObject *p = JS_VALUE_GET_OBJ(method);
15359         if (p->class_id == JS_CLASS_C_FUNCTION &&
15360             p->u.cfunc.cproto == JS_CFUNC_iterator_next) {
15361             JSCFunctionType func;
15362             JSValueConst args[1];
15363 
15364             /* in case the function expects one argument */
15365             if (argc == 0) {
15366                 args[0] = JS_UNDEFINED;
15367                 argv = args;
15368             }
15369             func = p->u.cfunc.c_function;
15370             return func.iterator_next(ctx, enum_obj, argc, argv,
15371                                       pdone, p->u.cfunc.magic);
15372         }
15373     }
15374     obj = JS_Call(ctx, method, enum_obj, argc, argv);
15375     if (JS_IsException(obj))
15376         goto fail;
15377     if (!JS_IsObject(obj)) {
15378         JS_FreeValue(ctx, obj);
15379         JS_ThrowTypeError(ctx, "iterator must return an object");
15380         goto fail;
15381     }
15382     *pdone = 2;
15383     return obj;
15384  fail:
15385     *pdone = FALSE;
15386     return JS_EXCEPTION;
15387 }
15388 
JS_IteratorNext(JSContext * ctx,JSValueConst enum_obj,JSValueConst method,int argc,JSValueConst * argv,BOOL * pdone)15389 static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj,
15390                                JSValueConst method,
15391                                int argc, JSValueConst *argv, BOOL *pdone)
15392 {
15393     JSValue obj, value, done_val;
15394     int done;
15395 
15396     obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done);
15397     if (JS_IsException(obj))
15398         goto fail;
15399     if (done != 2) {
15400         *pdone = done;
15401         return obj;
15402     } else {
15403         done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
15404         if (JS_IsException(done_val))
15405             goto fail;
15406         *pdone = JS_ToBoolFree(ctx, done_val);
15407         value = JS_UNDEFINED;
15408         if (!*pdone) {
15409             value = JS_GetProperty(ctx, obj, JS_ATOM_value);
15410         }
15411         JS_FreeValue(ctx, obj);
15412         return value;
15413     }
15414  fail:
15415     JS_FreeValue(ctx, obj);
15416     *pdone = FALSE;
15417     return JS_EXCEPTION;
15418 }
15419 
15420 /* return < 0 in case of exception */
JS_IteratorClose(JSContext * ctx,JSValueConst enum_obj,BOOL is_exception_pending)15421 static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj,
15422                             BOOL is_exception_pending)
15423 {
15424     JSValue method, ret, ex_obj;
15425     int res;
15426 
15427     if (is_exception_pending) {
15428         ex_obj = ctx->rt->current_exception;
15429         ctx->rt->current_exception = JS_NULL;
15430         res = -1;
15431     } else {
15432         ex_obj = JS_UNDEFINED;
15433         res = 0;
15434     }
15435     method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return);
15436     if (JS_IsException(method)) {
15437         res = -1;
15438         goto done;
15439     }
15440     if (JS_IsUndefined(method) || JS_IsNull(method)) {
15441         goto done;
15442     }
15443     ret = JS_CallFree(ctx, method, enum_obj, 0, NULL);
15444     if (!is_exception_pending) {
15445         if (JS_IsException(ret)) {
15446             res = -1;
15447         } else if (!JS_IsObject(ret)) {
15448             JS_ThrowTypeErrorNotAnObject(ctx);
15449             res = -1;
15450         }
15451     }
15452     JS_FreeValue(ctx, ret);
15453  done:
15454     if (is_exception_pending) {
15455         JS_Throw(ctx, ex_obj);
15456     }
15457     return res;
15458 }
15459 
15460 /* obj -> enum_rec (3 slots) */
js_for_of_start(JSContext * ctx,JSValue * sp,BOOL is_async)15461 static __exception int js_for_of_start(JSContext *ctx, JSValue *sp,
15462                                        BOOL is_async)
15463 {
15464     JSValue op1, obj, method;
15465     op1 = sp[-1];
15466     obj = JS_GetIterator(ctx, op1, is_async);
15467     if (JS_IsException(obj))
15468         return -1;
15469     JS_FreeValue(ctx, op1);
15470     sp[-1] = obj;
15471     method = JS_GetProperty(ctx, obj, JS_ATOM_next);
15472     if (JS_IsException(method))
15473         return -1;
15474     sp[0] = method;
15475     return 0;
15476 }
15477 
15478 /* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset'
15479    objs. If 'done' is true or in case of exception, 'enum_rec' is set
15480    to undefined. If 'done' is true, 'value' is always set to
15481    undefined. */
js_for_of_next(JSContext * ctx,JSValue * sp,int offset)15482 static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
15483 {
15484     JSValue value = JS_UNDEFINED;
15485     int done = 1;
15486 
15487     if (likely(!JS_IsUndefined(sp[offset]))) {
15488         value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done);
15489         if (JS_IsException(value))
15490             done = -1;
15491         if (done) {
15492             /* value is JS_UNDEFINED or JS_EXCEPTION */
15493             /* replace the iteration object with undefined */
15494             JS_FreeValue(ctx, sp[offset]);
15495             sp[offset] = JS_UNDEFINED;
15496             if (done < 0) {
15497                 return -1;
15498             } else {
15499                 JS_FreeValue(ctx, value);
15500                 value = JS_UNDEFINED;
15501             }
15502         }
15503     }
15504     sp[0] = value;
15505     sp[1] = JS_NewBool(ctx, done);
15506     return 0;
15507 }
15508 
JS_IteratorGetCompleteValue(JSContext * ctx,JSValueConst obj,BOOL * pdone)15509 static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj,
15510                                            BOOL *pdone)
15511 {
15512     JSValue done_val, value;
15513     BOOL done;
15514     done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
15515     if (JS_IsException(done_val))
15516         goto fail;
15517     done = JS_ToBoolFree(ctx, done_val);
15518     value = JS_GetProperty(ctx, obj, JS_ATOM_value);
15519     if (JS_IsException(value))
15520         goto fail;
15521     *pdone = done;
15522     return value;
15523  fail:
15524     *pdone = FALSE;
15525     return JS_EXCEPTION;
15526 }
15527 
js_iterator_get_value_done(JSContext * ctx,JSValue * sp)15528 static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
15529 {
15530     JSValue obj, value;
15531     BOOL done;
15532     obj = sp[-1];
15533     if (!JS_IsObject(obj)) {
15534         JS_ThrowTypeError(ctx, "iterator must return an object");
15535         return -1;
15536     }
15537     value = JS_IteratorGetCompleteValue(ctx, obj, &done);
15538     if (JS_IsException(value))
15539         return -1;
15540     JS_FreeValue(ctx, obj);
15541     sp[-1] = value;
15542     sp[0] = JS_NewBool(ctx, done);
15543     return 0;
15544 }
15545 
js_create_iterator_result(JSContext * ctx,JSValue val,BOOL done)15546 static JSValue js_create_iterator_result(JSContext *ctx,
15547                                          JSValue val,
15548                                          BOOL done)
15549 {
15550     JSValue obj;
15551     obj = JS_NewObject(ctx);
15552     if (JS_IsException(obj)) {
15553         JS_FreeValue(ctx, val);
15554         return obj;
15555     }
15556     if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value,
15557                                val, JS_PROP_C_W_E) < 0) {
15558         goto fail;
15559     }
15560     if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done,
15561                                JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) {
15562     fail:
15563         JS_FreeValue(ctx, obj);
15564         return JS_EXCEPTION;
15565     }
15566     return obj;
15567 }
15568 
15569 static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
15570                                       int argc, JSValueConst *argv,
15571                                       BOOL *pdone, int magic);
15572 
15573 static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
15574                                         int argc, JSValueConst *argv, int magic);
15575 
js_is_fast_array(JSContext * ctx,JSValueConst obj)15576 static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj)
15577 {
15578     /* Try and handle fast arrays explicitly */
15579     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
15580         JSObject *p = JS_VALUE_GET_OBJ(obj);
15581         if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {
15582             return TRUE;
15583         }
15584     }
15585     return FALSE;
15586 }
15587 
15588 /* Access an Array's internal JSValue array if available */
js_get_fast_array(JSContext * ctx,JSValueConst obj,JSValue ** arrpp,uint32_t * countp)15589 static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
15590                               JSValue **arrpp, uint32_t *countp)
15591 {
15592     /* Try and handle fast arrays explicitly */
15593     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
15594         JSObject *p = JS_VALUE_GET_OBJ(obj);
15595         if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {
15596             *countp = p->u.array.count;
15597             *arrpp = p->u.array.u.values;
15598             return TRUE;
15599         }
15600     }
15601     return FALSE;
15602 }
15603 
js_append_enumerate(JSContext * ctx,JSValue * sp)15604 static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)
15605 {
15606     JSValue iterator, enumobj, method, value;
15607     int is_array_iterator;
15608     JSValue *arrp;
15609     uint32_t i, count32, pos;
15610 
15611     if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) {
15612         JS_ThrowInternalError(ctx, "invalid index for append");
15613         return -1;
15614     }
15615 
15616     pos = JS_VALUE_GET_INT(sp[-2]);
15617 
15618     /* XXX: further optimisations:
15619        - use ctx->array_proto_values?
15620        - check if array_iterator_prototype next method is built-in and
15621          avoid constructing actual iterator object?
15622        - build this into js_for_of_start and use in all `for (x of o)` loops
15623      */
15624     iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator);
15625     if (JS_IsException(iterator))
15626         return -1;
15627     is_array_iterator = JS_IsCFunction(ctx, iterator,
15628                                        (JSCFunction *)js_create_array_iterator,
15629                                        JS_ITERATOR_KIND_VALUE);
15630     JS_FreeValue(ctx, iterator);
15631 
15632     enumobj = JS_GetIterator(ctx, sp[-1], FALSE);
15633     if (JS_IsException(enumobj))
15634         return -1;
15635     method = JS_GetProperty(ctx, enumobj, JS_ATOM_next);
15636     if (JS_IsException(method)) {
15637         JS_FreeValue(ctx, enumobj);
15638         return -1;
15639     }
15640     if (is_array_iterator
15641     &&  JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0)
15642     &&  js_get_fast_array(ctx, sp[-1], &arrp, &count32)) {
15643         uint32_t len;
15644         if (js_get_length32(ctx, &len, sp[-1]))
15645             goto exception;
15646         /* if len > count32, the elements >= count32 might be read in
15647            the prototypes and might have side effects */
15648         if (len != count32)
15649             goto general_case;
15650         /* Handle fast arrays explicitly */
15651         for (i = 0; i < count32; i++) {
15652             if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++,
15653                                              JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0)
15654                 goto exception;
15655         }
15656     } else {
15657     general_case:
15658         for (;;) {
15659             BOOL done;
15660             value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done);
15661             if (JS_IsException(value))
15662                 goto exception;
15663             if (done) {
15664                 /* value is JS_UNDEFINED */
15665                 break;
15666             }
15667             if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0)
15668                 goto exception;
15669         }
15670     }
15671     /* Note: could raise an error if too many elements */
15672     sp[-2] = JS_NewInt32(ctx, pos);
15673     JS_FreeValue(ctx, enumobj);
15674     JS_FreeValue(ctx, method);
15675     return 0;
15676 
15677 exception:
15678     JS_IteratorClose(ctx, enumobj, TRUE);
15679     JS_FreeValue(ctx, enumobj);
15680     JS_FreeValue(ctx, method);
15681     return -1;
15682 }
15683 
JS_CopyDataProperties(JSContext * ctx,JSValueConst target,JSValueConst source,JSValueConst excluded,BOOL setprop)15684 static __exception int JS_CopyDataProperties(JSContext *ctx,
15685                                              JSValueConst target,
15686                                              JSValueConst source,
15687                                              JSValueConst excluded,
15688                                              BOOL setprop)
15689 {
15690     JSPropertyEnum *tab_atom;
15691     JSValue val;
15692     uint32_t i, tab_atom_count;
15693     JSObject *p;
15694     JSObject *pexcl = NULL;
15695     int ret, gpn_flags;
15696     JSPropertyDescriptor desc;
15697     BOOL is_enumerable;
15698 
15699     if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT)
15700         return 0;
15701 
15702     if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT)
15703         pexcl = JS_VALUE_GET_OBJ(excluded);
15704 
15705     p = JS_VALUE_GET_OBJ(source);
15706 
15707     gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY;
15708     if (p->is_exotic) {
15709         const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
15710         /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it
15711            introduces a visible change */
15712         if (em && em->get_own_property_names) {
15713             gpn_flags &= ~JS_GPN_ENUM_ONLY;
15714         }
15715     }
15716     if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
15717                                        gpn_flags))
15718         return -1;
15719 
15720     for (i = 0; i < tab_atom_count; i++) {
15721         if (pexcl) {
15722             ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom);
15723             if (ret) {
15724                 if (ret < 0)
15725                     goto exception;
15726                 continue;
15727             }
15728         }
15729         if (!(gpn_flags & JS_GPN_ENUM_ONLY)) {
15730             /* test if the property is enumerable */
15731             ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom);
15732             if (ret < 0)
15733                 goto exception;
15734             if (!ret)
15735                 continue;
15736             is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0;
15737             js_free_desc(ctx, &desc);
15738             if (!is_enumerable)
15739                 continue;
15740         }
15741         val = JS_GetProperty(ctx, source, tab_atom[i].atom);
15742         if (JS_IsException(val))
15743             goto exception;
15744         if (setprop)
15745             ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val);
15746         else
15747             ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val,
15748                                          JS_PROP_C_W_E);
15749         if (ret < 0)
15750             goto exception;
15751     }
15752     js_free_prop_enum(ctx, tab_atom, tab_atom_count);
15753     return 0;
15754  exception:
15755     js_free_prop_enum(ctx, tab_atom, tab_atom_count);
15756     return -1;
15757 }
15758 
15759 /* only valid inside C functions */
JS_GetActiveFunction(JSContext * ctx)15760 static JSValueConst JS_GetActiveFunction(JSContext *ctx)
15761 {
15762     return ctx->rt->current_stack_frame->cur_func;
15763 }
15764 
get_var_ref(JSContext * ctx,JSStackFrame * sf,int var_idx,BOOL is_arg)15765 static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
15766                              int var_idx, BOOL is_arg)
15767 {
15768     JSVarRef *var_ref;
15769     struct list_head *el;
15770 
15771     list_for_each(el, &sf->var_ref_list) {
15772         var_ref = list_entry(el, JSVarRef, header.link);
15773         if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) {
15774             var_ref->header.ref_count++;
15775             return var_ref;
15776         }
15777     }
15778     /* create a new one */
15779     var_ref = js_malloc(ctx, sizeof(JSVarRef));
15780     if (!var_ref)
15781         return NULL;
15782     var_ref->header.ref_count = 1;
15783     var_ref->is_detached = FALSE;
15784     var_ref->is_arg = is_arg;
15785     var_ref->var_idx = var_idx;
15786     list_add_tail(&var_ref->header.link, &sf->var_ref_list);
15787     if (is_arg)
15788         var_ref->pvalue = &sf->arg_buf[var_idx];
15789     else
15790         var_ref->pvalue = &sf->var_buf[var_idx];
15791     var_ref->value = JS_UNDEFINED;
15792     return var_ref;
15793 }
15794 
js_closure2(JSContext * ctx,JSValue func_obj,JSFunctionBytecode * b,JSVarRef ** cur_var_refs,JSStackFrame * sf)15795 static JSValue js_closure2(JSContext *ctx, JSValue func_obj,
15796                            JSFunctionBytecode *b,
15797                            JSVarRef **cur_var_refs,
15798                            JSStackFrame *sf)
15799 {
15800     JSObject *p;
15801     JSVarRef **var_refs;
15802     int i;
15803 
15804     p = JS_VALUE_GET_OBJ(func_obj);
15805     p->u.func.function_bytecode = b;
15806     p->u.func.home_object = NULL;
15807     p->u.func.var_refs = NULL;
15808     if (b->closure_var_count) {
15809         var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
15810         if (!var_refs)
15811             goto fail;
15812         p->u.func.var_refs = var_refs;
15813         for(i = 0; i < b->closure_var_count; i++) {
15814             JSClosureVar *cv = &b->closure_var[i];
15815             JSVarRef *var_ref;
15816             if (cv->is_local) {
15817                 /* reuse the existing variable reference if it already exists */
15818                 var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg);
15819                 if (!var_ref)
15820                     goto fail;
15821             } else {
15822                 var_ref = cur_var_refs[cv->var_idx];
15823                 var_ref->header.ref_count++;
15824             }
15825             var_refs[i] = var_ref;
15826         }
15827     }
15828     return func_obj;
15829  fail:
15830     /* bfunc is freed when func_obj is freed */
15831     JS_FreeValue(ctx, func_obj);
15832     return JS_EXCEPTION;
15833 }
15834 
js_instantiate_prototype(JSContext * ctx,JSObject * p,JSAtom atom,void * opaque)15835 static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque)
15836 {
15837     JSValue obj, this_val;
15838     int ret;
15839 
15840     this_val = JS_MKPTR(JS_TAG_OBJECT, p);
15841     obj = JS_NewObject(ctx);
15842     if (JS_IsException(obj))
15843         return JS_EXCEPTION;
15844     set_cycle_flag(ctx, obj);
15845     set_cycle_flag(ctx, this_val);
15846     ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor,
15847                                  JS_DupValue(ctx, this_val),
15848                                  JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
15849     if (ret < 0) {
15850         JS_FreeValue(ctx, obj);
15851         return JS_EXCEPTION;
15852     }
15853     return obj;
15854 }
15855 
15856 static const uint16_t func_kind_to_class_id[] = {
15857     [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION,
15858     [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION,
15859     [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION,
15860     [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION,
15861 };
15862 
js_closure(JSContext * ctx,JSValue bfunc,JSVarRef ** cur_var_refs,JSStackFrame * sf)15863 static JSValue js_closure(JSContext *ctx, JSValue bfunc,
15864                           JSVarRef **cur_var_refs,
15865                           JSStackFrame *sf)
15866 {
15867     JSFunctionBytecode *b;
15868     JSValue func_obj;
15869     JSAtom name_atom;
15870 
15871     b = JS_VALUE_GET_PTR(bfunc);
15872     func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]);
15873     if (JS_IsException(func_obj)) {
15874         JS_FreeValue(ctx, bfunc);
15875         return JS_EXCEPTION;
15876     }
15877     func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf);
15878     if (JS_IsException(func_obj)) {
15879         /* bfunc has been freed */
15880         goto fail;
15881     }
15882     name_atom = b->func_name;
15883     if (name_atom == JS_ATOM_NULL)
15884         name_atom = JS_ATOM_empty_string;
15885     js_function_set_properties(ctx, func_obj, name_atom,
15886                                b->defined_arg_count);
15887 
15888     if (b->func_kind & JS_FUNC_GENERATOR) {
15889         JSValue proto;
15890         int proto_class_id;
15891         /* generators have a prototype field which is used as
15892            prototype for the generator object */
15893         if (b->func_kind == JS_FUNC_ASYNC_GENERATOR)
15894             proto_class_id = JS_CLASS_ASYNC_GENERATOR;
15895         else
15896             proto_class_id = JS_CLASS_GENERATOR;
15897         proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]);
15898         if (JS_IsException(proto))
15899             goto fail;
15900         JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto,
15901                                JS_PROP_WRITABLE);
15902     } else if (b->has_prototype) {
15903         /* add the 'prototype' property: delay instantiation to avoid
15904            creating cycles for every javascript function. The prototype
15905            object is created on the fly when first accessed */
15906         JS_SetConstructorBit(ctx, func_obj, TRUE);
15907         JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype,
15908                                   JS_AUTOINIT_ID_PROTOTYPE, NULL,
15909                                   JS_PROP_WRITABLE);
15910     }
15911     return func_obj;
15912  fail:
15913     /* bfunc is freed when func_obj is freed */
15914     JS_FreeValue(ctx, func_obj);
15915     return JS_EXCEPTION;
15916 }
15917 
15918 #define JS_DEFINE_CLASS_HAS_HERITAGE     (1 << 0)
15919 
js_op_define_class(JSContext * ctx,JSValue * sp,JSAtom class_name,int class_flags,JSVarRef ** cur_var_refs,JSStackFrame * sf,BOOL is_computed_name)15920 static int js_op_define_class(JSContext *ctx, JSValue *sp,
15921                               JSAtom class_name, int class_flags,
15922                               JSVarRef **cur_var_refs,
15923                               JSStackFrame *sf, BOOL is_computed_name)
15924 {
15925     JSValue bfunc, parent_class, proto = JS_UNDEFINED;
15926     JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED;
15927     JSFunctionBytecode *b;
15928 
15929     parent_class = sp[-2];
15930     bfunc = sp[-1];
15931 
15932     if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) {
15933         if (JS_IsNull(parent_class)) {
15934             parent_proto = JS_NULL;
15935             parent_class = JS_DupValue(ctx, ctx->function_proto);
15936         } else {
15937             if (!JS_IsConstructor(ctx, parent_class)) {
15938                 JS_ThrowTypeError(ctx, "parent class must be constructor");
15939                 goto fail;
15940             }
15941             parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype);
15942             if (JS_IsException(parent_proto))
15943                 goto fail;
15944             if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) {
15945                 JS_ThrowTypeError(ctx, "parent prototype must be an object or null");
15946                 goto fail;
15947             }
15948         }
15949     } else {
15950         /* parent_class is JS_UNDEFINED in this case */
15951         parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]);
15952         parent_class = JS_DupValue(ctx, ctx->function_proto);
15953     }
15954     proto = JS_NewObjectProto(ctx, parent_proto);
15955     if (JS_IsException(proto))
15956         goto fail;
15957 
15958     b = JS_VALUE_GET_PTR(bfunc);
15959     assert(b->func_kind == JS_FUNC_NORMAL);
15960     ctor = JS_NewObjectProtoClass(ctx, parent_class,
15961                                   JS_CLASS_BYTECODE_FUNCTION);
15962     if (JS_IsException(ctor))
15963         goto fail;
15964     ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf);
15965     bfunc = JS_UNDEFINED;
15966     if (JS_IsException(ctor))
15967         goto fail;
15968     js_method_set_home_object(ctx, ctor, proto);
15969     JS_SetConstructorBit(ctx, ctor, TRUE);
15970 
15971     JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length,
15972                            JS_NewInt32(ctx, b->defined_arg_count),
15973                            JS_PROP_CONFIGURABLE);
15974 
15975     if (is_computed_name) {
15976         if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3],
15977                                         JS_PROP_CONFIGURABLE) < 0)
15978             goto fail;
15979     } else {
15980         if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0)
15981             goto fail;
15982     }
15983 
15984     /* the constructor property must be first. It can be overriden by
15985        computed property names */
15986     if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
15987                                JS_DupValue(ctx, ctor),
15988                                JS_PROP_CONFIGURABLE |
15989                                JS_PROP_WRITABLE | JS_PROP_THROW) < 0)
15990         goto fail;
15991     /* set the prototype property */
15992     if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype,
15993                                JS_DupValue(ctx, proto), JS_PROP_THROW) < 0)
15994         goto fail;
15995     set_cycle_flag(ctx, ctor);
15996     set_cycle_flag(ctx, proto);
15997 
15998     JS_FreeValue(ctx, parent_proto);
15999     JS_FreeValue(ctx, parent_class);
16000 
16001     sp[-2] = ctor;
16002     sp[-1] = proto;
16003     return 0;
16004  fail:
16005     JS_FreeValue(ctx, parent_class);
16006     JS_FreeValue(ctx, parent_proto);
16007     JS_FreeValue(ctx, bfunc);
16008     JS_FreeValue(ctx, proto);
16009     JS_FreeValue(ctx, ctor);
16010     sp[-2] = JS_UNDEFINED;
16011     sp[-1] = JS_UNDEFINED;
16012     return -1;
16013 }
16014 
close_var_refs(JSRuntime * rt,JSStackFrame * sf)16015 static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
16016 {
16017     struct list_head *el, *el1;
16018     JSVarRef *var_ref;
16019     int var_idx;
16020 
16021     list_for_each_safe(el, el1, &sf->var_ref_list) {
16022         var_ref = list_entry(el, JSVarRef, header.link);
16023         var_idx = var_ref->var_idx;
16024         if (var_ref->is_arg)
16025             var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]);
16026         else
16027             var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]);
16028         var_ref->pvalue = &var_ref->value;
16029         /* the reference is no longer to a local variable */
16030         var_ref->is_detached = TRUE;
16031         add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
16032     }
16033 }
16034 
close_lexical_var(JSContext * ctx,JSStackFrame * sf,int idx,int is_arg)16035 static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg)
16036 {
16037     struct list_head *el, *el1;
16038     JSVarRef *var_ref;
16039     int var_idx = idx;
16040 
16041     list_for_each_safe(el, el1, &sf->var_ref_list) {
16042         var_ref = list_entry(el, JSVarRef, header.link);
16043         if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) {
16044             var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]);
16045             var_ref->pvalue = &var_ref->value;
16046             list_del(&var_ref->header.link);
16047             /* the reference is no longer to a local variable */
16048             var_ref->is_detached = TRUE;
16049             add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
16050         }
16051     }
16052 }
16053 
16054 #define JS_CALL_FLAG_COPY_ARGV   (1 << 1)
16055 #define JS_CALL_FLAG_GENERATOR   (1 << 2)
16056 
js_call_c_function(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)16057 static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
16058                                   JSValueConst this_obj,
16059                                   int argc, JSValueConst *argv, int flags)
16060 {
16061     JSRuntime *rt = ctx->rt;
16062     JSCFunctionType func;
16063     JSObject *p;
16064     JSStackFrame sf_s, *sf = &sf_s, *prev_sf;
16065     JSValue ret_val;
16066     JSValueConst *arg_buf;
16067     int arg_count, i;
16068     JSCFunctionEnum cproto;
16069 
16070     p = JS_VALUE_GET_OBJ(func_obj);
16071     cproto = p->u.cfunc.cproto;
16072     arg_count = p->u.cfunc.length;
16073 
16074     /* better to always check stack overflow */
16075     if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count))
16076         return JS_ThrowStackOverflow(ctx);
16077 
16078     prev_sf = rt->current_stack_frame;
16079     sf->prev_frame = prev_sf;
16080     rt->current_stack_frame = sf;
16081     ctx = p->u.cfunc.realm; /* change the current realm */
16082 
16083 #ifdef CONFIG_BIGNUM
16084     /* we only propagate the bignum mode as some runtime functions
16085        test it */
16086     if (prev_sf)
16087         sf->js_mode = prev_sf->js_mode & JS_MODE_MATH;
16088     else
16089         sf->js_mode = 0;
16090 #else
16091     sf->js_mode = 0;
16092 #endif
16093     sf->cur_func = JS_VALUE_CONST_CAST(func_obj);
16094     sf->arg_count = argc;
16095     arg_buf = argv;
16096 
16097     if (unlikely(argc < arg_count)) {
16098         /* ensure that at least argc_count arguments are readable */
16099         arg_buf = alloca(sizeof(arg_buf[0]) * arg_count);
16100         for(i = 0; i < argc; i++)
16101             arg_buf[i] = argv[i];
16102         for(i = argc; i < arg_count; i++)
16103             arg_buf[i] = JS_UNDEFINED;
16104         sf->arg_count = arg_count;
16105     }
16106     sf->arg_buf = (JSValue*)arg_buf;
16107 
16108     func = p->u.cfunc.c_function;
16109     switch(cproto) {
16110     case JS_CFUNC_constructor:
16111     case JS_CFUNC_constructor_or_func:
16112         if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
16113             if (cproto == JS_CFUNC_constructor) {
16114             not_a_constructor:
16115                 ret_val = JS_ThrowTypeError(ctx, "must be called with new");
16116                 break;
16117             } else {
16118                 this_obj = JS_UNDEFINED;
16119             }
16120         }
16121         /* here this_obj is new_target */
16122         /* fall thru */
16123     case JS_CFUNC_generic:
16124         ret_val = func.generic(ctx, this_obj, argc, arg_buf);
16125         break;
16126     case JS_CFUNC_constructor_magic:
16127     case JS_CFUNC_constructor_or_func_magic:
16128         if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
16129             if (cproto == JS_CFUNC_constructor_magic) {
16130                 goto not_a_constructor;
16131             } else {
16132                 this_obj = JS_UNDEFINED;
16133             }
16134         }
16135         /* fall thru */
16136     case JS_CFUNC_generic_magic:
16137         ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf,
16138                                      p->u.cfunc.magic);
16139         break;
16140     case JS_CFUNC_getter:
16141         ret_val = func.getter(ctx, this_obj);
16142         break;
16143     case JS_CFUNC_setter:
16144         ret_val = func.setter(ctx, this_obj, arg_buf[0]);
16145         break;
16146     case JS_CFUNC_getter_magic:
16147         ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic);
16148         break;
16149     case JS_CFUNC_setter_magic:
16150         ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic);
16151         break;
16152     case JS_CFUNC_f_f:
16153         {
16154             double d1;
16155 
16156             if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
16157                 ret_val = JS_EXCEPTION;
16158                 break;
16159             }
16160             ret_val = JS_NewFloat64(ctx, func.f_f(d1));
16161         }
16162         break;
16163     case JS_CFUNC_f_f_f:
16164         {
16165             double d1, d2;
16166 
16167             if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
16168                 ret_val = JS_EXCEPTION;
16169                 break;
16170             }
16171             if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) {
16172                 ret_val = JS_EXCEPTION;
16173                 break;
16174             }
16175             ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2));
16176         }
16177         break;
16178     case JS_CFUNC_iterator_next:
16179         {
16180             int done;
16181             ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf,
16182                                          &done, p->u.cfunc.magic);
16183             if (!JS_IsException(ret_val) && done != 2) {
16184                 ret_val = js_create_iterator_result(ctx, ret_val, done);
16185             }
16186         }
16187         break;
16188     default:
16189         abort();
16190     }
16191 
16192     rt->current_stack_frame = sf->prev_frame;
16193     return ret_val;
16194 }
16195 
js_call_bound_function(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)16196 static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
16197                                       JSValueConst this_obj,
16198                                       int argc, JSValueConst *argv, int flags)
16199 {
16200     JSObject *p;
16201     JSBoundFunction *bf;
16202     JSValueConst *arg_buf, new_target;
16203     int arg_count, i;
16204 
16205     p = JS_VALUE_GET_OBJ(func_obj);
16206     bf = p->u.bound_function;
16207     arg_count = bf->argc + argc;
16208     if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count))
16209         return JS_ThrowStackOverflow(ctx);
16210     arg_buf = alloca(sizeof(JSValue) * arg_count);
16211     for(i = 0; i < bf->argc; i++) {
16212         arg_buf[i] = bf->argv[i];
16213     }
16214     for(i = 0; i < argc; i++) {
16215         arg_buf[bf->argc + i] = argv[i];
16216     }
16217     if (flags & JS_CALL_FLAG_CONSTRUCTOR) {
16218         new_target = this_obj;
16219         if (js_same_value(ctx, func_obj, new_target))
16220             new_target = bf->func_obj;
16221         return JS_CallConstructor2(ctx, bf->func_obj, new_target,
16222                                    arg_count, arg_buf);
16223     } else {
16224         return JS_Call(ctx, bf->func_obj, bf->this_val,
16225                        arg_count, arg_buf);
16226     }
16227 }
16228 
16229 /* argument of OP_special_object */
16230 typedef enum {
16231     OP_SPECIAL_OBJECT_ARGUMENTS,
16232     OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS,
16233     OP_SPECIAL_OBJECT_THIS_FUNC,
16234     OP_SPECIAL_OBJECT_NEW_TARGET,
16235     OP_SPECIAL_OBJECT_HOME_OBJECT,
16236     OP_SPECIAL_OBJECT_VAR_OBJECT,
16237     OP_SPECIAL_OBJECT_IMPORT_META,
16238 } OPSpecialObjectEnum;
16239 
16240 #define FUNC_RET_AWAIT      0
16241 #define FUNC_RET_YIELD      1
16242 #define FUNC_RET_YIELD_STAR 2
16243 
16244 /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
JS_CallInternal(JSContext * caller_ctx,JSValueConst func_obj,JSValueConst this_obj,JSValueConst new_target,int argc,JSValue * argv,int flags)16245 static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
16246                                JSValueConst this_obj, JSValueConst new_target,
16247                                int argc, JSValue *argv, int flags)
16248 {
16249     JSRuntime *rt = caller_ctx->rt;
16250     JSContext *ctx;
16251     JSObject *p;
16252     JSFunctionBytecode *b;
16253     JSStackFrame sf_s, *sf = &sf_s;
16254     const uint8_t *pc;
16255     int opcode, arg_allocated_size, i;
16256     JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval;
16257     JSVarRef **var_refs;
16258     size_t alloca_size;
16259 
16260 #if !DIRECT_DISPATCH
16261 #define SWITCH(pc)      switch (opcode = *pc++)
16262 #define CASE(op)        case op
16263 #define DEFAULT         default
16264 #define BREAK           break
16265 #else
16266     static const void * const dispatch_table[256] = {
16267 #define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id,
16268 #if SHORT_OPCODES
16269 #define def(id, size, n_pop, n_push, f)
16270 #else
16271 #define def(id, size, n_pop, n_push, f) && case_default,
16272 #endif
16273 #include "quickjs-opcode.h"
16274         [ OP_COUNT ... 255 ] = &&case_default
16275     };
16276 #define SWITCH(pc)      goto *dispatch_table[opcode = *pc++];
16277 #define CASE(op)        case_ ## op
16278 #define DEFAULT         case_default
16279 #define BREAK           SWITCH(pc)
16280 #endif
16281 
16282     if (js_poll_interrupts(caller_ctx))
16283         return JS_EXCEPTION;
16284     if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) {
16285         if (flags & JS_CALL_FLAG_GENERATOR) {
16286             JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj);
16287             /* func_obj get contains a pointer to JSFuncAsyncState */
16288             /* the stack frame is already allocated */
16289             sf = &s->frame;
16290             p = JS_VALUE_GET_OBJ(sf->cur_func);
16291             b = p->u.func.function_bytecode;
16292             ctx = b->realm;
16293             var_refs = p->u.func.var_refs;
16294             local_buf = arg_buf = sf->arg_buf;
16295             var_buf = sf->var_buf;
16296             stack_buf = sf->var_buf + b->var_count;
16297             sp = sf->cur_sp;
16298             sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */
16299             pc = sf->cur_pc;
16300             sf->prev_frame = rt->current_stack_frame;
16301             rt->current_stack_frame = sf;
16302             if (s->throw_flag)
16303                 goto exception;
16304             else
16305                 goto restart;
16306         } else {
16307             goto not_a_function;
16308         }
16309     }
16310     p = JS_VALUE_GET_OBJ(func_obj);
16311     if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
16312         JSClassCall *call_func;
16313         call_func = rt->class_array[p->class_id].call;
16314         if (!call_func) {
16315         not_a_function:
16316             return JS_ThrowTypeError(caller_ctx, "not a function");
16317         }
16318         return call_func(caller_ctx, func_obj, this_obj, argc,
16319                          (JSValueConst *)argv, flags);
16320     }
16321     b = p->u.func.function_bytecode;
16322 
16323     if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) {
16324         arg_allocated_size = b->arg_count;
16325     } else {
16326         arg_allocated_size = 0;
16327     }
16328 
16329     alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count +
16330                                      b->stack_size);
16331     if (js_check_stack_overflow(rt, alloca_size))
16332         return JS_ThrowStackOverflow(caller_ctx);
16333 
16334     sf->js_mode = b->js_mode;
16335     arg_buf = argv;
16336     sf->arg_count = argc;
16337     sf->cur_func = JS_VALUE_CONST_CAST(func_obj);
16338     init_list_head(&sf->var_ref_list);
16339     var_refs = p->u.func.var_refs;
16340 
16341     local_buf = alloca(alloca_size);
16342     if (unlikely(arg_allocated_size)) {
16343         int n = min_int(argc, b->arg_count);
16344         arg_buf = local_buf;
16345         for(i = 0; i < n; i++)
16346             arg_buf[i] = JS_DupValue(caller_ctx, argv[i]);
16347         for(; i < b->arg_count; i++)
16348             arg_buf[i] = JS_UNDEFINED;
16349         sf->arg_count = b->arg_count;
16350     }
16351     var_buf = local_buf + arg_allocated_size;
16352     sf->var_buf = var_buf;
16353     sf->arg_buf = arg_buf;
16354 
16355     for(i = 0; i < b->var_count; i++)
16356         var_buf[i] = JS_UNDEFINED;
16357 
16358     stack_buf = var_buf + b->var_count;
16359     sp = stack_buf;
16360     pc = b->byte_code_buf;
16361     sf->prev_frame = rt->current_stack_frame;
16362     rt->current_stack_frame = sf;
16363     ctx = b->realm; /* set the current realm */
16364 
16365  restart:
16366     for(;;) {
16367         int call_argc;
16368         JSValue *call_argv;
16369 
16370         SWITCH(pc) {
16371         CASE(OP_push_i32):
16372             *sp++ = JS_NewInt32(ctx, get_u32(pc));
16373             pc += 4;
16374             BREAK;
16375         CASE(OP_push_const):
16376             *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
16377             pc += 4;
16378             BREAK;
16379 #if SHORT_OPCODES
16380         CASE(OP_push_minus1):
16381         CASE(OP_push_0):
16382         CASE(OP_push_1):
16383         CASE(OP_push_2):
16384         CASE(OP_push_3):
16385         CASE(OP_push_4):
16386         CASE(OP_push_5):
16387         CASE(OP_push_6):
16388         CASE(OP_push_7):
16389             *sp++ = JS_NewInt32(ctx, opcode - OP_push_0);
16390             BREAK;
16391         CASE(OP_push_i8):
16392             *sp++ = JS_NewInt32(ctx, get_i8(pc));
16393             pc += 1;
16394             BREAK;
16395         CASE(OP_push_i16):
16396             *sp++ = JS_NewInt32(ctx, get_i16(pc));
16397             pc += 2;
16398             BREAK;
16399         CASE(OP_push_const8):
16400             *sp++ = JS_DupValue(ctx, b->cpool[*pc++]);
16401             BREAK;
16402         CASE(OP_fclosure8):
16403             *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf);
16404             if (unlikely(JS_IsException(sp[-1])))
16405                 goto exception;
16406             BREAK;
16407         CASE(OP_push_empty_string):
16408             *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string);
16409             BREAK;
16410         CASE(OP_get_length):
16411             {
16412                 JSValue val;
16413 
16414                 val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length);
16415                 if (unlikely(JS_IsException(val)))
16416                     goto exception;
16417                 JS_FreeValue(ctx, sp[-1]);
16418                 sp[-1] = val;
16419             }
16420             BREAK;
16421 #endif
16422         CASE(OP_push_atom_value):
16423             *sp++ = JS_AtomToValue(ctx, get_u32(pc));
16424             pc += 4;
16425             BREAK;
16426         CASE(OP_undefined):
16427             *sp++ = JS_UNDEFINED;
16428             BREAK;
16429         CASE(OP_null):
16430             *sp++ = JS_NULL;
16431             BREAK;
16432         CASE(OP_push_this):
16433             /* OP_push_this is only called at the start of a function */
16434             {
16435                 JSValue val;
16436                 if (!(b->js_mode & JS_MODE_STRICT)) {
16437                     uint32_t tag = JS_VALUE_GET_TAG(this_obj);
16438                     if (likely(tag == JS_TAG_OBJECT))
16439                         goto normal_this;
16440                     if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) {
16441                         val = JS_DupValue(ctx, ctx->global_obj);
16442                     } else {
16443                         val = JS_ToObject(ctx, this_obj);
16444                         if (JS_IsException(val))
16445                             goto exception;
16446                     }
16447                 } else {
16448                 normal_this:
16449                     val = JS_DupValue(ctx, this_obj);
16450                 }
16451                 *sp++ = val;
16452             }
16453             BREAK;
16454         CASE(OP_push_false):
16455             *sp++ = JS_FALSE;
16456             BREAK;
16457         CASE(OP_push_true):
16458             *sp++ = JS_TRUE;
16459             BREAK;
16460         CASE(OP_object):
16461             *sp++ = JS_NewObject(ctx);
16462             if (unlikely(JS_IsException(sp[-1])))
16463                 goto exception;
16464             BREAK;
16465         CASE(OP_special_object):
16466             {
16467                 int arg = *pc++;
16468                 switch(arg) {
16469                 case OP_SPECIAL_OBJECT_ARGUMENTS:
16470                     *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv);
16471                     if (unlikely(JS_IsException(sp[-1])))
16472                         goto exception;
16473                     break;
16474                 case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS:
16475                     *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv,
16476                                                       sf, min_int(argc, b->arg_count));
16477                     if (unlikely(JS_IsException(sp[-1])))
16478                         goto exception;
16479                     break;
16480                 case OP_SPECIAL_OBJECT_THIS_FUNC:
16481                     *sp++ = JS_DupValue(ctx, sf->cur_func);
16482                     break;
16483                 case OP_SPECIAL_OBJECT_NEW_TARGET:
16484                     *sp++ = JS_DupValue(ctx, new_target);
16485                     break;
16486                 case OP_SPECIAL_OBJECT_HOME_OBJECT:
16487                     {
16488                         JSObject *p1;
16489                         p1 = p->u.func.home_object;
16490                         if (unlikely(!p1))
16491                             *sp++ = JS_UNDEFINED;
16492                         else
16493                             *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
16494                     }
16495                     break;
16496                 case OP_SPECIAL_OBJECT_VAR_OBJECT:
16497                     *sp++ = JS_NewObjectProto(ctx, JS_NULL);
16498                     if (unlikely(JS_IsException(sp[-1])))
16499                         goto exception;
16500                     break;
16501                 case OP_SPECIAL_OBJECT_IMPORT_META:
16502                     *sp++ = js_import_meta(ctx);
16503                     if (unlikely(JS_IsException(sp[-1])))
16504                         goto exception;
16505                     break;
16506                 default:
16507                     abort();
16508                 }
16509             }
16510             BREAK;
16511         CASE(OP_rest):
16512             {
16513                 int first = get_u16(pc);
16514                 pc += 2;
16515                 *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv);
16516                 if (unlikely(JS_IsException(sp[-1])))
16517                     goto exception;
16518             }
16519             BREAK;
16520 
16521         CASE(OP_drop):
16522             JS_FreeValue(ctx, sp[-1]);
16523             sp--;
16524             BREAK;
16525         CASE(OP_nip):
16526             JS_FreeValue(ctx, sp[-2]);
16527             sp[-2] = sp[-1];
16528             sp--;
16529             BREAK;
16530         CASE(OP_nip1): /* a b c -> b c */
16531             JS_FreeValue(ctx, sp[-3]);
16532             sp[-3] = sp[-2];
16533             sp[-2] = sp[-1];
16534             sp--;
16535             BREAK;
16536         CASE(OP_dup):
16537             sp[0] = JS_DupValue(ctx, sp[-1]);
16538             sp++;
16539             BREAK;
16540         CASE(OP_dup2): /* a b -> a b a b */
16541             sp[0] = JS_DupValue(ctx, sp[-2]);
16542             sp[1] = JS_DupValue(ctx, sp[-1]);
16543             sp += 2;
16544             BREAK;
16545         CASE(OP_dup3): /* a b c -> a b c a b c */
16546             sp[0] = JS_DupValue(ctx, sp[-3]);
16547             sp[1] = JS_DupValue(ctx, sp[-2]);
16548             sp[2] = JS_DupValue(ctx, sp[-1]);
16549             sp += 3;
16550             BREAK;
16551         CASE(OP_dup1): /* a b -> a a b */
16552             sp[0] = sp[-1];
16553             sp[-1] = JS_DupValue(ctx, sp[-2]);
16554             sp++;
16555             BREAK;
16556         CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */
16557             sp[0] = sp[-1];
16558             sp[-1] = sp[-2];
16559             sp[-2] = JS_DupValue(ctx, sp[0]);
16560             sp++;
16561             BREAK;
16562         CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */
16563             sp[0] = sp[-1];
16564             sp[-1] = sp[-2];
16565             sp[-2] = sp[-3];
16566             sp[-3] = JS_DupValue(ctx, sp[0]);
16567             sp++;
16568             BREAK;
16569         CASE(OP_insert4): /* this obj prop a -> a this obj prop a */
16570             sp[0] = sp[-1];
16571             sp[-1] = sp[-2];
16572             sp[-2] = sp[-3];
16573             sp[-3] = sp[-4];
16574             sp[-4] = JS_DupValue(ctx, sp[0]);
16575             sp++;
16576             BREAK;
16577         CASE(OP_perm3): /* obj a b -> a obj b (213) */
16578             {
16579                 JSValue tmp;
16580                 tmp = sp[-2];
16581                 sp[-2] = sp[-3];
16582                 sp[-3] = tmp;
16583             }
16584             BREAK;
16585         CASE(OP_rot3l): /* x a b -> a b x (231) */
16586             {
16587                 JSValue tmp;
16588                 tmp = sp[-3];
16589                 sp[-3] = sp[-2];
16590                 sp[-2] = sp[-1];
16591                 sp[-1] = tmp;
16592             }
16593             BREAK;
16594         CASE(OP_rot4l): /* x a b c -> a b c x */
16595             {
16596                 JSValue tmp;
16597                 tmp = sp[-4];
16598                 sp[-4] = sp[-3];
16599                 sp[-3] = sp[-2];
16600                 sp[-2] = sp[-1];
16601                 sp[-1] = tmp;
16602             }
16603             BREAK;
16604         CASE(OP_rot5l): /* x a b c d -> a b c d x */
16605             {
16606                 JSValue tmp;
16607                 tmp = sp[-5];
16608                 sp[-5] = sp[-4];
16609                 sp[-4] = sp[-3];
16610                 sp[-3] = sp[-2];
16611                 sp[-2] = sp[-1];
16612                 sp[-1] = tmp;
16613             }
16614             BREAK;
16615         CASE(OP_rot3r): /* a b x -> x a b (312) */
16616             {
16617                 JSValue tmp;
16618                 tmp = sp[-1];
16619                 sp[-1] = sp[-2];
16620                 sp[-2] = sp[-3];
16621                 sp[-3] = tmp;
16622             }
16623             BREAK;
16624         CASE(OP_perm4): /* obj prop a b -> a obj prop b */
16625             {
16626                 JSValue tmp;
16627                 tmp = sp[-2];
16628                 sp[-2] = sp[-3];
16629                 sp[-3] = sp[-4];
16630                 sp[-4] = tmp;
16631             }
16632             BREAK;
16633         CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */
16634             {
16635                 JSValue tmp;
16636                 tmp = sp[-2];
16637                 sp[-2] = sp[-3];
16638                 sp[-3] = sp[-4];
16639                 sp[-4] = sp[-5];
16640                 sp[-5] = tmp;
16641             }
16642             BREAK;
16643         CASE(OP_swap): /* a b -> b a */
16644             {
16645                 JSValue tmp;
16646                 tmp = sp[-2];
16647                 sp[-2] = sp[-1];
16648                 sp[-1] = tmp;
16649             }
16650             BREAK;
16651         CASE(OP_swap2): /* a b c d -> c d a b */
16652             {
16653                 JSValue tmp1, tmp2;
16654                 tmp1 = sp[-4];
16655                 tmp2 = sp[-3];
16656                 sp[-4] = sp[-2];
16657                 sp[-3] = sp[-1];
16658                 sp[-2] = tmp1;
16659                 sp[-1] = tmp2;
16660             }
16661             BREAK;
16662 
16663         CASE(OP_fclosure):
16664             {
16665                 JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
16666                 pc += 4;
16667                 *sp++ = js_closure(ctx, bfunc, var_refs, sf);
16668                 if (unlikely(JS_IsException(sp[-1])))
16669                     goto exception;
16670             }
16671             BREAK;
16672 #if SHORT_OPCODES
16673         CASE(OP_call0):
16674         CASE(OP_call1):
16675         CASE(OP_call2):
16676         CASE(OP_call3):
16677             call_argc = opcode - OP_call0;
16678             goto has_call_argc;
16679 #endif
16680         CASE(OP_call):
16681         CASE(OP_tail_call):
16682             {
16683                 call_argc = get_u16(pc);
16684                 pc += 2;
16685                 goto has_call_argc;
16686             has_call_argc:
16687                 call_argv = sp - call_argc;
16688                 sf->cur_pc = pc;
16689                 ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
16690                                           JS_UNDEFINED, call_argc, call_argv, 0);
16691                 if (unlikely(JS_IsException(ret_val)))
16692                     goto exception;
16693                 if (opcode == OP_tail_call)
16694                     goto done;
16695                 for(i = -1; i < call_argc; i++)
16696                     JS_FreeValue(ctx, call_argv[i]);
16697                 sp -= call_argc + 1;
16698                 *sp++ = ret_val;
16699             }
16700             BREAK;
16701         CASE(OP_call_constructor):
16702             {
16703                 call_argc = get_u16(pc);
16704                 pc += 2;
16705                 call_argv = sp - call_argc;
16706                 sf->cur_pc = pc;
16707                 ret_val = JS_CallConstructorInternal(ctx, call_argv[-2],
16708                                                      call_argv[-1],
16709                                                      call_argc, call_argv, 0);
16710                 if (unlikely(JS_IsException(ret_val)))
16711                     goto exception;
16712                 for(i = -2; i < call_argc; i++)
16713                     JS_FreeValue(ctx, call_argv[i]);
16714                 sp -= call_argc + 2;
16715                 *sp++ = ret_val;
16716             }
16717             BREAK;
16718         CASE(OP_call_method):
16719         CASE(OP_tail_call_method):
16720             {
16721                 call_argc = get_u16(pc);
16722                 pc += 2;
16723                 call_argv = sp - call_argc;
16724                 sf->cur_pc = pc;
16725                 ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2],
16726                                           JS_UNDEFINED, call_argc, call_argv, 0);
16727                 if (unlikely(JS_IsException(ret_val)))
16728                     goto exception;
16729                 if (opcode == OP_tail_call_method)
16730                     goto done;
16731                 for(i = -2; i < call_argc; i++)
16732                     JS_FreeValue(ctx, call_argv[i]);
16733                 sp -= call_argc + 2;
16734                 *sp++ = ret_val;
16735             }
16736             BREAK;
16737         CASE(OP_array_from):
16738             {
16739                 int i, ret;
16740 
16741                 call_argc = get_u16(pc);
16742                 pc += 2;
16743                 ret_val = JS_NewArray(ctx);
16744                 if (unlikely(JS_IsException(ret_val)))
16745                     goto exception;
16746                 call_argv = sp - call_argc;
16747                 for(i = 0; i < call_argc; i++) {
16748                     ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i],
16749                                                  JS_PROP_C_W_E | JS_PROP_THROW);
16750                     call_argv[i] = JS_UNDEFINED;
16751                     if (ret < 0) {
16752                         JS_FreeValue(ctx, ret_val);
16753                         goto exception;
16754                     }
16755                 }
16756                 sp -= call_argc;
16757                 *sp++ = ret_val;
16758             }
16759             BREAK;
16760 
16761         CASE(OP_apply):
16762             {
16763                 int magic;
16764                 magic = get_u16(pc);
16765                 pc += 2;
16766 
16767                 ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic);
16768                 if (unlikely(JS_IsException(ret_val)))
16769                     goto exception;
16770                 JS_FreeValue(ctx, sp[-3]);
16771                 JS_FreeValue(ctx, sp[-2]);
16772                 JS_FreeValue(ctx, sp[-1]);
16773                 sp -= 3;
16774                 *sp++ = ret_val;
16775             }
16776             BREAK;
16777         CASE(OP_return):
16778             ret_val = *--sp;
16779             goto done;
16780         CASE(OP_return_undef):
16781             ret_val = JS_UNDEFINED;
16782             goto done;
16783 
16784         CASE(OP_check_ctor_return):
16785             /* return TRUE if 'this' should be returned */
16786             if (!JS_IsObject(sp[-1])) {
16787                 if (!JS_IsUndefined(sp[-1])) {
16788                     JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined");
16789                     goto exception;
16790                 }
16791                 sp[0] = JS_TRUE;
16792             } else {
16793                 sp[0] = JS_FALSE;
16794             }
16795             sp++;
16796             BREAK;
16797         CASE(OP_check_ctor):
16798             if (JS_IsUndefined(new_target)) {
16799                 JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'");
16800                 goto exception;
16801             }
16802             BREAK;
16803         CASE(OP_check_brand):
16804             if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0)
16805                 goto exception;
16806             BREAK;
16807         CASE(OP_add_brand):
16808             if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0)
16809                 goto exception;
16810             JS_FreeValue(ctx, sp[-2]);
16811             JS_FreeValue(ctx, sp[-1]);
16812             sp -= 2;
16813             BREAK;
16814 
16815         CASE(OP_throw):
16816             JS_Throw(ctx, *--sp);
16817             goto exception;
16818 
16819         CASE(OP_throw_error):
16820 #define JS_THROW_VAR_RO             0
16821 #define JS_THROW_VAR_REDECL         1
16822 #define JS_THROW_VAR_UNINITIALIZED  2
16823 #define JS_THROW_ERROR_DELETE_SUPER   3
16824 #define JS_THROW_ERROR_ITERATOR_THROW 4
16825             {
16826                 JSAtom atom;
16827                 int type;
16828                 atom = get_u32(pc);
16829                 type = pc[4];
16830                 pc += 5;
16831                 if (type == JS_THROW_VAR_RO)
16832                     JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom);
16833                 else
16834                 if (type == JS_THROW_VAR_REDECL)
16835                     JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom);
16836                 else
16837                 if (type == JS_THROW_VAR_UNINITIALIZED)
16838                     JS_ThrowReferenceErrorUninitialized(ctx, atom);
16839                 else
16840                 if (type == JS_THROW_ERROR_DELETE_SUPER)
16841                     JS_ThrowReferenceError(ctx, "unsupported reference to 'super'");
16842                 else
16843                 if (type == JS_THROW_ERROR_ITERATOR_THROW)
16844                     JS_ThrowTypeError(ctx, "iterator does not have a throw method");
16845                 else
16846                     JS_ThrowInternalError(ctx, "invalid throw var type %d", type);
16847             }
16848             goto exception;
16849 
16850         CASE(OP_eval):
16851             {
16852                 JSValueConst obj;
16853                 int scope_idx;
16854                 call_argc = get_u16(pc);
16855                 scope_idx = get_u16(pc + 2) - 1;
16856                 pc += 4;
16857                 call_argv = sp - call_argc;
16858                 sf->cur_pc = pc;
16859                 if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) {
16860                     if (call_argc >= 1)
16861                         obj = call_argv[0];
16862                     else
16863                         obj = JS_UNDEFINED;
16864                     ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
16865                                             JS_EVAL_TYPE_DIRECT, scope_idx);
16866                 } else {
16867                     ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
16868                                               JS_UNDEFINED, call_argc, call_argv, 0);
16869                 }
16870                 if (unlikely(JS_IsException(ret_val)))
16871                     goto exception;
16872                 for(i = -1; i < call_argc; i++)
16873                     JS_FreeValue(ctx, call_argv[i]);
16874                 sp -= call_argc + 1;
16875                 *sp++ = ret_val;
16876             }
16877             BREAK;
16878             /* could merge with OP_apply */
16879         CASE(OP_apply_eval):
16880             {
16881                 int scope_idx;
16882                 uint32_t len;
16883                 JSValue *tab;
16884                 JSValueConst obj;
16885 
16886                 scope_idx = get_u16(pc) - 1;
16887                 pc += 2;
16888                 tab = build_arg_list(ctx, &len, sp[-1]);
16889                 if (!tab)
16890                     goto exception;
16891                 if (js_same_value(ctx, sp[-2], ctx->eval_obj)) {
16892                     if (len >= 1)
16893                         obj = tab[0];
16894                     else
16895                         obj = JS_UNDEFINED;
16896                     ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
16897                                             JS_EVAL_TYPE_DIRECT, scope_idx);
16898                 } else {
16899                     ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len,
16900                                       (JSValueConst *)tab);
16901                 }
16902                 free_arg_list(ctx, tab, len);
16903                 if (unlikely(JS_IsException(ret_val)))
16904                     goto exception;
16905                 JS_FreeValue(ctx, sp[-2]);
16906                 JS_FreeValue(ctx, sp[-1]);
16907                 sp -= 2;
16908                 *sp++ = ret_val;
16909             }
16910             BREAK;
16911 
16912         CASE(OP_regexp):
16913             {
16914                 sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED,
16915                                                         sp[-2], sp[-1]);
16916                 sp--;
16917             }
16918             BREAK;
16919 
16920         CASE(OP_get_super):
16921             {
16922                 JSValue proto;
16923                 proto = JS_GetPrototype(ctx, sp[-1]);
16924                 if (JS_IsException(proto))
16925                     goto exception;
16926                 JS_FreeValue(ctx, sp[-1]);
16927                 sp[-1] = proto;
16928             }
16929             BREAK;
16930 
16931         CASE(OP_import):
16932             {
16933                 JSValue val;
16934                 val = js_dynamic_import(ctx, sp[-1]);
16935                 if (JS_IsException(val))
16936                     goto exception;
16937                 JS_FreeValue(ctx, sp[-1]);
16938                 sp[-1] = val;
16939             }
16940             BREAK;
16941 
16942         CASE(OP_check_var):
16943             {
16944                 int ret;
16945                 JSAtom atom;
16946                 atom = get_u32(pc);
16947                 pc += 4;
16948 
16949                 ret = JS_CheckGlobalVar(ctx, atom);
16950                 if (ret < 0)
16951                     goto exception;
16952                 *sp++ = JS_NewBool(ctx, ret);
16953             }
16954             BREAK;
16955 
16956         CASE(OP_get_var_undef):
16957         CASE(OP_get_var):
16958             {
16959                 JSValue val;
16960                 JSAtom atom;
16961                 atom = get_u32(pc);
16962                 pc += 4;
16963 
16964                 val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef);
16965                 if (unlikely(JS_IsException(val)))
16966                     goto exception;
16967                 *sp++ = val;
16968             }
16969             BREAK;
16970 
16971         CASE(OP_put_var):
16972         CASE(OP_put_var_init):
16973             {
16974                 int ret;
16975                 JSAtom atom;
16976                 atom = get_u32(pc);
16977                 pc += 4;
16978 
16979                 ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var);
16980                 sp--;
16981                 if (unlikely(ret < 0))
16982                     goto exception;
16983             }
16984             BREAK;
16985 
16986         CASE(OP_put_var_strict):
16987             {
16988                 int ret;
16989                 JSAtom atom;
16990                 atom = get_u32(pc);
16991                 pc += 4;
16992 
16993                 /* sp[-2] is JS_TRUE or JS_FALSE */
16994                 if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) {
16995                     JS_ThrowReferenceErrorNotDefined(ctx, atom);
16996                     goto exception;
16997                 }
16998                 ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2);
16999                 sp -= 2;
17000                 if (unlikely(ret < 0))
17001                     goto exception;
17002             }
17003             BREAK;
17004 
17005         CASE(OP_check_define_var):
17006             {
17007                 JSAtom atom;
17008                 int flags;
17009                 atom = get_u32(pc);
17010                 flags = pc[4];
17011                 pc += 5;
17012                 if (JS_CheckDefineGlobalVar(ctx, atom, flags))
17013                     goto exception;
17014             }
17015             BREAK;
17016         CASE(OP_define_var):
17017             {
17018                 JSAtom atom;
17019                 int flags;
17020                 atom = get_u32(pc);
17021                 flags = pc[4];
17022                 pc += 5;
17023                 if (JS_DefineGlobalVar(ctx, atom, flags))
17024                     goto exception;
17025             }
17026             BREAK;
17027         CASE(OP_define_func):
17028             {
17029                 JSAtom atom;
17030                 int flags;
17031                 atom = get_u32(pc);
17032                 flags = pc[4];
17033                 pc += 5;
17034                 if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags))
17035                     goto exception;
17036                 JS_FreeValue(ctx, sp[-1]);
17037                 sp--;
17038             }
17039             BREAK;
17040 
17041         CASE(OP_get_loc):
17042             {
17043                 int idx;
17044                 idx = get_u16(pc);
17045                 pc += 2;
17046                 sp[0] = JS_DupValue(ctx, var_buf[idx]);
17047                 sp++;
17048             }
17049             BREAK;
17050         CASE(OP_put_loc):
17051             {
17052                 int idx;
17053                 idx = get_u16(pc);
17054                 pc += 2;
17055                 set_value(ctx, &var_buf[idx], sp[-1]);
17056                 sp--;
17057             }
17058             BREAK;
17059         CASE(OP_set_loc):
17060             {
17061                 int idx;
17062                 idx = get_u16(pc);
17063                 pc += 2;
17064                 set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1]));
17065             }
17066             BREAK;
17067         CASE(OP_get_arg):
17068             {
17069                 int idx;
17070                 idx = get_u16(pc);
17071                 pc += 2;
17072                 sp[0] = JS_DupValue(ctx, arg_buf[idx]);
17073                 sp++;
17074             }
17075             BREAK;
17076         CASE(OP_put_arg):
17077             {
17078                 int idx;
17079                 idx = get_u16(pc);
17080                 pc += 2;
17081                 set_value(ctx, &arg_buf[idx], sp[-1]);
17082                 sp--;
17083             }
17084             BREAK;
17085         CASE(OP_set_arg):
17086             {
17087                 int idx;
17088                 idx = get_u16(pc);
17089                 pc += 2;
17090                 set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1]));
17091             }
17092             BREAK;
17093 
17094 #if SHORT_OPCODES
17095         CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK;
17096         CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK;
17097         CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK;
17098 
17099         CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK;
17100         CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK;
17101         CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK;
17102         CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK;
17103         CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK;
17104         CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK;
17105         CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK;
17106         CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK;
17107         CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
17108         CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
17109         CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
17110         CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
17111         CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK;
17112         CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK;
17113         CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK;
17114         CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK;
17115         CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK;
17116         CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK;
17117         CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK;
17118         CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK;
17119         CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
17120         CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
17121         CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
17122         CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
17123         CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK;
17124         CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK;
17125         CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK;
17126         CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK;
17127         CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK;
17128         CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK;
17129         CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK;
17130         CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK;
17131         CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
17132         CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
17133         CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
17134         CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
17135 #endif
17136 
17137         CASE(OP_get_var_ref):
17138             {
17139                 int idx;
17140                 JSValue val;
17141                 idx = get_u16(pc);
17142                 pc += 2;
17143                 val = *var_refs[idx]->pvalue;
17144                 sp[0] = JS_DupValue(ctx, val);
17145                 sp++;
17146             }
17147             BREAK;
17148         CASE(OP_put_var_ref):
17149             {
17150                 int idx;
17151                 idx = get_u16(pc);
17152                 pc += 2;
17153                 set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
17154                 sp--;
17155             }
17156             BREAK;
17157         CASE(OP_set_var_ref):
17158             {
17159                 int idx;
17160                 idx = get_u16(pc);
17161                 pc += 2;
17162                 set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1]));
17163             }
17164             BREAK;
17165         CASE(OP_get_var_ref_check):
17166             {
17167                 int idx;
17168                 JSValue val;
17169                 idx = get_u16(pc);
17170                 pc += 2;
17171                 val = *var_refs[idx]->pvalue;
17172                 if (unlikely(JS_IsUninitialized(val))) {
17173                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
17174                     goto exception;
17175                 }
17176                 sp[0] = JS_DupValue(ctx, val);
17177                 sp++;
17178             }
17179             BREAK;
17180         CASE(OP_put_var_ref_check):
17181             {
17182                 int idx;
17183                 idx = get_u16(pc);
17184                 pc += 2;
17185                 if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) {
17186                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
17187                     goto exception;
17188                 }
17189                 set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
17190                 sp--;
17191             }
17192             BREAK;
17193         CASE(OP_put_var_ref_check_init):
17194             {
17195                 int idx;
17196                 idx = get_u16(pc);
17197                 pc += 2;
17198                 if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) {
17199                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
17200                     goto exception;
17201                 }
17202                 set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
17203                 sp--;
17204             }
17205             BREAK;
17206         CASE(OP_set_loc_uninitialized):
17207             {
17208                 int idx;
17209                 idx = get_u16(pc);
17210                 pc += 2;
17211                 set_value(ctx, &var_buf[idx], JS_UNINITIALIZED);
17212             }
17213             BREAK;
17214         CASE(OP_get_loc_check):
17215             {
17216                 int idx;
17217                 idx = get_u16(pc);
17218                 pc += 2;
17219                 if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
17220                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
17221                     goto exception;
17222                 }
17223                 sp[0] = JS_DupValue(ctx, var_buf[idx]);
17224                 sp++;
17225             }
17226             BREAK;
17227         CASE(OP_put_loc_check):
17228             {
17229                 int idx;
17230                 idx = get_u16(pc);
17231                 pc += 2;
17232                 if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
17233                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
17234                     goto exception;
17235                 }
17236                 set_value(ctx, &var_buf[idx], sp[-1]);
17237                 sp--;
17238             }
17239             BREAK;
17240         CASE(OP_put_loc_check_init):
17241             {
17242                 int idx;
17243                 idx = get_u16(pc);
17244                 pc += 2;
17245                 if (unlikely(!JS_IsUninitialized(var_buf[idx]))) {
17246                     JS_ThrowReferenceError(ctx, "'this' can be initialized only once");
17247                     goto exception;
17248                 }
17249                 set_value(ctx, &var_buf[idx], sp[-1]);
17250                 sp--;
17251             }
17252             BREAK;
17253         CASE(OP_close_loc):
17254             {
17255                 int idx;
17256                 idx = get_u16(pc);
17257                 pc += 2;
17258                 close_lexical_var(ctx, sf, idx, FALSE);
17259             }
17260             BREAK;
17261 
17262         CASE(OP_make_loc_ref):
17263         CASE(OP_make_arg_ref):
17264         CASE(OP_make_var_ref_ref):
17265             {
17266                 JSVarRef *var_ref;
17267                 JSProperty *pr;
17268                 JSAtom atom;
17269                 int idx;
17270                 atom = get_u32(pc);
17271                 idx = get_u16(pc + 4);
17272                 pc += 6;
17273                 *sp++ = JS_NewObjectProto(ctx, JS_NULL);
17274                 if (unlikely(JS_IsException(sp[-1])))
17275                     goto exception;
17276                 if (opcode == OP_make_var_ref_ref) {
17277                     var_ref = var_refs[idx];
17278                     var_ref->header.ref_count++;
17279                 } else {
17280                     var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref);
17281                     if (!var_ref)
17282                         goto exception;
17283                 }
17284                 pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom,
17285                                   JS_PROP_WRITABLE | JS_PROP_VARREF);
17286                 if (!pr) {
17287                     free_var_ref(rt, var_ref);
17288                     goto exception;
17289                 }
17290                 pr->u.var_ref = var_ref;
17291                 *sp++ = JS_AtomToValue(ctx, atom);
17292             }
17293             BREAK;
17294         CASE(OP_make_var_ref):
17295             {
17296                 JSAtom atom;
17297                 atom = get_u32(pc);
17298                 pc += 4;
17299 
17300                 if (JS_GetGlobalVarRef(ctx, atom, sp))
17301                     goto exception;
17302                 sp += 2;
17303             }
17304             BREAK;
17305 
17306         CASE(OP_goto):
17307             pc += (int32_t)get_u32(pc);
17308             if (unlikely(js_poll_interrupts(ctx)))
17309                 goto exception;
17310             BREAK;
17311 #if SHORT_OPCODES
17312         CASE(OP_goto16):
17313             pc += (int16_t)get_u16(pc);
17314             if (unlikely(js_poll_interrupts(ctx)))
17315                 goto exception;
17316             BREAK;
17317         CASE(OP_goto8):
17318             pc += (int8_t)pc[0];
17319             if (unlikely(js_poll_interrupts(ctx)))
17320                 goto exception;
17321             BREAK;
17322 #endif
17323         CASE(OP_if_true):
17324             {
17325                 int res;
17326                 JSValue op1;
17327 
17328                 op1 = sp[-1];
17329                 pc += 4;
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 += (int32_t)get_u32(pc - 4) - 4;
17338                 }
17339                 if (unlikely(js_poll_interrupts(ctx)))
17340                     goto exception;
17341             }
17342             BREAK;
17343         CASE(OP_if_false):
17344             {
17345                 int res;
17346                 JSValue op1;
17347 
17348                 op1 = sp[-1];
17349                 pc += 4;
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 += (int32_t)get_u32(pc - 4) - 4;
17358                 }
17359                 if (unlikely(js_poll_interrupts(ctx)))
17360                     goto exception;
17361             }
17362             BREAK;
17363 #if SHORT_OPCODES
17364         CASE(OP_if_true8):
17365             {
17366                 int res;
17367                 JSValue op1;
17368 
17369                 op1 = sp[-1];
17370                 pc += 1;
17371                 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
17372                     res = JS_VALUE_GET_INT(op1);
17373                 } else {
17374                     res = JS_ToBoolFree(ctx, op1);
17375                 }
17376                 sp--;
17377                 if (res) {
17378                     pc += (int8_t)pc[-1] - 1;
17379                 }
17380                 if (unlikely(js_poll_interrupts(ctx)))
17381                     goto exception;
17382             }
17383             BREAK;
17384         CASE(OP_if_false8):
17385             {
17386                 int res;
17387                 JSValue op1;
17388 
17389                 op1 = sp[-1];
17390                 pc += 1;
17391                 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
17392                     res = JS_VALUE_GET_INT(op1);
17393                 } else {
17394                     res = JS_ToBoolFree(ctx, op1);
17395                 }
17396                 sp--;
17397                 if (!res) {
17398                     pc += (int8_t)pc[-1] - 1;
17399                 }
17400                 if (unlikely(js_poll_interrupts(ctx)))
17401                     goto exception;
17402             }
17403             BREAK;
17404 #endif
17405         CASE(OP_catch):
17406             {
17407                 int32_t diff;
17408                 diff = get_u32(pc);
17409                 sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf);
17410                 sp++;
17411                 pc += 4;
17412             }
17413             BREAK;
17414         CASE(OP_gosub):
17415             {
17416                 int32_t diff;
17417                 diff = get_u32(pc);
17418                 /* XXX: should have a different tag to avoid security flaw */
17419                 sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf);
17420                 sp++;
17421                 pc += diff;
17422             }
17423             BREAK;
17424         CASE(OP_ret):
17425             {
17426                 JSValue op1;
17427                 uint32_t pos;
17428                 op1 = sp[-1];
17429                 if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT))
17430                     goto ret_fail;
17431                 pos = JS_VALUE_GET_INT(op1);
17432                 if (unlikely(pos >= b->byte_code_len)) {
17433                 ret_fail:
17434                     JS_ThrowInternalError(ctx, "invalid ret value");
17435                     goto exception;
17436                 }
17437                 sp--;
17438                 pc = b->byte_code_buf + pos;
17439             }
17440             BREAK;
17441 
17442         CASE(OP_for_in_start):
17443             if (js_for_in_start(ctx, sp))
17444                 goto exception;
17445             BREAK;
17446         CASE(OP_for_in_next):
17447             if (js_for_in_next(ctx, sp))
17448                 goto exception;
17449             sp += 2;
17450             BREAK;
17451         CASE(OP_for_of_start):
17452             if (js_for_of_start(ctx, sp, FALSE))
17453                 goto exception;
17454             sp += 1;
17455             *sp++ = JS_NewCatchOffset(ctx, 0);
17456             BREAK;
17457         CASE(OP_for_of_next):
17458             {
17459                 int offset = -3 - pc[0];
17460                 pc += 1;
17461                 if (js_for_of_next(ctx, sp, offset))
17462                     goto exception;
17463                 sp += 2;
17464             }
17465             BREAK;
17466         CASE(OP_for_await_of_start):
17467             if (js_for_of_start(ctx, sp, TRUE))
17468                 goto exception;
17469             sp += 1;
17470             *sp++ = JS_NewCatchOffset(ctx, 0);
17471             BREAK;
17472         CASE(OP_iterator_get_value_done):
17473             if (js_iterator_get_value_done(ctx, sp))
17474                 goto exception;
17475             sp += 1;
17476             BREAK;
17477         CASE(OP_iterator_check_object):
17478             if (unlikely(!JS_IsObject(sp[-1]))) {
17479                 JS_ThrowTypeError(ctx, "iterator must return an object");
17480                 goto exception;
17481             }
17482             BREAK;
17483 
17484         CASE(OP_iterator_close):
17485             /* iter_obj next catch_offset -> */
17486             sp--; /* drop the catch offset to avoid getting caught by exception */
17487             JS_FreeValue(ctx, sp[-1]); /* drop the next method */
17488             sp--;
17489             if (!JS_IsUndefined(sp[-1])) {
17490                 if (JS_IteratorClose(ctx, sp[-1], FALSE))
17491                     goto exception;
17492                 JS_FreeValue(ctx, sp[-1]);
17493             }
17494             sp--;
17495             BREAK;
17496         CASE(OP_iterator_close_return):
17497             {
17498                 JSValue ret_val;
17499                 /* iter_obj next catch_offset ... ret_val ->
17500                    ret_eval iter_obj next catch_offset */
17501                 ret_val = *--sp;
17502                 while (sp > stack_buf &&
17503                        JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {
17504                     JS_FreeValue(ctx, *--sp);
17505                 }
17506                 if (unlikely(sp < stack_buf + 3)) {
17507                     JS_ThrowInternalError(ctx, "iterator_close_return");
17508                     JS_FreeValue(ctx, ret_val);
17509                     goto exception;
17510                 }
17511                 sp[0] = sp[-1];
17512                 sp[-1] = sp[-2];
17513                 sp[-2] = sp[-3];
17514                 sp[-3] = ret_val;
17515                 sp++;
17516             }
17517             BREAK;
17518 
17519         CASE(OP_iterator_next):
17520             /* stack: iter_obj next catch_offset val */
17521             {
17522                 JSValue ret;
17523                 ret = JS_Call(ctx, sp[-3], sp[-4],
17524                               1, (JSValueConst *)(sp - 1));
17525                 if (JS_IsException(ret))
17526                     goto exception;
17527                 JS_FreeValue(ctx, sp[-1]);
17528                 sp[-1] = ret;
17529             }
17530             BREAK;
17531 
17532         CASE(OP_iterator_call):
17533             /* stack: iter_obj next catch_offset val */
17534             {
17535                 JSValue method, ret;
17536                 BOOL ret_flag;
17537                 int flags;
17538                 flags = *pc++;
17539                 method = JS_GetProperty(ctx, sp[-4], (flags & 1) ?
17540                                         JS_ATOM_throw : JS_ATOM_return);
17541                 if (JS_IsException(method))
17542                     goto exception;
17543                 if (JS_IsUndefined(method) || JS_IsNull(method)) {
17544                     ret_flag = TRUE;
17545                 } else {
17546                     if (flags & 2) {
17547                         /* no argument */
17548                         ret = JS_CallFree(ctx, method, sp[-4],
17549                                           0, NULL);
17550                     } else {
17551                         ret = JS_CallFree(ctx, method, sp[-4],
17552                                           1, (JSValueConst *)(sp - 1));
17553                     }
17554                     if (JS_IsException(ret))
17555                         goto exception;
17556                     JS_FreeValue(ctx, sp[-1]);
17557                     sp[-1] = ret;
17558                     ret_flag = FALSE;
17559                 }
17560                 sp[0] = JS_NewBool(ctx, ret_flag);
17561                 sp += 1;
17562             }
17563             BREAK;
17564 
17565         CASE(OP_lnot):
17566             {
17567                 int res;
17568                 JSValue op1;
17569 
17570                 op1 = sp[-1];
17571                 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
17572                     res = JS_VALUE_GET_INT(op1) != 0;
17573                 } else {
17574                     res = JS_ToBoolFree(ctx, op1);
17575                 }
17576                 sp[-1] = JS_NewBool(ctx, !res);
17577             }
17578             BREAK;
17579 
17580         CASE(OP_get_field):
17581             {
17582                 JSValue val;
17583                 JSAtom atom;
17584                 atom = get_u32(pc);
17585                 pc += 4;
17586 
17587                 val = JS_GetProperty(ctx, sp[-1], atom);
17588                 if (unlikely(JS_IsException(val)))
17589                     goto exception;
17590                 JS_FreeValue(ctx, sp[-1]);
17591                 sp[-1] = val;
17592             }
17593             BREAK;
17594 
17595         CASE(OP_get_field2):
17596             {
17597                 JSValue val;
17598                 JSAtom atom;
17599                 atom = get_u32(pc);
17600                 pc += 4;
17601 
17602                 val = JS_GetProperty(ctx, sp[-1], atom);
17603                 if (unlikely(JS_IsException(val)))
17604                     goto exception;
17605                 *sp++ = val;
17606             }
17607             BREAK;
17608 
17609         CASE(OP_put_field):
17610             {
17611                 int ret;
17612                 JSAtom atom;
17613                 atom = get_u32(pc);
17614                 pc += 4;
17615 
17616                 ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1],
17617                                              JS_PROP_THROW_STRICT);
17618                 JS_FreeValue(ctx, sp[-2]);
17619                 sp -= 2;
17620                 if (unlikely(ret < 0))
17621                     goto exception;
17622             }
17623             BREAK;
17624 
17625         CASE(OP_private_symbol):
17626             {
17627                 JSAtom atom;
17628                 JSValue val;
17629 
17630                 atom = get_u32(pc);
17631                 pc += 4;
17632                 val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE);
17633                 if (JS_IsException(val))
17634                     goto exception;
17635                 *sp++ = val;
17636             }
17637             BREAK;
17638 
17639         CASE(OP_get_private_field):
17640             {
17641                 JSValue val;
17642 
17643                 val = JS_GetPrivateField(ctx, sp[-2], sp[-1]);
17644                 JS_FreeValue(ctx, sp[-1]);
17645                 JS_FreeValue(ctx, sp[-2]);
17646                 sp[-2] = val;
17647                 sp--;
17648                 if (unlikely(JS_IsException(val)))
17649                     goto exception;
17650             }
17651             BREAK;
17652 
17653         CASE(OP_put_private_field):
17654             {
17655                 int ret;
17656                 ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]);
17657                 JS_FreeValue(ctx, sp[-3]);
17658                 JS_FreeValue(ctx, sp[-1]);
17659                 sp -= 3;
17660                 if (unlikely(ret < 0))
17661                     goto exception;
17662             }
17663             BREAK;
17664 
17665         CASE(OP_define_private_field):
17666             {
17667                 int ret;
17668                 ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]);
17669                 JS_FreeValue(ctx, sp[-2]);
17670                 sp -= 2;
17671                 if (unlikely(ret < 0))
17672                     goto exception;
17673             }
17674             BREAK;
17675 
17676         CASE(OP_define_field):
17677             {
17678                 int ret;
17679                 JSAtom atom;
17680                 atom = get_u32(pc);
17681                 pc += 4;
17682 
17683                 ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1],
17684                                              JS_PROP_C_W_E | JS_PROP_THROW);
17685                 sp--;
17686                 if (unlikely(ret < 0))
17687                     goto exception;
17688             }
17689             BREAK;
17690 
17691         CASE(OP_set_name):
17692             {
17693                 int ret;
17694                 JSAtom atom;
17695                 atom = get_u32(pc);
17696                 pc += 4;
17697 
17698                 ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE);
17699                 if (unlikely(ret < 0))
17700                     goto exception;
17701             }
17702             BREAK;
17703         CASE(OP_set_name_computed):
17704             {
17705                 int ret;
17706                 ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE);
17707                 if (unlikely(ret < 0))
17708                     goto exception;
17709             }
17710             BREAK;
17711         CASE(OP_set_proto):
17712             {
17713                 JSValue proto;
17714                 proto = sp[-1];
17715                 if (JS_IsObject(proto) || JS_IsNull(proto)) {
17716                     if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0)
17717                         goto exception;
17718                 }
17719                 JS_FreeValue(ctx, proto);
17720                 sp--;
17721             }
17722             BREAK;
17723         CASE(OP_set_home_object):
17724             js_method_set_home_object(ctx, sp[-1], sp[-2]);
17725             BREAK;
17726         CASE(OP_define_method):
17727         CASE(OP_define_method_computed):
17728             {
17729                 JSValue getter, setter, value;
17730                 JSValueConst obj;
17731                 JSAtom atom;
17732                 int flags, ret, op_flags;
17733                 BOOL is_computed;
17734 #define OP_DEFINE_METHOD_METHOD 0
17735 #define OP_DEFINE_METHOD_GETTER 1
17736 #define OP_DEFINE_METHOD_SETTER 2
17737 #define OP_DEFINE_METHOD_ENUMERABLE 4
17738 
17739                 is_computed = (opcode == OP_define_method_computed);
17740                 if (is_computed) {
17741                     atom = JS_ValueToAtom(ctx, sp[-2]);
17742                     if (unlikely(atom == JS_ATOM_NULL))
17743                         goto exception;
17744                     opcode += OP_define_method - OP_define_method_computed;
17745                 } else {
17746                     atom = get_u32(pc);
17747                     pc += 4;
17748                 }
17749                 op_flags = *pc++;
17750 
17751                 obj = sp[-2 - is_computed];
17752                 flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE |
17753                     JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW;
17754                 if (op_flags & OP_DEFINE_METHOD_ENUMERABLE)
17755                     flags |= JS_PROP_ENUMERABLE;
17756                 op_flags &= 3;
17757                 value = JS_UNDEFINED;
17758                 getter = JS_UNDEFINED;
17759                 setter = JS_UNDEFINED;
17760                 if (op_flags == OP_DEFINE_METHOD_METHOD) {
17761                     value = sp[-1];
17762                     flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE;
17763                 } else if (op_flags == OP_DEFINE_METHOD_GETTER) {
17764                     getter = sp[-1];
17765                     flags |= JS_PROP_HAS_GET;
17766                 } else {
17767                     setter = sp[-1];
17768                     flags |= JS_PROP_HAS_SET;
17769                 }
17770                 ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj);
17771                 if (ret >= 0) {
17772                     ret = JS_DefineProperty(ctx, obj, atom, value,
17773                                             getter, setter, flags);
17774                 }
17775                 JS_FreeValue(ctx, sp[-1]);
17776                 if (is_computed) {
17777                     JS_FreeAtom(ctx, atom);
17778                     JS_FreeValue(ctx, sp[-2]);
17779                 }
17780                 sp -= 1 + is_computed;
17781                 if (unlikely(ret < 0))
17782                     goto exception;
17783             }
17784             BREAK;
17785 
17786         CASE(OP_define_class):
17787         CASE(OP_define_class_computed):
17788             {
17789                 int class_flags;
17790                 JSAtom atom;
17791 
17792                 atom = get_u32(pc);
17793                 class_flags = pc[4];
17794                 pc += 5;
17795                 if (js_op_define_class(ctx, sp, atom, class_flags,
17796                                        var_refs, sf,
17797                                        (opcode == OP_define_class_computed)) < 0)
17798                     goto exception;
17799             }
17800             BREAK;
17801 
17802         CASE(OP_get_array_el):
17803             {
17804                 JSValue val;
17805 
17806                 val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
17807                 JS_FreeValue(ctx, sp[-2]);
17808                 sp[-2] = val;
17809                 sp--;
17810                 if (unlikely(JS_IsException(val)))
17811                     goto exception;
17812             }
17813             BREAK;
17814 
17815         CASE(OP_get_array_el2):
17816             {
17817                 JSValue val;
17818 
17819                 val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
17820                 sp[-1] = val;
17821                 if (unlikely(JS_IsException(val)))
17822                     goto exception;
17823             }
17824             BREAK;
17825 
17826         CASE(OP_get_ref_value):
17827             {
17828                 JSValue val;
17829                 if (unlikely(JS_IsUndefined(sp[-2]))) {
17830                     JSAtom atom = JS_ValueToAtom(ctx, sp[-1]);
17831                     if (atom != JS_ATOM_NULL) {
17832                         JS_ThrowReferenceErrorNotDefined(ctx, atom);
17833                         JS_FreeAtom(ctx, atom);
17834                     }
17835                     goto exception;
17836                 }
17837                 val = JS_GetPropertyValue(ctx, sp[-2],
17838                                           JS_DupValue(ctx, sp[-1]));
17839                 if (unlikely(JS_IsException(val)))
17840                     goto exception;
17841                 sp[0] = val;
17842                 sp++;
17843             }
17844             BREAK;
17845 
17846         CASE(OP_get_super_value):
17847             {
17848                 JSValue val;
17849                 JSAtom atom;
17850                 atom = JS_ValueToAtom(ctx, sp[-1]);
17851                 if (unlikely(atom == JS_ATOM_NULL))
17852                     goto exception;
17853                 val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE);
17854                 JS_FreeAtom(ctx, atom);
17855                 if (unlikely(JS_IsException(val)))
17856                     goto exception;
17857                 JS_FreeValue(ctx, sp[-1]);
17858                 JS_FreeValue(ctx, sp[-2]);
17859                 JS_FreeValue(ctx, sp[-3]);
17860                 sp[-3] = val;
17861                 sp -= 2;
17862             }
17863             BREAK;
17864 
17865         CASE(OP_put_array_el):
17866             {
17867                 int ret;
17868 
17869                 ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);
17870                 JS_FreeValue(ctx, sp[-3]);
17871                 sp -= 3;
17872                 if (unlikely(ret < 0))
17873                     goto exception;
17874             }
17875             BREAK;
17876 
17877         CASE(OP_put_ref_value):
17878             {
17879                 int ret, flags;
17880                 flags = JS_PROP_THROW_STRICT;
17881                 if (unlikely(JS_IsUndefined(sp[-3]))) {
17882                     if (is_strict_mode(ctx)) {
17883                         JSAtom atom = JS_ValueToAtom(ctx, sp[-2]);
17884                         if (atom != JS_ATOM_NULL) {
17885                             JS_ThrowReferenceErrorNotDefined(ctx, atom);
17886                             JS_FreeAtom(ctx, atom);
17887                         }
17888                         goto exception;
17889                     } else {
17890                         sp[-3] = JS_DupValue(ctx, ctx->global_obj);
17891                     }
17892                 } else {
17893                     if (is_strict_mode(ctx))
17894                         flags |= JS_PROP_NO_ADD;
17895                 }
17896                 ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags);
17897                 JS_FreeValue(ctx, sp[-3]);
17898                 sp -= 3;
17899                 if (unlikely(ret < 0))
17900                     goto exception;
17901             }
17902             BREAK;
17903 
17904         CASE(OP_put_super_value):
17905             {
17906                 int ret;
17907                 JSAtom atom;
17908                 if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) {
17909                     JS_ThrowTypeErrorNotAnObject(ctx);
17910                     goto exception;
17911                 }
17912                 atom = JS_ValueToAtom(ctx, sp[-2]);
17913                 if (unlikely(atom == JS_ATOM_NULL))
17914                     goto exception;
17915                 ret = JS_SetPropertyGeneric(ctx, sp[-3], atom, sp[-1], sp[-4],
17916                                             JS_PROP_THROW_STRICT);
17917                 JS_FreeAtom(ctx, atom);
17918                 JS_FreeValue(ctx, sp[-4]);
17919                 JS_FreeValue(ctx, sp[-3]);
17920                 JS_FreeValue(ctx, sp[-2]);
17921                 sp -= 4;
17922                 if (ret < 0)
17923                     goto exception;
17924             }
17925             BREAK;
17926 
17927         CASE(OP_define_array_el):
17928             {
17929                 int ret;
17930                 ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1],
17931                                                   JS_PROP_C_W_E | JS_PROP_THROW);
17932                 sp -= 1;
17933                 if (unlikely(ret < 0))
17934                     goto exception;
17935             }
17936             BREAK;
17937 
17938         CASE(OP_append):    /* array pos enumobj -- array pos */
17939             {
17940                 if (js_append_enumerate(ctx, sp))
17941                     goto exception;
17942                 JS_FreeValue(ctx, *--sp);
17943             }
17944             BREAK;
17945 
17946         CASE(OP_copy_data_properties):    /* target source excludeList */
17947             {
17948                 /* stack offsets (-1 based):
17949                    2 bits for target,
17950                    3 bits for source,
17951                    2 bits for exclusionList */
17952                 int mask;
17953 
17954                 mask = *pc++;
17955                 if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)],
17956                                           sp[-1 - ((mask >> 2) & 7)],
17957                                           sp[-1 - ((mask >> 5) & 7)], 0))
17958                     goto exception;
17959             }
17960             BREAK;
17961 
17962         CASE(OP_add):
17963             {
17964                 JSValue op1, op2;
17965                 op1 = sp[-2];
17966                 op2 = sp[-1];
17967                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
17968                     int64_t r;
17969                     r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2);
17970                     if (unlikely((int)r != r))
17971                         goto add_slow;
17972                     sp[-2] = JS_NewInt32(ctx, r);
17973                     sp--;
17974                 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
17975                     sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) +
17976                                              JS_VALUE_GET_FLOAT64(op2));
17977                     sp--;
17978                 } else {
17979                 add_slow:
17980                     if (js_add_slow(ctx, sp))
17981                         goto exception;
17982                     sp--;
17983                 }
17984             }
17985             BREAK;
17986         CASE(OP_add_loc):
17987             {
17988                 JSValue *pv;
17989                 int idx;
17990                 idx = *pc;
17991                 pc += 1;
17992 
17993                 pv = &var_buf[idx];
17994                 if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) {
17995                     int64_t r;
17996                     r = (int64_t)JS_VALUE_GET_INT(*pv) +
17997                         JS_VALUE_GET_INT(sp[-1]);
17998                     if (unlikely((int)r != r))
17999                         goto add_loc_slow;
18000                     *pv = JS_NewInt32(ctx, r);
18001                     sp--;
18002                 } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) {
18003                     JSValue op1;
18004                     op1 = sp[-1];
18005                     sp--;
18006                     op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
18007                     if (JS_IsException(op1))
18008                         goto exception;
18009                     op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1);
18010                     if (JS_IsException(op1))
18011                         goto exception;
18012                     set_value(ctx, pv, op1);
18013                 } else {
18014                     JSValue ops[2];
18015                 add_loc_slow:
18016                     /* In case of exception, js_add_slow frees ops[0]
18017                        and ops[1], so we must duplicate *pv */
18018                     ops[0] = JS_DupValue(ctx, *pv);
18019                     ops[1] = sp[-1];
18020                     sp--;
18021                     if (js_add_slow(ctx, ops + 2))
18022                         goto exception;
18023                     set_value(ctx, pv, ops[0]);
18024                 }
18025             }
18026             BREAK;
18027         CASE(OP_sub):
18028             {
18029                 JSValue op1, op2;
18030                 op1 = sp[-2];
18031                 op2 = sp[-1];
18032                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18033                     int64_t r;
18034                     r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2);
18035                     if (unlikely((int)r != r))
18036                         goto binary_arith_slow;
18037                     sp[-2] = JS_NewInt32(ctx, r);
18038                     sp--;
18039                 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
18040                     sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) -
18041                                              JS_VALUE_GET_FLOAT64(op2));
18042                     sp--;
18043                 } else {
18044                     goto binary_arith_slow;
18045                 }
18046             }
18047             BREAK;
18048         CASE(OP_mul):
18049             {
18050                 JSValue op1, op2;
18051                 double d;
18052                 op1 = sp[-2];
18053                 op2 = sp[-1];
18054                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18055                     int32_t v1, v2;
18056                     int64_t r;
18057                     v1 = JS_VALUE_GET_INT(op1);
18058                     v2 = JS_VALUE_GET_INT(op2);
18059                     r = (int64_t)v1 * v2;
18060                     if (unlikely((int)r != r)) {
18061 #ifdef CONFIG_BIGNUM
18062                         if (unlikely(sf->js_mode & JS_MODE_MATH) &&
18063                             (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER))
18064                             goto binary_arith_slow;
18065 #endif
18066                         d = (double)r;
18067                         goto mul_fp_res;
18068                     }
18069                     /* need to test zero case for -0 result */
18070                     if (unlikely(r == 0 && (v1 | v2) < 0)) {
18071                         d = -0.0;
18072                         goto mul_fp_res;
18073                     }
18074                     sp[-2] = JS_NewInt32(ctx, r);
18075                     sp--;
18076                 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
18077 #ifdef CONFIG_BIGNUM
18078                     if (unlikely(sf->js_mode & JS_MODE_MATH))
18079                         goto binary_arith_slow;
18080 #endif
18081                     d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);
18082                 mul_fp_res:
18083                     sp[-2] = __JS_NewFloat64(ctx, d);
18084                     sp--;
18085                 } else {
18086                     goto binary_arith_slow;
18087                 }
18088             }
18089             BREAK;
18090         CASE(OP_div):
18091             {
18092                 JSValue op1, op2;
18093                 op1 = sp[-2];
18094                 op2 = sp[-1];
18095                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18096                     int v1, v2;
18097                     if (unlikely(sf->js_mode & JS_MODE_MATH))
18098                         goto binary_arith_slow;
18099                     v1 = JS_VALUE_GET_INT(op1);
18100                     v2 = JS_VALUE_GET_INT(op2);
18101                     sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
18102                     sp--;
18103                 } else {
18104                     goto binary_arith_slow;
18105                 }
18106             }
18107             BREAK;
18108         CASE(OP_mod):
18109 #ifdef CONFIG_BIGNUM
18110         CASE(OP_math_mod):
18111 #endif
18112             {
18113                 JSValue op1, op2;
18114                 op1 = sp[-2];
18115                 op2 = sp[-1];
18116                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18117                     int v1, v2, r;
18118                     v1 = JS_VALUE_GET_INT(op1);
18119                     v2 = JS_VALUE_GET_INT(op2);
18120                     /* We must avoid v2 = 0, v1 = INT32_MIN and v2 =
18121                        -1 and the cases where the result is -0. */
18122                     if (unlikely(v1 < 0 || v2 <= 0))
18123                         goto binary_arith_slow;
18124                     r = v1 % v2;
18125                     sp[-2] = JS_NewInt32(ctx, r);
18126                     sp--;
18127                 } else {
18128                     goto binary_arith_slow;
18129                 }
18130             }
18131             BREAK;
18132         CASE(OP_pow):
18133         binary_arith_slow:
18134             if (js_binary_arith_slow(ctx, sp, opcode))
18135                 goto exception;
18136             sp--;
18137             BREAK;
18138 
18139         CASE(OP_plus):
18140             {
18141                 JSValue op1;
18142                 uint32_t tag;
18143                 op1 = sp[-1];
18144                 tag = JS_VALUE_GET_TAG(op1);
18145                 if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) {
18146                 } else {
18147                     if (js_unary_arith_slow(ctx, sp, opcode))
18148                         goto exception;
18149                 }
18150             }
18151             BREAK;
18152         CASE(OP_neg):
18153             {
18154                 JSValue op1;
18155                 uint32_t tag;
18156                 int val;
18157                 double d;
18158                 op1 = sp[-1];
18159                 tag = JS_VALUE_GET_TAG(op1);
18160                 if (tag == JS_TAG_INT) {
18161                     val = JS_VALUE_GET_INT(op1);
18162                     /* Note: -0 cannot be expressed as integer */
18163                     if (unlikely(val == 0)) {
18164                         d = -0.0;
18165                         goto neg_fp_res;
18166                     }
18167                     if (unlikely(val == INT32_MIN)) {
18168                         d = -(double)val;
18169                         goto neg_fp_res;
18170                     }
18171                     sp[-1] = JS_NewInt32(ctx, -val);
18172                 } else if (JS_TAG_IS_FLOAT64(tag)) {
18173                     d = -JS_VALUE_GET_FLOAT64(op1);
18174                 neg_fp_res:
18175                     sp[-1] = __JS_NewFloat64(ctx, d);
18176                 } else {
18177                     if (js_unary_arith_slow(ctx, sp, opcode))
18178                         goto exception;
18179                 }
18180             }
18181             BREAK;
18182         CASE(OP_inc):
18183             {
18184                 JSValue op1;
18185                 int val;
18186                 op1 = sp[-1];
18187                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18188                     val = JS_VALUE_GET_INT(op1);
18189                     if (unlikely(val == INT32_MAX))
18190                         goto inc_slow;
18191                     sp[-1] = JS_NewInt32(ctx, val + 1);
18192                 } else {
18193                 inc_slow:
18194                     if (js_unary_arith_slow(ctx, sp, opcode))
18195                         goto exception;
18196                 }
18197             }
18198             BREAK;
18199         CASE(OP_dec):
18200             {
18201                 JSValue op1;
18202                 int val;
18203                 op1 = sp[-1];
18204                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18205                     val = JS_VALUE_GET_INT(op1);
18206                     if (unlikely(val == INT32_MIN))
18207                         goto dec_slow;
18208                     sp[-1] = JS_NewInt32(ctx, val - 1);
18209                 } else {
18210                 dec_slow:
18211                     if (js_unary_arith_slow(ctx, sp, opcode))
18212                         goto exception;
18213                 }
18214             }
18215             BREAK;
18216         CASE(OP_post_inc):
18217         CASE(OP_post_dec):
18218             if (js_post_inc_slow(ctx, sp, opcode))
18219                 goto exception;
18220             sp++;
18221             BREAK;
18222         CASE(OP_inc_loc):
18223             {
18224                 JSValue op1;
18225                 int val;
18226                 int idx;
18227                 idx = *pc;
18228                 pc += 1;
18229 
18230                 op1 = var_buf[idx];
18231                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18232                     val = JS_VALUE_GET_INT(op1);
18233                     if (unlikely(val == INT32_MAX))
18234                         goto inc_loc_slow;
18235                     var_buf[idx] = JS_NewInt32(ctx, val + 1);
18236                 } else {
18237                 inc_loc_slow:
18238                     /* must duplicate otherwise the variable value may
18239                        be destroyed before JS code accesses it */
18240                     op1 = JS_DupValue(ctx, op1);
18241                     if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc))
18242                         goto exception;
18243                     set_value(ctx, &var_buf[idx], op1);
18244                 }
18245             }
18246             BREAK;
18247         CASE(OP_dec_loc):
18248             {
18249                 JSValue op1;
18250                 int val;
18251                 int idx;
18252                 idx = *pc;
18253                 pc += 1;
18254 
18255                 op1 = var_buf[idx];
18256                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18257                     val = JS_VALUE_GET_INT(op1);
18258                     if (unlikely(val == INT32_MIN))
18259                         goto dec_loc_slow;
18260                     var_buf[idx] = JS_NewInt32(ctx, val - 1);
18261                 } else {
18262                 dec_loc_slow:
18263                     /* must duplicate otherwise the variable value may
18264                        be destroyed before JS code accesses it */
18265                     op1 = JS_DupValue(ctx, op1);
18266                     if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec))
18267                         goto exception;
18268                     set_value(ctx, &var_buf[idx], op1);
18269                 }
18270             }
18271             BREAK;
18272         CASE(OP_not):
18273             {
18274                 JSValue op1;
18275                 op1 = sp[-1];
18276                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18277                     sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1));
18278                 } else {
18279                     if (js_not_slow(ctx, sp))
18280                         goto exception;
18281                 }
18282             }
18283             BREAK;
18284 
18285         CASE(OP_shl):
18286             {
18287                 JSValue op1, op2;
18288                 op1 = sp[-2];
18289                 op2 = sp[-1];
18290                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18291                     uint32_t v1, v2;
18292                     v1 = JS_VALUE_GET_INT(op1);
18293                     v2 = JS_VALUE_GET_INT(op2);
18294 #ifdef CONFIG_BIGNUM
18295                     {
18296                         int64_t r;
18297                         if (unlikely(sf->js_mode & JS_MODE_MATH)) {
18298                             if (v2 > 0x1f)
18299                                 goto shl_slow;
18300                             r = (int64_t)v1 << v2;
18301                             if ((int)r != r)
18302                                 goto shl_slow;
18303                         } else {
18304                             v2 &= 0x1f;
18305                         }
18306                     }
18307 #else
18308                     v2 &= 0x1f;
18309 #endif
18310                     sp[-2] = JS_NewInt32(ctx, v1 << v2);
18311                     sp--;
18312                 } else {
18313 #ifdef CONFIG_BIGNUM
18314                 shl_slow:
18315 #endif
18316                     if (js_binary_logic_slow(ctx, sp, opcode))
18317                         goto exception;
18318                     sp--;
18319                 }
18320             }
18321             BREAK;
18322         CASE(OP_shr):
18323             {
18324                 JSValue op1, op2;
18325                 op1 = sp[-2];
18326                 op2 = sp[-1];
18327                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18328                     uint32_t v2;
18329                     v2 = JS_VALUE_GET_INT(op2);
18330                     /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */
18331                     v2 &= 0x1f;
18332                     sp[-2] = JS_NewUint32(ctx,
18333                                           (uint32_t)JS_VALUE_GET_INT(op1) >>
18334                                           v2);
18335                     sp--;
18336                 } else {
18337                     if (js_shr_slow(ctx, sp))
18338                         goto exception;
18339                     sp--;
18340                 }
18341             }
18342             BREAK;
18343         CASE(OP_sar):
18344             {
18345                 JSValue op1, op2;
18346                 op1 = sp[-2];
18347                 op2 = sp[-1];
18348                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18349                     uint32_t v2;
18350                     v2 = JS_VALUE_GET_INT(op2);
18351 #ifdef CONFIG_BIGNUM
18352                     if (unlikely(v2 > 0x1f)) {
18353                         if (unlikely(sf->js_mode & JS_MODE_MATH))
18354                             goto sar_slow;
18355                         else
18356                             v2 &= 0x1f;
18357                     }
18358 #else
18359                     v2 &= 0x1f;
18360 #endif
18361                     sp[-2] = JS_NewInt32(ctx,
18362                                           (int)JS_VALUE_GET_INT(op1) >> v2);
18363                     sp--;
18364                 } else {
18365 #ifdef CONFIG_BIGNUM
18366                 sar_slow:
18367 #endif
18368                     if (js_binary_logic_slow(ctx, sp, opcode))
18369                         goto exception;
18370                     sp--;
18371                 }
18372             }
18373             BREAK;
18374         CASE(OP_and):
18375             {
18376                 JSValue op1, op2;
18377                 op1 = sp[-2];
18378                 op2 = sp[-1];
18379                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18380                     sp[-2] = JS_NewInt32(ctx,
18381                                          JS_VALUE_GET_INT(op1) &
18382                                          JS_VALUE_GET_INT(op2));
18383                     sp--;
18384                 } else {
18385                     if (js_binary_logic_slow(ctx, sp, opcode))
18386                         goto exception;
18387                     sp--;
18388                 }
18389             }
18390             BREAK;
18391         CASE(OP_or):
18392             {
18393                 JSValue op1, op2;
18394                 op1 = sp[-2];
18395                 op2 = sp[-1];
18396                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18397                     sp[-2] = JS_NewInt32(ctx,
18398                                          JS_VALUE_GET_INT(op1) |
18399                                          JS_VALUE_GET_INT(op2));
18400                     sp--;
18401                 } else {
18402                     if (js_binary_logic_slow(ctx, sp, opcode))
18403                         goto exception;
18404                     sp--;
18405                 }
18406             }
18407             BREAK;
18408         CASE(OP_xor):
18409             {
18410                 JSValue op1, op2;
18411                 op1 = sp[-2];
18412                 op2 = sp[-1];
18413                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18414                     sp[-2] = JS_NewInt32(ctx,
18415                                          JS_VALUE_GET_INT(op1) ^
18416                                          JS_VALUE_GET_INT(op2));
18417                     sp--;
18418                 } else {
18419                     if (js_binary_logic_slow(ctx, sp, opcode))
18420                         goto exception;
18421                     sp--;
18422                 }
18423             }
18424             BREAK;
18425 
18426 
18427 #define OP_CMP(opcode, binary_op, slow_call)              \
18428             CASE(opcode):                                 \
18429                 {                                         \
18430                 JSValue op1, op2;                         \
18431                 op1 = sp[-2];                             \
18432                 op2 = sp[-1];                                   \
18433                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {           \
18434                     sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \
18435                     sp--;                                               \
18436                 } else {                                                \
18437                     if (slow_call)                                      \
18438                         goto exception;                                 \
18439                     sp--;                                               \
18440                 }                                                       \
18441                 }                                                       \
18442             BREAK
18443 
18444             OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode));
18445             OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode));
18446             OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode));
18447             OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode));
18448             OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0));
18449             OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1));
18450             OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0));
18451             OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1));
18452 
18453 #ifdef CONFIG_BIGNUM
18454         CASE(OP_mul_pow10):
18455             if (rt->bigfloat_ops.mul_pow10(ctx, sp))
18456                 goto exception;
18457             sp--;
18458             BREAK;
18459 #endif
18460         CASE(OP_in):
18461             if (js_operator_in(ctx, sp))
18462                 goto exception;
18463             sp--;
18464             BREAK;
18465         CASE(OP_instanceof):
18466             if (js_operator_instanceof(ctx, sp))
18467                 goto exception;
18468             sp--;
18469             BREAK;
18470         CASE(OP_typeof):
18471             {
18472                 JSValue op1;
18473                 JSAtom atom;
18474 
18475                 op1 = sp[-1];
18476                 atom = js_operator_typeof(ctx, op1);
18477                 JS_FreeValue(ctx, op1);
18478                 sp[-1] = JS_AtomToString(ctx, atom);
18479             }
18480             BREAK;
18481         CASE(OP_delete):
18482             if (js_operator_delete(ctx, sp))
18483                 goto exception;
18484             sp--;
18485             BREAK;
18486         CASE(OP_delete_var):
18487             {
18488                 JSAtom atom;
18489                 int ret;
18490 
18491                 atom = get_u32(pc);
18492                 pc += 4;
18493 
18494                 ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0);
18495                 if (unlikely(ret < 0))
18496                     goto exception;
18497                 *sp++ = JS_NewBool(ctx, ret);
18498             }
18499             BREAK;
18500 
18501         CASE(OP_to_object):
18502             if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) {
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                 ret_val = JS_ToPropertyKey(ctx, sp[-1]);
18519                 if (JS_IsException(ret_val))
18520                     goto exception;
18521                 JS_FreeValue(ctx, sp[-1]);
18522                 sp[-1] = ret_val;
18523                 break;
18524             }
18525             BREAK;
18526 
18527         CASE(OP_to_propkey2):
18528             /* must be tested first */
18529             if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) {
18530                 JS_ThrowTypeError(ctx, "value has no property");
18531                 goto exception;
18532             }
18533             switch (JS_VALUE_GET_TAG(sp[-1])) {
18534             case JS_TAG_INT:
18535             case JS_TAG_STRING:
18536             case JS_TAG_SYMBOL:
18537                 break;
18538             default:
18539                 ret_val = JS_ToPropertyKey(ctx, sp[-1]);
18540                 if (JS_IsException(ret_val))
18541                     goto exception;
18542                 JS_FreeValue(ctx, sp[-1]);
18543                 sp[-1] = ret_val;
18544                 break;
18545             }
18546             BREAK;
18547 #if 0
18548         CASE(OP_to_string):
18549             if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) {
18550                 ret_val = JS_ToString(ctx, sp[-1]);
18551                 if (JS_IsException(ret_val))
18552                     goto exception;
18553                 JS_FreeValue(ctx, sp[-1]);
18554                 sp[-1] = ret_val;
18555             }
18556             BREAK;
18557 #endif
18558         CASE(OP_with_get_var):
18559         CASE(OP_with_put_var):
18560         CASE(OP_with_delete_var):
18561         CASE(OP_with_make_ref):
18562         CASE(OP_with_get_ref):
18563         CASE(OP_with_get_ref_undef):
18564             {
18565                 JSAtom atom;
18566                 int32_t diff;
18567                 JSValue obj, val;
18568                 int ret, is_with;
18569                 atom = get_u32(pc);
18570                 diff = get_u32(pc + 4);
18571                 is_with = pc[8];
18572                 pc += 9;
18573 
18574                 obj = sp[-1];
18575                 ret = JS_HasProperty(ctx, obj, atom);
18576                 if (unlikely(ret < 0))
18577                     goto exception;
18578                 if (ret) {
18579                     if (is_with) {
18580                         ret = js_has_unscopable(ctx, obj, atom);
18581                         if (unlikely(ret < 0))
18582                             goto exception;
18583                         if (ret)
18584                             goto no_with;
18585                     }
18586                     switch (opcode) {
18587                     case OP_with_get_var:
18588                         val = JS_GetProperty(ctx, obj, atom);
18589                         if (unlikely(JS_IsException(val)))
18590                             goto exception;
18591                         set_value(ctx, &sp[-1], val);
18592                         break;
18593                     case OP_with_put_var:
18594                         /* XXX: check if strict mode */
18595                         ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2],
18596                                                      JS_PROP_THROW_STRICT);
18597                         JS_FreeValue(ctx, sp[-1]);
18598                         sp -= 2;
18599                         if (unlikely(ret < 0))
18600                             goto exception;
18601                         break;
18602                     case OP_with_delete_var:
18603                         ret = JS_DeleteProperty(ctx, obj, atom, 0);
18604                         if (unlikely(ret < 0))
18605                             goto exception;
18606                         JS_FreeValue(ctx, sp[-1]);
18607                         sp[-1] = JS_NewBool(ctx, ret);
18608                         break;
18609                     case OP_with_make_ref:
18610                         /* produce a pair object/propname on the stack */
18611                         *sp++ = JS_AtomToValue(ctx, atom);
18612                         break;
18613                     case OP_with_get_ref:
18614                         /* produce a pair object/method on the stack */
18615                         val = JS_GetProperty(ctx, obj, atom);
18616                         if (unlikely(JS_IsException(val)))
18617                             goto exception;
18618                         *sp++ = val;
18619                         break;
18620                     case OP_with_get_ref_undef:
18621                         /* produce a pair undefined/function on the stack */
18622                         val = JS_GetProperty(ctx, obj, atom);
18623                         if (unlikely(JS_IsException(val)))
18624                             goto exception;
18625                         JS_FreeValue(ctx, sp[-1]);
18626                         sp[-1] = JS_UNDEFINED;
18627                         *sp++ = val;
18628                         break;
18629                     }
18630                     pc += diff - 5;
18631                 } else {
18632                 no_with:
18633                     /* if not jumping, drop the object argument */
18634                     JS_FreeValue(ctx, sp[-1]);
18635                     sp--;
18636                 }
18637             }
18638             BREAK;
18639 
18640         CASE(OP_await):
18641             ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT);
18642             goto done_generator;
18643         CASE(OP_yield):
18644             ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD);
18645             goto done_generator;
18646         CASE(OP_yield_star):
18647         CASE(OP_async_yield_star):
18648             ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);
18649             goto done_generator;
18650         CASE(OP_return_async):
18651         CASE(OP_initial_yield):
18652             ret_val = JS_UNDEFINED;
18653             goto done_generator;
18654 
18655         CASE(OP_nop):
18656             BREAK;
18657         CASE(OP_is_undefined_or_null):
18658             if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED ||
18659                 JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
18660                 goto set_true;
18661             } else {
18662                 goto free_and_set_false;
18663             }
18664 #if SHORT_OPCODES
18665         CASE(OP_is_undefined):
18666             if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) {
18667                 goto set_true;
18668             } else {
18669                 goto free_and_set_false;
18670             }
18671         CASE(OP_is_null):
18672             if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
18673                 goto set_true;
18674             } else {
18675                 goto free_and_set_false;
18676             }
18677             /* XXX: could merge to a single opcode */
18678         CASE(OP_typeof_is_undefined):
18679             /* different from OP_is_undefined because of isHTMLDDA */
18680             if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) {
18681                 goto free_and_set_true;
18682             } else {
18683                 goto free_and_set_false;
18684             }
18685         CASE(OP_typeof_is_function):
18686             if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) {
18687                 goto free_and_set_true;
18688             } else {
18689                 goto free_and_set_false;
18690             }
18691         free_and_set_true:
18692             JS_FreeValue(ctx, sp[-1]);
18693 #endif
18694         set_true:
18695             sp[-1] = JS_TRUE;
18696             BREAK;
18697         free_and_set_false:
18698             JS_FreeValue(ctx, sp[-1]);
18699             sp[-1] = JS_FALSE;
18700             BREAK;
18701         CASE(OP_invalid):
18702         DEFAULT:
18703             JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x",
18704                                   (int)(pc - b->byte_code_buf - 1), opcode);
18705             goto exception;
18706         }
18707     }
18708  exception:
18709     if (is_backtrace_needed(ctx, rt->current_exception)) {
18710         /* add the backtrace information now (it is not done
18711            before if the exception happens in a bytecode
18712            operation */
18713         sf->cur_pc = pc;
18714         build_backtrace(ctx, rt->current_exception, NULL, 0, 0);
18715     }
18716     if (!JS_IsUncatchableError(ctx, rt->current_exception)) {
18717         while (sp > stack_buf) {
18718             JSValue val = *--sp;
18719             JS_FreeValue(ctx, val);
18720             if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) {
18721                 int pos = JS_VALUE_GET_INT(val);
18722                 if (pos == 0) {
18723                     /* enumerator: close it with a throw */
18724                     JS_FreeValue(ctx, sp[-1]); /* drop the next method */
18725                     sp--;
18726                     JS_IteratorClose(ctx, sp[-1], TRUE);
18727                 } else {
18728                     *sp++ = rt->current_exception;
18729                     rt->current_exception = JS_NULL;
18730                     pc = b->byte_code_buf + pos;
18731                     goto restart;
18732                 }
18733             }
18734         }
18735     }
18736     ret_val = JS_EXCEPTION;
18737     /* the local variables are freed by the caller in the generator
18738        case. Hence the label 'done' should never be reached in a
18739        generator function. */
18740     if (b->func_kind != JS_FUNC_NORMAL) {
18741     done_generator:
18742         sf->cur_pc = pc;
18743         sf->cur_sp = sp;
18744     } else {
18745     done:
18746         if (unlikely(!list_empty(&sf->var_ref_list))) {
18747             /* variable references reference the stack: must close them */
18748             close_var_refs(rt, sf);
18749         }
18750         /* free the local variables and stack */
18751         for(pval = local_buf; pval < sp; pval++) {
18752             JS_FreeValue(ctx, *pval);
18753         }
18754     }
18755     rt->current_stack_frame = sf->prev_frame;
18756     return ret_val;
18757 }
18758 
JS_Call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv)18759 JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,
18760                 int argc, JSValueConst *argv)
18761 {
18762     return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
18763                            argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
18764 }
18765 
JS_CallFree(JSContext * ctx,JSValue func_obj,JSValueConst this_obj,int argc,JSValueConst * argv)18766 static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
18767                            int argc, JSValueConst *argv)
18768 {
18769     JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
18770                                   argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
18771     JS_FreeValue(ctx, func_obj);
18772     return res;
18773 }
18774 
18775 /* warning: the refcount of the context is not incremented. Return
18776    NULL in case of exception (case of revoked proxy only) */
JS_GetFunctionRealm(JSContext * ctx,JSValueConst func_obj)18777 static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj)
18778 {
18779     JSObject *p;
18780     JSContext *realm;
18781 
18782     if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
18783         return ctx;
18784     p = JS_VALUE_GET_OBJ(func_obj);
18785     switch(p->class_id) {
18786     case JS_CLASS_C_FUNCTION:
18787         realm = p->u.cfunc.realm;
18788         break;
18789     case JS_CLASS_BYTECODE_FUNCTION:
18790     case JS_CLASS_GENERATOR_FUNCTION:
18791     case JS_CLASS_ASYNC_FUNCTION:
18792     case JS_CLASS_ASYNC_GENERATOR_FUNCTION:
18793         {
18794             JSFunctionBytecode *b;
18795             b = p->u.func.function_bytecode;
18796             realm = b->realm;
18797         }
18798         break;
18799     case JS_CLASS_PROXY:
18800         {
18801             JSProxyData *s = p->u.opaque;
18802             if (!s)
18803                 return ctx;
18804             if (s->is_revoked) {
18805                 JS_ThrowTypeErrorRevokedProxy(ctx);
18806                 return NULL;
18807             } else {
18808                 realm = JS_GetFunctionRealm(ctx, s->target);
18809             }
18810         }
18811         break;
18812     case JS_CLASS_BOUND_FUNCTION:
18813         {
18814             JSBoundFunction *bf = p->u.bound_function;
18815             realm = JS_GetFunctionRealm(ctx, bf->func_obj);
18816         }
18817         break;
18818     default:
18819         realm = ctx;
18820         break;
18821     }
18822     return realm;
18823 }
18824 
js_create_from_ctor(JSContext * ctx,JSValueConst ctor,int class_id)18825 static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor,
18826                                    int class_id)
18827 {
18828     JSValue proto, obj;
18829     JSContext *realm;
18830 
18831     if (JS_IsUndefined(ctor)) {
18832         proto = JS_DupValue(ctx, ctx->class_proto[class_id]);
18833     } else {
18834         proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
18835         if (JS_IsException(proto))
18836             return proto;
18837         if (!JS_IsObject(proto)) {
18838             JS_FreeValue(ctx, proto);
18839             realm = JS_GetFunctionRealm(ctx, ctor);
18840             if (!realm)
18841                 return JS_EXCEPTION;
18842             proto = JS_DupValue(ctx, realm->class_proto[class_id]);
18843         }
18844     }
18845     obj = JS_NewObjectProtoClass(ctx, proto, class_id);
18846     JS_FreeValue(ctx, proto);
18847     return obj;
18848 }
18849 
18850 /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
JS_CallConstructorInternal(JSContext * ctx,JSValueConst func_obj,JSValueConst new_target,int argc,JSValue * argv,int flags)18851 static JSValue JS_CallConstructorInternal(JSContext *ctx,
18852                                           JSValueConst func_obj,
18853                                           JSValueConst new_target,
18854                                           int argc, JSValue *argv, int flags)
18855 {
18856     JSObject *p;
18857     JSFunctionBytecode *b;
18858 
18859     if (js_poll_interrupts(ctx))
18860         return JS_EXCEPTION;
18861     flags |= JS_CALL_FLAG_CONSTRUCTOR;
18862     if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT))
18863         goto not_a_function;
18864     p = JS_VALUE_GET_OBJ(func_obj);
18865     if (unlikely(!p->is_constructor))
18866         return JS_ThrowTypeError(ctx, "not a constructor");
18867     if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
18868         JSClassCall *call_func;
18869         call_func = ctx->rt->class_array[p->class_id].call;
18870         if (!call_func) {
18871         not_a_function:
18872             return JS_ThrowTypeError(ctx, "not a function");
18873         }
18874         return call_func(ctx, func_obj, new_target, argc,
18875                          (JSValueConst *)argv, flags);
18876     }
18877 
18878     b = p->u.func.function_bytecode;
18879     if (b->is_derived_class_constructor) {
18880         return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags);
18881     } else {
18882         JSValue obj, ret;
18883         /* legacy constructor behavior */
18884         obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
18885         if (JS_IsException(obj))
18886             return JS_EXCEPTION;
18887         ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags);
18888         if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT ||
18889             JS_IsException(ret)) {
18890             JS_FreeValue(ctx, obj);
18891             return ret;
18892         } else {
18893             JS_FreeValue(ctx, ret);
18894             return obj;
18895         }
18896     }
18897 }
18898 
JS_CallConstructor2(JSContext * ctx,JSValueConst func_obj,JSValueConst new_target,int argc,JSValueConst * argv)18899 JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
18900                             JSValueConst new_target,
18901                             int argc, JSValueConst *argv)
18902 {
18903     return JS_CallConstructorInternal(ctx, func_obj, new_target,
18904                                       argc, (JSValue *)argv,
18905                                       JS_CALL_FLAG_COPY_ARGV);
18906 }
18907 
JS_CallConstructor(JSContext * ctx,JSValueConst func_obj,int argc,JSValueConst * argv)18908 JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,
18909                            int argc, JSValueConst *argv)
18910 {
18911     return JS_CallConstructorInternal(ctx, func_obj, func_obj,
18912                                       argc, (JSValue *)argv,
18913                                       JS_CALL_FLAG_COPY_ARGV);
18914 }
18915 
JS_Invoke(JSContext * ctx,JSValueConst this_val,JSAtom atom,int argc,JSValueConst * argv)18916 JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,
18917                   int argc, JSValueConst *argv)
18918 {
18919     JSValue func_obj;
18920     func_obj = JS_GetProperty(ctx, this_val, atom);
18921     if (JS_IsException(func_obj))
18922         return func_obj;
18923     return JS_CallFree(ctx, func_obj, this_val, argc, argv);
18924 }
18925 
JS_InvokeFree(JSContext * ctx,JSValue this_val,JSAtom atom,int argc,JSValueConst * argv)18926 static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
18927                              int argc, JSValueConst *argv)
18928 {
18929     JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv);
18930     JS_FreeValue(ctx, this_val);
18931     return res;
18932 }
18933 
18934 /* JSAsyncFunctionState (used by generator and async functions) */
async_func_init(JSContext * ctx,JSAsyncFunctionState * s,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv)18935 static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
18936                                        JSValueConst func_obj, JSValueConst this_obj,
18937                                        int argc, JSValueConst *argv)
18938 {
18939     JSObject *p;
18940     JSFunctionBytecode *b;
18941     JSStackFrame *sf;
18942     int local_count, i, arg_buf_len, n;
18943 
18944     sf = &s->frame;
18945     init_list_head(&sf->var_ref_list);
18946     p = JS_VALUE_GET_OBJ(func_obj);
18947     b = p->u.func.function_bytecode;
18948     sf->js_mode = b->js_mode;
18949     sf->cur_pc = b->byte_code_buf;
18950     arg_buf_len = max_int(b->arg_count, argc);
18951     local_count = arg_buf_len + b->var_count + b->stack_size;
18952     sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));
18953     if (!sf->arg_buf)
18954         return -1;
18955     sf->cur_func = JS_DupValue(ctx, func_obj);
18956     s->this_val = JS_DupValue(ctx, this_obj);
18957     s->argc = argc;
18958     sf->arg_count = arg_buf_len;
18959     sf->var_buf = sf->arg_buf + arg_buf_len;
18960     sf->cur_sp = sf->var_buf + b->var_count;
18961     for(i = 0; i < argc; i++)
18962         sf->arg_buf[i] = JS_DupValue(ctx, argv[i]);
18963     n = arg_buf_len + b->var_count;
18964     for(i = argc; i < n; i++)
18965         sf->arg_buf[i] = JS_UNDEFINED;
18966     return 0;
18967 }
18968 
async_func_mark(JSRuntime * rt,JSAsyncFunctionState * s,JS_MarkFunc * mark_func)18969 static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,
18970                             JS_MarkFunc *mark_func)
18971 {
18972     JSStackFrame *sf;
18973     JSValue *sp;
18974 
18975     sf = &s->frame;
18976     JS_MarkValue(rt, sf->cur_func, mark_func);
18977     JS_MarkValue(rt, s->this_val, mark_func);
18978     if (sf->cur_sp) {
18979         /* if the function is running, cur_sp is not known so we
18980            cannot mark the stack. Marking the variables is not needed
18981            because a running function cannot be part of a removable
18982            cycle */
18983         for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)
18984             JS_MarkValue(rt, *sp, mark_func);
18985     }
18986 }
18987 
async_func_free(JSRuntime * rt,JSAsyncFunctionState * s)18988 static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
18989 {
18990     JSStackFrame *sf;
18991     JSValue *sp;
18992 
18993     sf = &s->frame;
18994 
18995     /* close the closure variables. */
18996     close_var_refs(rt, sf);
18997 
18998     if (sf->arg_buf) {
18999         /* cannot free the function if it is running */
19000         assert(sf->cur_sp != NULL);
19001         for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {
19002             JS_FreeValueRT(rt, *sp);
19003         }
19004         js_free_rt(rt, sf->arg_buf);
19005     }
19006     JS_FreeValueRT(rt, sf->cur_func);
19007     JS_FreeValueRT(rt, s->this_val);
19008 }
19009 
async_func_resume(JSContext * ctx,JSAsyncFunctionState * s)19010 static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)
19011 {
19012     JSValue func_obj;
19013 
19014     if (js_check_stack_overflow(ctx->rt, 0))
19015         return JS_ThrowStackOverflow(ctx);
19016 
19017     /* the tag does not matter provided it is not an object */
19018     func_obj = JS_MKPTR(JS_TAG_INT, s);
19019     return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
19020                            s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR);
19021 }
19022 
19023 
19024 /* Generators */
19025 
19026 typedef enum JSGeneratorStateEnum {
19027     JS_GENERATOR_STATE_SUSPENDED_START,
19028     JS_GENERATOR_STATE_SUSPENDED_YIELD,
19029     JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
19030     JS_GENERATOR_STATE_EXECUTING,
19031     JS_GENERATOR_STATE_COMPLETED,
19032 } JSGeneratorStateEnum;
19033 
19034 typedef struct JSGeneratorData {
19035     JSGeneratorStateEnum state;
19036     JSAsyncFunctionState func_state;
19037 } JSGeneratorData;
19038 
free_generator_stack_rt(JSRuntime * rt,JSGeneratorData * s)19039 static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)
19040 {
19041     if (s->state == JS_GENERATOR_STATE_COMPLETED)
19042         return;
19043     async_func_free(rt, &s->func_state);
19044     s->state = JS_GENERATOR_STATE_COMPLETED;
19045 }
19046 
js_generator_finalizer(JSRuntime * rt,JSValue obj)19047 static void js_generator_finalizer(JSRuntime *rt, JSValue obj)
19048 {
19049     JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR);
19050 
19051     if (s) {
19052         free_generator_stack_rt(rt, s);
19053         js_free_rt(rt, s);
19054     }
19055 }
19056 
free_generator_stack(JSContext * ctx,JSGeneratorData * s)19057 static void free_generator_stack(JSContext *ctx, JSGeneratorData *s)
19058 {
19059     free_generator_stack_rt(ctx->rt, s);
19060 }
19061 
js_generator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)19062 static void js_generator_mark(JSRuntime *rt, JSValueConst val,
19063                               JS_MarkFunc *mark_func)
19064 {
19065     JSObject *p = JS_VALUE_GET_OBJ(val);
19066     JSGeneratorData *s = p->u.generator_data;
19067 
19068     if (!s || s->state == JS_GENERATOR_STATE_COMPLETED)
19069         return;
19070     async_func_mark(rt, &s->func_state, mark_func);
19071 }
19072 
19073 /* XXX: use enum */
19074 #define GEN_MAGIC_NEXT   0
19075 #define GEN_MAGIC_RETURN 1
19076 #define GEN_MAGIC_THROW  2
19077 
js_generator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,BOOL * pdone,int magic)19078 static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
19079                                  int argc, JSValueConst *argv,
19080                                  BOOL *pdone, int magic)
19081 {
19082     JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR);
19083     JSStackFrame *sf;
19084     JSValue ret, func_ret;
19085 
19086     *pdone = TRUE;
19087     if (!s)
19088         return JS_ThrowTypeError(ctx, "not a generator");
19089     sf = &s->func_state.frame;
19090     switch(s->state) {
19091     default:
19092     case JS_GENERATOR_STATE_SUSPENDED_START:
19093         if (magic == GEN_MAGIC_NEXT) {
19094             goto exec_no_arg;
19095         } else {
19096             free_generator_stack(ctx, s);
19097             goto done;
19098         }
19099         break;
19100     case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
19101     case JS_GENERATOR_STATE_SUSPENDED_YIELD:
19102         /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */
19103         ret = JS_DupValue(ctx, argv[0]);
19104         if (magic == GEN_MAGIC_THROW &&
19105             s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {
19106             JS_Throw(ctx, ret);
19107             s->func_state.throw_flag = TRUE;
19108         } else {
19109             sf->cur_sp[-1] = ret;
19110             sf->cur_sp[0] = JS_NewInt32(ctx, magic);
19111             sf->cur_sp++;
19112         exec_no_arg:
19113             s->func_state.throw_flag = FALSE;
19114         }
19115         s->state = JS_GENERATOR_STATE_EXECUTING;
19116         func_ret = async_func_resume(ctx, &s->func_state);
19117         s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;
19118         if (JS_IsException(func_ret)) {
19119             /* finalize the execution in case of exception */
19120             free_generator_stack(ctx, s);
19121             return func_ret;
19122         }
19123         if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
19124             /* get the returned yield value at the top of the stack */
19125             ret = sf->cur_sp[-1];
19126             sf->cur_sp[-1] = JS_UNDEFINED;
19127             if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) {
19128                 s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
19129                 /* return (value, done) object */
19130                 *pdone = 2;
19131             } else {
19132                 *pdone = FALSE;
19133             }
19134         } else {
19135             /* end of iterator */
19136             ret = sf->cur_sp[-1];
19137             sf->cur_sp[-1] = JS_UNDEFINED;
19138             JS_FreeValue(ctx, func_ret);
19139             free_generator_stack(ctx, s);
19140         }
19141         break;
19142     case JS_GENERATOR_STATE_COMPLETED:
19143     done:
19144         /* execution is finished */
19145         switch(magic) {
19146         default:
19147         case GEN_MAGIC_NEXT:
19148             ret = JS_UNDEFINED;
19149             break;
19150         case GEN_MAGIC_RETURN:
19151             ret = JS_DupValue(ctx, argv[0]);
19152             break;
19153         case GEN_MAGIC_THROW:
19154             ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0]));
19155             break;
19156         }
19157         break;
19158     case JS_GENERATOR_STATE_EXECUTING:
19159         ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator");
19160         break;
19161     }
19162     return ret;
19163 }
19164 
js_generator_function_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)19165 static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
19166                                           JSValueConst this_obj,
19167                                           int argc, JSValueConst *argv,
19168                                           int flags)
19169 {
19170     JSValue obj, func_ret;
19171     JSGeneratorData *s;
19172 
19173     s = js_mallocz(ctx, sizeof(*s));
19174     if (!s)
19175         return JS_EXCEPTION;
19176     s->state = JS_GENERATOR_STATE_SUSPENDED_START;
19177     if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
19178         s->state = JS_GENERATOR_STATE_COMPLETED;
19179         goto fail;
19180     }
19181 
19182     /* execute the function up to 'OP_initial_yield' */
19183     func_ret = async_func_resume(ctx, &s->func_state);
19184     if (JS_IsException(func_ret))
19185         goto fail;
19186     JS_FreeValue(ctx, func_ret);
19187 
19188     obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR);
19189     if (JS_IsException(obj))
19190         goto fail;
19191     JS_SetOpaque(obj, s);
19192     return obj;
19193  fail:
19194     free_generator_stack_rt(ctx->rt, s);
19195     js_free(ctx, s);
19196     return JS_EXCEPTION;
19197 }
19198 
19199 /* AsyncFunction */
19200 
js_async_function_terminate(JSRuntime * rt,JSAsyncFunctionData * s)19201 static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s)
19202 {
19203     if (s->is_active) {
19204         async_func_free(rt, &s->func_state);
19205         s->is_active = FALSE;
19206     }
19207 }
19208 
js_async_function_free0(JSRuntime * rt,JSAsyncFunctionData * s)19209 static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s)
19210 {
19211     js_async_function_terminate(rt, s);
19212     JS_FreeValueRT(rt, s->resolving_funcs[0]);
19213     JS_FreeValueRT(rt, s->resolving_funcs[1]);
19214     remove_gc_object(&s->header);
19215     js_free_rt(rt, s);
19216 }
19217 
js_async_function_free(JSRuntime * rt,JSAsyncFunctionData * s)19218 static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s)
19219 {
19220     if (--s->header.ref_count == 0) {
19221         js_async_function_free0(rt, s);
19222     }
19223 }
19224 
js_async_function_resolve_finalizer(JSRuntime * rt,JSValue val)19225 static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val)
19226 {
19227     JSObject *p = JS_VALUE_GET_OBJ(val);
19228     JSAsyncFunctionData *s = p->u.async_function_data;
19229     if (s) {
19230         js_async_function_free(rt, s);
19231     }
19232 }
19233 
js_async_function_resolve_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)19234 static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
19235                                            JS_MarkFunc *mark_func)
19236 {
19237     JSObject *p = JS_VALUE_GET_OBJ(val);
19238     JSAsyncFunctionData *s = p->u.async_function_data;
19239     if (s) {
19240         mark_func(rt, &s->header);
19241     }
19242 }
19243 
js_async_function_resolve_create(JSContext * ctx,JSAsyncFunctionData * s,JSValue * resolving_funcs)19244 static int js_async_function_resolve_create(JSContext *ctx,
19245                                             JSAsyncFunctionData *s,
19246                                             JSValue *resolving_funcs)
19247 {
19248     int i;
19249     JSObject *p;
19250 
19251     for(i = 0; i < 2; i++) {
19252         resolving_funcs[i] =
19253             JS_NewObjectProtoClass(ctx, ctx->function_proto,
19254                                    JS_CLASS_ASYNC_FUNCTION_RESOLVE + i);
19255         if (JS_IsException(resolving_funcs[i])) {
19256             if (i == 1)
19257                 JS_FreeValue(ctx, resolving_funcs[0]);
19258             return -1;
19259         }
19260         p = JS_VALUE_GET_OBJ(resolving_funcs[i]);
19261         s->header.ref_count++;
19262         p->u.async_function_data = s;
19263     }
19264     return 0;
19265 }
19266 
js_async_function_resume(JSContext * ctx,JSAsyncFunctionData * s)19267 static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)
19268 {
19269     JSValue func_ret, ret2;
19270 
19271     func_ret = async_func_resume(ctx, &s->func_state);
19272     if (JS_IsException(func_ret)) {
19273         JSValue error;
19274     fail:
19275         error = JS_GetException(ctx);
19276         ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
19277                        1, (JSValueConst *)&error);
19278         JS_FreeValue(ctx, error);
19279         js_async_function_terminate(ctx->rt, s);
19280         JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
19281     } else {
19282         JSValue value;
19283         value = s->func_state.frame.cur_sp[-1];
19284         s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
19285         if (JS_IsUndefined(func_ret)) {
19286             /* function returned */
19287             ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
19288                            1, (JSValueConst *)&value);
19289             JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
19290             JS_FreeValue(ctx, value);
19291             js_async_function_terminate(ctx->rt, s);
19292         } else {
19293             JSValue promise, resolving_funcs[2], resolving_funcs1[2];
19294             int i, res;
19295 
19296             /* await */
19297             JS_FreeValue(ctx, func_ret); /* not used */
19298             promise = js_promise_resolve(ctx, ctx->promise_ctor,
19299                                          1, (JSValueConst *)&value, 0);
19300             JS_FreeValue(ctx, value);
19301             if (JS_IsException(promise))
19302                 goto fail;
19303             if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {
19304                 JS_FreeValue(ctx, promise);
19305                 goto fail;
19306             }
19307 
19308             /* Note: no need to create 'thrownawayCapability' as in
19309                the spec */
19310             for(i = 0; i < 2; i++)
19311                 resolving_funcs1[i] = JS_UNDEFINED;
19312             res = perform_promise_then(ctx, promise,
19313                                        (JSValueConst *)resolving_funcs,
19314                                        (JSValueConst *)resolving_funcs1);
19315             JS_FreeValue(ctx, promise);
19316             for(i = 0; i < 2; i++)
19317                 JS_FreeValue(ctx, resolving_funcs[i]);
19318             if (res)
19319                 goto fail;
19320         }
19321     }
19322 }
19323 
js_async_function_resolve_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)19324 static JSValue js_async_function_resolve_call(JSContext *ctx,
19325                                               JSValueConst func_obj,
19326                                               JSValueConst this_obj,
19327                                               int argc, JSValueConst *argv,
19328                                               int flags)
19329 {
19330     JSObject *p = JS_VALUE_GET_OBJ(func_obj);
19331     JSAsyncFunctionData *s = p->u.async_function_data;
19332     BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;
19333     JSValueConst arg;
19334 
19335     if (argc > 0)
19336         arg = argv[0];
19337     else
19338         arg = JS_UNDEFINED;
19339     s->func_state.throw_flag = is_reject;
19340     if (is_reject) {
19341         JS_Throw(ctx, JS_DupValue(ctx, arg));
19342     } else {
19343         /* return value of await */
19344         s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);
19345     }
19346     js_async_function_resume(ctx, s);
19347     return JS_UNDEFINED;
19348 }
19349 
js_async_function_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)19350 static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,
19351                                       JSValueConst this_obj,
19352                                       int argc, JSValueConst *argv, int flags)
19353 {
19354     JSValue promise;
19355     JSAsyncFunctionData *s;
19356 
19357     s = js_mallocz(ctx, sizeof(*s));
19358     if (!s)
19359         return JS_EXCEPTION;
19360     s->header.ref_count = 1;
19361     add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
19362     s->is_active = FALSE;
19363     s->resolving_funcs[0] = JS_UNDEFINED;
19364     s->resolving_funcs[1] = JS_UNDEFINED;
19365 
19366     promise = JS_NewPromiseCapability(ctx, s->resolving_funcs);
19367     if (JS_IsException(promise))
19368         goto fail;
19369 
19370     if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
19371     fail:
19372         JS_FreeValue(ctx, promise);
19373         js_async_function_free(ctx->rt, s);
19374         return JS_EXCEPTION;
19375     }
19376     s->is_active = TRUE;
19377 
19378     js_async_function_resume(ctx, s);
19379 
19380     js_async_function_free(ctx->rt, s);
19381 
19382     return promise;
19383 }
19384 
19385 /* AsyncGenerator */
19386 
19387 typedef enum JSAsyncGeneratorStateEnum {
19388     JS_ASYNC_GENERATOR_STATE_SUSPENDED_START,
19389     JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD,
19390     JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
19391     JS_ASYNC_GENERATOR_STATE_EXECUTING,
19392     JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN,
19393     JS_ASYNC_GENERATOR_STATE_COMPLETED,
19394 } JSAsyncGeneratorStateEnum;
19395 
19396 typedef struct JSAsyncGeneratorRequest {
19397     struct list_head link;
19398     /* completion */
19399     int completion_type; /* GEN_MAGIC_x */
19400     JSValue result;
19401     /* promise capability */
19402     JSValue promise;
19403     JSValue resolving_funcs[2];
19404 } JSAsyncGeneratorRequest;
19405 
19406 typedef struct JSAsyncGeneratorData {
19407     JSObject *generator; /* back pointer to the object (const) */
19408     JSAsyncGeneratorStateEnum state;
19409     JSAsyncFunctionState func_state;
19410     struct list_head queue; /* list of JSAsyncGeneratorRequest.link */
19411 } JSAsyncGeneratorData;
19412 
js_async_generator_free(JSRuntime * rt,JSAsyncGeneratorData * s)19413 static void js_async_generator_free(JSRuntime *rt,
19414                                     JSAsyncGeneratorData *s)
19415 {
19416     struct list_head *el, *el1;
19417     JSAsyncGeneratorRequest *req;
19418 
19419     list_for_each_safe(el, el1, &s->queue) {
19420         req = list_entry(el, JSAsyncGeneratorRequest, link);
19421         JS_FreeValueRT(rt, req->result);
19422         JS_FreeValueRT(rt, req->promise);
19423         JS_FreeValueRT(rt, req->resolving_funcs[0]);
19424         JS_FreeValueRT(rt, req->resolving_funcs[1]);
19425         js_free_rt(rt, req);
19426     }
19427     if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
19428         s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
19429         async_func_free(rt, &s->func_state);
19430     }
19431     js_free_rt(rt, s);
19432 }
19433 
js_async_generator_finalizer(JSRuntime * rt,JSValue obj)19434 static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj)
19435 {
19436     JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR);
19437 
19438     if (s) {
19439         js_async_generator_free(rt, s);
19440     }
19441 }
19442 
js_async_generator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)19443 static void js_async_generator_mark(JSRuntime *rt, JSValueConst val,
19444                                     JS_MarkFunc *mark_func)
19445 {
19446     JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR);
19447     struct list_head *el;
19448     JSAsyncGeneratorRequest *req;
19449     if (s) {
19450         list_for_each(el, &s->queue) {
19451             req = list_entry(el, JSAsyncGeneratorRequest, link);
19452             JS_MarkValue(rt, req->result, mark_func);
19453             JS_MarkValue(rt, req->promise, mark_func);
19454             JS_MarkValue(rt, req->resolving_funcs[0], mark_func);
19455             JS_MarkValue(rt, req->resolving_funcs[1], mark_func);
19456         }
19457         if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
19458             s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
19459             async_func_mark(rt, &s->func_state, mark_func);
19460         }
19461     }
19462 }
19463 
19464 static JSValue js_async_generator_resolve_function(JSContext *ctx,
19465                                           JSValueConst this_obj,
19466                                           int argc, JSValueConst *argv,
19467                                           int magic, JSValue *func_data);
19468 
js_async_generator_resolve_function_create(JSContext * ctx,JSValueConst generator,JSValue * resolving_funcs,BOOL is_resume_next)19469 static int js_async_generator_resolve_function_create(JSContext *ctx,
19470                                                       JSValueConst generator,
19471                                                       JSValue *resolving_funcs,
19472                                                       BOOL is_resume_next)
19473 {
19474     int i;
19475     JSValue func;
19476 
19477     for(i = 0; i < 2; i++) {
19478         func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1,
19479                                    i + is_resume_next * 2, 1, &generator);
19480         if (JS_IsException(func)) {
19481             if (i == 1)
19482                 JS_FreeValue(ctx, resolving_funcs[0]);
19483             return -1;
19484         }
19485         resolving_funcs[i] = func;
19486     }
19487     return 0;
19488 }
19489 
js_async_generator_await(JSContext * ctx,JSAsyncGeneratorData * s,JSValueConst value)19490 static int js_async_generator_await(JSContext *ctx,
19491                                     JSAsyncGeneratorData *s,
19492                                     JSValueConst value)
19493 {
19494     JSValue promise, resolving_funcs[2], resolving_funcs1[2];
19495     int i, res;
19496 
19497     promise = js_promise_resolve(ctx, ctx->promise_ctor,
19498                                  1, &value, 0);
19499     if (JS_IsException(promise))
19500         goto fail;
19501 
19502     if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator),
19503                                                    resolving_funcs, FALSE)) {
19504         JS_FreeValue(ctx, promise);
19505         goto fail;
19506     }
19507 
19508     /* Note: no need to create 'thrownawayCapability' as in
19509        the spec */
19510     for(i = 0; i < 2; i++)
19511         resolving_funcs1[i] = JS_UNDEFINED;
19512     res = perform_promise_then(ctx, promise,
19513                                (JSValueConst *)resolving_funcs,
19514                                (JSValueConst *)resolving_funcs1);
19515     JS_FreeValue(ctx, promise);
19516     for(i = 0; i < 2; i++)
19517         JS_FreeValue(ctx, resolving_funcs[i]);
19518     if (res)
19519         goto fail;
19520     return 0;
19521  fail:
19522     return -1;
19523 }
19524 
js_async_generator_resolve_or_reject(JSContext * ctx,JSAsyncGeneratorData * s,JSValueConst result,int is_reject)19525 static void js_async_generator_resolve_or_reject(JSContext *ctx,
19526                                                  JSAsyncGeneratorData *s,
19527                                                  JSValueConst result,
19528                                                  int is_reject)
19529 {
19530     JSAsyncGeneratorRequest *next;
19531     JSValue ret;
19532 
19533     next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
19534     list_del(&next->link);
19535     ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1,
19536                   &result);
19537     JS_FreeValue(ctx, ret);
19538     JS_FreeValue(ctx, next->result);
19539     JS_FreeValue(ctx, next->promise);
19540     JS_FreeValue(ctx, next->resolving_funcs[0]);
19541     JS_FreeValue(ctx, next->resolving_funcs[1]);
19542     js_free(ctx, next);
19543 }
19544 
js_async_generator_resolve(JSContext * ctx,JSAsyncGeneratorData * s,JSValueConst value,BOOL done)19545 static void js_async_generator_resolve(JSContext *ctx,
19546                                        JSAsyncGeneratorData *s,
19547                                        JSValueConst value,
19548                                        BOOL done)
19549 {
19550     JSValue result;
19551     result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done);
19552     /* XXX: better exception handling ? */
19553     js_async_generator_resolve_or_reject(ctx, s, result, 0);
19554     JS_FreeValue(ctx, result);
19555  }
19556 
js_async_generator_reject(JSContext * ctx,JSAsyncGeneratorData * s,JSValueConst exception)19557 static void js_async_generator_reject(JSContext *ctx,
19558                                        JSAsyncGeneratorData *s,
19559                                        JSValueConst exception)
19560 {
19561     js_async_generator_resolve_or_reject(ctx, s, exception, 1);
19562 }
19563 
js_async_generator_complete(JSContext * ctx,JSAsyncGeneratorData * s)19564 static void js_async_generator_complete(JSContext *ctx,
19565                                         JSAsyncGeneratorData *s)
19566 {
19567     if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {
19568         s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
19569         async_func_free(ctx->rt, &s->func_state);
19570     }
19571 }
19572 
js_async_generator_completed_return(JSContext * ctx,JSAsyncGeneratorData * s,JSValueConst value)19573 static int js_async_generator_completed_return(JSContext *ctx,
19574                                                JSAsyncGeneratorData *s,
19575                                                JSValueConst value)
19576 {
19577     JSValue promise, resolving_funcs[2], resolving_funcs1[2];
19578     int res;
19579 
19580     promise = js_promise_resolve(ctx, ctx->promise_ctor,
19581                                  1, (JSValueConst *)&value, 0);
19582     if (JS_IsException(promise))
19583         return -1;
19584     if (js_async_generator_resolve_function_create(ctx,
19585                                                    JS_MKPTR(JS_TAG_OBJECT, s->generator),
19586                                                    resolving_funcs1,
19587                                                    TRUE)) {
19588         JS_FreeValue(ctx, promise);
19589         return -1;
19590     }
19591     resolving_funcs[0] = JS_UNDEFINED;
19592     resolving_funcs[1] = JS_UNDEFINED;
19593     res = perform_promise_then(ctx, promise,
19594                                (JSValueConst *)resolving_funcs1,
19595                                (JSValueConst *)resolving_funcs);
19596     JS_FreeValue(ctx, resolving_funcs1[0]);
19597     JS_FreeValue(ctx, resolving_funcs1[1]);
19598     JS_FreeValue(ctx, promise);
19599     return res;
19600 }
19601 
js_async_generator_resume_next(JSContext * ctx,JSAsyncGeneratorData * s)19602 static void js_async_generator_resume_next(JSContext *ctx,
19603                                            JSAsyncGeneratorData *s)
19604 {
19605     JSAsyncGeneratorRequest *next;
19606     JSValue func_ret, value;
19607 
19608     for(;;) {
19609         if (list_empty(&s->queue))
19610             break;
19611         next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
19612         switch(s->state) {
19613         case JS_ASYNC_GENERATOR_STATE_EXECUTING:
19614             /* only happens when restarting execution after await() */
19615             goto resume_exec;
19616         case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN:
19617             goto done;
19618         case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START:
19619             if (next->completion_type == GEN_MAGIC_NEXT) {
19620                 goto exec_no_arg;
19621             } else {
19622                 js_async_generator_complete(ctx, s);
19623             }
19624             break;
19625         case JS_ASYNC_GENERATOR_STATE_COMPLETED:
19626             if (next->completion_type == GEN_MAGIC_NEXT) {
19627                 js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE);
19628             } else if (next->completion_type == GEN_MAGIC_RETURN) {
19629                 s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;
19630                 js_async_generator_completed_return(ctx, s, next->result);
19631                 goto done;
19632             } else {
19633                 js_async_generator_reject(ctx, s, next->result);
19634             }
19635             goto done;
19636         case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD:
19637         case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
19638             value = JS_DupValue(ctx, next->result);
19639             if (next->completion_type == GEN_MAGIC_THROW &&
19640                 s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {
19641                 JS_Throw(ctx, value);
19642                 s->func_state.throw_flag = TRUE;
19643             } else {
19644                 /* 'yield' returns a value. 'yield *' also returns a value
19645                    in case the 'throw' method is called */
19646                 s->func_state.frame.cur_sp[-1] = value;
19647                 s->func_state.frame.cur_sp[0] =
19648                     JS_NewInt32(ctx, next->completion_type);
19649                 s->func_state.frame.cur_sp++;
19650             exec_no_arg:
19651                 s->func_state.throw_flag = FALSE;
19652             }
19653             s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;
19654         resume_exec:
19655             func_ret = async_func_resume(ctx, &s->func_state);
19656             if (JS_IsException(func_ret)) {
19657                 value = JS_GetException(ctx);
19658                 js_async_generator_complete(ctx, s);
19659                 js_async_generator_reject(ctx, s, value);
19660                 JS_FreeValue(ctx, value);
19661             } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
19662                 int func_ret_code;
19663                 value = s->func_state.frame.cur_sp[-1];
19664                 s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
19665                 func_ret_code = JS_VALUE_GET_INT(func_ret);
19666                 switch(func_ret_code) {
19667                 case FUNC_RET_YIELD:
19668                 case FUNC_RET_YIELD_STAR:
19669                     if (func_ret_code == FUNC_RET_YIELD_STAR)
19670                         s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
19671                     else
19672                         s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD;
19673                     js_async_generator_resolve(ctx, s, value, FALSE);
19674                     JS_FreeValue(ctx, value);
19675                     break;
19676                 case FUNC_RET_AWAIT:
19677                     js_async_generator_await(ctx, s, value);
19678                     JS_FreeValue(ctx, value);
19679                     goto done;
19680                 default:
19681                     abort();
19682                 }
19683             } else {
19684                 assert(JS_IsUndefined(func_ret));
19685                 /* end of function */
19686                 value = s->func_state.frame.cur_sp[-1];
19687                 s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
19688                 js_async_generator_complete(ctx, s);
19689                 js_async_generator_resolve(ctx, s, value, TRUE);
19690                 JS_FreeValue(ctx, value);
19691             }
19692             break;
19693         default:
19694             abort();
19695         }
19696     }
19697  done: ;
19698 }
19699 
js_async_generator_resolve_function(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv,int magic,JSValue * func_data)19700 static JSValue js_async_generator_resolve_function(JSContext *ctx,
19701                                                    JSValueConst this_obj,
19702                                                    int argc, JSValueConst *argv,
19703                                                    int magic, JSValue *func_data)
19704 {
19705     BOOL is_reject = magic & 1;
19706     JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR);
19707     JSValueConst arg = argv[0];
19708 
19709     /* XXX: what if s == NULL */
19710 
19711     if (magic >= 2) {
19712         /* resume next case in AWAITING_RETURN state */
19713         assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN ||
19714                s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED);
19715         s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
19716         if (is_reject) {
19717             js_async_generator_reject(ctx, s, arg);
19718         } else {
19719             js_async_generator_resolve(ctx, s, arg, TRUE);
19720         }
19721     } else {
19722         /* restart function execution after await() */
19723         assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);
19724         s->func_state.throw_flag = is_reject;
19725         if (is_reject) {
19726             JS_Throw(ctx, JS_DupValue(ctx, arg));
19727         } else {
19728             /* return value of await */
19729             s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);
19730         }
19731         js_async_generator_resume_next(ctx, s);
19732     }
19733     return JS_UNDEFINED;
19734 }
19735 
19736 /* magic = GEN_MAGIC_x */
js_async_generator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)19737 static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val,
19738                                        int argc, JSValueConst *argv,
19739                                        int magic)
19740 {
19741     JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR);
19742     JSValue promise, resolving_funcs[2];
19743     JSAsyncGeneratorRequest *req;
19744 
19745     promise = JS_NewPromiseCapability(ctx, resolving_funcs);
19746     if (JS_IsException(promise))
19747         return JS_EXCEPTION;
19748     if (!s) {
19749         JSValue err, res2;
19750         JS_ThrowTypeError(ctx, "not an AsyncGenerator object");
19751         err = JS_GetException(ctx);
19752         res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
19753                        1, (JSValueConst *)&err);
19754         JS_FreeValue(ctx, err);
19755         JS_FreeValue(ctx, res2);
19756         JS_FreeValue(ctx, resolving_funcs[0]);
19757         JS_FreeValue(ctx, resolving_funcs[1]);
19758         return promise;
19759     }
19760     req = js_mallocz(ctx, sizeof(*req));
19761     if (!req)
19762         goto fail;
19763     req->completion_type = magic;
19764     req->result = JS_DupValue(ctx, argv[0]);
19765     req->promise = JS_DupValue(ctx, promise);
19766     req->resolving_funcs[0] = resolving_funcs[0];
19767     req->resolving_funcs[1] = resolving_funcs[1];
19768     list_add_tail(&req->link, &s->queue);
19769     if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) {
19770         js_async_generator_resume_next(ctx, s);
19771     }
19772     return promise;
19773  fail:
19774     JS_FreeValue(ctx, resolving_funcs[0]);
19775     JS_FreeValue(ctx, resolving_funcs[1]);
19776     JS_FreeValue(ctx, promise);
19777     return JS_EXCEPTION;
19778 }
19779 
js_async_generator_function_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)19780 static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj,
19781                                                 JSValueConst this_obj,
19782                                                 int argc, JSValueConst *argv,
19783                                                 int flags)
19784 {
19785     JSValue obj, func_ret;
19786     JSAsyncGeneratorData *s;
19787 
19788     s = js_mallocz(ctx, sizeof(*s));
19789     if (!s)
19790         return JS_EXCEPTION;
19791     s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;
19792     init_list_head(&s->queue);
19793     if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
19794         s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
19795         goto fail;
19796     }
19797 
19798     /* execute the function up to 'OP_initial_yield' (no yield nor
19799        await are possible) */
19800     func_ret = async_func_resume(ctx, &s->func_state);
19801     if (JS_IsException(func_ret))
19802         goto fail;
19803     JS_FreeValue(ctx, func_ret);
19804 
19805     obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR);
19806     if (JS_IsException(obj))
19807         goto fail;
19808     s->generator = JS_VALUE_GET_OBJ(obj);
19809     JS_SetOpaque(obj, s);
19810     return obj;
19811  fail:
19812     js_async_generator_free(ctx->rt, s);
19813     return JS_EXCEPTION;
19814 }
19815 
19816 /* JS parser */
19817 
19818 enum {
19819     TOK_NUMBER = -128,
19820     TOK_STRING,
19821     TOK_TEMPLATE,
19822     TOK_IDENT,
19823     TOK_REGEXP,
19824     /* warning: order matters (see js_parse_assign_expr) */
19825     TOK_MUL_ASSIGN,
19826     TOK_DIV_ASSIGN,
19827     TOK_MOD_ASSIGN,
19828     TOK_PLUS_ASSIGN,
19829     TOK_MINUS_ASSIGN,
19830     TOK_SHL_ASSIGN,
19831     TOK_SAR_ASSIGN,
19832     TOK_SHR_ASSIGN,
19833     TOK_AND_ASSIGN,
19834     TOK_XOR_ASSIGN,
19835     TOK_OR_ASSIGN,
19836 #ifdef CONFIG_BIGNUM
19837     TOK_MATH_POW_ASSIGN,
19838 #endif
19839     TOK_POW_ASSIGN,
19840     TOK_LAND_ASSIGN,
19841     TOK_LOR_ASSIGN,
19842     TOK_DOUBLE_QUESTION_MARK_ASSIGN,
19843     TOK_DEC,
19844     TOK_INC,
19845     TOK_SHL,
19846     TOK_SAR,
19847     TOK_SHR,
19848     TOK_LT,
19849     TOK_LTE,
19850     TOK_GT,
19851     TOK_GTE,
19852     TOK_EQ,
19853     TOK_STRICT_EQ,
19854     TOK_NEQ,
19855     TOK_STRICT_NEQ,
19856     TOK_LAND,
19857     TOK_LOR,
19858 #ifdef CONFIG_BIGNUM
19859     TOK_MATH_POW,
19860 #endif
19861     TOK_POW,
19862     TOK_ARROW,
19863     TOK_ELLIPSIS,
19864     TOK_DOUBLE_QUESTION_MARK,
19865     TOK_QUESTION_MARK_DOT,
19866     TOK_ERROR,
19867     TOK_PRIVATE_NAME,
19868     TOK_EOF,
19869     /* keywords: WARNING: same order as atoms */
19870     TOK_NULL, /* must be first */
19871     TOK_FALSE,
19872     TOK_TRUE,
19873     TOK_IF,
19874     TOK_ELSE,
19875     TOK_RETURN,
19876     TOK_VAR,
19877     TOK_THIS,
19878     TOK_DELETE,
19879     TOK_VOID,
19880     TOK_TYPEOF,
19881     TOK_NEW,
19882     TOK_IN,
19883     TOK_INSTANCEOF,
19884     TOK_DO,
19885     TOK_WHILE,
19886     TOK_FOR,
19887     TOK_BREAK,
19888     TOK_CONTINUE,
19889     TOK_SWITCH,
19890     TOK_CASE,
19891     TOK_DEFAULT,
19892     TOK_THROW,
19893     TOK_TRY,
19894     TOK_CATCH,
19895     TOK_FINALLY,
19896     TOK_FUNCTION,
19897     TOK_DEBUGGER,
19898     TOK_WITH,
19899     /* FutureReservedWord */
19900     TOK_CLASS,
19901     TOK_CONST,
19902     TOK_ENUM,
19903     TOK_EXPORT,
19904     TOK_EXTENDS,
19905     TOK_IMPORT,
19906     TOK_SUPER,
19907     /* FutureReservedWords when parsing strict mode code */
19908     TOK_IMPLEMENTS,
19909     TOK_INTERFACE,
19910     TOK_LET,
19911     TOK_PACKAGE,
19912     TOK_PRIVATE,
19913     TOK_PROTECTED,
19914     TOK_PUBLIC,
19915     TOK_STATIC,
19916     TOK_YIELD,
19917     TOK_AWAIT, /* must be last */
19918     TOK_OF,     /* only used for js_parse_skip_parens_token() */
19919 };
19920 
19921 #define TOK_FIRST_KEYWORD   TOK_NULL
19922 #define TOK_LAST_KEYWORD    TOK_AWAIT
19923 
19924 /* unicode code points */
19925 #define CP_NBSP 0x00a0
19926 #define CP_BOM  0xfeff
19927 
19928 #define CP_LS   0x2028
19929 #define CP_PS   0x2029
19930 
19931 typedef struct BlockEnv {
19932     struct BlockEnv *prev;
19933     JSAtom label_name; /* JS_ATOM_NULL if none */
19934     int label_break; /* -1 if none */
19935     int label_cont; /* -1 if none */
19936     int drop_count; /* number of stack elements to drop */
19937     int label_finally; /* -1 if none */
19938     int scope_level;
19939     int has_iterator;
19940 } BlockEnv;
19941 
19942 typedef struct JSGlobalVar {
19943     int cpool_idx; /* if >= 0, index in the constant pool for hoisted
19944                       function defintion*/
19945     uint8_t force_init : 1; /* force initialization to undefined */
19946     uint8_t is_lexical : 1; /* global let/const definition */
19947     uint8_t is_const   : 1; /* const definition */
19948     int scope_level;    /* scope of definition */
19949     JSAtom var_name;  /* variable name */
19950 } JSGlobalVar;
19951 
19952 typedef struct RelocEntry {
19953     struct RelocEntry *next;
19954     uint32_t addr; /* address to patch */
19955     int size;   /* address size: 1, 2 or 4 bytes */
19956 } RelocEntry;
19957 
19958 typedef struct JumpSlot {
19959     int op;
19960     int size;
19961     int pos;
19962     int label;
19963 } JumpSlot;
19964 
19965 typedef struct LabelSlot {
19966     int ref_count;
19967     int pos;    /* phase 1 address, -1 means not resolved yet */
19968     int pos2;   /* phase 2 address, -1 means not resolved yet */
19969     int addr;   /* phase 3 address, -1 means not resolved yet */
19970     RelocEntry *first_reloc;
19971 } LabelSlot;
19972 
19973 typedef struct LineNumberSlot {
19974     uint32_t pc;
19975     int line_num;
19976 } LineNumberSlot;
19977 
19978 typedef enum JSParseFunctionEnum {
19979     JS_PARSE_FUNC_STATEMENT,
19980     JS_PARSE_FUNC_VAR,
19981     JS_PARSE_FUNC_EXPR,
19982     JS_PARSE_FUNC_ARROW,
19983     JS_PARSE_FUNC_GETTER,
19984     JS_PARSE_FUNC_SETTER,
19985     JS_PARSE_FUNC_METHOD,
19986     JS_PARSE_FUNC_CLASS_CONSTRUCTOR,
19987     JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR,
19988 } JSParseFunctionEnum;
19989 
19990 typedef enum JSParseExportEnum {
19991     JS_PARSE_EXPORT_NONE,
19992     JS_PARSE_EXPORT_NAMED,
19993     JS_PARSE_EXPORT_DEFAULT,
19994 } JSParseExportEnum;
19995 
19996 typedef struct JSFunctionDef {
19997     JSContext *ctx;
19998     struct JSFunctionDef *parent;
19999     int parent_cpool_idx; /* index in the constant pool of the parent
20000                              or -1 if none */
20001     int parent_scope_level; /* scope level in parent at point of definition */
20002     struct list_head child_list; /* list of JSFunctionDef.link */
20003     struct list_head link;
20004 
20005     BOOL is_eval; /* TRUE if eval code */
20006     int eval_type; /* only valid if is_eval = TRUE */
20007     BOOL is_global_var; /* TRUE if variables are not defined locally:
20008                            eval global, eval module or non strict eval */
20009     BOOL is_func_expr; /* TRUE if function expression */
20010     BOOL has_home_object; /* TRUE if the home object is available */
20011     BOOL has_prototype; /* true if a prototype field is necessary */
20012     BOOL has_simple_parameter_list;
20013     BOOL has_parameter_expressions; /* if true, an argument scope is created */
20014     BOOL has_use_strict; /* to reject directive in special cases */
20015     BOOL has_eval_call; /* true if the function contains a call to eval() */
20016     BOOL has_arguments_binding; /* true if the 'arguments' binding is
20017                                    available in the function */
20018     BOOL has_this_binding; /* true if the 'this' and new.target binding are
20019                               available in the function */
20020     BOOL new_target_allowed; /* true if the 'new.target' does not
20021                                 throw a syntax error */
20022     BOOL super_call_allowed; /* true if super() is allowed */
20023     BOOL super_allowed; /* true if super. or super[] is allowed */
20024     BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */
20025     BOOL is_derived_class_constructor;
20026     BOOL in_function_body;
20027     BOOL backtrace_barrier;
20028     JSFunctionKindEnum func_kind : 8;
20029     JSParseFunctionEnum func_type : 8;
20030     uint8_t js_mode; /* bitmap of JS_MODE_x */
20031     JSAtom func_name; /* JS_ATOM_NULL if no name */
20032 
20033     JSVarDef *vars;
20034     int var_size; /* allocated size for vars[] */
20035     int var_count;
20036     JSVarDef *args;
20037     int arg_size; /* allocated size for args[] */
20038     int arg_count; /* number of arguments */
20039     int defined_arg_count;
20040     int var_object_idx; /* -1 if none */
20041     int arg_var_object_idx; /* -1 if none (var object for the argument scope) */
20042     int arguments_var_idx; /* -1 if none */
20043     int arguments_arg_idx; /* argument variable definition in argument scope,
20044                               -1 if none */
20045     int func_var_idx; /* variable containing the current function (-1
20046                          if none, only used if is_func_expr is true) */
20047     int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */
20048     int this_var_idx; /* variable containg the 'this' value, -1 if none */
20049     int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */
20050     int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */
20051     int home_object_var_idx;
20052     BOOL need_home_object;
20053 
20054     int scope_level;    /* index into fd->scopes if the current lexical scope */
20055     int scope_first;    /* index into vd->vars of first lexically scoped variable */
20056     int scope_size;     /* allocated size of fd->scopes array */
20057     int scope_count;    /* number of entries used in the fd->scopes array */
20058     JSVarScope *scopes;
20059     JSVarScope def_scope_array[4];
20060     int body_scope; /* scope of the body of the function or eval */
20061 
20062     int global_var_count;
20063     int global_var_size;
20064     JSGlobalVar *global_vars;
20065 
20066     DynBuf byte_code;
20067     int last_opcode_pos; /* -1 if no last opcode */
20068     int last_opcode_line_num;
20069     BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */
20070 
20071     LabelSlot *label_slots;
20072     int label_size; /* allocated size for label_slots[] */
20073     int label_count;
20074     BlockEnv *top_break; /* break/continue label stack */
20075 
20076     /* constant pool (strings, functions, numbers) */
20077     JSValue *cpool;
20078     int cpool_count;
20079     int cpool_size;
20080 
20081     /* list of variables in the closure */
20082     int closure_var_count;
20083     int closure_var_size;
20084     JSClosureVar *closure_var;
20085 
20086     JumpSlot *jump_slots;
20087     int jump_size;
20088     int jump_count;
20089 
20090     LineNumberSlot *line_number_slots;
20091     int line_number_size;
20092     int line_number_count;
20093     int line_number_last;
20094     int line_number_last_pc;
20095 
20096     /* pc2line table */
20097     JSAtom filename;
20098     int line_num;
20099     DynBuf pc2line;
20100 
20101     char *source;  /* raw source, utf-8 encoded */
20102     int source_len;
20103 
20104     JSModuleDef *module; /* != NULL when parsing a module */
20105 } JSFunctionDef;
20106 
20107 typedef struct JSToken {
20108     int val;
20109     int line_num;   /* line number of token start */
20110     const uint8_t *ptr;
20111     union {
20112         struct {
20113             JSValue str;
20114             int sep;
20115         } str;
20116         struct {
20117             JSValue val;
20118 #ifdef CONFIG_BIGNUM
20119             slimb_t exponent; /* may be != 0 only if val is a float */
20120 #endif
20121         } num;
20122         struct {
20123             JSAtom atom;
20124             BOOL has_escape;
20125             BOOL is_reserved;
20126         } ident;
20127         struct {
20128             JSValue body;
20129             JSValue flags;
20130         } regexp;
20131     } u;
20132 } JSToken;
20133 
20134 typedef struct JSParseState {
20135     JSContext *ctx;
20136     int last_line_num;  /* line number of last token */
20137     int line_num;       /* line number of current offset */
20138     const char *filename;
20139     JSToken token;
20140     BOOL got_lf; /* true if got line feed before the current token */
20141     const uint8_t *last_ptr;
20142     const uint8_t *buf_ptr;
20143     const uint8_t *buf_end;
20144 
20145     /* current function code */
20146     JSFunctionDef *cur_func;
20147     BOOL is_module; /* parsing a module */
20148     BOOL allow_html_comments;
20149     BOOL ext_json; /* true if accepting JSON superset */
20150 } JSParseState;
20151 
20152 typedef struct JSOpCode {
20153 #ifdef DUMP_BYTECODE
20154     const char *name;
20155 #endif
20156     uint8_t size; /* in bytes */
20157     /* the opcodes remove n_pop items from the top of the stack, then
20158        pushes n_push items */
20159     uint8_t n_pop;
20160     uint8_t n_push;
20161     uint8_t fmt;
20162 } JSOpCode;
20163 
20164 static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
20165 #define FMT(f)
20166 #ifdef DUMP_BYTECODE
20167 #define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f },
20168 #else
20169 #define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f },
20170 #endif
20171 #include "quickjs-opcode.h"
20172 #undef DEF
20173 #undef FMT
20174 };
20175 
20176 #if SHORT_OPCODES
20177 /* After the final compilation pass, short opcodes are used. Their
20178    opcodes overlap with the temporary opcodes which cannot appear in
20179    the final bytecode. Their description is after the temporary
20180    opcodes in opcode_info[]. */
20181 #define short_opcode_info(op)           \
20182     opcode_info[(op) >= OP_TEMP_START ? \
20183                 (op) + (OP_TEMP_END - OP_TEMP_START) : (op)]
20184 #else
20185 #define short_opcode_info(op) opcode_info[op]
20186 #endif
20187 
20188 static __exception int next_token(JSParseState *s);
20189 
free_token(JSParseState * s,JSToken * token)20190 static void free_token(JSParseState *s, JSToken *token)
20191 {
20192     switch(token->val) {
20193 #ifdef CONFIG_BIGNUM
20194     case TOK_NUMBER:
20195         JS_FreeValue(s->ctx, token->u.num.val);
20196         break;
20197 #endif
20198     case TOK_STRING:
20199     case TOK_TEMPLATE:
20200         JS_FreeValue(s->ctx, token->u.str.str);
20201         break;
20202     case TOK_REGEXP:
20203         JS_FreeValue(s->ctx, token->u.regexp.body);
20204         JS_FreeValue(s->ctx, token->u.regexp.flags);
20205         break;
20206     case TOK_IDENT:
20207     case TOK_PRIVATE_NAME:
20208         JS_FreeAtom(s->ctx, token->u.ident.atom);
20209         break;
20210     default:
20211         if (token->val >= TOK_FIRST_KEYWORD &&
20212             token->val <= TOK_LAST_KEYWORD) {
20213             JS_FreeAtom(s->ctx, token->u.ident.atom);
20214         }
20215         break;
20216     }
20217 }
20218 
dump_token(JSParseState * s,const JSToken * token)20219 static void __attribute((unused)) dump_token(JSParseState *s,
20220                                              const JSToken *token)
20221 {
20222     switch(token->val) {
20223     case TOK_NUMBER:
20224         {
20225             double d;
20226             JS_ToFloat64(s->ctx, &d, token->u.num.val);  /* no exception possible */
20227             printf("number: %.14g\n", d);
20228         }
20229         break;
20230     case TOK_IDENT:
20231     dump_atom:
20232         {
20233             char buf[ATOM_GET_STR_BUF_SIZE];
20234             printf("ident: '%s'\n",
20235                    JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom));
20236         }
20237         break;
20238     case TOK_STRING:
20239         {
20240             const char *str;
20241             /* XXX: quote the string */
20242             str = JS_ToCString(s->ctx, token->u.str.str);
20243             printf("string: '%s'\n", str);
20244             JS_FreeCString(s->ctx, str);
20245         }
20246         break;
20247     case TOK_TEMPLATE:
20248         {
20249             const char *str;
20250             str = JS_ToCString(s->ctx, token->u.str.str);
20251             printf("template: `%s`\n", str);
20252             JS_FreeCString(s->ctx, str);
20253         }
20254         break;
20255     case TOK_REGEXP:
20256         {
20257             const char *str, *str2;
20258             str = JS_ToCString(s->ctx, token->u.regexp.body);
20259             str2 = JS_ToCString(s->ctx, token->u.regexp.flags);
20260             printf("regexp: '%s' '%s'\n", str, str2);
20261             JS_FreeCString(s->ctx, str);
20262             JS_FreeCString(s->ctx, str2);
20263         }
20264         break;
20265     case TOK_EOF:
20266         printf("eof\n");
20267         break;
20268     default:
20269         if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) {
20270             goto dump_atom;
20271         } else if (s->token.val >= 256) {
20272             printf("token: %d\n", token->val);
20273         } else {
20274             printf("token: '%c'\n", token->val);
20275         }
20276         break;
20277     }
20278 }
20279 
js_parse_error(JSParseState * s,const char * fmt,...)20280 int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...)
20281 {
20282     JSContext *ctx = s->ctx;
20283     va_list ap;
20284     int backtrace_flags;
20285 
20286     va_start(ap, fmt);
20287     JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE);
20288     va_end(ap);
20289     backtrace_flags = 0;
20290     if (s->cur_func && s->cur_func->backtrace_barrier)
20291         backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
20292     build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num,
20293                     backtrace_flags);
20294     return -1;
20295 }
20296 
js_parse_expect(JSParseState * s,int tok)20297 static int js_parse_expect(JSParseState *s, int tok)
20298 {
20299     if (s->token.val != tok) {
20300         /* XXX: dump token correctly in all cases */
20301         return js_parse_error(s, "expecting '%c'", tok);
20302     }
20303     return next_token(s);
20304 }
20305 
js_parse_expect_semi(JSParseState * s)20306 static int js_parse_expect_semi(JSParseState *s)
20307 {
20308     if (s->token.val != ';') {
20309         /* automatic insertion of ';' */
20310         if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) {
20311             return 0;
20312         }
20313         return js_parse_error(s, "expecting '%c'", ';');
20314     }
20315     return next_token(s);
20316 }
20317 
js_parse_error_reserved_identifier(JSParseState * s)20318 static int js_parse_error_reserved_identifier(JSParseState *s)
20319 {
20320     char buf1[ATOM_GET_STR_BUF_SIZE];
20321     return js_parse_error(s, "'%s' is a reserved identifier",
20322                           JS_AtomGetStr(s->ctx, buf1, sizeof(buf1),
20323                                         s->token.u.ident.atom));
20324 }
20325 
js_parse_template_part(JSParseState * s,const uint8_t * p)20326 static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)
20327 {
20328     uint32_t c;
20329     StringBuffer b_s, *b = &b_s;
20330 
20331     /* p points to the first byte of the template part */
20332     if (string_buffer_init(s->ctx, b, 32))
20333         goto fail;
20334     for(;;) {
20335         if (p >= s->buf_end)
20336             goto unexpected_eof;
20337         c = *p++;
20338         if (c == '`') {
20339             /* template end part */
20340             break;
20341         }
20342         if (c == '$' && *p == '{') {
20343             /* template start or middle part */
20344             p++;
20345             break;
20346         }
20347         if (c == '\\') {
20348             if (string_buffer_putc8(b, c))
20349                 goto fail;
20350             if (p >= s->buf_end)
20351                 goto unexpected_eof;
20352             c = *p++;
20353         }
20354         /* newline sequences are normalized as single '\n' bytes */
20355         if (c == '\r') {
20356             if (*p == '\n')
20357                 p++;
20358             c = '\n';
20359         }
20360         if (c == '\n') {
20361             s->line_num++;
20362         } else if (c >= 0x80) {
20363             const uint8_t *p_next;
20364             c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
20365             if (c > 0x10FFFF) {
20366                 js_parse_error(s, "invalid UTF-8 sequence");
20367                 goto fail;
20368             }
20369             p = p_next;
20370         }
20371         if (string_buffer_putc(b, c))
20372             goto fail;
20373     }
20374     s->token.val = TOK_TEMPLATE;
20375     s->token.u.str.sep = c;
20376     s->token.u.str.str = string_buffer_end(b);
20377     s->buf_ptr = p;
20378     return 0;
20379 
20380  unexpected_eof:
20381     js_parse_error(s, "unexpected end of string");
20382  fail:
20383     string_buffer_free(b);
20384     return -1;
20385 }
20386 
js_parse_string(JSParseState * s,int sep,BOOL do_throw,const uint8_t * p,JSToken * token,const uint8_t ** pp)20387 static __exception int js_parse_string(JSParseState *s, int sep,
20388                                        BOOL do_throw, const uint8_t *p,
20389                                        JSToken *token, const uint8_t **pp)
20390 {
20391     int ret;
20392     uint32_t c;
20393     StringBuffer b_s, *b = &b_s;
20394 
20395     /* string */
20396     if (string_buffer_init(s->ctx, b, 32))
20397         goto fail;
20398     for(;;) {
20399         if (p >= s->buf_end)
20400             goto invalid_char;
20401         c = *p;
20402         if (c < 0x20) {
20403             if (!s->cur_func) {
20404                 if (do_throw)
20405                     js_parse_error(s, "invalid character in a JSON string");
20406                 goto fail;
20407             }
20408             if (sep == '`') {
20409                 if (c == '\r') {
20410                     if (p[1] == '\n')
20411                         p++;
20412                     c = '\n';
20413                 }
20414                 /* do not update s->line_num */
20415             } else if (c == '\n' || c == '\r')
20416                 goto invalid_char;
20417         }
20418         p++;
20419         if (c == sep)
20420             break;
20421         if (c == '$' && *p == '{' && sep == '`') {
20422             /* template start or middle part */
20423             p++;
20424             break;
20425         }
20426         if (c == '\\') {
20427             c = *p;
20428             /* XXX: need a specific JSON case to avoid
20429                accepting invalid escapes */
20430             switch(c) {
20431             case '\0':
20432                 if (p >= s->buf_end)
20433                     goto invalid_char;
20434                 p++;
20435                 break;
20436             case '\'':
20437             case '\"':
20438             case '\\':
20439                 p++;
20440                 break;
20441             case '\r':  /* accept DOS and MAC newline sequences */
20442                 if (p[1] == '\n') {
20443                     p++;
20444                 }
20445                 /* fall thru */
20446             case '\n':
20447                 /* ignore escaped newline sequence */
20448                 p++;
20449                 if (sep != '`')
20450                     s->line_num++;
20451                 continue;
20452             default:
20453                 if (c >= '0' && c <= '9') {
20454                     if (!s->cur_func)
20455                         goto invalid_escape; /* JSON case */
20456                     if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`')
20457                         goto parse_escape;
20458                     if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) {
20459                         p++;
20460                         c = '\0';
20461                     } else {
20462                         if (c >= '8' || sep == '`') {
20463                             /* Note: according to ES2021, \8 and \9 are not
20464                                accepted in strict mode or in templates. */
20465                             goto invalid_escape;
20466                         } else {
20467                             if (do_throw)
20468                                 js_parse_error(s, "octal escape sequences are not allowed in strict mode");
20469                         }
20470                         goto fail;
20471                     }
20472                 } else if (c >= 0x80) {
20473                     const uint8_t *p_next;
20474                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
20475                     if (c > 0x10FFFF) {
20476                         goto invalid_utf8;
20477                     }
20478                     p = p_next;
20479                     /* LS or PS are skipped */
20480                     if (c == CP_LS || c == CP_PS)
20481                         continue;
20482                 } else {
20483                 parse_escape:
20484                     ret = lre_parse_escape(&p, TRUE);
20485                     if (ret == -1) {
20486                     invalid_escape:
20487                         if (do_throw)
20488                             js_parse_error(s, "malformed escape sequence in string literal");
20489                         goto fail;
20490                     } else if (ret < 0) {
20491                         /* ignore the '\' (could output a warning) */
20492                         p++;
20493                     } else {
20494                         c = ret;
20495                     }
20496                 }
20497                 break;
20498             }
20499         } else if (c >= 0x80) {
20500             const uint8_t *p_next;
20501             c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
20502             if (c > 0x10FFFF)
20503                 goto invalid_utf8;
20504             p = p_next;
20505         }
20506         if (string_buffer_putc(b, c))
20507             goto fail;
20508     }
20509     token->val = TOK_STRING;
20510     token->u.str.sep = c;
20511     token->u.str.str = string_buffer_end(b);
20512     *pp = p;
20513     return 0;
20514 
20515  invalid_utf8:
20516     if (do_throw)
20517         js_parse_error(s, "invalid UTF-8 sequence");
20518     goto fail;
20519  invalid_char:
20520     if (do_throw)
20521         js_parse_error(s, "unexpected end of string");
20522  fail:
20523     string_buffer_free(b);
20524     return -1;
20525 }
20526 
token_is_pseudo_keyword(JSParseState * s,JSAtom atom)20527 static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) {
20528     return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom &&
20529         !s->token.u.ident.has_escape;
20530 }
20531 
js_parse_regexp(JSParseState * s)20532 static __exception int js_parse_regexp(JSParseState *s)
20533 {
20534     const uint8_t *p;
20535     BOOL in_class;
20536     StringBuffer b_s, *b = &b_s;
20537     StringBuffer b2_s, *b2 = &b2_s;
20538     uint32_t c;
20539 
20540     p = s->buf_ptr;
20541     p++;
20542     in_class = FALSE;
20543     if (string_buffer_init(s->ctx, b, 32))
20544         return -1;
20545     if (string_buffer_init(s->ctx, b2, 1))
20546         goto fail;
20547     for(;;) {
20548         if (p >= s->buf_end) {
20549         eof_error:
20550             js_parse_error(s, "unexpected end of regexp");
20551             goto fail;
20552         }
20553         c = *p++;
20554         if (c == '\n' || c == '\r') {
20555             goto eol_error;
20556         } else if (c == '/') {
20557             if (!in_class)
20558                 break;
20559         } else if (c == '[') {
20560             in_class = TRUE;
20561         } else if (c == ']') {
20562             /* XXX: incorrect as the first character in a class */
20563             in_class = FALSE;
20564         } else if (c == '\\') {
20565             if (string_buffer_putc8(b, c))
20566                 goto fail;
20567             c = *p++;
20568             if (c == '\n' || c == '\r')
20569                 goto eol_error;
20570             else if (c == '\0' && p >= s->buf_end)
20571                 goto eof_error;
20572             else if (c >= 0x80) {
20573                 const uint8_t *p_next;
20574                 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
20575                 if (c > 0x10FFFF) {
20576                     goto invalid_utf8;
20577                 }
20578                 p = p_next;
20579                 if (c == CP_LS || c == CP_PS)
20580                     goto eol_error;
20581             }
20582         } else if (c >= 0x80) {
20583             const uint8_t *p_next;
20584             c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
20585             if (c > 0x10FFFF) {
20586             invalid_utf8:
20587                 js_parse_error(s, "invalid UTF-8 sequence");
20588                 goto fail;
20589             }
20590             p = p_next;
20591             /* LS or PS are considered as line terminator */
20592             if (c == CP_LS || c == CP_PS) {
20593             eol_error:
20594                 js_parse_error(s, "unexpected line terminator in regexp");
20595                 goto fail;
20596             }
20597         }
20598         if (string_buffer_putc(b, c))
20599             goto fail;
20600     }
20601 
20602     /* flags */
20603     for(;;) {
20604         const uint8_t *p_next = p;
20605         c = *p_next++;
20606         if (c >= 0x80) {
20607             c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
20608             if (c > 0x10FFFF) {
20609                 goto invalid_utf8;
20610             }
20611         }
20612         if (!lre_js_is_ident_next(c))
20613             break;
20614         if (string_buffer_putc(b2, c))
20615             goto fail;
20616         p = p_next;
20617     }
20618 
20619     s->token.val = TOK_REGEXP;
20620     s->token.u.regexp.body = string_buffer_end(b);
20621     s->token.u.regexp.flags = string_buffer_end(b2);
20622     s->buf_ptr = p;
20623     return 0;
20624  fail:
20625     string_buffer_free(b);
20626     string_buffer_free(b2);
20627     return -1;
20628 }
20629 
ident_realloc(JSContext * ctx,char ** pbuf,size_t * psize,char * static_buf)20630 static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
20631                                      char *static_buf)
20632 {
20633     char *buf, *new_buf;
20634     size_t size, new_size;
20635 
20636     buf = *pbuf;
20637     size = *psize;
20638     if (size >= (SIZE_MAX / 3) * 2)
20639         new_size = SIZE_MAX;
20640     else
20641         new_size = size + (size >> 1);
20642     if (buf == static_buf) {
20643         new_buf = js_malloc(ctx, new_size);
20644         if (!new_buf)
20645             return -1;
20646         memcpy(new_buf, buf, size);
20647     } else {
20648         new_buf = js_realloc(ctx, buf, new_size);
20649         if (!new_buf)
20650             return -1;
20651     }
20652     *pbuf = new_buf;
20653     *psize = new_size;
20654     return 0;
20655 }
20656 
20657 /* 'c' is the first character. Return JS_ATOM_NULL in case of error */
parse_ident(JSParseState * s,const uint8_t ** pp,BOOL * pident_has_escape,int c,BOOL is_private)20658 static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,
20659                           BOOL *pident_has_escape, int c, BOOL is_private)
20660 {
20661     const uint8_t *p, *p1;
20662     char ident_buf[128], *buf;
20663     size_t ident_size, ident_pos;
20664     JSAtom atom;
20665 
20666     p = *pp;
20667     buf = ident_buf;
20668     ident_size = sizeof(ident_buf);
20669     ident_pos = 0;
20670     if (is_private)
20671         buf[ident_pos++] = '#';
20672     for(;;) {
20673         p1 = p;
20674 
20675         if (c < 128) {
20676             buf[ident_pos++] = c;
20677         } else {
20678             ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c);
20679         }
20680         c = *p1++;
20681         if (c == '\\' && *p1 == 'u') {
20682             c = lre_parse_escape(&p1, TRUE);
20683             *pident_has_escape = TRUE;
20684         } else if (c >= 128) {
20685             c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
20686         }
20687         if (!lre_js_is_ident_next(c))
20688             break;
20689         p = p1;
20690         if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
20691             if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
20692                 atom = JS_ATOM_NULL;
20693                 goto done;
20694             }
20695         }
20696     }
20697     atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
20698  done:
20699     if (unlikely(buf != ident_buf))
20700         js_free(s->ctx, buf);
20701     *pp = p;
20702     return atom;
20703 }
20704 
20705 
next_token(JSParseState * s)20706 static __exception int next_token(JSParseState *s)
20707 {
20708     const uint8_t *p;
20709     int c;
20710     BOOL ident_has_escape;
20711     JSAtom atom;
20712 
20713     if (js_check_stack_overflow(s->ctx->rt, 0)) {
20714         return js_parse_error(s, "stack overflow");
20715     }
20716 
20717     free_token(s, &s->token);
20718 
20719     p = s->last_ptr = s->buf_ptr;
20720     s->got_lf = FALSE;
20721     s->last_line_num = s->token.line_num;
20722  redo:
20723     s->token.line_num = s->line_num;
20724     s->token.ptr = p;
20725     c = *p;
20726     switch(c) {
20727     case 0:
20728         if (p >= s->buf_end) {
20729             s->token.val = TOK_EOF;
20730         } else {
20731             goto def_token;
20732         }
20733         break;
20734     case '`':
20735         if (js_parse_template_part(s, p + 1))
20736             goto fail;
20737         p = s->buf_ptr;
20738         break;
20739     case '\'':
20740     case '\"':
20741         if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
20742             goto fail;
20743         break;
20744     case '\r':  /* accept DOS and MAC newline sequences */
20745         if (p[1] == '\n') {
20746             p++;
20747         }
20748         /* fall thru */
20749     case '\n':
20750         p++;
20751     line_terminator:
20752         s->got_lf = TRUE;
20753         s->line_num++;
20754         goto redo;
20755     case '\f':
20756     case '\v':
20757     case ' ':
20758     case '\t':
20759         p++;
20760         goto redo;
20761     case '/':
20762         if (p[1] == '*') {
20763             /* comment */
20764             p += 2;
20765             for(;;) {
20766                 if (*p == '\0' && p >= s->buf_end) {
20767                     js_parse_error(s, "unexpected end of comment");
20768                     goto fail;
20769                 }
20770                 if (p[0] == '*' && p[1] == '/') {
20771                     p += 2;
20772                     break;
20773                 }
20774                 if (*p == '\n') {
20775                     s->line_num++;
20776                     s->got_lf = TRUE; /* considered as LF for ASI */
20777                     p++;
20778                 } else if (*p == '\r') {
20779                     s->got_lf = TRUE; /* considered as LF for ASI */
20780                     p++;
20781                 } else if (*p >= 0x80) {
20782                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
20783                     if (c == CP_LS || c == CP_PS) {
20784                         s->got_lf = TRUE; /* considered as LF for ASI */
20785                     } else if (c == -1) {
20786                         p++; /* skip invalid UTF-8 */
20787                     }
20788                 } else {
20789                     p++;
20790                 }
20791             }
20792             goto redo;
20793         } else if (p[1] == '/') {
20794             /* line comment */
20795             p += 2;
20796         skip_line_comment:
20797             for(;;) {
20798                 if (*p == '\0' && p >= s->buf_end)
20799                     break;
20800                 if (*p == '\r' || *p == '\n')
20801                     break;
20802                 if (*p >= 0x80) {
20803                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
20804                     /* LS or PS are considered as line terminator */
20805                     if (c == CP_LS || c == CP_PS) {
20806                         break;
20807                     } else if (c == -1) {
20808                         p++; /* skip invalid UTF-8 */
20809                     }
20810                 } else {
20811                     p++;
20812                 }
20813             }
20814             goto redo;
20815         } else if (p[1] == '=') {
20816             p += 2;
20817             s->token.val = TOK_DIV_ASSIGN;
20818         } else {
20819             p++;
20820             s->token.val = c;
20821         }
20822         break;
20823     case '\\':
20824         if (p[1] == 'u') {
20825             const uint8_t *p1 = p + 1;
20826             int c1 = lre_parse_escape(&p1, TRUE);
20827             if (c1 >= 0 && lre_js_is_ident_first(c1)) {
20828                 c = c1;
20829                 p = p1;
20830                 ident_has_escape = TRUE;
20831                 goto has_ident;
20832             } else {
20833                 /* XXX: syntax error? */
20834             }
20835         }
20836         goto def_token;
20837     case 'a': case 'b': case 'c': case 'd':
20838     case 'e': case 'f': case 'g': case 'h':
20839     case 'i': case 'j': case 'k': case 'l':
20840     case 'm': case 'n': case 'o': case 'p':
20841     case 'q': case 'r': case 's': case 't':
20842     case 'u': case 'v': case 'w': case 'x':
20843     case 'y': case 'z':
20844     case 'A': case 'B': case 'C': case 'D':
20845     case 'E': case 'F': case 'G': case 'H':
20846     case 'I': case 'J': case 'K': case 'L':
20847     case 'M': case 'N': case 'O': case 'P':
20848     case 'Q': case 'R': case 'S': case 'T':
20849     case 'U': case 'V': case 'W': case 'X':
20850     case 'Y': case 'Z':
20851     case '_':
20852     case '$':
20853         /* identifier */
20854         p++;
20855         ident_has_escape = FALSE;
20856     has_ident:
20857         atom = parse_ident(s, &p, &ident_has_escape, c, FALSE);
20858         if (atom == JS_ATOM_NULL)
20859             goto fail;
20860         s->token.u.ident.atom = atom;
20861         s->token.u.ident.has_escape = ident_has_escape;
20862         s->token.u.ident.is_reserved = FALSE;
20863         if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
20864             (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
20865              (s->cur_func->js_mode & JS_MODE_STRICT)) ||
20866             (s->token.u.ident.atom == JS_ATOM_yield &&
20867              ((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||
20868               (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
20869                !s->cur_func->in_function_body && s->cur_func->parent &&
20870                (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||
20871             (s->token.u.ident.atom == JS_ATOM_await &&
20872              (s->is_module ||
20873               (((s->cur_func->func_kind & JS_FUNC_ASYNC) ||
20874                 (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
20875                  !s->cur_func->in_function_body && s->cur_func->parent &&
20876                  (s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) {
20877                   if (ident_has_escape) {
20878                       s->token.u.ident.is_reserved = TRUE;
20879                       s->token.val = TOK_IDENT;
20880                   } else {
20881                       /* The keywords atoms are pre allocated */
20882                       s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;
20883                   }
20884         } else {
20885             s->token.val = TOK_IDENT;
20886         }
20887         break;
20888     case '#':
20889         /* private name */
20890         {
20891             const uint8_t *p1;
20892             p++;
20893             p1 = p;
20894             c = *p1++;
20895             if (c == '\\' && *p1 == 'u') {
20896                 c = lre_parse_escape(&p1, TRUE);
20897             } else if (c >= 128) {
20898                 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
20899             }
20900             if (!lre_js_is_ident_first(c)) {
20901                 js_parse_error(s, "invalid first character of private name");
20902                 goto fail;
20903             }
20904             p = p1;
20905             ident_has_escape = FALSE; /* not used */
20906             atom = parse_ident(s, &p, &ident_has_escape, c, TRUE);
20907             if (atom == JS_ATOM_NULL)
20908                 goto fail;
20909             s->token.u.ident.atom = atom;
20910             s->token.val = TOK_PRIVATE_NAME;
20911         }
20912         break;
20913     case '.':
20914         if (p[1] == '.' && p[2] == '.') {
20915             p += 3;
20916             s->token.val = TOK_ELLIPSIS;
20917             break;
20918         }
20919         if (p[1] >= '0' && p[1] <= '9') {
20920             goto parse_number;
20921         } else {
20922             goto def_token;
20923         }
20924         break;
20925     case '0':
20926         /* in strict mode, octal literals are not accepted */
20927         if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) {
20928             js_parse_error(s, "octal literals are deprecated in strict mode");
20929             goto fail;
20930         }
20931         goto parse_number;
20932     case '1': case '2': case '3': case '4':
20933     case '5': case '6': case '7': case '8':
20934     case '9':
20935         /* number */
20936     parse_number:
20937         {
20938             JSValue ret;
20939             const uint8_t *p1;
20940             int flags, radix;
20941             flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |
20942                 ATOD_ACCEPT_UNDERSCORES;
20943 #ifdef CONFIG_BIGNUM
20944             flags |= ATOD_ACCEPT_SUFFIX;
20945             if (s->cur_func->js_mode & JS_MODE_MATH) {
20946                 flags |= ATOD_MODE_BIGINT;
20947                 if (s->cur_func->js_mode & JS_MODE_MATH)
20948                     flags |= ATOD_TYPE_BIG_FLOAT;
20949             }
20950 #endif
20951             radix = 0;
20952 #ifdef CONFIG_BIGNUM
20953             s->token.u.num.exponent = 0;
20954             ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix,
20955                            flags, &s->token.u.num.exponent);
20956 #else
20957             ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
20958                           flags);
20959 #endif
20960             if (JS_IsException(ret))
20961                 goto fail;
20962             /* reject `10instanceof Number` */
20963             if (JS_VALUE_IS_NAN(ret) ||
20964                 lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) {
20965                 JS_FreeValue(s->ctx, ret);
20966                 js_parse_error(s, "invalid number literal");
20967                 goto fail;
20968             }
20969             s->token.val = TOK_NUMBER;
20970             s->token.u.num.val = ret;
20971         }
20972         break;
20973     case '*':
20974         if (p[1] == '=') {
20975             p += 2;
20976             s->token.val = TOK_MUL_ASSIGN;
20977         } else if (p[1] == '*') {
20978             if (p[2] == '=') {
20979                 p += 3;
20980                 s->token.val = TOK_POW_ASSIGN;
20981             } else {
20982                 p += 2;
20983                 s->token.val = TOK_POW;
20984             }
20985         } else {
20986             goto def_token;
20987         }
20988         break;
20989     case '%':
20990         if (p[1] == '=') {
20991             p += 2;
20992             s->token.val = TOK_MOD_ASSIGN;
20993         } else {
20994             goto def_token;
20995         }
20996         break;
20997     case '+':
20998         if (p[1] == '=') {
20999             p += 2;
21000             s->token.val = TOK_PLUS_ASSIGN;
21001         } else if (p[1] == '+') {
21002             p += 2;
21003             s->token.val = TOK_INC;
21004         } else {
21005             goto def_token;
21006         }
21007         break;
21008     case '-':
21009         if (p[1] == '=') {
21010             p += 2;
21011             s->token.val = TOK_MINUS_ASSIGN;
21012         } else if (p[1] == '-') {
21013             if (s->allow_html_comments &&
21014                 p[2] == '>' && s->last_line_num != s->line_num) {
21015                 /* Annex B: `-->` at beginning of line is an html comment end.
21016                    It extends to the end of the line.
21017                  */
21018                 goto skip_line_comment;
21019             }
21020             p += 2;
21021             s->token.val = TOK_DEC;
21022         } else {
21023             goto def_token;
21024         }
21025         break;
21026     case '<':
21027         if (p[1] == '=') {
21028             p += 2;
21029             s->token.val = TOK_LTE;
21030         } else if (p[1] == '<') {
21031             if (p[2] == '=') {
21032                 p += 3;
21033                 s->token.val = TOK_SHL_ASSIGN;
21034             } else {
21035                 p += 2;
21036                 s->token.val = TOK_SHL;
21037             }
21038         } else if (s->allow_html_comments &&
21039                    p[1] == '!' && p[2] == '-' && p[3] == '-') {
21040             /* Annex B: handle `<!--` single line html comments */
21041             goto skip_line_comment;
21042         } else {
21043             goto def_token;
21044         }
21045         break;
21046     case '>':
21047         if (p[1] == '=') {
21048             p += 2;
21049             s->token.val = TOK_GTE;
21050         } else if (p[1] == '>') {
21051             if (p[2] == '>') {
21052                 if (p[3] == '=') {
21053                     p += 4;
21054                     s->token.val = TOK_SHR_ASSIGN;
21055                 } else {
21056                     p += 3;
21057                     s->token.val = TOK_SHR;
21058                 }
21059             } else if (p[2] == '=') {
21060                 p += 3;
21061                 s->token.val = TOK_SAR_ASSIGN;
21062             } else {
21063                 p += 2;
21064                 s->token.val = TOK_SAR;
21065             }
21066         } else {
21067             goto def_token;
21068         }
21069         break;
21070     case '=':
21071         if (p[1] == '=') {
21072             if (p[2] == '=') {
21073                 p += 3;
21074                 s->token.val = TOK_STRICT_EQ;
21075             } else {
21076                 p += 2;
21077                 s->token.val = TOK_EQ;
21078             }
21079         } else if (p[1] == '>') {
21080             p += 2;
21081             s->token.val = TOK_ARROW;
21082         } else {
21083             goto def_token;
21084         }
21085         break;
21086     case '!':
21087         if (p[1] == '=') {
21088             if (p[2] == '=') {
21089                 p += 3;
21090                 s->token.val = TOK_STRICT_NEQ;
21091             } else {
21092                 p += 2;
21093                 s->token.val = TOK_NEQ;
21094             }
21095         } else {
21096             goto def_token;
21097         }
21098         break;
21099     case '&':
21100         if (p[1] == '=') {
21101             p += 2;
21102             s->token.val = TOK_AND_ASSIGN;
21103         } else if (p[1] == '&') {
21104             if (p[2] == '=') {
21105                 p += 3;
21106                 s->token.val = TOK_LAND_ASSIGN;
21107             } else {
21108                 p += 2;
21109                 s->token.val = TOK_LAND;
21110             }
21111         } else {
21112             goto def_token;
21113         }
21114         break;
21115 #ifdef CONFIG_BIGNUM
21116         /* in math mode, '^' is the power operator. '^^' is always the
21117            xor operator and '**' is always the power operator */
21118     case '^':
21119         if (p[1] == '=') {
21120             p += 2;
21121             if (s->cur_func->js_mode & JS_MODE_MATH)
21122                 s->token.val = TOK_MATH_POW_ASSIGN;
21123             else
21124                 s->token.val = TOK_XOR_ASSIGN;
21125         } else if (p[1] == '^') {
21126             if (p[2] == '=') {
21127                 p += 3;
21128                 s->token.val = TOK_XOR_ASSIGN;
21129             } else {
21130                 p += 2;
21131                 s->token.val = '^';
21132             }
21133         } else {
21134             p++;
21135             if (s->cur_func->js_mode & JS_MODE_MATH)
21136                 s->token.val = TOK_MATH_POW;
21137             else
21138                 s->token.val = '^';
21139         }
21140         break;
21141 #else
21142     case '^':
21143         if (p[1] == '=') {
21144             p += 2;
21145             s->token.val = TOK_XOR_ASSIGN;
21146         } else {
21147             goto def_token;
21148         }
21149         break;
21150 #endif
21151     case '|':
21152         if (p[1] == '=') {
21153             p += 2;
21154             s->token.val = TOK_OR_ASSIGN;
21155         } else if (p[1] == '|') {
21156             if (p[2] == '=') {
21157                 p += 3;
21158                 s->token.val = TOK_LOR_ASSIGN;
21159             } else {
21160                 p += 2;
21161                 s->token.val = TOK_LOR;
21162             }
21163         } else {
21164             goto def_token;
21165         }
21166         break;
21167     case '?':
21168         if (p[1] == '?') {
21169             if (p[2] == '=') {
21170                 p += 3;
21171                 s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN;
21172             } else {
21173                 p += 2;
21174                 s->token.val = TOK_DOUBLE_QUESTION_MARK;
21175             }
21176         } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) {
21177             p += 2;
21178             s->token.val = TOK_QUESTION_MARK_DOT;
21179         } else {
21180             goto def_token;
21181         }
21182         break;
21183     default:
21184         if (c >= 128) {
21185             /* unicode value */
21186             c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
21187             switch(c) {
21188             case CP_PS:
21189             case CP_LS:
21190                 /* XXX: should avoid incrementing line_number, but
21191                    needed to handle HTML comments */
21192                 goto line_terminator;
21193             default:
21194                 if (lre_is_space(c)) {
21195                     goto redo;
21196                 } else if (lre_js_is_ident_first(c)) {
21197                     ident_has_escape = FALSE;
21198                     goto has_ident;
21199                 } else {
21200                     js_parse_error(s, "unexpected character");
21201                     goto fail;
21202                 }
21203             }
21204         }
21205     def_token:
21206         s->token.val = c;
21207         p++;
21208         break;
21209     }
21210     s->buf_ptr = p;
21211 
21212     //    dump_token(s, &s->token);
21213     return 0;
21214 
21215  fail:
21216     s->token.val = TOK_ERROR;
21217     return -1;
21218 }
21219 
21220 /* 'c' is the first character. Return JS_ATOM_NULL in case of error */
json_parse_ident(JSParseState * s,const uint8_t ** pp,int c)21221 static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)
21222 {
21223     const uint8_t *p;
21224     char ident_buf[128], *buf;
21225     size_t ident_size, ident_pos;
21226     JSAtom atom;
21227 
21228     p = *pp;
21229     buf = ident_buf;
21230     ident_size = sizeof(ident_buf);
21231     ident_pos = 0;
21232     for(;;) {
21233         buf[ident_pos++] = c;
21234         c = *p;
21235         if (c >= 128 ||
21236             !((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1))
21237             break;
21238         p++;
21239         if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
21240             if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
21241                 atom = JS_ATOM_NULL;
21242                 goto done;
21243             }
21244         }
21245     }
21246     atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
21247  done:
21248     if (unlikely(buf != ident_buf))
21249         js_free(s->ctx, buf);
21250     *pp = p;
21251     return atom;
21252 }
21253 
json_next_token(JSParseState * s)21254 static __exception int json_next_token(JSParseState *s)
21255 {
21256     const uint8_t *p;
21257     int c;
21258     JSAtom atom;
21259 
21260     if (js_check_stack_overflow(s->ctx->rt, 0)) {
21261         return js_parse_error(s, "stack overflow");
21262     }
21263 
21264     free_token(s, &s->token);
21265 
21266     p = s->last_ptr = s->buf_ptr;
21267     s->last_line_num = s->token.line_num;
21268  redo:
21269     s->token.line_num = s->line_num;
21270     s->token.ptr = p;
21271     c = *p;
21272     switch(c) {
21273     case 0:
21274         if (p >= s->buf_end) {
21275             s->token.val = TOK_EOF;
21276         } else {
21277             goto def_token;
21278         }
21279         break;
21280     case '\'':
21281         if (!s->ext_json) {
21282             /* JSON does not accept single quoted strings */
21283             goto def_token;
21284         }
21285         /* fall through */
21286     case '\"':
21287         if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
21288             goto fail;
21289         break;
21290     case '\r':  /* accept DOS and MAC newline sequences */
21291         if (p[1] == '\n') {
21292             p++;
21293         }
21294         /* fall thru */
21295     case '\n':
21296         p++;
21297         s->line_num++;
21298         goto redo;
21299     case '\f':
21300     case '\v':
21301         if (!s->ext_json) {
21302             /* JSONWhitespace does not match <VT>, nor <FF> */
21303             goto def_token;
21304         }
21305         /* fall through */
21306     case ' ':
21307     case '\t':
21308         p++;
21309         goto redo;
21310     case '/':
21311         if (!s->ext_json) {
21312             /* JSON does not accept comments */
21313             goto def_token;
21314         }
21315         if (p[1] == '*') {
21316             /* comment */
21317             p += 2;
21318             for(;;) {
21319                 if (*p == '\0' && p >= s->buf_end) {
21320                     js_parse_error(s, "unexpected end of comment");
21321                     goto fail;
21322                 }
21323                 if (p[0] == '*' && p[1] == '/') {
21324                     p += 2;
21325                     break;
21326                 }
21327                 if (*p == '\n') {
21328                     s->line_num++;
21329                     p++;
21330                 } else if (*p == '\r') {
21331                     p++;
21332                 } else if (*p >= 0x80) {
21333                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
21334                     if (c == -1) {
21335                         p++; /* skip invalid UTF-8 */
21336                     }
21337                 } else {
21338                     p++;
21339                 }
21340             }
21341             goto redo;
21342         } else if (p[1] == '/') {
21343             /* line comment */
21344             p += 2;
21345             for(;;) {
21346                 if (*p == '\0' && p >= s->buf_end)
21347                     break;
21348                 if (*p == '\r' || *p == '\n')
21349                     break;
21350                 if (*p >= 0x80) {
21351                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
21352                     /* LS or PS are considered as line terminator */
21353                     if (c == CP_LS || c == CP_PS) {
21354                         break;
21355                     } else if (c == -1) {
21356                         p++; /* skip invalid UTF-8 */
21357                     }
21358                 } else {
21359                     p++;
21360                 }
21361             }
21362             goto redo;
21363         } else {
21364             goto def_token;
21365         }
21366         break;
21367     case 'a': case 'b': case 'c': case 'd':
21368     case 'e': case 'f': case 'g': case 'h':
21369     case 'i': case 'j': case 'k': case 'l':
21370     case 'm': case 'n': case 'o': case 'p':
21371     case 'q': case 'r': case 's': case 't':
21372     case 'u': case 'v': case 'w': case 'x':
21373     case 'y': case 'z':
21374     case 'A': case 'B': case 'C': case 'D':
21375     case 'E': case 'F': case 'G': case 'H':
21376     case 'I': case 'J': case 'K': case 'L':
21377     case 'M': case 'N': case 'O': case 'P':
21378     case 'Q': case 'R': case 'S': case 'T':
21379     case 'U': case 'V': case 'W': case 'X':
21380     case 'Y': case 'Z':
21381     case '_':
21382     case '$':
21383         /* identifier : only pure ascii characters are accepted */
21384         p++;
21385         atom = json_parse_ident(s, &p, c);
21386         if (atom == JS_ATOM_NULL)
21387             goto fail;
21388         s->token.u.ident.atom = atom;
21389         s->token.u.ident.has_escape = FALSE;
21390         s->token.u.ident.is_reserved = FALSE;
21391         s->token.val = TOK_IDENT;
21392         break;
21393     case '+':
21394         if (!s->ext_json || !is_digit(p[1]))
21395             goto def_token;
21396         goto parse_number;
21397     case '0':
21398         if (is_digit(p[1]))
21399             goto def_token;
21400         goto parse_number;
21401     case '-':
21402         if (!is_digit(p[1]))
21403             goto def_token;
21404         goto parse_number;
21405     case '1': case '2': case '3': case '4':
21406     case '5': case '6': case '7': case '8':
21407     case '9':
21408         /* number */
21409     parse_number:
21410         {
21411             JSValue ret;
21412             int flags, radix;
21413             if (!s->ext_json) {
21414                 flags = 0;
21415                 radix = 10;
21416             } else {
21417                 flags = ATOD_ACCEPT_BIN_OCT;
21418                 radix = 0;
21419             }
21420             ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
21421                           flags);
21422             if (JS_IsException(ret))
21423                 goto fail;
21424             s->token.val = TOK_NUMBER;
21425             s->token.u.num.val = ret;
21426         }
21427         break;
21428     default:
21429         if (c >= 128) {
21430             js_parse_error(s, "unexpected character");
21431             goto fail;
21432         }
21433     def_token:
21434         s->token.val = c;
21435         p++;
21436         break;
21437     }
21438     s->buf_ptr = p;
21439 
21440     //    dump_token(s, &s->token);
21441     return 0;
21442 
21443  fail:
21444     s->token.val = TOK_ERROR;
21445     return -1;
21446 }
21447 
21448 /* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is
21449    only set if TOK_IMPORT is returned */
21450 /* XXX: handle all unicode cases */
simple_next_token(const uint8_t ** pp,BOOL no_line_terminator)21451 static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator)
21452 {
21453     const uint8_t *p;
21454     uint32_t c;
21455 
21456     /* skip spaces and comments */
21457     p = *pp;
21458     for (;;) {
21459         switch(c = *p++) {
21460         case '\r':
21461         case '\n':
21462             if (no_line_terminator)
21463                 return '\n';
21464             continue;
21465         case ' ':
21466         case '\t':
21467         case '\v':
21468         case '\f':
21469             continue;
21470         case '/':
21471             if (*p == '/') {
21472                 if (no_line_terminator)
21473                     return '\n';
21474                 while (*p && *p != '\r' && *p != '\n')
21475                     p++;
21476                 continue;
21477             }
21478             if (*p == '*') {
21479                 while (*++p) {
21480                     if ((*p == '\r' || *p == '\n') && no_line_terminator)
21481                         return '\n';
21482                     if (*p == '*' && p[1] == '/') {
21483                         p += 2;
21484                         break;
21485                     }
21486                 }
21487                 continue;
21488             }
21489             break;
21490         case '=':
21491             if (*p == '>')
21492                 return TOK_ARROW;
21493             break;
21494         default:
21495             if (lre_js_is_ident_first(c)) {
21496                 if (c == 'i') {
21497                     if (p[0] == 'n' && !lre_js_is_ident_next(p[1])) {
21498                         return TOK_IN;
21499                     }
21500                     if (p[0] == 'm' && p[1] == 'p' && p[2] == 'o' &&
21501                         p[3] == 'r' && p[4] == 't' &&
21502                         !lre_js_is_ident_next(p[5])) {
21503                         *pp = p + 5;
21504                         return TOK_IMPORT;
21505                     }
21506                 } else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) {
21507                     return TOK_OF;
21508                 } else if (c == 'e' &&
21509                            p[0] == 'x' && p[1] == 'p' && p[2] == 'o' &&
21510                            p[3] == 'r' && p[4] == 't' &&
21511                            !lre_js_is_ident_next(p[5])) {
21512                     *pp = p + 5;
21513                     return TOK_EXPORT;
21514                 } else if (c == 'f' && p[0] == 'u' && p[1] == 'n' &&
21515                          p[2] == 'c' && p[3] == 't' && p[4] == 'i' &&
21516                          p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) {
21517                     return TOK_FUNCTION;
21518                 }
21519                 return TOK_IDENT;
21520             }
21521             break;
21522         }
21523         return c;
21524     }
21525 }
21526 
peek_token(JSParseState * s,BOOL no_line_terminator)21527 static int peek_token(JSParseState *s, BOOL no_line_terminator)
21528 {
21529     const uint8_t *p = s->buf_ptr;
21530     return simple_next_token(&p, no_line_terminator);
21531 }
21532 
21533 /* return true if 'input' contains the source of a module
21534    (heuristic). 'input' must be a zero terminated.
21535 
21536    Heuristic: skip comments and expect 'import' keyword not followed
21537    by '(' or '.' or export keyword.
21538 */
JS_DetectModule(const char * input,size_t input_len)21539 BOOL JS_DetectModule(const char *input, size_t input_len)
21540 {
21541     const uint8_t *p = (const uint8_t *)input;
21542     int tok;
21543     switch(simple_next_token(&p, FALSE)) {
21544     case TOK_IMPORT:
21545         tok = simple_next_token(&p, FALSE);
21546         return (tok != '.' && tok != '(');
21547     case TOK_EXPORT:
21548         return TRUE;
21549     default:
21550         return FALSE;
21551     }
21552 }
21553 
get_prev_opcode(JSFunctionDef * fd)21554 static inline int get_prev_opcode(JSFunctionDef *fd) {
21555     if (fd->last_opcode_pos < 0)
21556         return OP_invalid;
21557     else
21558         return fd->byte_code.buf[fd->last_opcode_pos];
21559 }
21560 
js_is_live_code(JSParseState * s)21561 static BOOL js_is_live_code(JSParseState *s) {
21562     switch (get_prev_opcode(s->cur_func)) {
21563     case OP_tail_call:
21564     case OP_tail_call_method:
21565     case OP_return:
21566     case OP_return_undef:
21567     case OP_return_async:
21568     case OP_throw:
21569     case OP_throw_error:
21570     case OP_goto:
21571 #if SHORT_OPCODES
21572     case OP_goto8:
21573     case OP_goto16:
21574 #endif
21575     case OP_ret:
21576         return FALSE;
21577     default:
21578         return TRUE;
21579     }
21580 }
21581 
emit_u8(JSParseState * s,uint8_t val)21582 static void emit_u8(JSParseState *s, uint8_t val)
21583 {
21584     dbuf_putc(&s->cur_func->byte_code, val);
21585 }
21586 
emit_u16(JSParseState * s,uint16_t val)21587 static void emit_u16(JSParseState *s, uint16_t val)
21588 {
21589     dbuf_put_u16(&s->cur_func->byte_code, val);
21590 }
21591 
emit_u32(JSParseState * s,uint32_t val)21592 static void emit_u32(JSParseState *s, uint32_t val)
21593 {
21594     dbuf_put_u32(&s->cur_func->byte_code, val);
21595 }
21596 
emit_op(JSParseState * s,uint8_t val)21597 static void emit_op(JSParseState *s, uint8_t val)
21598 {
21599     JSFunctionDef *fd = s->cur_func;
21600     DynBuf *bc = &fd->byte_code;
21601 
21602     /* Use the line number of the last token used, not the next token,
21603        nor the current offset in the source file.
21604      */
21605     if (unlikely(fd->last_opcode_line_num != s->last_line_num)) {
21606         dbuf_putc(bc, OP_line_num);
21607         dbuf_put_u32(bc, s->last_line_num);
21608         fd->last_opcode_line_num = s->last_line_num;
21609     }
21610     fd->last_opcode_pos = bc->size;
21611     dbuf_putc(bc, val);
21612 }
21613 
emit_atom(JSParseState * s,JSAtom name)21614 static void emit_atom(JSParseState *s, JSAtom name)
21615 {
21616     emit_u32(s, JS_DupAtom(s->ctx, name));
21617 }
21618 
update_label(JSFunctionDef * s,int label,int delta)21619 static int update_label(JSFunctionDef *s, int label, int delta)
21620 {
21621     LabelSlot *ls;
21622 
21623     assert(label >= 0 && label < s->label_count);
21624     ls = &s->label_slots[label];
21625     ls->ref_count += delta;
21626     assert(ls->ref_count >= 0);
21627     return ls->ref_count;
21628 }
21629 
new_label_fd(JSFunctionDef * fd,int label)21630 static int new_label_fd(JSFunctionDef *fd, int label)
21631 {
21632     LabelSlot *ls;
21633 
21634     if (label < 0) {
21635         if (js_resize_array(fd->ctx, (void *)&fd->label_slots,
21636                             sizeof(fd->label_slots[0]),
21637                             &fd->label_size, fd->label_count + 1))
21638             return -1;
21639         label = fd->label_count++;
21640         ls = &fd->label_slots[label];
21641         ls->ref_count = 0;
21642         ls->pos = -1;
21643         ls->pos2 = -1;
21644         ls->addr = -1;
21645         ls->first_reloc = NULL;
21646     }
21647     return label;
21648 }
21649 
new_label(JSParseState * s)21650 static int new_label(JSParseState *s)
21651 {
21652     return new_label_fd(s->cur_func, -1);
21653 }
21654 
21655 /* return the label ID offset */
emit_label(JSParseState * s,int label)21656 static int emit_label(JSParseState *s, int label)
21657 {
21658     if (label >= 0) {
21659         emit_op(s, OP_label);
21660         emit_u32(s, label);
21661         s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;
21662         return s->cur_func->byte_code.size - 4;
21663     } else {
21664         return -1;
21665     }
21666 }
21667 
21668 /* return label or -1 if dead code */
emit_goto(JSParseState * s,int opcode,int label)21669 static int emit_goto(JSParseState *s, int opcode, int label)
21670 {
21671     if (js_is_live_code(s)) {
21672         if (label < 0)
21673             label = new_label(s);
21674         emit_op(s, opcode);
21675         emit_u32(s, label);
21676         s->cur_func->label_slots[label].ref_count++;
21677         return label;
21678     }
21679     return -1;
21680 }
21681 
21682 /* return the constant pool index. 'val' is not duplicated. */
cpool_add(JSParseState * s,JSValue val)21683 static int cpool_add(JSParseState *s, JSValue val)
21684 {
21685     JSFunctionDef *fd = s->cur_func;
21686 
21687     if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]),
21688                         &fd->cpool_size, fd->cpool_count + 1))
21689         return -1;
21690     fd->cpool[fd->cpool_count++] = val;
21691     return fd->cpool_count - 1;
21692 }
21693 
emit_push_const(JSParseState * s,JSValueConst val,BOOL as_atom)21694 static __exception int emit_push_const(JSParseState *s, JSValueConst val,
21695                                        BOOL as_atom)
21696 {
21697     int idx;
21698 
21699     if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) {
21700         JSAtom atom;
21701         /* warning: JS_NewAtomStr frees the string value */
21702         JS_DupValue(s->ctx, val);
21703         atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val));
21704         if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) {
21705             emit_op(s, OP_push_atom_value);
21706             emit_u32(s, atom);
21707             return 0;
21708         }
21709     }
21710 
21711     idx = cpool_add(s, JS_DupValue(s->ctx, val));
21712     if (idx < 0)
21713         return -1;
21714     emit_op(s, OP_push_const);
21715     emit_u32(s, idx);
21716     return 0;
21717 }
21718 
21719 /* return the variable index or -1 if not found,
21720    add ARGUMENT_VAR_OFFSET for argument variables */
find_arg(JSContext * ctx,JSFunctionDef * fd,JSAtom name)21721 static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
21722 {
21723     int i;
21724     for(i = fd->arg_count; i-- > 0;) {
21725         if (fd->args[i].var_name == name)
21726             return i | ARGUMENT_VAR_OFFSET;
21727     }
21728     return -1;
21729 }
21730 
find_var(JSContext * ctx,JSFunctionDef * fd,JSAtom name)21731 static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
21732 {
21733     int i;
21734     for(i = fd->var_count; i-- > 0;) {
21735         if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0)
21736             return i;
21737     }
21738     return find_arg(ctx, fd, name);
21739 }
21740 
21741 /* find a variable declaration in a given scope */
find_var_in_scope(JSContext * ctx,JSFunctionDef * fd,JSAtom name,int scope_level)21742 static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd,
21743                              JSAtom name, int scope_level)
21744 {
21745     int scope_idx;
21746     for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0;
21747         scope_idx = fd->vars[scope_idx].scope_next) {
21748         if (fd->vars[scope_idx].scope_level != scope_level)
21749             break;
21750         if (fd->vars[scope_idx].var_name == name)
21751             return scope_idx;
21752     }
21753     return -1;
21754 }
21755 
21756 /* return true if scope == parent_scope or if scope is a child of
21757    parent_scope */
is_child_scope(JSContext * ctx,JSFunctionDef * fd,int scope,int parent_scope)21758 static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd,
21759                            int scope, int parent_scope)
21760 {
21761     while (scope >= 0) {
21762         if (scope == parent_scope)
21763             return TRUE;
21764         scope = fd->scopes[scope].parent;
21765     }
21766     return FALSE;
21767 }
21768 
21769 /* find a 'var' declaration in the same scope or a child scope */
find_var_in_child_scope(JSContext * ctx,JSFunctionDef * fd,JSAtom name,int scope_level)21770 static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd,
21771                                    JSAtom name, int scope_level)
21772 {
21773     int i;
21774     for(i = 0; i < fd->var_count; i++) {
21775         JSVarDef *vd = &fd->vars[i];
21776         if (vd->var_name == name && vd->scope_level == 0) {
21777             if (is_child_scope(ctx, fd, vd->scope_next,
21778                                scope_level))
21779                 return i;
21780         }
21781     }
21782     return -1;
21783 }
21784 
21785 
find_global_var(JSFunctionDef * fd,JSAtom name)21786 static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name)
21787 {
21788     int i;
21789     for(i = 0; i < fd->global_var_count; i++) {
21790         JSGlobalVar *hf = &fd->global_vars[i];
21791         if (hf->var_name == name)
21792             return hf;
21793     }
21794     return NULL;
21795 
21796 }
21797 
find_lexical_global_var(JSFunctionDef * fd,JSAtom name)21798 static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name)
21799 {
21800     JSGlobalVar *hf = find_global_var(fd, name);
21801     if (hf && hf->is_lexical)
21802         return hf;
21803     else
21804         return NULL;
21805 }
21806 
find_lexical_decl(JSContext * ctx,JSFunctionDef * fd,JSAtom name,int scope_idx,BOOL check_catch_var)21807 static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
21808                              int scope_idx, BOOL check_catch_var)
21809 {
21810     while (scope_idx >= 0) {
21811         JSVarDef *vd = &fd->vars[scope_idx];
21812         if (vd->var_name == name &&
21813             (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH &&
21814                                 check_catch_var)))
21815             return scope_idx;
21816         scope_idx = vd->scope_next;
21817     }
21818 
21819     if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) {
21820         if (find_lexical_global_var(fd, name))
21821             return GLOBAL_VAR_OFFSET;
21822     }
21823     return -1;
21824 }
21825 
push_scope(JSParseState * s)21826 static int push_scope(JSParseState *s) {
21827     if (s->cur_func) {
21828         JSFunctionDef *fd = s->cur_func;
21829         int scope = fd->scope_count;
21830         /* XXX: should check for scope overflow */
21831         if ((fd->scope_count + 1) > fd->scope_size) {
21832             int new_size;
21833             size_t slack;
21834             JSVarScope *new_buf;
21835             /* XXX: potential arithmetic overflow */
21836             new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2);
21837             if (fd->scopes == fd->def_scope_array) {
21838                 new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack);
21839                 if (!new_buf)
21840                     return -1;
21841                 memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes));
21842             } else {
21843                 new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack);
21844                 if (!new_buf)
21845                     return -1;
21846             }
21847             new_size += slack / sizeof(*new_buf);
21848             fd->scopes = new_buf;
21849             fd->scope_size = new_size;
21850         }
21851         fd->scope_count++;
21852         fd->scopes[scope].parent = fd->scope_level;
21853         fd->scopes[scope].first = fd->scope_first;
21854         emit_op(s, OP_enter_scope);
21855         emit_u16(s, scope);
21856         return fd->scope_level = scope;
21857     }
21858     return 0;
21859 }
21860 
get_first_lexical_var(JSFunctionDef * fd,int scope)21861 static int get_first_lexical_var(JSFunctionDef *fd, int scope)
21862 {
21863     while (scope >= 0) {
21864         int scope_idx = fd->scopes[scope].first;
21865         if (scope_idx >= 0)
21866             return scope_idx;
21867         scope = fd->scopes[scope].parent;
21868     }
21869     return -1;
21870 }
21871 
pop_scope(JSParseState * s)21872 static void pop_scope(JSParseState *s) {
21873     if (s->cur_func) {
21874         /* disable scoped variables */
21875         JSFunctionDef *fd = s->cur_func;
21876         int scope = fd->scope_level;
21877         emit_op(s, OP_leave_scope);
21878         emit_u16(s, scope);
21879         fd->scope_level = fd->scopes[scope].parent;
21880         fd->scope_first = get_first_lexical_var(fd, fd->scope_level);
21881     }
21882 }
21883 
close_scopes(JSParseState * s,int scope,int scope_stop)21884 static void close_scopes(JSParseState *s, int scope, int scope_stop)
21885 {
21886     while (scope > scope_stop) {
21887         emit_op(s, OP_leave_scope);
21888         emit_u16(s, scope);
21889         scope = s->cur_func->scopes[scope].parent;
21890     }
21891 }
21892 
21893 /* return the variable index or -1 if error */
add_var(JSContext * ctx,JSFunctionDef * fd,JSAtom name)21894 static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
21895 {
21896     JSVarDef *vd;
21897 
21898     /* the local variable indexes are currently stored on 16 bits */
21899     if (fd->var_count >= JS_MAX_LOCAL_VARS) {
21900         JS_ThrowInternalError(ctx, "too many local variables");
21901         return -1;
21902     }
21903     if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]),
21904                         &fd->var_size, fd->var_count + 1))
21905         return -1;
21906     vd = &fd->vars[fd->var_count++];
21907     memset(vd, 0, sizeof(*vd));
21908     vd->var_name = JS_DupAtom(ctx, name);
21909     vd->func_pool_idx = -1;
21910     return fd->var_count - 1;
21911 }
21912 
add_scope_var(JSContext * ctx,JSFunctionDef * fd,JSAtom name,JSVarKindEnum var_kind)21913 static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
21914                          JSVarKindEnum var_kind)
21915 {
21916     int idx = add_var(ctx, fd, name);
21917     if (idx >= 0) {
21918         JSVarDef *vd = &fd->vars[idx];
21919         vd->var_kind = var_kind;
21920         vd->scope_level = fd->scope_level;
21921         vd->scope_next = fd->scope_first;
21922         fd->scopes[fd->scope_level].first = idx;
21923         fd->scope_first = idx;
21924     }
21925     return idx;
21926 }
21927 
add_func_var(JSContext * ctx,JSFunctionDef * fd,JSAtom name)21928 static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
21929 {
21930     int idx = fd->func_var_idx;
21931     if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) {
21932         fd->func_var_idx = idx;
21933         fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME;
21934         if (fd->js_mode & JS_MODE_STRICT)
21935             fd->vars[idx].is_const = TRUE;
21936     }
21937     return idx;
21938 }
21939 
add_arguments_var(JSContext * ctx,JSFunctionDef * fd)21940 static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd)
21941 {
21942     int idx = fd->arguments_var_idx;
21943     if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) {
21944         fd->arguments_var_idx = idx;
21945     }
21946     return idx;
21947 }
21948 
21949 /* add an argument definition in the argument scope. Only needed when
21950    "eval()" may be called in the argument scope. Return 0 if OK. */
add_arguments_arg(JSContext * ctx,JSFunctionDef * fd)21951 static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd)
21952 {
21953     int idx;
21954     if (fd->arguments_arg_idx < 0) {
21955         idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX);
21956         if (idx < 0) {
21957             /* XXX: the scope links are not fully updated. May be an
21958                issue if there are child scopes of the argument
21959                scope */
21960             idx = add_var(ctx, fd, JS_ATOM_arguments);
21961             if (idx < 0)
21962                 return -1;
21963             fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first;
21964             fd->scopes[ARG_SCOPE_INDEX].first = idx;
21965             fd->vars[idx].scope_level = ARG_SCOPE_INDEX;
21966             fd->vars[idx].is_lexical = TRUE;
21967 
21968             fd->arguments_arg_idx = idx;
21969         }
21970     }
21971     return 0;
21972 }
21973 
add_arg(JSContext * ctx,JSFunctionDef * fd,JSAtom name)21974 static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
21975 {
21976     JSVarDef *vd;
21977 
21978     /* the local variable indexes are currently stored on 16 bits */
21979     if (fd->arg_count >= JS_MAX_LOCAL_VARS) {
21980         JS_ThrowInternalError(ctx, "too many arguments");
21981         return -1;
21982     }
21983     if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]),
21984                         &fd->arg_size, fd->arg_count + 1))
21985         return -1;
21986     vd = &fd->args[fd->arg_count++];
21987     memset(vd, 0, sizeof(*vd));
21988     vd->var_name = JS_DupAtom(ctx, name);
21989     vd->func_pool_idx = -1;
21990     return fd->arg_count - 1;
21991 }
21992 
21993 /* add a global variable definition */
add_global_var(JSContext * ctx,JSFunctionDef * s,JSAtom name)21994 static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s,
21995                                      JSAtom name)
21996 {
21997     JSGlobalVar *hf;
21998 
21999     if (js_resize_array(ctx, (void **)&s->global_vars,
22000                         sizeof(s->global_vars[0]),
22001                         &s->global_var_size, s->global_var_count + 1))
22002         return NULL;
22003     hf = &s->global_vars[s->global_var_count++];
22004     hf->cpool_idx = -1;
22005     hf->force_init = FALSE;
22006     hf->is_lexical = FALSE;
22007     hf->is_const = FALSE;
22008     hf->scope_level = s->scope_level;
22009     hf->var_name = JS_DupAtom(ctx, name);
22010     return hf;
22011 }
22012 
22013 typedef enum {
22014     JS_VAR_DEF_WITH,
22015     JS_VAR_DEF_LET,
22016     JS_VAR_DEF_CONST,
22017     JS_VAR_DEF_FUNCTION_DECL, /* function declaration */
22018     JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */
22019     JS_VAR_DEF_CATCH,
22020     JS_VAR_DEF_VAR,
22021 } JSVarDefEnum;
22022 
define_var(JSParseState * s,JSFunctionDef * fd,JSAtom name,JSVarDefEnum var_def_type)22023 static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,
22024                       JSVarDefEnum var_def_type)
22025 {
22026     JSContext *ctx = s->ctx;
22027     JSVarDef *vd;
22028     int idx;
22029 
22030     switch (var_def_type) {
22031     case JS_VAR_DEF_WITH:
22032         idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL);
22033         break;
22034 
22035     case JS_VAR_DEF_LET:
22036     case JS_VAR_DEF_CONST:
22037     case JS_VAR_DEF_FUNCTION_DECL:
22038     case JS_VAR_DEF_NEW_FUNCTION_DECL:
22039         idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE);
22040         if (idx >= 0) {
22041             if (idx < GLOBAL_VAR_OFFSET) {
22042                 if (fd->vars[idx].scope_level == fd->scope_level) {
22043                     /* same scope: in non strict mode, functions
22044                        can be redefined (annex B.3.3.4). */
22045                     if (!(!(fd->js_mode & JS_MODE_STRICT) &&
22046                           var_def_type == JS_VAR_DEF_FUNCTION_DECL &&
22047                           fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) {
22048                         goto redef_lex_error;
22049                     }
22050                 } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) {
22051                     goto redef_lex_error;
22052                 }
22053             } else {
22054                 if (fd->scope_level == fd->body_scope) {
22055                 redef_lex_error:
22056                     /* redefining a scoped var in the same scope: error */
22057                     return js_parse_error(s, "invalid redefinition of lexical identifier");
22058                 }
22059             }
22060         }
22061         if (var_def_type != JS_VAR_DEF_FUNCTION_DECL &&
22062             var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL &&
22063             fd->scope_level == fd->body_scope &&
22064             find_arg(ctx, fd, name) >= 0) {
22065             /* lexical variable redefines a parameter name */
22066             return js_parse_error(s, "invalid redefinition of parameter name");
22067         }
22068 
22069         if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) {
22070             return js_parse_error(s, "invalid redefinition of a variable");
22071         }
22072 
22073         if (fd->is_global_var) {
22074             JSGlobalVar *hf;
22075             hf = find_global_var(fd, name);
22076             if (hf && is_child_scope(ctx, fd, hf->scope_level,
22077                                      fd->scope_level)) {
22078                 return js_parse_error(s, "invalid redefinition of global identifier");
22079             }
22080         }
22081 
22082         if (fd->is_eval &&
22083             (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
22084              fd->eval_type == JS_EVAL_TYPE_MODULE) &&
22085             fd->scope_level == fd->body_scope) {
22086             JSGlobalVar *hf;
22087             hf = add_global_var(s->ctx, fd, name);
22088             if (!hf)
22089                 return -1;
22090             hf->is_lexical = TRUE;
22091             hf->is_const = (var_def_type == JS_VAR_DEF_CONST);
22092             idx = GLOBAL_VAR_OFFSET;
22093         } else {
22094             JSVarKindEnum var_kind;
22095             if (var_def_type == JS_VAR_DEF_FUNCTION_DECL)
22096                 var_kind = JS_VAR_FUNCTION_DECL;
22097             else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL)
22098                 var_kind = JS_VAR_NEW_FUNCTION_DECL;
22099             else
22100                 var_kind = JS_VAR_NORMAL;
22101             idx = add_scope_var(ctx, fd, name, var_kind);
22102             if (idx >= 0) {
22103                 vd = &fd->vars[idx];
22104                 vd->is_lexical = 1;
22105                 vd->is_const = (var_def_type == JS_VAR_DEF_CONST);
22106             }
22107         }
22108         break;
22109 
22110     case JS_VAR_DEF_CATCH:
22111         idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH);
22112         break;
22113 
22114     case JS_VAR_DEF_VAR:
22115         if (find_lexical_decl(ctx, fd, name, fd->scope_first,
22116                               FALSE) >= 0) {
22117        invalid_lexical_redefinition:
22118             /* error to redefine a var that inside a lexical scope */
22119             return js_parse_error(s, "invalid redefinition of lexical identifier");
22120         }
22121         if (fd->is_global_var) {
22122             JSGlobalVar *hf;
22123             hf = find_global_var(fd, name);
22124             if (hf && hf->is_lexical && hf->scope_level == fd->scope_level &&
22125                 fd->eval_type == JS_EVAL_TYPE_MODULE) {
22126                 goto invalid_lexical_redefinition;
22127             }
22128             hf = add_global_var(s->ctx, fd, name);
22129             if (!hf)
22130                 return -1;
22131             idx = GLOBAL_VAR_OFFSET;
22132         } else {
22133             /* if the variable already exists, don't add it again  */
22134             idx = find_var(ctx, fd, name);
22135             if (idx >= 0)
22136                 break;
22137             idx = add_var(ctx, fd, name);
22138             if (idx >= 0) {
22139                 if (name == JS_ATOM_arguments && fd->has_arguments_binding)
22140                     fd->arguments_var_idx = idx;
22141                 fd->vars[idx].scope_next = fd->scope_level;
22142             }
22143         }
22144         break;
22145     default:
22146         abort();
22147     }
22148     return idx;
22149 }
22150 
22151 /* add a private field variable in the current scope */
add_private_class_field(JSParseState * s,JSFunctionDef * fd,JSAtom name,JSVarKindEnum var_kind)22152 static int add_private_class_field(JSParseState *s, JSFunctionDef *fd,
22153                                    JSAtom name, JSVarKindEnum var_kind)
22154 {
22155     JSContext *ctx = s->ctx;
22156     JSVarDef *vd;
22157     int idx;
22158 
22159     idx = add_scope_var(ctx, fd, name, var_kind);
22160     if (idx < 0)
22161         return idx;
22162     vd = &fd->vars[idx];
22163     vd->is_lexical = 1;
22164     vd->is_const = 1;
22165     return idx;
22166 }
22167 
22168 static __exception int js_parse_expr(JSParseState *s);
22169 static __exception int js_parse_function_decl(JSParseState *s,
22170                                               JSParseFunctionEnum func_type,
22171                                               JSFunctionKindEnum func_kind,
22172                                               JSAtom func_name, const uint8_t *ptr,
22173                                               int start_line);
22174 static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s);
22175 static __exception int js_parse_function_decl2(JSParseState *s,
22176                                                JSParseFunctionEnum func_type,
22177                                                JSFunctionKindEnum func_kind,
22178                                                JSAtom func_name,
22179                                                const uint8_t *ptr,
22180                                                int function_line_num,
22181                                                JSParseExportEnum export_flag,
22182                                                JSFunctionDef **pfd);
22183 static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags);
22184 static __exception int js_parse_assign_expr(JSParseState *s);
22185 static __exception int js_parse_unary(JSParseState *s, int parse_flags);
22186 static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
22187                              JSAtom label_name,
22188                              int label_break, int label_cont,
22189                              int drop_count);
22190 static void pop_break_entry(JSFunctionDef *fd);
22191 static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
22192                                        JSAtom local_name, JSAtom export_name,
22193                                        JSExportTypeEnum export_type);
22194 
22195 /* Note: all the fields are already sealed except length */
seal_template_obj(JSContext * ctx,JSValueConst obj)22196 static int seal_template_obj(JSContext *ctx, JSValueConst obj)
22197 {
22198     JSObject *p;
22199     JSShapeProperty *prs;
22200 
22201     p = JS_VALUE_GET_OBJ(obj);
22202     prs = find_own_property1(p, JS_ATOM_length);
22203     if (prs) {
22204         if (js_update_property_flags(ctx, p, &prs,
22205                                      prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)))
22206             return -1;
22207     }
22208     p->extensible = FALSE;
22209     return 0;
22210 }
22211 
js_parse_template(JSParseState * s,int call,int * argc)22212 static __exception int js_parse_template(JSParseState *s, int call, int *argc)
22213 {
22214     JSContext *ctx = s->ctx;
22215     JSValue raw_array, template_object;
22216     JSToken cooked;
22217     int depth, ret;
22218 
22219     raw_array = JS_UNDEFINED; /* avoid warning */
22220     template_object = JS_UNDEFINED; /* avoid warning */
22221     if (call) {
22222         /* Create a template object: an array of cooked strings */
22223         /* Create an array of raw strings and store it to the raw property */
22224         template_object = JS_NewArray(ctx);
22225         if (JS_IsException(template_object))
22226             return -1;
22227         //        pool_idx = s->cur_func->cpool_count;
22228         ret = emit_push_const(s, template_object, 0);
22229         JS_FreeValue(ctx, template_object);
22230         if (ret)
22231             return -1;
22232         raw_array = JS_NewArray(ctx);
22233         if (JS_IsException(raw_array))
22234             return -1;
22235         if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw,
22236                                    raw_array, JS_PROP_THROW) < 0) {
22237             return -1;
22238         }
22239     }
22240 
22241     depth = 0;
22242     while (s->token.val == TOK_TEMPLATE) {
22243         const uint8_t *p = s->token.ptr + 1;
22244         cooked = s->token;
22245         if (call) {
22246             if (JS_DefinePropertyValueUint32(ctx, raw_array, depth,
22247                                              JS_DupValue(ctx, s->token.u.str.str),
22248                                              JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
22249                 return -1;
22250             }
22251             /* re-parse the string with escape sequences but do not throw a
22252                syntax error if it contains invalid sequences
22253              */
22254             if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) {
22255                 cooked.u.str.str = JS_UNDEFINED;
22256             }
22257             if (JS_DefinePropertyValueUint32(ctx, template_object, depth,
22258                                              cooked.u.str.str,
22259                                              JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
22260                 return -1;
22261             }
22262         } else {
22263             JSString *str;
22264             /* re-parse the string with escape sequences and throw a
22265                syntax error if it contains invalid sequences
22266              */
22267             JS_FreeValue(ctx, s->token.u.str.str);
22268             s->token.u.str.str = JS_UNDEFINED;
22269             if (js_parse_string(s, '`', TRUE, p, &cooked, &p))
22270                 return -1;
22271             str = JS_VALUE_GET_STRING(cooked.u.str.str);
22272             if (str->len != 0 || depth == 0) {
22273                 ret = emit_push_const(s, cooked.u.str.str, 1);
22274                 JS_FreeValue(s->ctx, cooked.u.str.str);
22275                 if (ret)
22276                     return -1;
22277                 if (depth == 0) {
22278                     if (s->token.u.str.sep == '`')
22279                         goto done1;
22280                     emit_op(s, OP_get_field2);
22281                     emit_atom(s, JS_ATOM_concat);
22282                 }
22283                 depth++;
22284             } else {
22285                 JS_FreeValue(s->ctx, cooked.u.str.str);
22286             }
22287         }
22288         if (s->token.u.str.sep == '`')
22289             goto done;
22290         if (next_token(s))
22291             return -1;
22292         if (js_parse_expr(s))
22293             return -1;
22294         depth++;
22295         if (s->token.val != '}') {
22296             return js_parse_error(s, "expected '}' after template expression");
22297         }
22298         /* XXX: should convert to string at this stage? */
22299         free_token(s, &s->token);
22300         /* Resume TOK_TEMPLATE parsing (s->token.line_num and
22301          * s->token.ptr are OK) */
22302         s->got_lf = FALSE;
22303         s->last_line_num = s->token.line_num;
22304         if (js_parse_template_part(s, s->buf_ptr))
22305             return -1;
22306     }
22307     return js_parse_expect(s, TOK_TEMPLATE);
22308 
22309  done:
22310     if (call) {
22311         /* Seal the objects */
22312         seal_template_obj(ctx, raw_array);
22313         seal_template_obj(ctx, template_object);
22314         *argc = depth + 1;
22315     } else {
22316         emit_op(s, OP_call_method);
22317         emit_u16(s, depth - 1);
22318     }
22319  done1:
22320     return next_token(s);
22321 }
22322 
22323 
22324 #define PROP_TYPE_IDENT 0
22325 #define PROP_TYPE_VAR   1
22326 #define PROP_TYPE_GET   2
22327 #define PROP_TYPE_SET   3
22328 #define PROP_TYPE_STAR  4
22329 #define PROP_TYPE_ASYNC 5
22330 #define PROP_TYPE_ASYNC_STAR 6
22331 
22332 #define PROP_TYPE_PRIVATE (1 << 4)
22333 
token_is_ident(int tok)22334 static BOOL token_is_ident(int tok)
22335 {
22336     /* Accept keywords and reserved words as property names */
22337     return (tok == TOK_IDENT ||
22338             (tok >= TOK_FIRST_KEYWORD &&
22339              tok <= TOK_LAST_KEYWORD));
22340 }
22341 
22342 /* if the property is an expression, name = JS_ATOM_NULL */
js_parse_property_name(JSParseState * s,JSAtom * pname,BOOL allow_method,BOOL allow_var,BOOL allow_private)22343 static int __exception js_parse_property_name(JSParseState *s,
22344                                               JSAtom *pname,
22345                                               BOOL allow_method, BOOL allow_var,
22346                                               BOOL allow_private)
22347 {
22348     int is_private = 0;
22349     BOOL is_non_reserved_ident;
22350     JSAtom name;
22351     int prop_type;
22352 
22353     prop_type = PROP_TYPE_IDENT;
22354     if (allow_method) {
22355         if (token_is_pseudo_keyword(s, JS_ATOM_get)
22356         ||  token_is_pseudo_keyword(s, JS_ATOM_set)) {
22357             /* get x(), set x() */
22358             name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
22359             if (next_token(s))
22360                 goto fail1;
22361             if (s->token.val == ':' || s->token.val == ',' ||
22362                 s->token.val == '}' || s->token.val == '(') {
22363                 is_non_reserved_ident = TRUE;
22364                 goto ident_found;
22365             }
22366             prop_type = PROP_TYPE_GET + (name == JS_ATOM_set);
22367             JS_FreeAtom(s->ctx, name);
22368         } else if (s->token.val == '*') {
22369             if (next_token(s))
22370                 goto fail;
22371             prop_type = PROP_TYPE_STAR;
22372         } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
22373                    peek_token(s, TRUE) != '\n') {
22374             name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
22375             if (next_token(s))
22376                 goto fail1;
22377             if (s->token.val == ':' || s->token.val == ',' ||
22378                 s->token.val == '}' || s->token.val == '(') {
22379                 is_non_reserved_ident = TRUE;
22380                 goto ident_found;
22381             }
22382             JS_FreeAtom(s->ctx, name);
22383             if (s->token.val == '*') {
22384                 if (next_token(s))
22385                     goto fail;
22386                 prop_type = PROP_TYPE_ASYNC_STAR;
22387             } else {
22388                 prop_type = PROP_TYPE_ASYNC;
22389             }
22390         }
22391     }
22392 
22393     if (token_is_ident(s->token.val)) {
22394         /* variable can only be a non-reserved identifier */
22395         is_non_reserved_ident =
22396             (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved);
22397         /* keywords and reserved words have a valid atom */
22398         name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
22399         if (next_token(s))
22400             goto fail1;
22401     ident_found:
22402         if (is_non_reserved_ident &&
22403             prop_type == PROP_TYPE_IDENT && allow_var) {
22404             if (!(s->token.val == ':' ||
22405                   (s->token.val == '(' && allow_method))) {
22406                 prop_type = PROP_TYPE_VAR;
22407             }
22408         }
22409     } else if (s->token.val == TOK_STRING) {
22410         name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
22411         if (name == JS_ATOM_NULL)
22412             goto fail;
22413         if (next_token(s))
22414             goto fail1;
22415     } else if (s->token.val == TOK_NUMBER) {
22416         JSValue val;
22417         val = s->token.u.num.val;
22418 #ifdef CONFIG_BIGNUM
22419         if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
22420             JSBigFloat *p = JS_VALUE_GET_PTR(val);
22421             val = s->ctx->rt->bigfloat_ops.
22422                 mul_pow10_to_float64(s->ctx, &p->num,
22423                                      s->token.u.num.exponent);
22424             if (JS_IsException(val))
22425                 goto fail;
22426             name = JS_ValueToAtom(s->ctx, val);
22427             JS_FreeValue(s->ctx, val);
22428         } else
22429 #endif
22430         {
22431             name = JS_ValueToAtom(s->ctx, val);
22432         }
22433         if (name == JS_ATOM_NULL)
22434             goto fail;
22435         if (next_token(s))
22436             goto fail1;
22437     } else if (s->token.val == '[') {
22438         if (next_token(s))
22439             goto fail;
22440         if (js_parse_expr(s))
22441             goto fail;
22442         if (js_parse_expect(s, ']'))
22443             goto fail;
22444         name = JS_ATOM_NULL;
22445     } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) {
22446         name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
22447         if (next_token(s))
22448             goto fail1;
22449         is_private = PROP_TYPE_PRIVATE;
22450     } else {
22451         goto invalid_prop;
22452     }
22453     if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR &&
22454         s->token.val != '(') {
22455         JS_FreeAtom(s->ctx, name);
22456     invalid_prop:
22457         js_parse_error(s, "invalid property name");
22458         goto fail;
22459     }
22460     *pname = name;
22461     return prop_type | is_private;
22462  fail1:
22463     JS_FreeAtom(s->ctx, name);
22464  fail:
22465     *pname = JS_ATOM_NULL;
22466     return -1;
22467 }
22468 
22469 typedef struct JSParsePos {
22470     int last_line_num;
22471     int line_num;
22472     BOOL got_lf;
22473     const uint8_t *ptr;
22474 } JSParsePos;
22475 
js_parse_get_pos(JSParseState * s,JSParsePos * sp)22476 static int js_parse_get_pos(JSParseState *s, JSParsePos *sp)
22477 {
22478     sp->last_line_num = s->last_line_num;
22479     sp->line_num = s->token.line_num;
22480     sp->ptr = s->token.ptr;
22481     sp->got_lf = s->got_lf;
22482     return 0;
22483 }
22484 
js_parse_seek_token(JSParseState * s,const JSParsePos * sp)22485 static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)
22486 {
22487     s->token.line_num = sp->last_line_num;
22488     s->line_num = sp->line_num;
22489     s->buf_ptr = sp->ptr;
22490     s->got_lf = sp->got_lf;
22491     return next_token(s);
22492 }
22493 
22494 /* return TRUE if a regexp literal is allowed after this token */
is_regexp_allowed(int tok)22495 static BOOL is_regexp_allowed(int tok)
22496 {
22497     switch (tok) {
22498     case TOK_NUMBER:
22499     case TOK_STRING:
22500     case TOK_REGEXP:
22501     case TOK_DEC:
22502     case TOK_INC:
22503     case TOK_NULL:
22504     case TOK_FALSE:
22505     case TOK_TRUE:
22506     case TOK_THIS:
22507     case ')':
22508     case ']':
22509     case '}': /* XXX: regexp may occur after */
22510     case TOK_IDENT:
22511         return FALSE;
22512     default:
22513         return TRUE;
22514     }
22515 }
22516 
22517 #define SKIP_HAS_SEMI       (1 << 0)
22518 #define SKIP_HAS_ELLIPSIS   (1 << 1)
22519 #define SKIP_HAS_ASSIGNMENT (1 << 2)
22520 
22521 /* XXX: improve speed with early bailout */
22522 /* XXX: no longer works if regexps are present. Could use previous
22523    regexp parsing heuristics to handle most cases */
js_parse_skip_parens_token(JSParseState * s,int * pbits,BOOL no_line_terminator)22524 static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator)
22525 {
22526     char state[256];
22527     size_t level = 0;
22528     JSParsePos pos;
22529     int last_tok, tok = TOK_EOF;
22530     int c, tok_len, bits = 0;
22531 
22532     /* protect from underflow */
22533     state[level++] = 0;
22534 
22535     js_parse_get_pos(s, &pos);
22536     last_tok = 0;
22537     for (;;) {
22538         switch(s->token.val) {
22539         case '(':
22540         case '[':
22541         case '{':
22542             if (level >= sizeof(state))
22543                 goto done;
22544             state[level++] = s->token.val;
22545             break;
22546         case ')':
22547             if (state[--level] != '(')
22548                 goto done;
22549             break;
22550         case ']':
22551             if (state[--level] != '[')
22552                 goto done;
22553             break;
22554         case '}':
22555             c = state[--level];
22556             if (c == '`') {
22557                 /* continue the parsing of the template */
22558                 free_token(s, &s->token);
22559                 /* Resume TOK_TEMPLATE parsing (s->token.line_num and
22560                  * s->token.ptr are OK) */
22561                 s->got_lf = FALSE;
22562                 s->last_line_num = s->token.line_num;
22563                 if (js_parse_template_part(s, s->buf_ptr))
22564                     goto done;
22565                 goto handle_template;
22566             } else if (c != '{') {
22567                 goto done;
22568             }
22569             break;
22570         case TOK_TEMPLATE:
22571         handle_template:
22572             if (s->token.u.str.sep != '`') {
22573                 /* '${' inside the template : closing '}' and continue
22574                    parsing the template */
22575                 if (level >= sizeof(state))
22576                     goto done;
22577                 state[level++] = '`';
22578             }
22579             break;
22580         case TOK_EOF:
22581             goto done;
22582         case ';':
22583             if (level == 2) {
22584                 bits |= SKIP_HAS_SEMI;
22585             }
22586             break;
22587         case TOK_ELLIPSIS:
22588             if (level == 2) {
22589                 bits |= SKIP_HAS_ELLIPSIS;
22590             }
22591             break;
22592         case '=':
22593             bits |= SKIP_HAS_ASSIGNMENT;
22594             break;
22595 
22596         case TOK_DIV_ASSIGN:
22597             tok_len = 2;
22598             goto parse_regexp;
22599         case '/':
22600             tok_len = 1;
22601         parse_regexp:
22602             if (is_regexp_allowed(last_tok)) {
22603                 s->buf_ptr -= tok_len;
22604                 if (js_parse_regexp(s)) {
22605                     /* XXX: should clear the exception */
22606                     goto done;
22607                 }
22608             }
22609             break;
22610         }
22611         /* last_tok is only used to recognize regexps */
22612         if (s->token.val == TOK_IDENT &&
22613             (token_is_pseudo_keyword(s, JS_ATOM_of) ||
22614              token_is_pseudo_keyword(s, JS_ATOM_yield))) {
22615             last_tok = TOK_OF;
22616         } else {
22617             last_tok = s->token.val;
22618         }
22619         if (next_token(s)) {
22620             /* XXX: should clear the exception generated by next_token() */
22621             break;
22622         }
22623         if (level <= 1) {
22624             tok = s->token.val;
22625             if (token_is_pseudo_keyword(s, JS_ATOM_of))
22626                 tok = TOK_OF;
22627             if (no_line_terminator && s->last_line_num != s->token.line_num)
22628                 tok = '\n';
22629             break;
22630         }
22631     }
22632  done:
22633     if (pbits) {
22634         *pbits = bits;
22635     }
22636     if (js_parse_seek_token(s, &pos))
22637         return -1;
22638     return tok;
22639 }
22640 
set_object_name(JSParseState * s,JSAtom name)22641 static void set_object_name(JSParseState *s, JSAtom name)
22642 {
22643     JSFunctionDef *fd = s->cur_func;
22644     int opcode;
22645 
22646     opcode = get_prev_opcode(fd);
22647     if (opcode == OP_set_name) {
22648         /* XXX: should free atom after OP_set_name? */
22649         fd->byte_code.size = fd->last_opcode_pos;
22650         fd->last_opcode_pos = -1;
22651         emit_op(s, OP_set_name);
22652         emit_atom(s, name);
22653     } else if (opcode == OP_set_class_name) {
22654         int define_class_pos;
22655         JSAtom atom;
22656         define_class_pos = fd->last_opcode_pos + 1 -
22657             get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
22658         assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
22659         /* for consistency we free the previous atom which is
22660            JS_ATOM_empty_string */
22661         atom = get_u32(fd->byte_code.buf + define_class_pos + 1);
22662         JS_FreeAtom(s->ctx, atom);
22663         put_u32(fd->byte_code.buf + define_class_pos + 1,
22664                 JS_DupAtom(s->ctx, name));
22665         fd->last_opcode_pos = -1;
22666     }
22667 }
22668 
set_object_name_computed(JSParseState * s)22669 static void set_object_name_computed(JSParseState *s)
22670 {
22671     JSFunctionDef *fd = s->cur_func;
22672     int opcode;
22673 
22674     opcode = get_prev_opcode(fd);
22675     if (opcode == OP_set_name) {
22676         /* XXX: should free atom after OP_set_name? */
22677         fd->byte_code.size = fd->last_opcode_pos;
22678         fd->last_opcode_pos = -1;
22679         emit_op(s, OP_set_name_computed);
22680     } else if (opcode == OP_set_class_name) {
22681         int define_class_pos;
22682         define_class_pos = fd->last_opcode_pos + 1 -
22683             get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
22684         assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
22685         fd->byte_code.buf[define_class_pos] = OP_define_class_computed;
22686         fd->last_opcode_pos = -1;
22687     }
22688 }
22689 
js_parse_object_literal(JSParseState * s)22690 static __exception int js_parse_object_literal(JSParseState *s)
22691 {
22692     JSAtom name = JS_ATOM_NULL;
22693     const uint8_t *start_ptr;
22694     int start_line, prop_type;
22695     BOOL has_proto;
22696 
22697     if (next_token(s))
22698         goto fail;
22699     /* XXX: add an initial length that will be patched back */
22700     emit_op(s, OP_object);
22701     has_proto = FALSE;
22702     while (s->token.val != '}') {
22703         /* specific case for getter/setter */
22704         start_ptr = s->token.ptr;
22705         start_line = s->token.line_num;
22706 
22707         if (s->token.val == TOK_ELLIPSIS) {
22708             if (next_token(s))
22709                 return -1;
22710             if (js_parse_assign_expr(s))
22711                 return -1;
22712             emit_op(s, OP_null);  /* dummy excludeList */
22713             emit_op(s, OP_copy_data_properties);
22714             emit_u8(s, 2 | (1 << 2) | (0 << 5));
22715             emit_op(s, OP_drop); /* pop excludeList */
22716             emit_op(s, OP_drop); /* pop src object */
22717             goto next;
22718         }
22719 
22720         prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE);
22721         if (prop_type < 0)
22722             goto fail;
22723 
22724         if (prop_type == PROP_TYPE_VAR) {
22725             /* shortcut for x: x */
22726             emit_op(s, OP_scope_get_var);
22727             emit_atom(s, name);
22728             emit_u16(s, s->cur_func->scope_level);
22729             emit_op(s, OP_define_field);
22730             emit_atom(s, name);
22731         } else if (s->token.val == '(') {
22732             BOOL is_getset = (prop_type == PROP_TYPE_GET ||
22733                               prop_type == PROP_TYPE_SET);
22734             JSParseFunctionEnum func_type;
22735             JSFunctionKindEnum func_kind;
22736             int op_flags;
22737 
22738             func_kind = JS_FUNC_NORMAL;
22739             if (is_getset) {
22740                 func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET;
22741             } else {
22742                 func_type = JS_PARSE_FUNC_METHOD;
22743                 if (prop_type == PROP_TYPE_STAR)
22744                     func_kind = JS_FUNC_GENERATOR;
22745                 else if (prop_type == PROP_TYPE_ASYNC)
22746                     func_kind = JS_FUNC_ASYNC;
22747                 else if (prop_type == PROP_TYPE_ASYNC_STAR)
22748                     func_kind = JS_FUNC_ASYNC_GENERATOR;
22749             }
22750             if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL,
22751                                        start_ptr, start_line))
22752                 goto fail;
22753             if (name == JS_ATOM_NULL) {
22754                 emit_op(s, OP_define_method_computed);
22755             } else {
22756                 emit_op(s, OP_define_method);
22757                 emit_atom(s, name);
22758             }
22759             if (is_getset) {
22760                 op_flags = OP_DEFINE_METHOD_GETTER +
22761                     prop_type - PROP_TYPE_GET;
22762             } else {
22763                 op_flags = OP_DEFINE_METHOD_METHOD;
22764             }
22765             emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE);
22766         } else {
22767             if (js_parse_expect(s, ':'))
22768                 goto fail;
22769             if (js_parse_assign_expr(s))
22770                 goto fail;
22771             if (name == JS_ATOM_NULL) {
22772                 set_object_name_computed(s);
22773                 emit_op(s, OP_define_array_el);
22774                 emit_op(s, OP_drop);
22775             } else if (name == JS_ATOM___proto__) {
22776                 if (has_proto) {
22777                     js_parse_error(s, "duplicate __proto__ property name");
22778                     goto fail;
22779                 }
22780                 emit_op(s, OP_set_proto);
22781                 has_proto = TRUE;
22782             } else {
22783                 set_object_name(s, name);
22784                 emit_op(s, OP_define_field);
22785                 emit_atom(s, name);
22786             }
22787         }
22788         JS_FreeAtom(s->ctx, name);
22789     next:
22790         name = JS_ATOM_NULL;
22791         if (s->token.val != ',')
22792             break;
22793         if (next_token(s))
22794             goto fail;
22795     }
22796     if (js_parse_expect(s, '}'))
22797         goto fail;
22798     return 0;
22799  fail:
22800     JS_FreeAtom(s->ctx, name);
22801     return -1;
22802 }
22803 
22804 /* allow the 'in' binary operator */
22805 #define PF_IN_ACCEPTED  (1 << 0)
22806 /* allow function calls parsing in js_parse_postfix_expr() */
22807 #define PF_POSTFIX_CALL (1 << 1)
22808 /* allow arrow functions parsing in js_parse_postfix_expr() */
22809 #define PF_ARROW_FUNC   (1 << 2)
22810 /* allow the exponentiation operator in js_parse_unary() */
22811 #define PF_POW_ALLOWED  (1 << 3)
22812 /* forbid the exponentiation operator in js_parse_unary() */
22813 #define PF_POW_FORBIDDEN (1 << 4)
22814 
22815 static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags);
22816 
js_parse_left_hand_side_expr(JSParseState * s)22817 static __exception int js_parse_left_hand_side_expr(JSParseState *s)
22818 {
22819     return js_parse_postfix_expr(s, PF_POSTFIX_CALL);
22820 }
22821 
22822 /* XXX: could generate specific bytecode */
js_parse_class_default_ctor(JSParseState * s,BOOL has_super,JSFunctionDef ** pfd)22823 static __exception int js_parse_class_default_ctor(JSParseState *s,
22824                                                    BOOL has_super,
22825                                                    JSFunctionDef **pfd)
22826 {
22827     JSParsePos pos;
22828     const char *str;
22829     int ret, line_num;
22830     JSParseFunctionEnum func_type;
22831     const uint8_t *saved_buf_end;
22832 
22833     js_parse_get_pos(s, &pos);
22834     if (has_super) {
22835         /* spec change: no argument evaluation */
22836         str = "(){super(...arguments);}";
22837         func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
22838     } else {
22839         str = "(){}";
22840         func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
22841     }
22842     line_num = s->token.line_num;
22843     saved_buf_end = s->buf_end;
22844     s->buf_ptr = (uint8_t *)str;
22845     s->buf_end = (uint8_t *)(str + strlen(str));
22846     ret = next_token(s);
22847     if (!ret) {
22848         ret = js_parse_function_decl2(s, func_type, JS_FUNC_NORMAL,
22849                                       JS_ATOM_NULL, (uint8_t *)str,
22850                                       line_num, JS_PARSE_EXPORT_NONE, pfd);
22851     }
22852     s->buf_end = saved_buf_end;
22853     ret |= js_parse_seek_token(s, &pos);
22854     return ret;
22855 }
22856 
22857 /* find field in the current scope */
find_private_class_field(JSContext * ctx,JSFunctionDef * fd,JSAtom name,int scope_level)22858 static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd,
22859                                     JSAtom name, int scope_level)
22860 {
22861     int idx;
22862     idx = fd->scopes[scope_level].first;
22863     while (idx != -1) {
22864         if (fd->vars[idx].scope_level != scope_level)
22865             break;
22866         if (fd->vars[idx].var_name == name)
22867             return idx;
22868         idx = fd->vars[idx].scope_next;
22869     }
22870     return -1;
22871 }
22872 
22873 /* initialize the class fields, called by the constructor. Note:
22874    super() can be called in an arrow function, so <this> and
22875    <class_fields_init> can be variable references */
emit_class_field_init(JSParseState * s)22876 static void emit_class_field_init(JSParseState *s)
22877 {
22878     int label_next;
22879 
22880     emit_op(s, OP_scope_get_var);
22881     emit_atom(s, JS_ATOM_class_fields_init);
22882     emit_u16(s, s->cur_func->scope_level);
22883 
22884     /* no need to call the class field initializer if not defined */
22885     emit_op(s, OP_dup);
22886     label_next = emit_goto(s, OP_if_false, -1);
22887 
22888     emit_op(s, OP_scope_get_var);
22889     emit_atom(s, JS_ATOM_this);
22890     emit_u16(s, 0);
22891 
22892     emit_op(s, OP_swap);
22893 
22894     emit_op(s, OP_call_method);
22895     emit_u16(s, 0);
22896 
22897     emit_label(s, label_next);
22898     emit_op(s, OP_drop);
22899 }
22900 
22901 /* build a private setter function name from the private getter name */
get_private_setter_name(JSContext * ctx,JSAtom name)22902 static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name)
22903 {
22904     return js_atom_concat_str(ctx, name, "<set>");
22905 }
22906 
22907 typedef struct {
22908     JSFunctionDef *fields_init_fd;
22909     int computed_fields_count;
22910     BOOL has_brand;
22911     int brand_push_pos;
22912 } ClassFieldsDef;
22913 
emit_class_init_start(JSParseState * s,ClassFieldsDef * cf)22914 static __exception int emit_class_init_start(JSParseState *s,
22915                                              ClassFieldsDef *cf)
22916 {
22917     int label_add_brand;
22918 
22919     cf->fields_init_fd = js_parse_function_class_fields_init(s);
22920     if (!cf->fields_init_fd)
22921         return -1;
22922 
22923     s->cur_func = cf->fields_init_fd;
22924 
22925     /* XXX: would be better to add the code only if needed, maybe in a
22926        later pass */
22927     emit_op(s, OP_push_false); /* will be patched later */
22928     cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;
22929     label_add_brand = emit_goto(s, OP_if_false, -1);
22930 
22931     emit_op(s, OP_scope_get_var);
22932     emit_atom(s, JS_ATOM_this);
22933     emit_u16(s, 0);
22934 
22935     emit_op(s, OP_scope_get_var);
22936     emit_atom(s, JS_ATOM_home_object);
22937     emit_u16(s, 0);
22938 
22939     emit_op(s, OP_add_brand);
22940 
22941     emit_label(s, label_add_brand);
22942 
22943     s->cur_func = s->cur_func->parent;
22944     return 0;
22945 }
22946 
add_brand(JSParseState * s,ClassFieldsDef * cf)22947 static __exception int add_brand(JSParseState *s, ClassFieldsDef *cf)
22948 {
22949     if (!cf->has_brand) {
22950         /* define the brand field in 'this' of the initializer */
22951         if (!cf->fields_init_fd) {
22952             if (emit_class_init_start(s, cf))
22953                 return -1;
22954         }
22955         /* patch the start of the function to enable the OP_add_brand code */
22956         cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;
22957 
22958         cf->has_brand = TRUE;
22959     }
22960     return 0;
22961 }
22962 
emit_class_init_end(JSParseState * s,ClassFieldsDef * cf)22963 static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf)
22964 {
22965     int cpool_idx;
22966 
22967     s->cur_func = cf->fields_init_fd;
22968     emit_op(s, OP_return_undef);
22969     s->cur_func = s->cur_func->parent;
22970 
22971     cpool_idx = cpool_add(s, JS_NULL);
22972     cf->fields_init_fd->parent_cpool_idx = cpool_idx;
22973     emit_op(s, OP_fclosure);
22974     emit_u32(s, cpool_idx);
22975     emit_op(s, OP_set_home_object);
22976 }
22977 
22978 
js_parse_class(JSParseState * s,BOOL is_class_expr,JSParseExportEnum export_flag)22979 static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
22980                                       JSParseExportEnum export_flag)
22981 {
22982     JSContext *ctx = s->ctx;
22983     JSFunctionDef *fd = s->cur_func;
22984     JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1;
22985     JSAtom class_var_name = JS_ATOM_NULL;
22986     JSFunctionDef *method_fd, *ctor_fd;
22987     int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset;
22988     int class_flags = 0, i, define_class_offset;
22989     BOOL is_static, is_private;
22990     const uint8_t *class_start_ptr = s->token.ptr;
22991     const uint8_t *start_ptr;
22992     ClassFieldsDef class_fields[2];
22993 
22994     /* classes are parsed and executed in strict mode */
22995     saved_js_mode = fd->js_mode;
22996     fd->js_mode |= JS_MODE_STRICT;
22997     if (next_token(s))
22998         goto fail;
22999     if (s->token.val == TOK_IDENT) {
23000         if (s->token.u.ident.is_reserved) {
23001             js_parse_error_reserved_identifier(s);
23002             goto fail;
23003         }
23004         class_name = JS_DupAtom(ctx, s->token.u.ident.atom);
23005         if (next_token(s))
23006             goto fail;
23007     } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) {
23008         js_parse_error(s, "class statement requires a name");
23009         goto fail;
23010     }
23011     if (!is_class_expr) {
23012         if (class_name == JS_ATOM_NULL)
23013             class_var_name = JS_ATOM__default_; /* export default */
23014         else
23015             class_var_name = class_name;
23016         class_var_name = JS_DupAtom(ctx, class_var_name);
23017     }
23018 
23019     push_scope(s);
23020 
23021     if (s->token.val == TOK_EXTENDS) {
23022         class_flags = JS_DEFINE_CLASS_HAS_HERITAGE;
23023         if (next_token(s))
23024             goto fail;
23025         if (js_parse_left_hand_side_expr(s))
23026             goto fail;
23027     } else {
23028         emit_op(s, OP_undefined);
23029     }
23030 
23031     /* add a 'const' definition for the class name */
23032     if (class_name != JS_ATOM_NULL) {
23033         class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST);
23034         if (class_name_var_idx < 0)
23035             goto fail;
23036     }
23037 
23038     if (js_parse_expect(s, '{'))
23039         goto fail;
23040 
23041     /* this scope contains the private fields */
23042     push_scope(s);
23043 
23044     emit_op(s, OP_push_const);
23045     ctor_cpool_offset = fd->byte_code.size;
23046     emit_u32(s, 0); /* will be patched at the end of the class parsing */
23047 
23048     if (class_name == JS_ATOM_NULL) {
23049         if (class_var_name != JS_ATOM_NULL)
23050             class_name1 = JS_ATOM_default;
23051         else
23052             class_name1 = JS_ATOM_empty_string;
23053     } else {
23054         class_name1 = class_name;
23055     }
23056 
23057     emit_op(s, OP_define_class);
23058     emit_atom(s, class_name1);
23059     emit_u8(s, class_flags);
23060     define_class_offset = fd->last_opcode_pos;
23061 
23062     for(i = 0; i < 2; i++) {
23063         ClassFieldsDef *cf = &class_fields[i];
23064         cf->fields_init_fd = NULL;
23065         cf->computed_fields_count = 0;
23066         cf->has_brand = FALSE;
23067     }
23068 
23069     ctor_fd = NULL;
23070     while (s->token.val != '}') {
23071         if (s->token.val == ';') {
23072             if (next_token(s))
23073                 goto fail;
23074             continue;
23075         }
23076         is_static = (s->token.val == TOK_STATIC);
23077         prop_type = -1;
23078         if (is_static) {
23079             if (next_token(s))
23080                 goto fail;
23081             /* allow "static" field name */
23082             if (s->token.val == ';' || s->token.val == '=') {
23083                 is_static = FALSE;
23084                 name = JS_DupAtom(ctx, JS_ATOM_static);
23085                 prop_type = PROP_TYPE_IDENT;
23086             }
23087         }
23088         if (is_static)
23089             emit_op(s, OP_swap);
23090         start_ptr = s->token.ptr;
23091         if (prop_type < 0) {
23092             prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE);
23093             if (prop_type < 0)
23094                 goto fail;
23095         }
23096         is_private = prop_type & PROP_TYPE_PRIVATE;
23097         prop_type &= ~PROP_TYPE_PRIVATE;
23098 
23099         if ((name == JS_ATOM_constructor && !is_static &&
23100              prop_type != PROP_TYPE_IDENT) ||
23101             (name == JS_ATOM_prototype && is_static) ||
23102             name == JS_ATOM_hash_constructor) {
23103             js_parse_error(s, "invalid method name");
23104             goto fail;
23105         }
23106         if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) {
23107             BOOL is_set = prop_type - PROP_TYPE_GET;
23108             JSFunctionDef *method_fd;
23109 
23110             if (is_private) {
23111                 int idx, var_kind;
23112                 idx = find_private_class_field(ctx, fd, name, fd->scope_level);
23113                 if (idx >= 0) {
23114                     var_kind = fd->vars[idx].var_kind;
23115                     if (var_kind == JS_VAR_PRIVATE_FIELD ||
23116                         var_kind == JS_VAR_PRIVATE_METHOD ||
23117                         var_kind == JS_VAR_PRIVATE_GETTER_SETTER ||
23118                         var_kind == (JS_VAR_PRIVATE_GETTER + is_set)) {
23119                         goto private_field_already_defined;
23120                     }
23121                     fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER;
23122                 } else {
23123                     if (add_private_class_field(s, fd, name,
23124                                                 JS_VAR_PRIVATE_GETTER + is_set) < 0)
23125                         goto fail;
23126                 }
23127                 if (add_brand(s, &class_fields[is_static]) < 0)
23128                     goto fail;
23129             }
23130 
23131             if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set,
23132                                         JS_FUNC_NORMAL, JS_ATOM_NULL,
23133                                         start_ptr, s->token.line_num,
23134                                         JS_PARSE_EXPORT_NONE, &method_fd))
23135                 goto fail;
23136             if (is_private) {
23137                 method_fd->need_home_object = TRUE; /* needed for brand check */
23138                 emit_op(s, OP_set_home_object);
23139                 /* XXX: missing function name */
23140                 emit_op(s, OP_scope_put_var_init);
23141                 if (is_set) {
23142                     JSAtom setter_name;
23143                     int ret;
23144 
23145                     setter_name = get_private_setter_name(ctx, name);
23146                     if (setter_name == JS_ATOM_NULL)
23147                         goto fail;
23148                     emit_atom(s, setter_name);
23149                     ret = add_private_class_field(s, fd, setter_name,
23150                                                   JS_VAR_PRIVATE_SETTER);
23151                     JS_FreeAtom(ctx, setter_name);
23152                     if (ret < 0)
23153                         goto fail;
23154                 } else {
23155                     emit_atom(s, name);
23156                 }
23157                 emit_u16(s, s->cur_func->scope_level);
23158             } else {
23159                 if (name == JS_ATOM_NULL) {
23160                     emit_op(s, OP_define_method_computed);
23161                 } else {
23162                     emit_op(s, OP_define_method);
23163                     emit_atom(s, name);
23164                 }
23165                 emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set);
23166             }
23167         } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') {
23168             ClassFieldsDef *cf = &class_fields[is_static];
23169             JSAtom field_var_name = JS_ATOM_NULL;
23170 
23171             /* class field */
23172 
23173             /* XXX: spec: not consistent with method name checks */
23174             if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) {
23175                 js_parse_error(s, "invalid field name");
23176                 goto fail;
23177             }
23178 
23179             if (is_private) {
23180                 if (find_private_class_field(ctx, fd, name,
23181                                              fd->scope_level) >= 0) {
23182                     goto private_field_already_defined;
23183                 }
23184                 if (add_private_class_field(s, fd, name,
23185                                             JS_VAR_PRIVATE_FIELD) < 0)
23186                     goto fail;
23187                 emit_op(s, OP_private_symbol);
23188                 emit_atom(s, name);
23189                 emit_op(s, OP_scope_put_var_init);
23190                 emit_atom(s, name);
23191                 emit_u16(s, s->cur_func->scope_level);
23192             }
23193 
23194             if (!cf->fields_init_fd) {
23195                 if (emit_class_init_start(s, cf))
23196                     goto fail;
23197             }
23198             if (name == JS_ATOM_NULL ) {
23199                 /* save the computed field name into a variable */
23200                 field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count);
23201                 if (field_var_name == JS_ATOM_NULL)
23202                     goto fail;
23203                 if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) {
23204                     JS_FreeAtom(ctx, field_var_name);
23205                     goto fail;
23206                 }
23207                 emit_op(s, OP_to_propkey);
23208                 emit_op(s, OP_scope_put_var_init);
23209                 emit_atom(s, field_var_name);
23210                 emit_u16(s, s->cur_func->scope_level);
23211             }
23212             s->cur_func = cf->fields_init_fd;
23213             emit_op(s, OP_scope_get_var);
23214             emit_atom(s, JS_ATOM_this);
23215             emit_u16(s, 0);
23216 
23217             if (name == JS_ATOM_NULL) {
23218                 emit_op(s, OP_scope_get_var);
23219                 emit_atom(s, field_var_name);
23220                 emit_u16(s, s->cur_func->scope_level);
23221                 cf->computed_fields_count++;
23222                 JS_FreeAtom(ctx, field_var_name);
23223             } else if (is_private) {
23224                 emit_op(s, OP_scope_get_var);
23225                 emit_atom(s, name);
23226                 emit_u16(s, s->cur_func->scope_level);
23227             }
23228 
23229             if (s->token.val == '=') {
23230                 if (next_token(s))
23231                     goto fail;
23232                 if (js_parse_assign_expr(s))
23233                     goto fail;
23234             } else {
23235                 emit_op(s, OP_undefined);
23236             }
23237             if (is_private) {
23238                 set_object_name_computed(s);
23239                 emit_op(s, OP_define_private_field);
23240             } else if (name == JS_ATOM_NULL) {
23241                 set_object_name_computed(s);
23242                 emit_op(s, OP_define_array_el);
23243                 emit_op(s, OP_drop);
23244             } else {
23245                 set_object_name(s, name);
23246                 emit_op(s, OP_define_field);
23247                 emit_atom(s, name);
23248             }
23249             s->cur_func = s->cur_func->parent;
23250             if (js_parse_expect_semi(s))
23251                 goto fail;
23252         } else {
23253             JSParseFunctionEnum func_type;
23254             JSFunctionKindEnum func_kind;
23255 
23256             func_type = JS_PARSE_FUNC_METHOD;
23257             func_kind = JS_FUNC_NORMAL;
23258             if (prop_type == PROP_TYPE_STAR) {
23259                 func_kind = JS_FUNC_GENERATOR;
23260             } else if (prop_type == PROP_TYPE_ASYNC) {
23261                 func_kind = JS_FUNC_ASYNC;
23262             } else if (prop_type == PROP_TYPE_ASYNC_STAR) {
23263                 func_kind = JS_FUNC_ASYNC_GENERATOR;
23264             } else if (name == JS_ATOM_constructor && !is_static) {
23265                 if (ctor_fd) {
23266                     js_parse_error(s, "property constructor appears more than once");
23267                     goto fail;
23268                 }
23269                 if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE)
23270                     func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
23271                 else
23272                     func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
23273             }
23274             if (is_private) {
23275                 if (add_brand(s, &class_fields[is_static]) < 0)
23276                     goto fail;
23277             }
23278             if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, s->token.line_num, JS_PARSE_EXPORT_NONE, &method_fd))
23279                 goto fail;
23280             if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR ||
23281                 func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
23282                 ctor_fd = method_fd;
23283             } else if (is_private) {
23284                 method_fd->need_home_object = TRUE; /* needed for brand check */
23285                 if (find_private_class_field(ctx, fd, name,
23286                                              fd->scope_level) >= 0) {
23287                 private_field_already_defined:
23288                     js_parse_error(s, "private class field is already defined");
23289                     goto fail;
23290                 }
23291                 if (add_private_class_field(s, fd, name,
23292                                             JS_VAR_PRIVATE_METHOD) < 0)
23293                     goto fail;
23294                 emit_op(s, OP_set_home_object);
23295                 emit_op(s, OP_set_name);
23296                 emit_atom(s, name);
23297                 emit_op(s, OP_scope_put_var_init);
23298                 emit_atom(s, name);
23299                 emit_u16(s, s->cur_func->scope_level);
23300             } else {
23301                 if (name == JS_ATOM_NULL) {
23302                     emit_op(s, OP_define_method_computed);
23303                 } else {
23304                     emit_op(s, OP_define_method);
23305                     emit_atom(s, name);
23306                 }
23307                 emit_u8(s, OP_DEFINE_METHOD_METHOD);
23308             }
23309         }
23310         if (is_static)
23311             emit_op(s, OP_swap);
23312         JS_FreeAtom(ctx, name);
23313         name = JS_ATOM_NULL;
23314     }
23315 
23316     if (s->token.val != '}') {
23317         js_parse_error(s, "expecting '%c'", '}');
23318         goto fail;
23319     }
23320 
23321     if (!ctor_fd) {
23322         if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd))
23323             goto fail;
23324     }
23325     /* patch the constant pool index for the constructor */
23326     put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx);
23327 
23328     /* store the class source code in the constructor. */
23329     if (!(fd->js_mode & JS_MODE_STRIP)) {
23330         js_free(ctx, ctor_fd->source);
23331         ctor_fd->source_len = s->buf_ptr - class_start_ptr;
23332         ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr,
23333                                      ctor_fd->source_len);
23334         if (!ctor_fd->source)
23335             goto fail;
23336     }
23337 
23338     /* consume the '}' */
23339     if (next_token(s))
23340         goto fail;
23341 
23342     /* store the function to initialize the fields to that it can be
23343        referenced by the constructor */
23344     {
23345         ClassFieldsDef *cf = &class_fields[0];
23346         int var_idx;
23347 
23348         var_idx = define_var(s, fd, JS_ATOM_class_fields_init,
23349                              JS_VAR_DEF_CONST);
23350         if (var_idx < 0)
23351             goto fail;
23352         if (cf->fields_init_fd) {
23353             emit_class_init_end(s, cf);
23354         } else {
23355             emit_op(s, OP_undefined);
23356         }
23357         emit_op(s, OP_scope_put_var_init);
23358         emit_atom(s, JS_ATOM_class_fields_init);
23359         emit_u16(s, s->cur_func->scope_level);
23360     }
23361 
23362     /* drop the prototype */
23363     emit_op(s, OP_drop);
23364 
23365     /* initialize the static fields */
23366     if (class_fields[1].fields_init_fd != NULL) {
23367         ClassFieldsDef *cf = &class_fields[1];
23368         emit_op(s, OP_dup);
23369         emit_class_init_end(s, cf);
23370         emit_op(s, OP_call_method);
23371         emit_u16(s, 0);
23372         emit_op(s, OP_drop);
23373     }
23374 
23375     if (class_name != JS_ATOM_NULL) {
23376         /* store the class name in the scoped class name variable (it
23377            is independent from the class statement variable
23378            definition) */
23379         emit_op(s, OP_dup);
23380         emit_op(s, OP_scope_put_var_init);
23381         emit_atom(s, class_name);
23382         emit_u16(s, fd->scope_level);
23383     }
23384     pop_scope(s);
23385     pop_scope(s);
23386 
23387     /* the class statements have a block level scope */
23388     if (class_var_name != JS_ATOM_NULL) {
23389         if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0)
23390             goto fail;
23391         emit_op(s, OP_scope_put_var_init);
23392         emit_atom(s, class_var_name);
23393         emit_u16(s, fd->scope_level);
23394     } else {
23395         if (class_name == JS_ATOM_NULL) {
23396             /* cannot use OP_set_name because the name of the class
23397                must be defined before the static initializers are
23398                executed */
23399             emit_op(s, OP_set_class_name);
23400             emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset);
23401         }
23402     }
23403 
23404     if (export_flag != JS_PARSE_EXPORT_NONE) {
23405         if (!add_export_entry(s, fd->module,
23406                               class_var_name,
23407                               export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default,
23408                               JS_EXPORT_TYPE_LOCAL))
23409             goto fail;
23410     }
23411 
23412     JS_FreeAtom(ctx, class_name);
23413     JS_FreeAtom(ctx, class_var_name);
23414     fd->js_mode = saved_js_mode;
23415     return 0;
23416  fail:
23417     JS_FreeAtom(ctx, name);
23418     JS_FreeAtom(ctx, class_name);
23419     JS_FreeAtom(ctx, class_var_name);
23420     fd->js_mode = saved_js_mode;
23421     return -1;
23422 }
23423 
js_parse_array_literal(JSParseState * s)23424 static __exception int js_parse_array_literal(JSParseState *s)
23425 {
23426     uint32_t idx;
23427     BOOL need_length;
23428 
23429     if (next_token(s))
23430         return -1;
23431     /* small regular arrays are created on the stack */
23432     idx = 0;
23433     while (s->token.val != ']' && idx < 32) {
23434         if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS)
23435             break;
23436         if (js_parse_assign_expr(s))
23437             return -1;
23438         idx++;
23439         /* accept trailing comma */
23440         if (s->token.val == ',') {
23441             if (next_token(s))
23442                 return -1;
23443         } else
23444         if (s->token.val != ']')
23445             goto done;
23446     }
23447     emit_op(s, OP_array_from);
23448     emit_u16(s, idx);
23449 
23450     /* larger arrays and holes are handled with explicit indices */
23451     need_length = FALSE;
23452     while (s->token.val != ']' && idx < 0x7fffffff) {
23453         if (s->token.val == TOK_ELLIPSIS)
23454             break;
23455         need_length = TRUE;
23456         if (s->token.val != ',') {
23457             if (js_parse_assign_expr(s))
23458                 return -1;
23459             emit_op(s, OP_define_field);
23460             emit_u32(s, __JS_AtomFromUInt32(idx));
23461             need_length = FALSE;
23462         }
23463         idx++;
23464         /* accept trailing comma */
23465         if (s->token.val == ',') {
23466             if (next_token(s))
23467                 return -1;
23468         }
23469     }
23470     if (s->token.val == ']') {
23471         if (need_length) {
23472             /* Set the length: Cannot use OP_define_field because
23473                length is not configurable */
23474             emit_op(s, OP_dup);
23475             emit_op(s, OP_push_i32);
23476             emit_u32(s, idx);
23477             emit_op(s, OP_put_field);
23478             emit_atom(s, JS_ATOM_length);
23479         }
23480         goto done;
23481     }
23482 
23483     /* huge arrays and spread elements require a dynamic index on the stack */
23484     emit_op(s, OP_push_i32);
23485     emit_u32(s, idx);
23486 
23487     /* stack has array, index */
23488     while (s->token.val != ']') {
23489         if (s->token.val == TOK_ELLIPSIS) {
23490             if (next_token(s))
23491                 return -1;
23492             if (js_parse_assign_expr(s))
23493                 return -1;
23494 #if 1
23495             emit_op(s, OP_append);
23496 #else
23497             int label_next, label_done;
23498             label_next = new_label(s);
23499             label_done = new_label(s);
23500             /* enumerate object */
23501             emit_op(s, OP_for_of_start);
23502             emit_op(s, OP_rot5l);
23503             emit_op(s, OP_rot5l);
23504             emit_label(s, label_next);
23505             /* on stack: enum_rec array idx */
23506             emit_op(s, OP_for_of_next);
23507             emit_u8(s, 2);
23508             emit_goto(s, OP_if_true, label_done);
23509             /* append element */
23510             /* enum_rec array idx val -> enum_rec array new_idx */
23511             emit_op(s, OP_define_array_el);
23512             emit_op(s, OP_inc);
23513             emit_goto(s, OP_goto, label_next);
23514             emit_label(s, label_done);
23515             /* close enumeration */
23516             emit_op(s, OP_drop); /* drop undef val */
23517             emit_op(s, OP_nip1); /* drop enum_rec */
23518             emit_op(s, OP_nip1);
23519             emit_op(s, OP_nip1);
23520 #endif
23521         } else {
23522             need_length = TRUE;
23523             if (s->token.val != ',') {
23524                 if (js_parse_assign_expr(s))
23525                     return -1;
23526                 /* a idx val */
23527                 emit_op(s, OP_define_array_el);
23528                 need_length = FALSE;
23529             }
23530             emit_op(s, OP_inc);
23531         }
23532         if (s->token.val != ',')
23533             break;
23534         if (next_token(s))
23535             return -1;
23536     }
23537     if (need_length) {
23538         /* Set the length: cannot use OP_define_field because
23539            length is not configurable */
23540         emit_op(s, OP_dup1);    /* array length - array array length */
23541         emit_op(s, OP_put_field);
23542         emit_atom(s, JS_ATOM_length);
23543     } else {
23544         emit_op(s, OP_drop);    /* array length - array */
23545     }
23546 done:
23547     return js_parse_expect(s, ']');
23548 }
23549 
23550 /* XXX: remove */
has_with_scope(JSFunctionDef * s,int scope_level)23551 static BOOL has_with_scope(JSFunctionDef *s, int scope_level)
23552 {
23553     /* check if scope chain contains a with statement */
23554     while (s) {
23555         int scope_idx = s->scopes[scope_level].first;
23556         while (scope_idx >= 0) {
23557             JSVarDef *vd = &s->vars[scope_idx];
23558 
23559             if (vd->var_name == JS_ATOM__with_)
23560                 return TRUE;
23561             scope_idx = vd->scope_next;
23562         }
23563         /* check parent scopes */
23564         scope_level = s->parent_scope_level;
23565         s = s->parent;
23566     }
23567     return FALSE;
23568 }
23569 
get_lvalue(JSParseState * s,int * popcode,int * pscope,JSAtom * pname,int * plabel,int * pdepth,BOOL keep,int tok)23570 static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
23571                                   JSAtom *pname, int *plabel, int *pdepth, BOOL keep,
23572                                   int tok)
23573 {
23574     JSFunctionDef *fd;
23575     int opcode, scope, label, depth;
23576     JSAtom name;
23577 
23578     /* we check the last opcode to get the lvalue type */
23579     fd = s->cur_func;
23580     scope = 0;
23581     name = JS_ATOM_NULL;
23582     label = -1;
23583     depth = 0;
23584     switch(opcode = get_prev_opcode(fd)) {
23585     case OP_scope_get_var:
23586         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
23587         scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
23588         if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) &&
23589             (fd->js_mode & JS_MODE_STRICT)) {
23590             return js_parse_error(s, "invalid lvalue in strict mode");
23591         }
23592         if (name == JS_ATOM_this || name == JS_ATOM_new_target)
23593             goto invalid_lvalue;
23594         depth = 2;  /* will generate OP_get_ref_value */
23595         break;
23596     case OP_get_field:
23597         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
23598         depth = 1;
23599         break;
23600     case OP_scope_get_private_field:
23601         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
23602         scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
23603         depth = 1;
23604         break;
23605     case OP_get_array_el:
23606         depth = 2;
23607         break;
23608     case OP_get_super_value:
23609         depth = 3;
23610         break;
23611     default:
23612     invalid_lvalue:
23613         if (tok == TOK_FOR) {
23614             return js_parse_error(s, "invalid for in/of left hand-side");
23615         } else if (tok == TOK_INC || tok == TOK_DEC) {
23616             return js_parse_error(s, "invalid increment/decrement operand");
23617         } else if (tok == '[' || tok == '{') {
23618             return js_parse_error(s, "invalid destructuring target");
23619         } else {
23620             return js_parse_error(s, "invalid assignment left-hand side");
23621         }
23622     }
23623     /* remove the last opcode */
23624     fd->byte_code.size = fd->last_opcode_pos;
23625     fd->last_opcode_pos = -1;
23626 
23627     if (keep) {
23628         /* get the value but keep the object/fields on the stack */
23629         switch(opcode) {
23630         case OP_scope_get_var:
23631             label = new_label(s);
23632             emit_op(s, OP_scope_make_ref);
23633             emit_atom(s, name);
23634             emit_u32(s, label);
23635             emit_u16(s, scope);
23636             update_label(fd, label, 1);
23637             emit_op(s, OP_get_ref_value);
23638             opcode = OP_get_ref_value;
23639             break;
23640         case OP_get_field:
23641             emit_op(s, OP_get_field2);
23642             emit_atom(s, name);
23643             break;
23644         case OP_scope_get_private_field:
23645             emit_op(s, OP_scope_get_private_field2);
23646             emit_atom(s, name);
23647             emit_u16(s, scope);
23648             break;
23649         case OP_get_array_el:
23650             /* XXX: replace by a single opcode ? */
23651             emit_op(s, OP_to_propkey2);
23652             emit_op(s, OP_dup2);
23653             emit_op(s, OP_get_array_el);
23654             break;
23655         case OP_get_super_value:
23656             emit_op(s, OP_to_propkey);
23657             emit_op(s, OP_dup3);
23658             emit_op(s, OP_get_super_value);
23659             break;
23660         default:
23661             abort();
23662         }
23663     } else {
23664         switch(opcode) {
23665         case OP_scope_get_var:
23666             label = new_label(s);
23667             emit_op(s, OP_scope_make_ref);
23668             emit_atom(s, name);
23669             emit_u32(s, label);
23670             emit_u16(s, scope);
23671             update_label(fd, label, 1);
23672             opcode = OP_get_ref_value;
23673             break;
23674         case OP_get_array_el:
23675             emit_op(s, OP_to_propkey2);
23676             break;
23677         case OP_get_super_value:
23678             emit_op(s, OP_to_propkey);
23679             break;
23680         }
23681     }
23682 
23683     *popcode = opcode;
23684     *pscope = scope;
23685     /* name has refcount for OP_get_field and OP_get_ref_value,
23686        and JS_ATOM_NULL for other opcodes */
23687     *pname = name;
23688     *plabel = label;
23689     if (pdepth)
23690         *pdepth = depth;
23691     return 0;
23692 }
23693 
23694 typedef enum {
23695     PUT_LVALUE_NOKEEP, /* [depth] v -> */
23696     PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently
23697                                 just disable optimizations) */
23698     PUT_LVALUE_KEEP_TOP,  /* [depth] v -> v */
23699     PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */
23700     PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */
23701 } PutLValueEnum;
23702 
23703 /* name has a live reference. 'is_let' is only used with opcode =
23704    OP_scope_get_var which is never generated by get_lvalue(). */
put_lvalue(JSParseState * s,int opcode,int scope,JSAtom name,int label,PutLValueEnum special,BOOL is_let)23705 static void put_lvalue(JSParseState *s, int opcode, int scope,
23706                        JSAtom name, int label, PutLValueEnum special,
23707                        BOOL is_let)
23708 {
23709     switch(opcode) {
23710     case OP_get_field:
23711     case OP_scope_get_private_field:
23712         /* depth = 1 */
23713         switch(special) {
23714         case PUT_LVALUE_NOKEEP:
23715         case PUT_LVALUE_NOKEEP_DEPTH:
23716             break;
23717         case PUT_LVALUE_KEEP_TOP:
23718             emit_op(s, OP_insert2); /* obj v -> v obj v */
23719             break;
23720         case PUT_LVALUE_KEEP_SECOND:
23721             emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */
23722             break;
23723         case PUT_LVALUE_NOKEEP_BOTTOM:
23724             emit_op(s, OP_swap);
23725             break;
23726         default:
23727             abort();
23728         }
23729         break;
23730     case OP_get_array_el:
23731     case OP_get_ref_value:
23732         /* depth = 2 */
23733         if (opcode == OP_get_ref_value) {
23734             JS_FreeAtom(s->ctx, name);
23735             emit_label(s, label);
23736         }
23737         switch(special) {
23738         case PUT_LVALUE_NOKEEP:
23739             emit_op(s, OP_nop); /* will trigger optimization */
23740             break;
23741         case PUT_LVALUE_NOKEEP_DEPTH:
23742             break;
23743         case PUT_LVALUE_KEEP_TOP:
23744             emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */
23745             break;
23746         case PUT_LVALUE_KEEP_SECOND:
23747             emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */
23748             break;
23749         case PUT_LVALUE_NOKEEP_BOTTOM:
23750             emit_op(s, OP_rot3l);
23751             break;
23752         default:
23753             abort();
23754         }
23755         break;
23756     case OP_get_super_value:
23757         /* depth = 3 */
23758         switch(special) {
23759         case PUT_LVALUE_NOKEEP:
23760         case PUT_LVALUE_NOKEEP_DEPTH:
23761             break;
23762         case PUT_LVALUE_KEEP_TOP:
23763             emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */
23764             break;
23765         case PUT_LVALUE_KEEP_SECOND:
23766             emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */
23767             break;
23768         case PUT_LVALUE_NOKEEP_BOTTOM:
23769             emit_op(s, OP_rot4l);
23770             break;
23771         default:
23772             abort();
23773         }
23774         break;
23775     default:
23776         break;
23777     }
23778 
23779     switch(opcode) {
23780     case OP_scope_get_var:  /* val -- */
23781         assert(special == PUT_LVALUE_NOKEEP ||
23782                special == PUT_LVALUE_NOKEEP_DEPTH);
23783         emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var);
23784         emit_u32(s, name);  /* has refcount */
23785         emit_u16(s, scope);
23786         break;
23787     case OP_get_field:
23788         emit_op(s, OP_put_field);
23789         emit_u32(s, name);  /* name has refcount */
23790         break;
23791     case OP_scope_get_private_field:
23792         emit_op(s, OP_scope_put_private_field);
23793         emit_u32(s, name);  /* name has refcount */
23794         emit_u16(s, scope);
23795         break;
23796     case OP_get_array_el:
23797         emit_op(s, OP_put_array_el);
23798         break;
23799     case OP_get_ref_value:
23800         emit_op(s, OP_put_ref_value);
23801         break;
23802     case OP_get_super_value:
23803         emit_op(s, OP_put_super_value);
23804         break;
23805     default:
23806         abort();
23807     }
23808 }
23809 
js_parse_expr_paren(JSParseState * s)23810 static __exception int js_parse_expr_paren(JSParseState *s)
23811 {
23812     if (js_parse_expect(s, '('))
23813         return -1;
23814     if (js_parse_expr(s))
23815         return -1;
23816     if (js_parse_expect(s, ')'))
23817         return -1;
23818     return 0;
23819 }
23820 
js_unsupported_keyword(JSParseState * s,JSAtom atom)23821 static int js_unsupported_keyword(JSParseState *s, JSAtom atom)
23822 {
23823     char buf[ATOM_GET_STR_BUF_SIZE];
23824     return js_parse_error(s, "unsupported keyword: %s",
23825                           JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom));
23826 }
23827 
js_define_var(JSParseState * s,JSAtom name,int tok)23828 static __exception int js_define_var(JSParseState *s, JSAtom name, int tok)
23829 {
23830     JSFunctionDef *fd = s->cur_func;
23831     JSVarDefEnum var_def_type;
23832 
23833     if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
23834         return js_parse_error(s, "yield is a reserved identifier");
23835     }
23836     if ((name == JS_ATOM_arguments || name == JS_ATOM_eval)
23837     &&  (fd->js_mode & JS_MODE_STRICT)) {
23838         return js_parse_error(s, "invalid variable name in strict mode");
23839     }
23840     if ((name == JS_ATOM_let || name == JS_ATOM_undefined)
23841     &&  (tok == TOK_LET || tok == TOK_CONST)) {
23842         return js_parse_error(s, "invalid lexical variable name");
23843     }
23844     switch(tok) {
23845     case TOK_LET:
23846         var_def_type = JS_VAR_DEF_LET;
23847         break;
23848     case TOK_CONST:
23849         var_def_type = JS_VAR_DEF_CONST;
23850         break;
23851     case TOK_VAR:
23852         var_def_type = JS_VAR_DEF_VAR;
23853         break;
23854     case TOK_CATCH:
23855         var_def_type = JS_VAR_DEF_CATCH;
23856         break;
23857     default:
23858         abort();
23859     }
23860     if (define_var(s, fd, name, var_def_type) < 0)
23861         return -1;
23862     return 0;
23863 }
23864 
js_emit_spread_code(JSParseState * s,int depth)23865 static void js_emit_spread_code(JSParseState *s, int depth)
23866 {
23867     int label_rest_next, label_rest_done;
23868 
23869     /* XXX: could check if enum object is an actual array and optimize
23870        slice extraction. enumeration record and target array are in a
23871        different order from OP_append case. */
23872     /* enum_rec xxx -- enum_rec xxx array 0 */
23873     emit_op(s, OP_array_from);
23874     emit_u16(s, 0);
23875     emit_op(s, OP_push_i32);
23876     emit_u32(s, 0);
23877     emit_label(s, label_rest_next = new_label(s));
23878     emit_op(s, OP_for_of_next);
23879     emit_u8(s, 2 + depth);
23880     label_rest_done = emit_goto(s, OP_if_true, -1);
23881     /* array idx val -- array idx */
23882     emit_op(s, OP_define_array_el);
23883     emit_op(s, OP_inc);
23884     emit_goto(s, OP_goto, label_rest_next);
23885     emit_label(s, label_rest_done);
23886     /* enum_rec xxx array idx undef -- enum_rec xxx array */
23887     emit_op(s, OP_drop);
23888     emit_op(s, OP_drop);
23889 }
23890 
js_parse_check_duplicate_parameter(JSParseState * s,JSAtom name)23891 static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name)
23892 {
23893     /* Check for duplicate parameter names */
23894     JSFunctionDef *fd = s->cur_func;
23895     int i;
23896     for (i = 0; i < fd->arg_count; i++) {
23897         if (fd->args[i].var_name == name)
23898             goto duplicate;
23899     }
23900     for (i = 0; i < fd->var_count; i++) {
23901         if (fd->vars[i].var_name == name)
23902             goto duplicate;
23903     }
23904     return 0;
23905 
23906 duplicate:
23907     return js_parse_error(s, "duplicate parameter names not allowed in this context");
23908 }
23909 
js_parse_destructuring_var(JSParseState * s,int tok,int is_arg)23910 static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg)
23911 {
23912     JSAtom name;
23913 
23914     if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
23915     ||  ((s->cur_func->js_mode & JS_MODE_STRICT) &&
23916          (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) {
23917         js_parse_error(s, "invalid destructuring target");
23918         return JS_ATOM_NULL;
23919     }
23920     name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
23921     if (is_arg && js_parse_check_duplicate_parameter(s, name))
23922         goto fail;
23923     if (next_token(s))
23924         goto fail;
23925 
23926     return name;
23927 fail:
23928     JS_FreeAtom(s->ctx, name);
23929     return JS_ATOM_NULL;
23930 }
23931 
23932 /* Return -1 if error, 0 if no initializer, 1 if an initializer is
23933    present at the top level. */
js_parse_destructuring_element(JSParseState * s,int tok,int is_arg,int hasval,int has_ellipsis,BOOL allow_initializer)23934 static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
23935                                         int hasval, int has_ellipsis,
23936                                         BOOL allow_initializer)
23937 {
23938     int label_parse, label_assign, label_done, label_lvalue, depth_lvalue;
23939     int start_addr, assign_addr;
23940     JSAtom prop_name, var_name;
23941     int opcode, scope, tok1, skip_bits;
23942     BOOL has_initializer;
23943 
23944     if (has_ellipsis < 0) {
23945         /* pre-parse destructuration target for spread detection */
23946         js_parse_skip_parens_token(s, &skip_bits, FALSE);
23947         has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS;
23948     }
23949 
23950     label_parse = new_label(s);
23951     label_assign = new_label(s);
23952 
23953     start_addr = s->cur_func->byte_code.size;
23954     if (hasval) {
23955         /* consume value from the stack */
23956         emit_op(s, OP_dup);
23957         emit_op(s, OP_undefined);
23958         emit_op(s, OP_strict_eq);
23959         emit_goto(s, OP_if_true, label_parse);
23960         emit_label(s, label_assign);
23961     } else {
23962         emit_goto(s, OP_goto, label_parse);
23963         emit_label(s, label_assign);
23964         /* leave value on the stack */
23965         emit_op(s, OP_dup);
23966     }
23967     assign_addr = s->cur_func->byte_code.size;
23968     if (s->token.val == '{') {
23969         if (next_token(s))
23970             return -1;
23971         /* throw an exception if the value cannot be converted to an object */
23972         emit_op(s, OP_to_object);
23973         if (has_ellipsis) {
23974             /* add excludeList on stack just below src object */
23975             emit_op(s, OP_object);
23976             emit_op(s, OP_swap);
23977         }
23978         while (s->token.val != '}') {
23979             int prop_type;
23980             if (s->token.val == TOK_ELLIPSIS) {
23981                 if (!has_ellipsis) {
23982                     JS_ThrowInternalError(s->ctx, "unexpected ellipsis token");
23983                     return -1;
23984                 }
23985                 if (next_token(s))
23986                     return -1;
23987                 if (tok) {
23988                     var_name = js_parse_destructuring_var(s, tok, is_arg);
23989                     if (var_name == JS_ATOM_NULL)
23990                         return -1;
23991                     opcode = OP_scope_get_var;
23992                     scope = s->cur_func->scope_level;
23993                     label_lvalue = -1;
23994                     depth_lvalue = 0;
23995                 } else {
23996                     if (js_parse_left_hand_side_expr(s))
23997                         return -1;
23998 
23999                     if (get_lvalue(s, &opcode, &scope, &var_name,
24000                                    &label_lvalue, &depth_lvalue, FALSE, '{'))
24001                         return -1;
24002                 }
24003                 if (s->token.val != '}') {
24004                     js_parse_error(s, "assignment rest property must be last");
24005                     goto var_error;
24006                 }
24007                 emit_op(s, OP_object);  /* target */
24008                 emit_op(s, OP_copy_data_properties);
24009                 emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5));
24010                 goto set_val;
24011             }
24012             prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE);
24013             if (prop_type < 0)
24014                 return -1;
24015             var_name = JS_ATOM_NULL;
24016             opcode = OP_scope_get_var;
24017             scope = s->cur_func->scope_level;
24018             label_lvalue = -1;
24019             depth_lvalue = 0;
24020             if (prop_type == PROP_TYPE_IDENT) {
24021                 if (next_token(s))
24022                     goto prop_error;
24023                 if ((s->token.val == '[' || s->token.val == '{')
24024                     &&  ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
24025                          tok1 == '=' || tok1 == '}')) {
24026                     if (prop_name == JS_ATOM_NULL) {
24027                         /* computed property name on stack */
24028                         if (has_ellipsis) {
24029                             /* define the property in excludeList */
24030                             emit_op(s, OP_to_propkey); /* avoid calling ToString twice */
24031                             emit_op(s, OP_perm3); /* TOS: src excludeList prop */
24032                             emit_op(s, OP_null); /* TOS: src excludeList prop null */
24033                             emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */
24034                             emit_op(s, OP_perm3); /* TOS: excludeList src prop */
24035                         }
24036                         /* get the computed property from the source object */
24037                         emit_op(s, OP_get_array_el2);
24038                     } else {
24039                         /* named property */
24040                         if (has_ellipsis) {
24041                             /* define the property in excludeList */
24042                             emit_op(s, OP_swap); /* TOS: src excludeList */
24043                             emit_op(s, OP_null); /* TOS: src excludeList null */
24044                             emit_op(s, OP_define_field); /* TOS: src excludeList */
24045                             emit_atom(s, prop_name);
24046                             emit_op(s, OP_swap); /* TOS: excludeList src */
24047                         }
24048                         /* get the named property from the source object */
24049                         emit_op(s, OP_get_field2);
24050                         emit_u32(s, prop_name);
24051                     }
24052                     if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0)
24053                         return -1;
24054                     if (s->token.val == '}')
24055                         break;
24056                     /* accept a trailing comma before the '}' */
24057                     if (js_parse_expect(s, ','))
24058                         return -1;
24059                     continue;
24060                 }
24061                 if (prop_name == JS_ATOM_NULL) {
24062                     emit_op(s, OP_to_propkey2);
24063                     if (has_ellipsis) {
24064                         /* define the property in excludeList */
24065                         emit_op(s, OP_perm3);
24066                         emit_op(s, OP_null);
24067                         emit_op(s, OP_define_array_el);
24068                         emit_op(s, OP_perm3);
24069                     }
24070                     /* source prop -- source source prop */
24071                     emit_op(s, OP_dup1);
24072                 } else {
24073                     if (has_ellipsis) {
24074                         /* define the property in excludeList */
24075                         emit_op(s, OP_swap);
24076                         emit_op(s, OP_null);
24077                         emit_op(s, OP_define_field);
24078                         emit_atom(s, prop_name);
24079                         emit_op(s, OP_swap);
24080                     }
24081                     /* source -- source source */
24082                     emit_op(s, OP_dup);
24083                 }
24084                 if (tok) {
24085                     var_name = js_parse_destructuring_var(s, tok, is_arg);
24086                     if (var_name == JS_ATOM_NULL)
24087                         goto prop_error;
24088                 } else {
24089                     if (js_parse_left_hand_side_expr(s))
24090                         goto prop_error;
24091                 lvalue:
24092                     if (get_lvalue(s, &opcode, &scope, &var_name,
24093                                    &label_lvalue, &depth_lvalue, FALSE, '{'))
24094                         goto prop_error;
24095                     /* swap ref and lvalue object if any */
24096                     if (prop_name == JS_ATOM_NULL) {
24097                         switch(depth_lvalue) {
24098                         case 1:
24099                             /* source prop x -> x source prop */
24100                             emit_op(s, OP_rot3r);
24101                             break;
24102                         case 2:
24103                             /* source prop x y -> x y source prop */
24104                             emit_op(s, OP_swap2);   /* t p2 s p1 */
24105                             break;
24106                         case 3:
24107                             /* source prop x y z -> x y z source prop */
24108                             emit_op(s, OP_rot5l);
24109                             emit_op(s, OP_rot5l);
24110                             break;
24111                         }
24112                     } else {
24113                         switch(depth_lvalue) {
24114                         case 1:
24115                             /* source x -> x source */
24116                             emit_op(s, OP_swap);
24117                             break;
24118                         case 2:
24119                             /* source x y -> x y source */
24120                             emit_op(s, OP_rot3l);
24121                             break;
24122                         case 3:
24123                             /* source x y z -> x y z source */
24124                             emit_op(s, OP_rot4l);
24125                             break;
24126                         }
24127                     }
24128                 }
24129                 if (prop_name == JS_ATOM_NULL) {
24130                     /* computed property name on stack */
24131                     /* XXX: should have OP_get_array_el2x with depth */
24132                     /* source prop -- val */
24133                     emit_op(s, OP_get_array_el);
24134                 } else {
24135                     /* named property */
24136                     /* XXX: should have OP_get_field2x with depth */
24137                     /* source -- val */
24138                     emit_op(s, OP_get_field);
24139                     emit_u32(s, prop_name);
24140                 }
24141             } else {
24142                 /* prop_type = PROP_TYPE_VAR, cannot be a computed property */
24143                 if (is_arg && js_parse_check_duplicate_parameter(s, prop_name))
24144                     goto prop_error;
24145                 if ((s->cur_func->js_mode & JS_MODE_STRICT) &&
24146                     (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) {
24147                     js_parse_error(s, "invalid destructuring target");
24148                     goto prop_error;
24149                 }
24150                 if (has_ellipsis) {
24151                     /* define the property in excludeList */
24152                     emit_op(s, OP_swap);
24153                     emit_op(s, OP_null);
24154                     emit_op(s, OP_define_field);
24155                     emit_atom(s, prop_name);
24156                     emit_op(s, OP_swap);
24157                 }
24158                 if (!tok || tok == TOK_VAR) {
24159                     /* generate reference */
24160                     /* source -- source source */
24161                     emit_op(s, OP_dup);
24162                     emit_op(s, OP_scope_get_var);
24163                     emit_atom(s, prop_name);
24164                     emit_u16(s, s->cur_func->scope_level);
24165                     goto lvalue;
24166                 }
24167                 var_name = JS_DupAtom(s->ctx, prop_name);
24168                 /* source -- source val */
24169                 emit_op(s, OP_get_field2);
24170                 emit_u32(s, prop_name);
24171             }
24172         set_val:
24173             if (tok) {
24174                 if (js_define_var(s, var_name, tok))
24175                     goto var_error;
24176                 scope = s->cur_func->scope_level;
24177             }
24178             if (s->token.val == '=') {  /* handle optional default value */
24179                 int label_hasval;
24180                 emit_op(s, OP_dup);
24181                 emit_op(s, OP_undefined);
24182                 emit_op(s, OP_strict_eq);
24183                 label_hasval = emit_goto(s, OP_if_false, -1);
24184                 if (next_token(s))
24185                     goto var_error;
24186                 emit_op(s, OP_drop);
24187                 if (js_parse_assign_expr(s))
24188                     goto var_error;
24189                 if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
24190                     set_object_name(s, var_name);
24191                 emit_label(s, label_hasval);
24192             }
24193             /* store value into lvalue object */
24194             put_lvalue(s, opcode, scope, var_name, label_lvalue,
24195                        PUT_LVALUE_NOKEEP_DEPTH,
24196                        (tok == TOK_CONST || tok == TOK_LET));
24197             if (s->token.val == '}')
24198                 break;
24199             /* accept a trailing comma before the '}' */
24200             if (js_parse_expect(s, ','))
24201                 return -1;
24202         }
24203         /* drop the source object */
24204         emit_op(s, OP_drop);
24205         if (has_ellipsis) {
24206             emit_op(s, OP_drop); /* pop excludeList */
24207         }
24208         if (next_token(s))
24209             return -1;
24210     } else if (s->token.val == '[') {
24211         BOOL has_spread;
24212         int enum_depth;
24213         BlockEnv block_env;
24214 
24215         if (next_token(s))
24216             return -1;
24217         /* the block environment is only needed in generators in case
24218            'yield' triggers a 'return' */
24219         push_break_entry(s->cur_func, &block_env,
24220                          JS_ATOM_NULL, -1, -1, 2);
24221         block_env.has_iterator = TRUE;
24222         emit_op(s, OP_for_of_start);
24223         has_spread = FALSE;
24224         while (s->token.val != ']') {
24225             /* get the next value */
24226             if (s->token.val == TOK_ELLIPSIS) {
24227                 if (next_token(s))
24228                     return -1;
24229                 if (s->token.val == ',' || s->token.val == ']')
24230                     return js_parse_error(s, "missing binding pattern...");
24231                 has_spread = TRUE;
24232             }
24233             if (s->token.val == ',') {
24234                 /* do nothing, skip the value, has_spread is false */
24235                 emit_op(s, OP_for_of_next);
24236                 emit_u8(s, 0);
24237                 emit_op(s, OP_drop);
24238                 emit_op(s, OP_drop);
24239             } else if ((s->token.val == '[' || s->token.val == '{')
24240                    &&  ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
24241                         tok1 == '=' || tok1 == ']')) {
24242                 if (has_spread) {
24243                     if (tok1 == '=')
24244                         return js_parse_error(s, "rest element cannot have a default value");
24245                     js_emit_spread_code(s, 0);
24246                 } else {
24247                     emit_op(s, OP_for_of_next);
24248                     emit_u8(s, 0);
24249                     emit_op(s, OP_drop);
24250                 }
24251                 if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
24252                     return -1;
24253             } else {
24254                 var_name = JS_ATOM_NULL;
24255                 enum_depth = 0;
24256                 if (tok) {
24257                     var_name = js_parse_destructuring_var(s, tok, is_arg);
24258                     if (var_name == JS_ATOM_NULL)
24259                         goto var_error;
24260                     if (js_define_var(s, var_name, tok))
24261                         goto var_error;
24262                     opcode = OP_scope_get_var;
24263                     scope = s->cur_func->scope_level;
24264                 } else {
24265                     if (js_parse_left_hand_side_expr(s))
24266                         return -1;
24267                     if (get_lvalue(s, &opcode, &scope, &var_name,
24268                                    &label_lvalue, &enum_depth, FALSE, '[')) {
24269                         return -1;
24270                     }
24271                 }
24272                 if (has_spread) {
24273                     js_emit_spread_code(s, enum_depth);
24274                 } else {
24275                     emit_op(s, OP_for_of_next);
24276                     emit_u8(s, enum_depth);
24277                     emit_op(s, OP_drop);
24278                 }
24279                 if (s->token.val == '=' && !has_spread) {
24280                     /* handle optional default value */
24281                     int label_hasval;
24282                     emit_op(s, OP_dup);
24283                     emit_op(s, OP_undefined);
24284                     emit_op(s, OP_strict_eq);
24285                     label_hasval = emit_goto(s, OP_if_false, -1);
24286                     if (next_token(s))
24287                         goto var_error;
24288                     emit_op(s, OP_drop);
24289                     if (js_parse_assign_expr(s))
24290                         goto var_error;
24291                     if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
24292                         set_object_name(s, var_name);
24293                     emit_label(s, label_hasval);
24294                 }
24295                 /* store value into lvalue object */
24296                 put_lvalue(s, opcode, scope, var_name,
24297                            label_lvalue, PUT_LVALUE_NOKEEP_DEPTH,
24298                            (tok == TOK_CONST || tok == TOK_LET));
24299             }
24300             if (s->token.val == ']')
24301                 break;
24302             if (has_spread)
24303                 return js_parse_error(s, "rest element must be the last one");
24304             /* accept a trailing comma before the ']' */
24305             if (js_parse_expect(s, ','))
24306                 return -1;
24307         }
24308         /* close iterator object:
24309            if completed, enum_obj has been replaced by undefined */
24310         emit_op(s, OP_iterator_close);
24311         pop_break_entry(s->cur_func);
24312         if (next_token(s))
24313             return -1;
24314     } else {
24315         return js_parse_error(s, "invalid assignment syntax");
24316     }
24317     if (s->token.val == '=' && allow_initializer) {
24318         label_done = emit_goto(s, OP_goto, -1);
24319         if (next_token(s))
24320             return -1;
24321         emit_label(s, label_parse);
24322         if (hasval)
24323             emit_op(s, OP_drop);
24324         if (js_parse_assign_expr(s))
24325             return -1;
24326         emit_goto(s, OP_goto, label_assign);
24327         emit_label(s, label_done);
24328         has_initializer = TRUE;
24329     } else {
24330         /* normally hasval is true except if
24331            js_parse_skip_parens_token() was wrong in the parsing */
24332         //        assert(hasval);
24333         if (!hasval) {
24334             js_parse_error(s, "too complicated destructuring expression");
24335             return -1;
24336         }
24337         /* remove test and decrement label ref count */
24338         memset(s->cur_func->byte_code.buf + start_addr, OP_nop,
24339                assign_addr - start_addr);
24340         s->cur_func->label_slots[label_parse].ref_count--;
24341         has_initializer = FALSE;
24342     }
24343     return has_initializer;
24344 
24345  prop_error:
24346     JS_FreeAtom(s->ctx, prop_name);
24347  var_error:
24348     JS_FreeAtom(s->ctx, var_name);
24349     return -1;
24350 }
24351 
24352 typedef enum FuncCallType {
24353     FUNC_CALL_NORMAL,
24354     FUNC_CALL_NEW,
24355     FUNC_CALL_SUPER_CTOR,
24356     FUNC_CALL_TEMPLATE,
24357 } FuncCallType;
24358 
optional_chain_test(JSParseState * s,int * poptional_chaining_label,int drop_count)24359 static void optional_chain_test(JSParseState *s, int *poptional_chaining_label,
24360                                 int drop_count)
24361 {
24362     int label_next, i;
24363     if (*poptional_chaining_label < 0)
24364         *poptional_chaining_label = new_label(s);
24365    /* XXX: could be more efficient with a specific opcode */
24366     emit_op(s, OP_dup);
24367     emit_op(s, OP_is_undefined_or_null);
24368     label_next = emit_goto(s, OP_if_false, -1);
24369     for(i = 0; i < drop_count; i++)
24370         emit_op(s, OP_drop);
24371     emit_op(s, OP_undefined);
24372     emit_goto(s, OP_goto, *poptional_chaining_label);
24373     emit_label(s, label_next);
24374 }
24375 
24376 /* allowed parse_flags: PF_POSTFIX_CALL, PF_ARROW_FUNC */
js_parse_postfix_expr(JSParseState * s,int parse_flags)24377 static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
24378 {
24379     FuncCallType call_type;
24380     int optional_chaining_label;
24381     BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0;
24382 
24383     call_type = FUNC_CALL_NORMAL;
24384     switch(s->token.val) {
24385     case TOK_NUMBER:
24386         {
24387             JSValue val;
24388             val = s->token.u.num.val;
24389 
24390             if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
24391                 emit_op(s, OP_push_i32);
24392                 emit_u32(s, JS_VALUE_GET_INT(val));
24393             } else
24394 #ifdef CONFIG_BIGNUM
24395             if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
24396                 slimb_t e;
24397                 int ret;
24398 
24399                 /* need a runtime conversion */
24400                 /* XXX: could add a cache and/or do it once at
24401                    the start of the function */
24402                 if (emit_push_const(s, val, 0) < 0)
24403                     return -1;
24404                 e = s->token.u.num.exponent;
24405                 if (e == (int32_t)e) {
24406                     emit_op(s, OP_push_i32);
24407                     emit_u32(s, e);
24408                 } else {
24409                     val = JS_NewBigInt64_1(s->ctx, e);
24410                     if (JS_IsException(val))
24411                         return -1;
24412                     ret = emit_push_const(s, val, 0);
24413                     JS_FreeValue(s->ctx, val);
24414                     if (ret < 0)
24415                         return -1;
24416                 }
24417                 emit_op(s, OP_mul_pow10);
24418             } else
24419 #endif
24420             {
24421                 if (emit_push_const(s, val, 0) < 0)
24422                     return -1;
24423             }
24424         }
24425         if (next_token(s))
24426             return -1;
24427         break;
24428     case TOK_TEMPLATE:
24429         if (js_parse_template(s, 0, NULL))
24430             return -1;
24431         break;
24432     case TOK_STRING:
24433         if (emit_push_const(s, s->token.u.str.str, 1))
24434             return -1;
24435         if (next_token(s))
24436             return -1;
24437         break;
24438 
24439     case TOK_DIV_ASSIGN:
24440         s->buf_ptr -= 2;
24441         goto parse_regexp;
24442     case '/':
24443         s->buf_ptr--;
24444     parse_regexp:
24445         {
24446             JSValue str;
24447             int ret, backtrace_flags;
24448             if (!s->ctx->compile_regexp)
24449                 return js_parse_error(s, "RegExp are not supported");
24450             /* the previous token is '/' or '/=', so no need to free */
24451             if (js_parse_regexp(s))
24452                 return -1;
24453             ret = emit_push_const(s, s->token.u.regexp.body, 0);
24454             str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body,
24455                                          s->token.u.regexp.flags);
24456             if (JS_IsException(str)) {
24457                 /* add the line number info */
24458                 backtrace_flags = 0;
24459                 if (s->cur_func && s->cur_func->backtrace_barrier)
24460                     backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
24461                 build_backtrace(s->ctx, s->ctx->rt->current_exception,
24462                                 s->filename, s->token.line_num,
24463                                 backtrace_flags);
24464                 return -1;
24465             }
24466             ret = emit_push_const(s, str, 0);
24467             JS_FreeValue(s->ctx, str);
24468             if (ret)
24469                 return -1;
24470             /* we use a specific opcode to be sure the correct
24471                function is called (otherwise the bytecode would have
24472                to be verified by the RegExp constructor) */
24473             emit_op(s, OP_regexp);
24474             if (next_token(s))
24475                 return -1;
24476         }
24477         break;
24478     case '(':
24479         if ((parse_flags & PF_ARROW_FUNC) &&
24480             js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) {
24481             if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
24482                                        JS_FUNC_NORMAL, JS_ATOM_NULL,
24483                                        s->token.ptr, s->token.line_num))
24484                 return -1;
24485         } else {
24486             if (js_parse_expr_paren(s))
24487                 return -1;
24488         }
24489         break;
24490     case TOK_FUNCTION:
24491         if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
24492                                    JS_FUNC_NORMAL, JS_ATOM_NULL,
24493                                    s->token.ptr, s->token.line_num))
24494             return -1;
24495         break;
24496     case TOK_CLASS:
24497         if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE))
24498             return -1;
24499         break;
24500     case TOK_NULL:
24501         if (next_token(s))
24502             return -1;
24503         emit_op(s, OP_null);
24504         break;
24505     case TOK_THIS:
24506         if (next_token(s))
24507             return -1;
24508         emit_op(s, OP_scope_get_var);
24509         emit_atom(s, JS_ATOM_this);
24510         emit_u16(s, 0);
24511         break;
24512     case TOK_FALSE:
24513         if (next_token(s))
24514             return -1;
24515         emit_op(s, OP_push_false);
24516         break;
24517     case TOK_TRUE:
24518         if (next_token(s))
24519             return -1;
24520         emit_op(s, OP_push_true);
24521         break;
24522     case TOK_IDENT:
24523         {
24524             JSAtom name;
24525             if (s->token.u.ident.is_reserved) {
24526                 return js_parse_error_reserved_identifier(s);
24527             }
24528             if ((parse_flags & PF_ARROW_FUNC) &&
24529                 peek_token(s, TRUE) == TOK_ARROW) {
24530                 if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
24531                                            JS_FUNC_NORMAL, JS_ATOM_NULL,
24532                                            s->token.ptr, s->token.line_num))
24533                     return -1;
24534             } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
24535                        peek_token(s, TRUE) != '\n') {
24536                 const uint8_t *source_ptr;
24537                 int source_line_num;
24538 
24539                 source_ptr = s->token.ptr;
24540                 source_line_num = s->token.line_num;
24541                 if (next_token(s))
24542                     return -1;
24543                 if (s->token.val == TOK_FUNCTION) {
24544                     if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
24545                                                JS_FUNC_ASYNC, JS_ATOM_NULL,
24546                                                source_ptr, source_line_num))
24547                         return -1;
24548                 } else if ((parse_flags & PF_ARROW_FUNC) &&
24549                            ((s->token.val == '(' &&
24550                              js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) ||
24551                             (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
24552                              peek_token(s, TRUE) == TOK_ARROW))) {
24553                     if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
24554                                                JS_FUNC_ASYNC, JS_ATOM_NULL,
24555                                                source_ptr, source_line_num))
24556                         return -1;
24557                 } else {
24558                     name = JS_DupAtom(s->ctx, JS_ATOM_async);
24559                     goto do_get_var;
24560                 }
24561             } else {
24562                 if (s->token.u.ident.atom == JS_ATOM_arguments &&
24563                     !s->cur_func->arguments_allowed) {
24564                     js_parse_error(s, "'arguments' identifier is not allowed in class field initializer");
24565                     return -1;
24566                 }
24567                 name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
24568                 if (next_token(s))  /* update line number before emitting code */
24569                     return -1;
24570             do_get_var:
24571                 emit_op(s, OP_scope_get_var);
24572                 emit_u32(s, name);
24573                 emit_u16(s, s->cur_func->scope_level);
24574             }
24575         }
24576         break;
24577     case '{':
24578     case '[':
24579         {
24580             int skip_bits;
24581             if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
24582                 if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
24583                     return -1;
24584             } else {
24585                 if (s->token.val == '{') {
24586                     if (js_parse_object_literal(s))
24587                         return -1;
24588                 } else {
24589                     if (js_parse_array_literal(s))
24590                         return -1;
24591                 }
24592             }
24593         }
24594         break;
24595     case TOK_NEW:
24596         if (next_token(s))
24597             return -1;
24598         if (s->token.val == '.') {
24599             if (next_token(s))
24600                 return -1;
24601             if (!token_is_pseudo_keyword(s, JS_ATOM_target))
24602                 return js_parse_error(s, "expecting target");
24603             if (!s->cur_func->new_target_allowed)
24604                 return js_parse_error(s, "new.target only allowed within functions");
24605             if (next_token(s))
24606                 return -1;
24607             emit_op(s, OP_scope_get_var);
24608             emit_atom(s, JS_ATOM_new_target);
24609             emit_u16(s, 0);
24610         } else {
24611             if (js_parse_postfix_expr(s, 0))
24612                 return -1;
24613             accept_lparen = TRUE;
24614             if (s->token.val != '(') {
24615                 /* new operator on an object */
24616                 emit_op(s, OP_dup);
24617                 emit_op(s, OP_call_constructor);
24618                 emit_u16(s, 0);
24619             } else {
24620                 call_type = FUNC_CALL_NEW;
24621             }
24622         }
24623         break;
24624     case TOK_SUPER:
24625         if (next_token(s))
24626             return -1;
24627         if (s->token.val == '(') {
24628             if (!s->cur_func->super_call_allowed)
24629                 return js_parse_error(s, "super() is only valid in a derived class constructor");
24630             call_type = FUNC_CALL_SUPER_CTOR;
24631         } else if (s->token.val == '.' || s->token.val == '[') {
24632             if (!s->cur_func->super_allowed)
24633                 return js_parse_error(s, "'super' is only valid in a method");
24634             emit_op(s, OP_scope_get_var);
24635             emit_atom(s, JS_ATOM_this);
24636             emit_u16(s, 0);
24637             emit_op(s, OP_scope_get_var);
24638             emit_atom(s, JS_ATOM_home_object);
24639             emit_u16(s, 0);
24640             emit_op(s, OP_get_super);
24641         } else {
24642             return js_parse_error(s, "invalid use of 'super'");
24643         }
24644         break;
24645     case TOK_IMPORT:
24646         if (next_token(s))
24647             return -1;
24648         if (s->token.val == '.') {
24649             if (next_token(s))
24650                 return -1;
24651             if (!token_is_pseudo_keyword(s, JS_ATOM_meta))
24652                 return js_parse_error(s, "meta expected");
24653             if (!s->is_module)
24654                 return js_parse_error(s, "import.meta only valid in module code");
24655             if (next_token(s))
24656                 return -1;
24657             emit_op(s, OP_special_object);
24658             emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META);
24659         } else {
24660             if (js_parse_expect(s, '('))
24661                 return -1;
24662             if (!accept_lparen)
24663                 return js_parse_error(s, "invalid use of 'import()'");
24664             if (js_parse_assign_expr(s))
24665                 return -1;
24666             if (js_parse_expect(s, ')'))
24667                 return -1;
24668             emit_op(s, OP_import);
24669         }
24670         break;
24671     default:
24672         return js_parse_error(s, "unexpected token in expression: '%.*s'",
24673                               (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
24674     }
24675 
24676     optional_chaining_label = -1;
24677     for(;;) {
24678         JSFunctionDef *fd = s->cur_func;
24679         BOOL has_optional_chain = FALSE;
24680 
24681         if (s->token.val == TOK_QUESTION_MARK_DOT) {
24682             /* optional chaining */
24683             if (next_token(s))
24684                 return -1;
24685             has_optional_chain = TRUE;
24686             if (s->token.val == '(' && accept_lparen) {
24687                 goto parse_func_call;
24688             } else if (s->token.val == '[') {
24689                 goto parse_array_access;
24690             } else {
24691                 goto parse_property;
24692             }
24693         } else if (s->token.val == TOK_TEMPLATE &&
24694                    call_type == FUNC_CALL_NORMAL) {
24695             if (optional_chaining_label >= 0) {
24696                 return js_parse_error(s, "template literal cannot appear in an optional chain");
24697             }
24698             call_type = FUNC_CALL_TEMPLATE;
24699             goto parse_func_call2;
24700         } else if (s->token.val == '(' && accept_lparen) {
24701             int opcode, arg_count, drop_count;
24702 
24703             /* function call */
24704         parse_func_call:
24705             if (next_token(s))
24706                 return -1;
24707 
24708             if (call_type == FUNC_CALL_NORMAL) {
24709             parse_func_call2:
24710                 switch(opcode = get_prev_opcode(fd)) {
24711                 case OP_get_field:
24712                     /* keep the object on the stack */
24713                     fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
24714                     drop_count = 2;
24715                     break;
24716                 case OP_scope_get_private_field:
24717                     /* keep the object on the stack */
24718                     fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2;
24719                     drop_count = 2;
24720                     break;
24721                 case OP_get_array_el:
24722                     /* keep the object on the stack */
24723                     fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
24724                     drop_count = 2;
24725                     break;
24726                 case OP_scope_get_var:
24727                     {
24728                         JSAtom name;
24729                         int scope;
24730                         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
24731                         scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
24732                         if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) {
24733                             /* direct 'eval' */
24734                             opcode = OP_eval;
24735                         } else {
24736                             /* verify if function name resolves to a simple
24737                                get_loc/get_arg: a function call inside a `with`
24738                                statement can resolve to a method call of the
24739                                `with` context object
24740                              */
24741                             /* XXX: always generate the OP_scope_get_ref
24742                                and remove it in variable resolution
24743                                pass ? */
24744                             if (has_with_scope(fd, scope)) {
24745                                 opcode = OP_scope_get_ref;
24746                                 fd->byte_code.buf[fd->last_opcode_pos] = opcode;
24747                             }
24748                         }
24749                         drop_count = 1;
24750                     }
24751                     break;
24752                 case OP_get_super_value:
24753                     fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el;
24754                     /* on stack: this func_obj */
24755                     opcode = OP_get_array_el;
24756                     drop_count = 2;
24757                     break;
24758                 default:
24759                     opcode = OP_invalid;
24760                     drop_count = 1;
24761                     break;
24762                 }
24763                 if (has_optional_chain) {
24764                     optional_chain_test(s, &optional_chaining_label,
24765                                         drop_count);
24766                 }
24767             } else {
24768                 opcode = OP_invalid;
24769             }
24770 
24771             if (call_type == FUNC_CALL_TEMPLATE) {
24772                 if (js_parse_template(s, 1, &arg_count))
24773                     return -1;
24774                 goto emit_func_call;
24775             } else if (call_type == FUNC_CALL_SUPER_CTOR) {
24776                 emit_op(s, OP_scope_get_var);
24777                 emit_atom(s, JS_ATOM_this_active_func);
24778                 emit_u16(s, 0);
24779 
24780                 emit_op(s, OP_get_super);
24781 
24782                 emit_op(s, OP_scope_get_var);
24783                 emit_atom(s, JS_ATOM_new_target);
24784                 emit_u16(s, 0);
24785             } else if (call_type == FUNC_CALL_NEW) {
24786                 emit_op(s, OP_dup); /* new.target = function */
24787             }
24788 
24789             /* parse arguments */
24790             arg_count = 0;
24791             while (s->token.val != ')') {
24792                 if (arg_count >= 65535) {
24793                     return js_parse_error(s, "Too many call arguments");
24794                 }
24795                 if (s->token.val == TOK_ELLIPSIS)
24796                     break;
24797                 if (js_parse_assign_expr(s))
24798                     return -1;
24799                 arg_count++;
24800                 if (s->token.val == ')')
24801                     break;
24802                 /* accept a trailing comma before the ')' */
24803                 if (js_parse_expect(s, ','))
24804                     return -1;
24805             }
24806             if (s->token.val == TOK_ELLIPSIS) {
24807                 emit_op(s, OP_array_from);
24808                 emit_u16(s, arg_count);
24809                 emit_op(s, OP_push_i32);
24810                 emit_u32(s, arg_count);
24811 
24812                 /* on stack: array idx */
24813                 while (s->token.val != ')') {
24814                     if (s->token.val == TOK_ELLIPSIS) {
24815                         if (next_token(s))
24816                             return -1;
24817                         if (js_parse_assign_expr(s))
24818                             return -1;
24819 #if 1
24820                         /* XXX: could pass is_last indicator? */
24821                         emit_op(s, OP_append);
24822 #else
24823                         int label_next, label_done;
24824                         label_next = new_label(s);
24825                         label_done = new_label(s);
24826                         /* push enumerate object below array/idx pair */
24827                         emit_op(s, OP_for_of_start);
24828                         emit_op(s, OP_rot5l);
24829                         emit_op(s, OP_rot5l);
24830                         emit_label(s, label_next);
24831                         /* on stack: enum_rec array idx */
24832                         emit_op(s, OP_for_of_next);
24833                         emit_u8(s, 2);
24834                         emit_goto(s, OP_if_true, label_done);
24835                         /* append element */
24836                         /* enum_rec array idx val -> enum_rec array new_idx */
24837                         emit_op(s, OP_define_array_el);
24838                         emit_op(s, OP_inc);
24839                         emit_goto(s, OP_goto, label_next);
24840                         emit_label(s, label_done);
24841                         /* close enumeration, drop enum_rec and idx */
24842                         emit_op(s, OP_drop); /* drop undef */
24843                         emit_op(s, OP_nip1); /* drop enum_rec */
24844                         emit_op(s, OP_nip1);
24845                         emit_op(s, OP_nip1);
24846 #endif
24847                     } else {
24848                         if (js_parse_assign_expr(s))
24849                             return -1;
24850                         /* array idx val */
24851                         emit_op(s, OP_define_array_el);
24852                         emit_op(s, OP_inc);
24853                     }
24854                     if (s->token.val == ')')
24855                         break;
24856                     /* accept a trailing comma before the ')' */
24857                     if (js_parse_expect(s, ','))
24858                         return -1;
24859                 }
24860                 if (next_token(s))
24861                     return -1;
24862                 /* drop the index */
24863                 emit_op(s, OP_drop);
24864 
24865                 /* apply function call */
24866                 switch(opcode) {
24867                 case OP_get_field:
24868                 case OP_scope_get_private_field:
24869                 case OP_get_array_el:
24870                 case OP_scope_get_ref:
24871                     /* obj func array -> func obj array */
24872                     emit_op(s, OP_perm3);
24873                     emit_op(s, OP_apply);
24874                     emit_u16(s, call_type == FUNC_CALL_NEW);
24875                     break;
24876                 case OP_eval:
24877                     emit_op(s, OP_apply_eval);
24878                     emit_u16(s, fd->scope_level);
24879                     fd->has_eval_call = TRUE;
24880                     break;
24881                 default:
24882                     if (call_type == FUNC_CALL_SUPER_CTOR) {
24883                         emit_op(s, OP_apply);
24884                         emit_u16(s, 1);
24885                         /* set the 'this' value */
24886                         emit_op(s, OP_dup);
24887                         emit_op(s, OP_scope_put_var_init);
24888                         emit_atom(s, JS_ATOM_this);
24889                         emit_u16(s, 0);
24890 
24891                         emit_class_field_init(s);
24892                     } else if (call_type == FUNC_CALL_NEW) {
24893                         /* obj func array -> func obj array */
24894                         emit_op(s, OP_perm3);
24895                         emit_op(s, OP_apply);
24896                         emit_u16(s, 1);
24897                     } else {
24898                         /* func array -> func undef array */
24899                         emit_op(s, OP_undefined);
24900                         emit_op(s, OP_swap);
24901                         emit_op(s, OP_apply);
24902                         emit_u16(s, 0);
24903                     }
24904                     break;
24905                 }
24906             } else {
24907                 if (next_token(s))
24908                     return -1;
24909             emit_func_call:
24910                 switch(opcode) {
24911                 case OP_get_field:
24912                 case OP_scope_get_private_field:
24913                 case OP_get_array_el:
24914                 case OP_scope_get_ref:
24915                     emit_op(s, OP_call_method);
24916                     emit_u16(s, arg_count);
24917                     break;
24918                 case OP_eval:
24919                     emit_op(s, OP_eval);
24920                     emit_u16(s, arg_count);
24921                     emit_u16(s, fd->scope_level);
24922                     fd->has_eval_call = TRUE;
24923                     break;
24924                 default:
24925                     if (call_type == FUNC_CALL_SUPER_CTOR) {
24926                         emit_op(s, OP_call_constructor);
24927                         emit_u16(s, arg_count);
24928 
24929                         /* set the 'this' value */
24930                         emit_op(s, OP_dup);
24931                         emit_op(s, OP_scope_put_var_init);
24932                         emit_atom(s, JS_ATOM_this);
24933                         emit_u16(s, 0);
24934 
24935                         emit_class_field_init(s);
24936                     } else if (call_type == FUNC_CALL_NEW) {
24937                         emit_op(s, OP_call_constructor);
24938                         emit_u16(s, arg_count);
24939                     } else {
24940                         emit_op(s, OP_call);
24941                         emit_u16(s, arg_count);
24942                     }
24943                     break;
24944                 }
24945             }
24946             call_type = FUNC_CALL_NORMAL;
24947         } else if (s->token.val == '.') {
24948             if (next_token(s))
24949                 return -1;
24950         parse_property:
24951             if (s->token.val == TOK_PRIVATE_NAME) {
24952                 /* private class field */
24953                 if (get_prev_opcode(fd) == OP_get_super) {
24954                     return js_parse_error(s, "private class field forbidden after super");
24955                 }
24956                 if (has_optional_chain) {
24957                     optional_chain_test(s, &optional_chaining_label, 1);
24958                 }
24959                 emit_op(s, OP_scope_get_private_field);
24960                 emit_atom(s, s->token.u.ident.atom);
24961                 emit_u16(s, s->cur_func->scope_level);
24962             } else {
24963                 if (!token_is_ident(s->token.val)) {
24964                     return js_parse_error(s, "expecting field name");
24965                 }
24966                 if (get_prev_opcode(fd) == OP_get_super) {
24967                     JSValue val;
24968                     int ret;
24969                     val = JS_AtomToValue(s->ctx, s->token.u.ident.atom);
24970                     ret = emit_push_const(s, val, 1);
24971                     JS_FreeValue(s->ctx, val);
24972                     if (ret)
24973                         return -1;
24974                     emit_op(s, OP_get_super_value);
24975                 } else {
24976                     if (has_optional_chain) {
24977                         optional_chain_test(s, &optional_chaining_label, 1);
24978                     }
24979                     emit_op(s, OP_get_field);
24980                     emit_atom(s, s->token.u.ident.atom);
24981                 }
24982             }
24983             if (next_token(s))
24984                 return -1;
24985         } else if (s->token.val == '[') {
24986             int prev_op;
24987 
24988         parse_array_access:
24989             prev_op = get_prev_opcode(fd);
24990             if (has_optional_chain) {
24991                 optional_chain_test(s, &optional_chaining_label, 1);
24992             }
24993             if (next_token(s))
24994                 return -1;
24995             if (js_parse_expr(s))
24996                 return -1;
24997             if (js_parse_expect(s, ']'))
24998                 return -1;
24999             if (prev_op == OP_get_super) {
25000                 emit_op(s, OP_get_super_value);
25001             } else {
25002                 emit_op(s, OP_get_array_el);
25003             }
25004         } else {
25005             break;
25006         }
25007     }
25008     if (optional_chaining_label >= 0)
25009         emit_label(s, optional_chaining_label);
25010     return 0;
25011 }
25012 
js_parse_delete(JSParseState * s)25013 static __exception int js_parse_delete(JSParseState *s)
25014 {
25015     JSFunctionDef *fd = s->cur_func;
25016     JSAtom name;
25017     int opcode;
25018 
25019     if (next_token(s))
25020         return -1;
25021     if (js_parse_unary(s, PF_POW_FORBIDDEN))
25022         return -1;
25023     switch(opcode = get_prev_opcode(fd)) {
25024     case OP_get_field:
25025         {
25026             JSValue val;
25027             int ret;
25028 
25029             name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
25030             fd->byte_code.size = fd->last_opcode_pos;
25031             fd->last_opcode_pos = -1;
25032             val = JS_AtomToValue(s->ctx, name);
25033             ret = emit_push_const(s, val, 1);
25034             JS_FreeValue(s->ctx, val);
25035             JS_FreeAtom(s->ctx, name);
25036             if (ret)
25037                 return ret;
25038         }
25039         goto do_delete;
25040     case OP_get_array_el:
25041         fd->byte_code.size = fd->last_opcode_pos;
25042         fd->last_opcode_pos = -1;
25043     do_delete:
25044         emit_op(s, OP_delete);
25045         break;
25046     case OP_scope_get_var:
25047         /* 'delete this': this is not a reference */
25048         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
25049         if (name == JS_ATOM_this || name == JS_ATOM_new_target)
25050             goto ret_true;
25051         if (fd->js_mode & JS_MODE_STRICT) {
25052             return js_parse_error(s, "cannot delete a direct reference in strict mode");
25053         } else {
25054             fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var;
25055         }
25056         break;
25057     case OP_scope_get_private_field:
25058         return js_parse_error(s, "cannot delete a private class field");
25059     case OP_get_super_value:
25060         emit_op(s, OP_throw_error);
25061         emit_atom(s, JS_ATOM_NULL);
25062         emit_u8(s, JS_THROW_ERROR_DELETE_SUPER);
25063         break;
25064     default:
25065     ret_true:
25066         emit_op(s, OP_drop);
25067         emit_op(s, OP_push_true);
25068         break;
25069     }
25070     return 0;
25071 }
25072 
25073 /* allowed parse_flags: PF_ARROW_FUNC, PF_POW_ALLOWED, PF_POW_FORBIDDEN */
js_parse_unary(JSParseState * s,int parse_flags)25074 static __exception int js_parse_unary(JSParseState *s, int parse_flags)
25075 {
25076     int op;
25077 
25078     switch(s->token.val) {
25079     case '+':
25080     case '-':
25081     case '!':
25082     case '~':
25083     case TOK_VOID:
25084         op = s->token.val;
25085         if (next_token(s))
25086             return -1;
25087         if (js_parse_unary(s, PF_POW_FORBIDDEN))
25088             return -1;
25089         switch(op) {
25090         case '-':
25091             emit_op(s, OP_neg);
25092             break;
25093         case '+':
25094             emit_op(s, OP_plus);
25095             break;
25096         case '!':
25097             emit_op(s, OP_lnot);
25098             break;
25099         case '~':
25100             emit_op(s, OP_not);
25101             break;
25102         case TOK_VOID:
25103             emit_op(s, OP_drop);
25104             emit_op(s, OP_undefined);
25105             break;
25106         default:
25107             abort();
25108         }
25109         parse_flags = 0;
25110         break;
25111     case TOK_DEC:
25112     case TOK_INC:
25113         {
25114             int opcode, op, scope, label;
25115             JSAtom name;
25116             op = s->token.val;
25117             if (next_token(s))
25118                 return -1;
25119             if (js_parse_unary(s, 0))
25120                 return -1;
25121             if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
25122                 return -1;
25123             emit_op(s, OP_dec + op - TOK_DEC);
25124             put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP,
25125                        FALSE);
25126         }
25127         break;
25128     case TOK_TYPEOF:
25129         {
25130             JSFunctionDef *fd;
25131             if (next_token(s))
25132                 return -1;
25133             if (js_parse_unary(s, PF_POW_FORBIDDEN))
25134                 return -1;
25135             /* reference access should not return an exception, so we
25136                patch the get_var */
25137             fd = s->cur_func;
25138             if (get_prev_opcode(fd) == OP_scope_get_var) {
25139                 fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef;
25140             }
25141             emit_op(s, OP_typeof);
25142             parse_flags = 0;
25143         }
25144         break;
25145     case TOK_DELETE:
25146         if (js_parse_delete(s))
25147             return -1;
25148         parse_flags = 0;
25149         break;
25150     case TOK_AWAIT:
25151         if (!(s->cur_func->func_kind & JS_FUNC_ASYNC))
25152             return js_parse_error(s, "unexpected 'await' keyword");
25153         if (!s->cur_func->in_function_body)
25154             return js_parse_error(s, "await in default expression");
25155         if (next_token(s))
25156             return -1;
25157         if (js_parse_unary(s, PF_POW_FORBIDDEN))
25158             return -1;
25159         emit_op(s, OP_await);
25160         parse_flags = 0;
25161         break;
25162     default:
25163         if (js_parse_postfix_expr(s, (parse_flags & PF_ARROW_FUNC) |
25164                                   PF_POSTFIX_CALL))
25165             return -1;
25166         if (!s->got_lf &&
25167             (s->token.val == TOK_DEC || s->token.val == TOK_INC)) {
25168             int opcode, op, scope, label;
25169             JSAtom name;
25170             op = s->token.val;
25171             if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
25172                 return -1;
25173             emit_op(s, OP_post_dec + op - TOK_DEC);
25174             put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND,
25175                        FALSE);
25176             if (next_token(s))
25177                 return -1;
25178         }
25179         break;
25180     }
25181     if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) {
25182 #ifdef CONFIG_BIGNUM
25183         if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) {
25184             /* Extended exponentiation syntax rules: we extend the ES7
25185                grammar in order to have more intuitive semantics:
25186                -2**2 evaluates to -4. */
25187             if (!(s->cur_func->js_mode & JS_MODE_MATH)) {
25188                 if (parse_flags & PF_POW_FORBIDDEN) {
25189                     JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
25190                     return -1;
25191                 }
25192             }
25193             if (next_token(s))
25194                 return -1;
25195             if (js_parse_unary(s, PF_POW_ALLOWED))
25196                 return -1;
25197             emit_op(s, OP_pow);
25198         }
25199 #else
25200         if (s->token.val == TOK_POW) {
25201             /* Strict ES7 exponentiation syntax rules: To solve
25202                conficting semantics between different implementations
25203                regarding the precedence of prefix operators and the
25204                postifx exponential, ES7 specifies that -2**2 is a
25205                syntax error. */
25206             if (parse_flags & PF_POW_FORBIDDEN) {
25207                 JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
25208                 return -1;
25209             }
25210             if (next_token(s))
25211                 return -1;
25212             if (js_parse_unary(s, PF_POW_ALLOWED))
25213                 return -1;
25214             emit_op(s, OP_pow);
25215         }
25216 #endif
25217     }
25218     return 0;
25219 }
25220 
25221 /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
js_parse_expr_binary(JSParseState * s,int level,int parse_flags)25222 static __exception int js_parse_expr_binary(JSParseState *s, int level,
25223                                             int parse_flags)
25224 {
25225     int op, opcode;
25226 
25227     if (level == 0) {
25228         return js_parse_unary(s, (parse_flags & PF_ARROW_FUNC) |
25229                               PF_POW_ALLOWED);
25230     }
25231     if (js_parse_expr_binary(s, level - 1, parse_flags))
25232         return -1;
25233     for(;;) {
25234         op = s->token.val;
25235         switch(level) {
25236         case 1:
25237             switch(op) {
25238             case '*':
25239                 opcode = OP_mul;
25240                 break;
25241             case '/':
25242                 opcode = OP_div;
25243                 break;
25244             case '%':
25245 #ifdef CONFIG_BIGNUM
25246                 if (s->cur_func->js_mode & JS_MODE_MATH)
25247                     opcode = OP_math_mod;
25248                 else
25249 #endif
25250                     opcode = OP_mod;
25251                 break;
25252             default:
25253                 return 0;
25254             }
25255             break;
25256         case 2:
25257             switch(op) {
25258             case '+':
25259                 opcode = OP_add;
25260                 break;
25261             case '-':
25262                 opcode = OP_sub;
25263                 break;
25264             default:
25265                 return 0;
25266             }
25267             break;
25268         case 3:
25269             switch(op) {
25270             case TOK_SHL:
25271                 opcode = OP_shl;
25272                 break;
25273             case TOK_SAR:
25274                 opcode = OP_sar;
25275                 break;
25276             case TOK_SHR:
25277                 opcode = OP_shr;
25278                 break;
25279             default:
25280                 return 0;
25281             }
25282             break;
25283         case 4:
25284             switch(op) {
25285             case '<':
25286                 opcode = OP_lt;
25287                 break;
25288             case '>':
25289                 opcode = OP_gt;
25290                 break;
25291             case TOK_LTE:
25292                 opcode = OP_lte;
25293                 break;
25294             case TOK_GTE:
25295                 opcode = OP_gte;
25296                 break;
25297             case TOK_INSTANCEOF:
25298                 opcode = OP_instanceof;
25299                 break;
25300             case TOK_IN:
25301                 if (parse_flags & PF_IN_ACCEPTED) {
25302                     opcode = OP_in;
25303                 } else {
25304                     return 0;
25305                 }
25306                 break;
25307             default:
25308                 return 0;
25309             }
25310             break;
25311         case 5:
25312             switch(op) {
25313             case TOK_EQ:
25314                 opcode = OP_eq;
25315                 break;
25316             case TOK_NEQ:
25317                 opcode = OP_neq;
25318                 break;
25319             case TOK_STRICT_EQ:
25320                 opcode = OP_strict_eq;
25321                 break;
25322             case TOK_STRICT_NEQ:
25323                 opcode = OP_strict_neq;
25324                 break;
25325             default:
25326                 return 0;
25327             }
25328             break;
25329         case 6:
25330             switch(op) {
25331             case '&':
25332                 opcode = OP_and;
25333                 break;
25334             default:
25335                 return 0;
25336             }
25337             break;
25338         case 7:
25339             switch(op) {
25340             case '^':
25341                 opcode = OP_xor;
25342                 break;
25343             default:
25344                 return 0;
25345             }
25346             break;
25347         case 8:
25348             switch(op) {
25349             case '|':
25350                 opcode = OP_or;
25351                 break;
25352             default:
25353                 return 0;
25354             }
25355             break;
25356         default:
25357             abort();
25358         }
25359         if (next_token(s))
25360             return -1;
25361         if (js_parse_expr_binary(s, level - 1, parse_flags & ~PF_ARROW_FUNC))
25362             return -1;
25363         emit_op(s, opcode);
25364     }
25365     return 0;
25366 }
25367 
25368 /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
js_parse_logical_and_or(JSParseState * s,int op,int parse_flags)25369 static __exception int js_parse_logical_and_or(JSParseState *s, int op,
25370                                                int parse_flags)
25371 {
25372     int label1;
25373 
25374     if (op == TOK_LAND) {
25375         if (js_parse_expr_binary(s, 8, parse_flags))
25376             return -1;
25377     } else {
25378         if (js_parse_logical_and_or(s, TOK_LAND, parse_flags))
25379             return -1;
25380     }
25381     if (s->token.val == op) {
25382         label1 = new_label(s);
25383 
25384         for(;;) {
25385             if (next_token(s))
25386                 return -1;
25387             emit_op(s, OP_dup);
25388             emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1);
25389             emit_op(s, OP_drop);
25390 
25391             if (op == TOK_LAND) {
25392                 if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC))
25393                     return -1;
25394             } else {
25395                 if (js_parse_logical_and_or(s, TOK_LAND,
25396                                             parse_flags & ~PF_ARROW_FUNC))
25397                     return -1;
25398             }
25399             if (s->token.val != op) {
25400                 if (s->token.val == TOK_DOUBLE_QUESTION_MARK)
25401                     return js_parse_error(s, "cannot mix ?? with && or ||");
25402                 break;
25403             }
25404         }
25405 
25406         emit_label(s, label1);
25407     }
25408     return 0;
25409 }
25410 
js_parse_coalesce_expr(JSParseState * s,int parse_flags)25411 static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
25412 {
25413     int label1;
25414 
25415     if (js_parse_logical_and_or(s, TOK_LOR, parse_flags))
25416         return -1;
25417     if (s->token.val == TOK_DOUBLE_QUESTION_MARK) {
25418         label1 = new_label(s);
25419         for(;;) {
25420             if (next_token(s))
25421                 return -1;
25422 
25423             emit_op(s, OP_dup);
25424             emit_op(s, OP_is_undefined_or_null);
25425             emit_goto(s, OP_if_false, label1);
25426             emit_op(s, OP_drop);
25427 
25428             if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC))
25429                 return -1;
25430             if (s->token.val != TOK_DOUBLE_QUESTION_MARK)
25431                 break;
25432         }
25433         emit_label(s, label1);
25434     }
25435     return 0;
25436 }
25437 
25438 /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
js_parse_cond_expr(JSParseState * s,int parse_flags)25439 static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags)
25440 {
25441     int label1, label2;
25442 
25443     if (js_parse_coalesce_expr(s, parse_flags))
25444         return -1;
25445     if (s->token.val == '?') {
25446         if (next_token(s))
25447             return -1;
25448         label1 = emit_goto(s, OP_if_false, -1);
25449 
25450         if (js_parse_assign_expr(s))
25451             return -1;
25452         if (js_parse_expect(s, ':'))
25453             return -1;
25454 
25455         label2 = emit_goto(s, OP_goto, -1);
25456 
25457         emit_label(s, label1);
25458 
25459         if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED))
25460             return -1;
25461 
25462         emit_label(s, label2);
25463     }
25464     return 0;
25465 }
25466 
25467 static void emit_return(JSParseState *s, BOOL hasval);
25468 
25469 /* allowed parse_flags: PF_IN_ACCEPTED */
js_parse_assign_expr2(JSParseState * s,int parse_flags)25470 static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
25471 {
25472     int opcode, op, scope;
25473     JSAtom name0 = JS_ATOM_NULL;
25474     JSAtom name;
25475 
25476     if (s->token.val == TOK_YIELD) {
25477         BOOL is_star = FALSE, is_async;
25478 
25479         if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR))
25480             return js_parse_error(s, "unexpected 'yield' keyword");
25481         if (!s->cur_func->in_function_body)
25482             return js_parse_error(s, "yield in default expression");
25483         if (next_token(s))
25484             return -1;
25485         /* XXX: is there a better method to detect 'yield' without
25486            parameters ? */
25487         if (s->token.val != ';' && s->token.val != ')' &&
25488             s->token.val != ']' && s->token.val != '}' &&
25489             s->token.val != ',' && s->token.val != ':' && !s->got_lf) {
25490             if (s->token.val == '*') {
25491                 is_star = TRUE;
25492                 if (next_token(s))
25493                     return -1;
25494             }
25495             if (js_parse_assign_expr2(s, parse_flags))
25496                 return -1;
25497         } else {
25498             emit_op(s, OP_undefined);
25499         }
25500         is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR);
25501 
25502         if (is_star) {
25503             int label_loop, label_return, label_next;
25504             int label_return1, label_yield, label_throw, label_throw1;
25505             int label_throw2;
25506 
25507             label_loop = new_label(s);
25508             label_yield = new_label(s);
25509 
25510             emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start);
25511 
25512             /* remove the catch offset (XXX: could avoid pushing back
25513                undefined) */
25514             emit_op(s, OP_drop);
25515             emit_op(s, OP_undefined);
25516 
25517             emit_op(s, OP_undefined); /* initial value */
25518 
25519             emit_label(s, label_loop);
25520             emit_op(s, OP_iterator_next);
25521             if (is_async)
25522                 emit_op(s, OP_await);
25523             emit_op(s, OP_iterator_check_object);
25524             emit_op(s, OP_get_field2);
25525             emit_atom(s, JS_ATOM_done);
25526             label_next = emit_goto(s, OP_if_true, -1); /* end of loop */
25527             emit_label(s, label_yield);
25528             if (is_async) {
25529                 /* OP_async_yield_star takes the value as parameter */
25530                 emit_op(s, OP_get_field);
25531                 emit_atom(s, JS_ATOM_value);
25532                 emit_op(s, OP_await);
25533                 emit_op(s, OP_async_yield_star);
25534             } else {
25535                 /* OP_yield_star takes (value, done) as parameter */
25536                 emit_op(s, OP_yield_star);
25537             }
25538             emit_op(s, OP_dup);
25539             label_return = emit_goto(s, OP_if_true, -1);
25540             emit_op(s, OP_drop);
25541             emit_goto(s, OP_goto, label_loop);
25542 
25543             emit_label(s, label_return);
25544             emit_op(s, OP_push_i32);
25545             emit_u32(s, 2);
25546             emit_op(s, OP_strict_eq);
25547             label_throw = emit_goto(s, OP_if_true, -1);
25548 
25549             /* return handling */
25550             if (is_async)
25551                 emit_op(s, OP_await);
25552             emit_op(s, OP_iterator_call);
25553             emit_u8(s, 0);
25554             label_return1 = emit_goto(s, OP_if_true, -1);
25555             if (is_async)
25556                 emit_op(s, OP_await);
25557             emit_op(s, OP_iterator_check_object);
25558             emit_op(s, OP_get_field2);
25559             emit_atom(s, JS_ATOM_done);
25560             emit_goto(s, OP_if_false, label_yield);
25561 
25562             emit_op(s, OP_get_field);
25563             emit_atom(s, JS_ATOM_value);
25564 
25565             emit_label(s, label_return1);
25566             emit_op(s, OP_nip);
25567             emit_op(s, OP_nip);
25568             emit_op(s, OP_nip);
25569             emit_return(s, TRUE);
25570 
25571             /* throw handling */
25572             emit_label(s, label_throw);
25573             emit_op(s, OP_iterator_call);
25574             emit_u8(s, 1);
25575             label_throw1 = emit_goto(s, OP_if_true, -1);
25576             if (is_async)
25577                 emit_op(s, OP_await);
25578             emit_op(s, OP_iterator_check_object);
25579             emit_op(s, OP_get_field2);
25580             emit_atom(s, JS_ATOM_done);
25581             emit_goto(s, OP_if_false, label_yield);
25582             emit_goto(s, OP_goto, label_next);
25583             /* close the iterator and throw a type error exception */
25584             emit_label(s, label_throw1);
25585             emit_op(s, OP_iterator_call);
25586             emit_u8(s, 2);
25587             label_throw2 = emit_goto(s, OP_if_true, -1);
25588             if (is_async)
25589                 emit_op(s, OP_await);
25590             emit_label(s, label_throw2);
25591 
25592             emit_op(s, OP_throw_error);
25593             emit_atom(s, JS_ATOM_NULL);
25594             emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW);
25595 
25596             emit_label(s, label_next);
25597             emit_op(s, OP_get_field);
25598             emit_atom(s, JS_ATOM_value);
25599             emit_op(s, OP_nip); /* keep the value associated with
25600                                    done = true */
25601             emit_op(s, OP_nip);
25602             emit_op(s, OP_nip);
25603         } else {
25604             int label_next;
25605 
25606             if (is_async)
25607                 emit_op(s, OP_await);
25608             emit_op(s, OP_yield);
25609             label_next = emit_goto(s, OP_if_false, -1);
25610             emit_return(s, TRUE);
25611             emit_label(s, label_next);
25612         }
25613         return 0;
25614     }
25615     if (s->token.val == TOK_IDENT) {
25616         /* name0 is used to check for OP_set_name pattern, not duplicated */
25617         name0 = s->token.u.ident.atom;
25618     }
25619     if (js_parse_cond_expr(s, parse_flags | PF_ARROW_FUNC))
25620         return -1;
25621 
25622     op = s->token.val;
25623     if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) {
25624         int label;
25625         if (next_token(s))
25626             return -1;
25627         if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0)
25628             return -1;
25629 
25630         if (js_parse_assign_expr2(s, parse_flags)) {
25631             JS_FreeAtom(s->ctx, name);
25632             return -1;
25633         }
25634 
25635         if (op == '=') {
25636             if (opcode == OP_get_ref_value && name == name0) {
25637                 set_object_name(s, name);
25638             }
25639         } else {
25640             static const uint8_t assign_opcodes[] = {
25641                 OP_mul, OP_div, OP_mod, OP_add, OP_sub,
25642                 OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or,
25643 #ifdef CONFIG_BIGNUM
25644                 OP_pow,
25645 #endif
25646                 OP_pow,
25647             };
25648             op = assign_opcodes[op - TOK_MUL_ASSIGN];
25649 #ifdef CONFIG_BIGNUM
25650             if (s->cur_func->js_mode & JS_MODE_MATH) {
25651                 if (op == OP_mod)
25652                     op = OP_math_mod;
25653             }
25654 #endif
25655             emit_op(s, op);
25656         }
25657         put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);
25658     } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) {
25659         int label, label1, depth_lvalue, label2;
25660 
25661         if (next_token(s))
25662             return -1;
25663         if (get_lvalue(s, &opcode, &scope, &name, &label,
25664                        &depth_lvalue, TRUE, op) < 0)
25665             return -1;
25666 
25667         emit_op(s, OP_dup);
25668         if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN)
25669             emit_op(s, OP_is_undefined_or_null);
25670         label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false,
25671                            -1);
25672         emit_op(s, OP_drop);
25673 
25674         if (js_parse_assign_expr2(s, parse_flags)) {
25675             JS_FreeAtom(s->ctx, name);
25676             return -1;
25677         }
25678 
25679         if (opcode == OP_get_ref_value && name == name0) {
25680             set_object_name(s, name);
25681         }
25682 
25683         switch(depth_lvalue) {
25684         case 1:
25685             emit_op(s, OP_insert2);
25686             break;
25687         case 2:
25688             emit_op(s, OP_insert3);
25689             break;
25690         case 3:
25691             emit_op(s, OP_insert4);
25692             break;
25693         default:
25694             abort();
25695         }
25696 
25697         /* XXX: we disable the OP_put_ref_value optimization by not
25698            using put_lvalue() otherwise depth_lvalue is not correct */
25699         put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH,
25700                    FALSE);
25701         label2 = emit_goto(s, OP_goto, -1);
25702 
25703         emit_label(s, label1);
25704 
25705         /* remove the lvalue stack entries */
25706         while (depth_lvalue != 0) {
25707             emit_op(s, OP_nip);
25708             depth_lvalue--;
25709         }
25710 
25711         emit_label(s, label2);
25712     }
25713     return 0;
25714 }
25715 
js_parse_assign_expr(JSParseState * s)25716 static __exception int js_parse_assign_expr(JSParseState *s)
25717 {
25718     return js_parse_assign_expr2(s, PF_IN_ACCEPTED);
25719 }
25720 
25721 /* allowed parse_flags: PF_IN_ACCEPTED */
js_parse_expr2(JSParseState * s,int parse_flags)25722 static __exception int js_parse_expr2(JSParseState *s, int parse_flags)
25723 {
25724     BOOL comma = FALSE;
25725     for(;;) {
25726         if (js_parse_assign_expr2(s, parse_flags))
25727             return -1;
25728         if (comma) {
25729             /* prevent get_lvalue from using the last expression
25730                as an lvalue. This also prevents the conversion of
25731                of get_var to get_ref for method lookup in function
25732                call inside `with` statement.
25733              */
25734             s->cur_func->last_opcode_pos = -1;
25735         }
25736         if (s->token.val != ',')
25737             break;
25738         comma = TRUE;
25739         if (next_token(s))
25740             return -1;
25741         emit_op(s, OP_drop);
25742     }
25743     return 0;
25744 }
25745 
js_parse_expr(JSParseState * s)25746 static __exception int js_parse_expr(JSParseState *s)
25747 {
25748     return js_parse_expr2(s, PF_IN_ACCEPTED);
25749 }
25750 
push_break_entry(JSFunctionDef * fd,BlockEnv * be,JSAtom label_name,int label_break,int label_cont,int drop_count)25751 static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
25752                              JSAtom label_name,
25753                              int label_break, int label_cont,
25754                              int drop_count)
25755 {
25756     be->prev = fd->top_break;
25757     fd->top_break = be;
25758     be->label_name = label_name;
25759     be->label_break = label_break;
25760     be->label_cont = label_cont;
25761     be->drop_count = drop_count;
25762     be->label_finally = -1;
25763     be->scope_level = fd->scope_level;
25764     be->has_iterator = FALSE;
25765 }
25766 
pop_break_entry(JSFunctionDef * fd)25767 static void pop_break_entry(JSFunctionDef *fd)
25768 {
25769     BlockEnv *be;
25770     be = fd->top_break;
25771     fd->top_break = be->prev;
25772 }
25773 
emit_break(JSParseState * s,JSAtom name,int is_cont)25774 static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
25775 {
25776     BlockEnv *top;
25777     int i, scope_level;
25778 
25779     scope_level = s->cur_func->scope_level;
25780     top = s->cur_func->top_break;
25781     while (top != NULL) {
25782         close_scopes(s, scope_level, top->scope_level);
25783         scope_level = top->scope_level;
25784         if (is_cont &&
25785             top->label_cont != -1 &&
25786             (name == JS_ATOM_NULL || top->label_name == name)) {
25787             /* continue stays inside the same block */
25788             emit_goto(s, OP_goto, top->label_cont);
25789             return 0;
25790         }
25791         if (!is_cont &&
25792             top->label_break != -1 &&
25793             (name == JS_ATOM_NULL || top->label_name == name)) {
25794             emit_goto(s, OP_goto, top->label_break);
25795             return 0;
25796         }
25797         i = 0;
25798         if (top->has_iterator) {
25799             emit_op(s, OP_iterator_close);
25800             i += 3;
25801         }
25802         for(; i < top->drop_count; i++)
25803             emit_op(s, OP_drop);
25804         if (top->label_finally != -1) {
25805             /* must push dummy value to keep same stack depth */
25806             emit_op(s, OP_undefined);
25807             emit_goto(s, OP_gosub, top->label_finally);
25808             emit_op(s, OP_drop);
25809         }
25810         top = top->prev;
25811     }
25812     if (name == JS_ATOM_NULL) {
25813         if (is_cont)
25814             return js_parse_error(s, "continue must be inside loop");
25815         else
25816             return js_parse_error(s, "break must be inside loop or switch");
25817     } else {
25818         return js_parse_error(s, "break/continue label not found");
25819     }
25820 }
25821 
25822 /* execute the finally blocks before return */
emit_return(JSParseState * s,BOOL hasval)25823 static void emit_return(JSParseState *s, BOOL hasval)
25824 {
25825     BlockEnv *top;
25826     int drop_count;
25827 
25828     drop_count = 0;
25829     top = s->cur_func->top_break;
25830     while (top != NULL) {
25831         /* XXX: emit the appropriate OP_leave_scope opcodes? Probably not
25832            required as all local variables will be closed upon returning
25833            from JS_CallInternal, but not in the same order. */
25834         if (top->has_iterator) {
25835             /* with 'yield', the exact number of OP_drop to emit is
25836                unknown, so we use a specific operation to look for
25837                the catch offset */
25838             if (!hasval) {
25839                 emit_op(s, OP_undefined);
25840                 hasval = TRUE;
25841             }
25842             emit_op(s, OP_iterator_close_return);
25843             if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
25844                 int label_next, label_next2;
25845 
25846                 emit_op(s, OP_drop); /* catch offset */
25847                 emit_op(s, OP_drop); /* next */
25848                 emit_op(s, OP_get_field2);
25849                 emit_atom(s, JS_ATOM_return);
25850                 /* stack: iter_obj return_func */
25851                 emit_op(s, OP_dup);
25852                 emit_op(s, OP_is_undefined_or_null);
25853                 label_next = emit_goto(s, OP_if_true, -1);
25854                 emit_op(s, OP_call_method);
25855                 emit_u16(s, 0);
25856                 emit_op(s, OP_iterator_check_object);
25857                 emit_op(s, OP_await);
25858                 label_next2 = emit_goto(s, OP_goto, -1);
25859                 emit_label(s, label_next);
25860                 emit_op(s, OP_drop);
25861                 emit_label(s, label_next2);
25862                 emit_op(s, OP_drop);
25863             } else {
25864                 emit_op(s, OP_iterator_close);
25865             }
25866             drop_count = -3;
25867         }
25868         drop_count += top->drop_count;
25869         if (top->label_finally != -1) {
25870             while(drop_count) {
25871                 /* must keep the stack top if hasval */
25872                 emit_op(s, hasval ? OP_nip : OP_drop);
25873                 drop_count--;
25874             }
25875             if (!hasval) {
25876                 /* must push return value to keep same stack size */
25877                 emit_op(s, OP_undefined);
25878                 hasval = TRUE;
25879             }
25880             emit_goto(s, OP_gosub, top->label_finally);
25881         }
25882         top = top->prev;
25883     }
25884     if (s->cur_func->is_derived_class_constructor) {
25885         int label_return;
25886 
25887         /* 'this' can be uninitialized, so it may be accessed only if
25888            the derived class constructor does not return an object */
25889         if (hasval) {
25890             emit_op(s, OP_check_ctor_return);
25891             label_return = emit_goto(s, OP_if_false, -1);
25892             emit_op(s, OP_drop);
25893         } else {
25894             label_return = -1;
25895         }
25896 
25897         /* XXX: if this is not initialized, should throw the
25898            ReferenceError in the caller realm */
25899         emit_op(s, OP_scope_get_var);
25900         emit_atom(s, JS_ATOM_this);
25901         emit_u16(s, 0);
25902 
25903         emit_label(s, label_return);
25904         emit_op(s, OP_return);
25905     } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
25906         if (!hasval) {
25907             emit_op(s, OP_undefined);
25908         } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
25909             emit_op(s, OP_await);
25910         }
25911         emit_op(s, OP_return_async);
25912     } else {
25913         emit_op(s, hasval ? OP_return : OP_return_undef);
25914     }
25915 }
25916 
25917 #define DECL_MASK_FUNC  (1 << 0) /* allow normal function declaration */
25918 /* ored with DECL_MASK_FUNC if function declarations are allowed with a label */
25919 #define DECL_MASK_FUNC_WITH_LABEL (1 << 1)
25920 #define DECL_MASK_OTHER (1 << 2) /* all other declarations */
25921 #define DECL_MASK_ALL   (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER)
25922 
25923 static __exception int js_parse_statement_or_decl(JSParseState *s,
25924                                                   int decl_mask);
25925 
js_parse_statement(JSParseState * s)25926 static __exception int js_parse_statement(JSParseState *s)
25927 {
25928     return js_parse_statement_or_decl(s, 0);
25929 }
25930 
js_parse_block(JSParseState * s)25931 static __exception int js_parse_block(JSParseState *s)
25932 {
25933     if (js_parse_expect(s, '{'))
25934         return -1;
25935     if (s->token.val != '}') {
25936         push_scope(s);
25937         for(;;) {
25938             if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
25939                 return -1;
25940             if (s->token.val == '}')
25941                 break;
25942         }
25943         pop_scope(s);
25944     }
25945     if (next_token(s))
25946         return -1;
25947     return 0;
25948 }
25949 
25950 /* allowed parse_flags: PF_IN_ACCEPTED */
js_parse_var(JSParseState * s,int parse_flags,int tok,BOOL export_flag)25951 static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,
25952                                     BOOL export_flag)
25953 {
25954     JSContext *ctx = s->ctx;
25955     JSFunctionDef *fd = s->cur_func;
25956     JSAtom name = JS_ATOM_NULL;
25957 
25958     for (;;) {
25959         if (s->token.val == TOK_IDENT) {
25960             if (s->token.u.ident.is_reserved) {
25961                 return js_parse_error_reserved_identifier(s);
25962             }
25963             name = JS_DupAtom(ctx, s->token.u.ident.atom);
25964             if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) {
25965                 js_parse_error(s, "'let' is not a valid lexical identifier");
25966                 goto var_error;
25967             }
25968             if (next_token(s))
25969                 goto var_error;
25970             if (js_define_var(s, name, tok))
25971                 goto var_error;
25972             if (export_flag) {
25973                 if (!add_export_entry(s, s->cur_func->module, name, name,
25974                                       JS_EXPORT_TYPE_LOCAL))
25975                     goto var_error;
25976             }
25977 
25978             if (s->token.val == '=') {
25979                 if (next_token(s))
25980                     goto var_error;
25981                 if (tok == TOK_VAR) {
25982                     /* Must make a reference for proper `with` semantics */
25983                     int opcode, scope, label;
25984                     JSAtom name1;
25985 
25986                     emit_op(s, OP_scope_get_var);
25987                     emit_atom(s, name);
25988                     emit_u16(s, fd->scope_level);
25989                     if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0)
25990                         goto var_error;
25991                     if (js_parse_assign_expr2(s, parse_flags)) {
25992                         JS_FreeAtom(ctx, name1);
25993                         goto var_error;
25994                     }
25995                     set_object_name(s, name);
25996                     put_lvalue(s, opcode, scope, name1, label,
25997                                PUT_LVALUE_NOKEEP, FALSE);
25998                 } else {
25999                     if (js_parse_assign_expr2(s, parse_flags))
26000                         goto var_error;
26001                     set_object_name(s, name);
26002                     emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
26003                         OP_scope_put_var_init : OP_scope_put_var);
26004                     emit_atom(s, name);
26005                     emit_u16(s, fd->scope_level);
26006                 }
26007             } else {
26008                 if (tok == TOK_CONST) {
26009                     js_parse_error(s, "missing initializer for const variable");
26010                     goto var_error;
26011                 }
26012                 if (tok == TOK_LET) {
26013                     /* initialize lexical variable upon entering its scope */
26014                     emit_op(s, OP_undefined);
26015                     emit_op(s, OP_scope_put_var_init);
26016                     emit_atom(s, name);
26017                     emit_u16(s, fd->scope_level);
26018                 }
26019             }
26020             JS_FreeAtom(ctx, name);
26021         } else {
26022             int skip_bits;
26023             if ((s->token.val == '[' || s->token.val == '{')
26024             &&  js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
26025                 emit_op(s, OP_undefined);
26026                 if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
26027                     return -1;
26028             } else {
26029                 return js_parse_error(s, "variable name expected");
26030             }
26031         }
26032         if (s->token.val != ',')
26033             break;
26034         if (next_token(s))
26035             return -1;
26036     }
26037     return 0;
26038 
26039  var_error:
26040     JS_FreeAtom(ctx, name);
26041     return -1;
26042 }
26043 
26044 /* test if the current token is a label. Use simplistic look-ahead scanner */
is_label(JSParseState * s)26045 static BOOL is_label(JSParseState *s)
26046 {
26047     return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
26048             peek_token(s, FALSE) == ':');
26049 }
26050 
26051 /* test if the current token is a let keyword. Use simplistic look-ahead scanner */
is_let(JSParseState * s,int decl_mask)26052 static int is_let(JSParseState *s, int decl_mask)
26053 {
26054     int res = FALSE;
26055 
26056     if (token_is_pseudo_keyword(s, JS_ATOM_let)) {
26057 #if 1
26058         JSParsePos pos;
26059         js_parse_get_pos(s, &pos);
26060         for (;;) {
26061             if (next_token(s)) {
26062                 res = -1;
26063                 break;
26064             }
26065             if (s->token.val == '[') {
26066                 /* let [ is a syntax restriction:
26067                    it never introduces an ExpressionStatement */
26068                 res = TRUE;
26069                 break;
26070             }
26071             if (s->token.val == '{' ||
26072                 (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) ||
26073                 s->token.val == TOK_LET ||
26074                 s->token.val == TOK_YIELD ||
26075                 s->token.val == TOK_AWAIT) {
26076                 /* Check for possible ASI if not scanning for Declaration */
26077                 /* XXX: should also check that `{` introduces a BindingPattern,
26078                    but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */
26079                 if (s->last_line_num == s->token.line_num || (decl_mask & DECL_MASK_OTHER)) {
26080                     res = TRUE;
26081                     break;
26082                 }
26083                 break;
26084             }
26085             break;
26086         }
26087         if (js_parse_seek_token(s, &pos)) {
26088             res = -1;
26089         }
26090 #else
26091         int tok = peek_token(s, TRUE);
26092         if (tok == '{' || tok == TOK_IDENT || peek_token(s, FALSE) == '[') {
26093             res = TRUE;
26094         }
26095 #endif
26096     }
26097     return res;
26098 }
26099 
26100 /* XXX: handle IteratorClose when exiting the loop before the
26101    enumeration is done */
js_parse_for_in_of(JSParseState * s,int label_name,BOOL is_async)26102 static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
26103                                           BOOL is_async)
26104 {
26105     JSContext *ctx = s->ctx;
26106     JSFunctionDef *fd = s->cur_func;
26107     JSAtom var_name;
26108     BOOL has_initializer, is_for_of, has_destructuring;
26109     int tok, tok1, opcode, scope, block_scope_level;
26110     int label_next, label_expr, label_cont, label_body, label_break;
26111     int pos_next, pos_expr;
26112     BlockEnv break_entry;
26113 
26114     has_initializer = FALSE;
26115     has_destructuring = FALSE;
26116     is_for_of = FALSE;
26117     block_scope_level = fd->scope_level;
26118     label_cont = new_label(s);
26119     label_body = new_label(s);
26120     label_break = new_label(s);
26121     label_next = new_label(s);
26122 
26123     /* create scope for the lexical variables declared in the enumeration
26124        expressions. XXX: Not completely correct because of weird capturing
26125        semantics in `for (i of o) a.push(function(){return i})` */
26126     push_scope(s);
26127 
26128     /* local for_in scope starts here so individual elements
26129        can be closed in statement. */
26130     push_break_entry(s->cur_func, &break_entry,
26131                      label_name, label_break, label_cont, 1);
26132     break_entry.scope_level = block_scope_level;
26133 
26134     label_expr = emit_goto(s, OP_goto, -1);
26135 
26136     pos_next = s->cur_func->byte_code.size;
26137     emit_label(s, label_next);
26138 
26139     tok = s->token.val;
26140     switch (is_let(s, DECL_MASK_OTHER)) {
26141     case TRUE:
26142         tok = TOK_LET;
26143         break;
26144     case FALSE:
26145         break;
26146     default:
26147         return -1;
26148     }
26149     if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
26150         if (next_token(s))
26151             return -1;
26152 
26153         if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
26154             if (s->token.val == '[' || s->token.val == '{') {
26155                 if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0)
26156                     return -1;
26157                 has_destructuring = TRUE;
26158             } else {
26159                 return js_parse_error(s, "variable name expected");
26160             }
26161             var_name = JS_ATOM_NULL;
26162         } else {
26163             var_name = JS_DupAtom(ctx, s->token.u.ident.atom);
26164             if (next_token(s)) {
26165                 JS_FreeAtom(s->ctx, var_name);
26166                 return -1;
26167             }
26168             if (js_define_var(s, var_name, tok)) {
26169                 JS_FreeAtom(s->ctx, var_name);
26170                 return -1;
26171             }
26172             emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
26173                     OP_scope_put_var_init : OP_scope_put_var);
26174             emit_atom(s, var_name);
26175             emit_u16(s, fd->scope_level);
26176         }
26177     } else {
26178         int skip_bits;
26179         if ((s->token.val == '[' || s->token.val == '{')
26180         &&  ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) {
26181             if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
26182                 return -1;
26183         } else {
26184             int lvalue_label;
26185             if (js_parse_left_hand_side_expr(s))
26186                 return -1;
26187             if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label,
26188                            NULL, FALSE, TOK_FOR))
26189                 return -1;
26190             put_lvalue(s, opcode, scope, var_name, lvalue_label,
26191                        PUT_LVALUE_NOKEEP_BOTTOM, FALSE);
26192         }
26193         var_name = JS_ATOM_NULL;
26194     }
26195     emit_goto(s, OP_goto, label_body);
26196 
26197     pos_expr = s->cur_func->byte_code.size;
26198     emit_label(s, label_expr);
26199     if (s->token.val == '=') {
26200         /* XXX: potential scoping issue if inside `with` statement */
26201         has_initializer = TRUE;
26202         /* parse and evaluate initializer prior to evaluating the
26203            object (only used with "for in" with a non lexical variable
26204            in non strict mode */
26205         if (next_token(s) || js_parse_assign_expr2(s, 0)) {
26206             JS_FreeAtom(ctx, var_name);
26207             return -1;
26208         }
26209         if (var_name != JS_ATOM_NULL) {
26210             emit_op(s, OP_scope_put_var);
26211             emit_atom(s, var_name);
26212             emit_u16(s, fd->scope_level);
26213         }
26214     }
26215     JS_FreeAtom(ctx, var_name);
26216 
26217     if (token_is_pseudo_keyword(s, JS_ATOM_of)) {
26218         break_entry.has_iterator = is_for_of = TRUE;
26219         break_entry.drop_count += 2;
26220         if (has_initializer)
26221             goto initializer_error;
26222     } else if (s->token.val == TOK_IN) {
26223         if (is_async)
26224             return js_parse_error(s, "'for await' loop should be used with 'of'");
26225         if (has_initializer &&
26226             (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) ||
26227              has_destructuring)) {
26228         initializer_error:
26229             return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer",
26230                                   is_for_of ? "of" : "in");
26231         }
26232     } else {
26233         return js_parse_error(s, "expected 'of' or 'in' in for control expression");
26234     }
26235     if (next_token(s))
26236         return -1;
26237     if (is_for_of) {
26238         if (js_parse_assign_expr(s))
26239             return -1;
26240     } else {
26241         if (js_parse_expr(s))
26242             return -1;
26243     }
26244     /* close the scope after having evaluated the expression so that
26245        the TDZ values are in the closures */
26246     close_scopes(s, s->cur_func->scope_level, block_scope_level);
26247     if (is_for_of) {
26248         if (is_async)
26249             emit_op(s, OP_for_await_of_start);
26250         else
26251             emit_op(s, OP_for_of_start);
26252         /* on stack: enum_rec */
26253     } else {
26254         emit_op(s, OP_for_in_start);
26255         /* on stack: enum_obj */
26256     }
26257     emit_goto(s, OP_goto, label_cont);
26258 
26259     if (js_parse_expect(s, ')'))
26260         return -1;
26261 
26262     if (OPTIMIZE) {
26263         /* move the `next` code here */
26264         DynBuf *bc = &s->cur_func->byte_code;
26265         int chunk_size = pos_expr - pos_next;
26266         int offset = bc->size - pos_next;
26267         int i;
26268         dbuf_realloc(bc, bc->size + chunk_size);
26269         dbuf_put(bc, bc->buf + pos_next, chunk_size);
26270         memset(bc->buf + pos_next, OP_nop, chunk_size);
26271         /* `next` part ends with a goto */
26272         s->cur_func->last_opcode_pos = bc->size - 5;
26273         /* relocate labels */
26274         for (i = label_cont; i < s->cur_func->label_count; i++) {
26275             LabelSlot *ls = &s->cur_func->label_slots[i];
26276             if (ls->pos >= pos_next && ls->pos < pos_expr)
26277                 ls->pos += offset;
26278         }
26279     }
26280 
26281     emit_label(s, label_body);
26282     if (js_parse_statement(s))
26283         return -1;
26284 
26285     close_scopes(s, s->cur_func->scope_level, block_scope_level);
26286 
26287     emit_label(s, label_cont);
26288     if (is_for_of) {
26289         if (is_async) {
26290             /* call the next method */
26291             /* stack: iter_obj next catch_offset */
26292             emit_op(s, OP_dup3);
26293             emit_op(s, OP_drop);
26294             emit_op(s, OP_call_method);
26295             emit_u16(s, 0);
26296             /* get the result of the promise */
26297             emit_op(s, OP_await);
26298             /* unwrap the value and done values */
26299             emit_op(s, OP_iterator_get_value_done);
26300         } else {
26301             emit_op(s, OP_for_of_next);
26302             emit_u8(s, 0);
26303         }
26304     } else {
26305         emit_op(s, OP_for_in_next);
26306     }
26307     /* on stack: enum_rec / enum_obj value bool */
26308     emit_goto(s, OP_if_false, label_next);
26309     /* drop the undefined value from for_xx_next */
26310     emit_op(s, OP_drop);
26311 
26312     emit_label(s, label_break);
26313     if (is_for_of) {
26314         /* close and drop enum_rec */
26315         emit_op(s, OP_iterator_close);
26316     } else {
26317         emit_op(s, OP_drop);
26318     }
26319     pop_break_entry(s->cur_func);
26320     pop_scope(s);
26321     return 0;
26322 }
26323 
set_eval_ret_undefined(JSParseState * s)26324 static void set_eval_ret_undefined(JSParseState *s)
26325 {
26326     if (s->cur_func->eval_ret_idx >= 0) {
26327         emit_op(s, OP_undefined);
26328         emit_op(s, OP_put_loc);
26329         emit_u16(s, s->cur_func->eval_ret_idx);
26330     }
26331 }
26332 
js_parse_statement_or_decl(JSParseState * s,int decl_mask)26333 static __exception int js_parse_statement_or_decl(JSParseState *s,
26334                                                   int decl_mask)
26335 {
26336     JSContext *ctx = s->ctx;
26337     JSAtom label_name;
26338     int tok;
26339 
26340     /* specific label handling */
26341     /* XXX: support multiple labels on loop statements */
26342     label_name = JS_ATOM_NULL;
26343     if (is_label(s)) {
26344         BlockEnv *be;
26345 
26346         label_name = JS_DupAtom(ctx, s->token.u.ident.atom);
26347 
26348         for (be = s->cur_func->top_break; be; be = be->prev) {
26349             if (be->label_name == label_name) {
26350                 js_parse_error(s, "duplicate label name");
26351                 goto fail;
26352             }
26353         }
26354 
26355         if (next_token(s))
26356             goto fail;
26357         if (js_parse_expect(s, ':'))
26358             goto fail;
26359         if (s->token.val != TOK_FOR
26360         &&  s->token.val != TOK_DO
26361         &&  s->token.val != TOK_WHILE) {
26362             /* labelled regular statement */
26363             int label_break, mask;
26364             BlockEnv break_entry;
26365 
26366             label_break = new_label(s);
26367             push_break_entry(s->cur_func, &break_entry,
26368                              label_name, label_break, -1, 0);
26369             if (!(s->cur_func->js_mode & JS_MODE_STRICT) &&
26370                 (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) {
26371                 mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL;
26372             } else {
26373                 mask = 0;
26374             }
26375             if (js_parse_statement_or_decl(s, mask))
26376                 goto fail;
26377             emit_label(s, label_break);
26378             pop_break_entry(s->cur_func);
26379             goto done;
26380         }
26381     }
26382 
26383     switch(tok = s->token.val) {
26384     case '{':
26385         if (js_parse_block(s))
26386             goto fail;
26387         break;
26388     case TOK_RETURN:
26389         if (s->cur_func->is_eval) {
26390             js_parse_error(s, "return not in a function");
26391             goto fail;
26392         }
26393         if (next_token(s))
26394             goto fail;
26395         if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {
26396             if (js_parse_expr(s))
26397                 goto fail;
26398             emit_return(s, TRUE);
26399         } else {
26400             emit_return(s, FALSE);
26401         }
26402         if (js_parse_expect_semi(s))
26403             goto fail;
26404         break;
26405     case TOK_THROW:
26406         if (next_token(s))
26407             goto fail;
26408         if (s->got_lf) {
26409             js_parse_error(s, "line terminator not allowed after throw");
26410             goto fail;
26411         }
26412         if (js_parse_expr(s))
26413             goto fail;
26414         emit_op(s, OP_throw);
26415         if (js_parse_expect_semi(s))
26416             goto fail;
26417         break;
26418     case TOK_LET:
26419     case TOK_CONST:
26420     haslet:
26421         if (!(decl_mask & DECL_MASK_OTHER)) {
26422             js_parse_error(s, "lexical declarations can't appear in single-statement context");
26423             goto fail;
26424         }
26425         /* fall thru */
26426     case TOK_VAR:
26427         if (next_token(s))
26428             goto fail;
26429         if (js_parse_var(s, TRUE, tok, FALSE))
26430             goto fail;
26431         if (js_parse_expect_semi(s))
26432             goto fail;
26433         break;
26434     case TOK_IF:
26435         {
26436             int label1, label2, mask;
26437             if (next_token(s))
26438                 goto fail;
26439             /* create a new scope for `let f;if(1) function f(){}` */
26440             push_scope(s);
26441             set_eval_ret_undefined(s);
26442             if (js_parse_expr_paren(s))
26443                 goto fail;
26444             label1 = emit_goto(s, OP_if_false, -1);
26445             if (s->cur_func->js_mode & JS_MODE_STRICT)
26446                 mask = 0;
26447             else
26448                 mask = DECL_MASK_FUNC; /* Annex B.3.4 */
26449 
26450             if (js_parse_statement_or_decl(s, mask))
26451                 goto fail;
26452 
26453             if (s->token.val == TOK_ELSE) {
26454                 label2 = emit_goto(s, OP_goto, -1);
26455                 if (next_token(s))
26456                     goto fail;
26457 
26458                 emit_label(s, label1);
26459                 if (js_parse_statement_or_decl(s, mask))
26460                     goto fail;
26461 
26462                 label1 = label2;
26463             }
26464             emit_label(s, label1);
26465             pop_scope(s);
26466         }
26467         break;
26468     case TOK_WHILE:
26469         {
26470             int label_cont, label_break;
26471             BlockEnv break_entry;
26472 
26473             label_cont = new_label(s);
26474             label_break = new_label(s);
26475 
26476             push_break_entry(s->cur_func, &break_entry,
26477                              label_name, label_break, label_cont, 0);
26478 
26479             if (next_token(s))
26480                 goto fail;
26481 
26482             set_eval_ret_undefined(s);
26483 
26484             emit_label(s, label_cont);
26485             if (js_parse_expr_paren(s))
26486                 goto fail;
26487             emit_goto(s, OP_if_false, label_break);
26488 
26489             if (js_parse_statement(s))
26490                 goto fail;
26491             emit_goto(s, OP_goto, label_cont);
26492 
26493             emit_label(s, label_break);
26494 
26495             pop_break_entry(s->cur_func);
26496         }
26497         break;
26498     case TOK_DO:
26499         {
26500             int label_cont, label_break, label1;
26501             BlockEnv break_entry;
26502 
26503             label_cont = new_label(s);
26504             label_break = new_label(s);
26505             label1 = new_label(s);
26506 
26507             push_break_entry(s->cur_func, &break_entry,
26508                              label_name, label_break, label_cont, 0);
26509 
26510             if (next_token(s))
26511                 goto fail;
26512 
26513             emit_label(s, label1);
26514 
26515             set_eval_ret_undefined(s);
26516 
26517             if (js_parse_statement(s))
26518                 goto fail;
26519 
26520             emit_label(s, label_cont);
26521             if (js_parse_expect(s, TOK_WHILE))
26522                 goto fail;
26523             if (js_parse_expr_paren(s))
26524                 goto fail;
26525             /* Insert semicolon if missing */
26526             if (s->token.val == ';') {
26527                 if (next_token(s))
26528                     goto fail;
26529             }
26530             emit_goto(s, OP_if_true, label1);
26531 
26532             emit_label(s, label_break);
26533 
26534             pop_break_entry(s->cur_func);
26535         }
26536         break;
26537     case TOK_FOR:
26538         {
26539             int label_cont, label_break, label_body, label_test;
26540             int pos_cont, pos_body, block_scope_level;
26541             BlockEnv break_entry;
26542             int tok, bits;
26543             BOOL is_async;
26544 
26545             if (next_token(s))
26546                 goto fail;
26547 
26548             set_eval_ret_undefined(s);
26549             bits = 0;
26550             is_async = FALSE;
26551             if (s->token.val == '(') {
26552                 js_parse_skip_parens_token(s, &bits, FALSE);
26553             } else if (s->token.val == TOK_AWAIT) {
26554                 if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) {
26555                     js_parse_error(s, "for await is only valid in asynchronous functions");
26556                     goto fail;
26557                 }
26558                 is_async = TRUE;
26559                 if (next_token(s))
26560                     goto fail;
26561             }
26562             if (js_parse_expect(s, '('))
26563                 goto fail;
26564 
26565             if (!(bits & SKIP_HAS_SEMI)) {
26566                 /* parse for/in or for/of */
26567                 if (js_parse_for_in_of(s, label_name, is_async))
26568                     goto fail;
26569                 break;
26570             }
26571             block_scope_level = s->cur_func->scope_level;
26572 
26573             /* create scope for the lexical variables declared in the initial,
26574                test and increment expressions */
26575             push_scope(s);
26576             /* initial expression */
26577             tok = s->token.val;
26578             if (tok != ';') {
26579                 switch (is_let(s, DECL_MASK_OTHER)) {
26580                 case TRUE:
26581                     tok = TOK_LET;
26582                     break;
26583                 case FALSE:
26584                     break;
26585                 default:
26586                     goto fail;
26587                 }
26588                 if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
26589                     if (next_token(s))
26590                         goto fail;
26591                     if (js_parse_var(s, FALSE, tok, FALSE))
26592                         goto fail;
26593                 } else {
26594                     if (js_parse_expr2(s, FALSE))
26595                         goto fail;
26596                     emit_op(s, OP_drop);
26597                 }
26598 
26599                 /* close the closures before the first iteration */
26600                 close_scopes(s, s->cur_func->scope_level, block_scope_level);
26601             }
26602             if (js_parse_expect(s, ';'))
26603                 goto fail;
26604 
26605             label_test = new_label(s);
26606             label_cont = new_label(s);
26607             label_body = new_label(s);
26608             label_break = new_label(s);
26609 
26610             push_break_entry(s->cur_func, &break_entry,
26611                              label_name, label_break, label_cont, 0);
26612 
26613             /* test expression */
26614             if (s->token.val == ';') {
26615                 /* no test expression */
26616                 label_test = label_body;
26617             } else {
26618                 emit_label(s, label_test);
26619                 if (js_parse_expr(s))
26620                     goto fail;
26621                 emit_goto(s, OP_if_false, label_break);
26622             }
26623             if (js_parse_expect(s, ';'))
26624                 goto fail;
26625 
26626             if (s->token.val == ')') {
26627                 /* no end expression */
26628                 break_entry.label_cont = label_cont = label_test;
26629                 pos_cont = 0; /* avoid warning */
26630             } else {
26631                 /* skip the end expression */
26632                 emit_goto(s, OP_goto, label_body);
26633 
26634                 pos_cont = s->cur_func->byte_code.size;
26635                 emit_label(s, label_cont);
26636                 if (js_parse_expr(s))
26637                     goto fail;
26638                 emit_op(s, OP_drop);
26639                 if (label_test != label_body)
26640                     emit_goto(s, OP_goto, label_test);
26641             }
26642             if (js_parse_expect(s, ')'))
26643                 goto fail;
26644 
26645             pos_body = s->cur_func->byte_code.size;
26646             emit_label(s, label_body);
26647             if (js_parse_statement(s))
26648                 goto fail;
26649 
26650             /* close the closures before the next iteration */
26651             /* XXX: check continue case */
26652             close_scopes(s, s->cur_func->scope_level, block_scope_level);
26653 
26654             if (OPTIMIZE && label_test != label_body && label_cont != label_test) {
26655                 /* move the increment code here */
26656                 DynBuf *bc = &s->cur_func->byte_code;
26657                 int chunk_size = pos_body - pos_cont;
26658                 int offset = bc->size - pos_cont;
26659                 int i;
26660                 dbuf_realloc(bc, bc->size + chunk_size);
26661                 dbuf_put(bc, bc->buf + pos_cont, chunk_size);
26662                 memset(bc->buf + pos_cont, OP_nop, chunk_size);
26663                 /* increment part ends with a goto */
26664                 s->cur_func->last_opcode_pos = bc->size - 5;
26665                 /* relocate labels */
26666                 for (i = label_cont; i < s->cur_func->label_count; i++) {
26667                     LabelSlot *ls = &s->cur_func->label_slots[i];
26668                     if (ls->pos >= pos_cont && ls->pos < pos_body)
26669                         ls->pos += offset;
26670                 }
26671             } else {
26672                 emit_goto(s, OP_goto, label_cont);
26673             }
26674 
26675             emit_label(s, label_break);
26676 
26677             pop_break_entry(s->cur_func);
26678             pop_scope(s);
26679         }
26680         break;
26681     case TOK_BREAK:
26682     case TOK_CONTINUE:
26683         {
26684             int is_cont = s->token.val - TOK_BREAK;
26685             int label;
26686 
26687             if (next_token(s))
26688                 goto fail;
26689             if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
26690                 label = s->token.u.ident.atom;
26691             else
26692                 label = JS_ATOM_NULL;
26693             if (emit_break(s, label, is_cont))
26694                 goto fail;
26695             if (label != JS_ATOM_NULL) {
26696                 if (next_token(s))
26697                     goto fail;
26698             }
26699             if (js_parse_expect_semi(s))
26700                 goto fail;
26701         }
26702         break;
26703     case TOK_SWITCH:
26704         {
26705             int label_case, label_break, label1;
26706             int default_label_pos;
26707             BlockEnv break_entry;
26708 
26709             if (next_token(s))
26710                 goto fail;
26711 
26712             set_eval_ret_undefined(s);
26713             if (js_parse_expr_paren(s))
26714                 goto fail;
26715 
26716             push_scope(s);
26717             label_break = new_label(s);
26718             push_break_entry(s->cur_func, &break_entry,
26719                              label_name, label_break, -1, 1);
26720 
26721             if (js_parse_expect(s, '{'))
26722                 goto fail;
26723 
26724             default_label_pos = -1;
26725             label_case = -1;
26726             while (s->token.val != '}') {
26727                 if (s->token.val == TOK_CASE) {
26728                     label1 = -1;
26729                     if (label_case >= 0) {
26730                         /* skip the case if needed */
26731                         label1 = emit_goto(s, OP_goto, -1);
26732                     }
26733                     emit_label(s, label_case);
26734                     label_case = -1;
26735                     for (;;) {
26736                         /* parse a sequence of case clauses */
26737                         if (next_token(s))
26738                             goto fail;
26739                         emit_op(s, OP_dup);
26740                         if (js_parse_expr(s))
26741                             goto fail;
26742                         if (js_parse_expect(s, ':'))
26743                             goto fail;
26744                         emit_op(s, OP_strict_eq);
26745                         if (s->token.val == TOK_CASE) {
26746                             label1 = emit_goto(s, OP_if_true, label1);
26747                         } else {
26748                             label_case = emit_goto(s, OP_if_false, -1);
26749                             emit_label(s, label1);
26750                             break;
26751                         }
26752                     }
26753                 } else if (s->token.val == TOK_DEFAULT) {
26754                     if (next_token(s))
26755                         goto fail;
26756                     if (js_parse_expect(s, ':'))
26757                         goto fail;
26758                     if (default_label_pos >= 0) {
26759                         js_parse_error(s, "duplicate default");
26760                         goto fail;
26761                     }
26762                     if (label_case < 0) {
26763                         /* falling thru direct from switch expression */
26764                         label_case = emit_goto(s, OP_goto, -1);
26765                     }
26766                     /* Emit a dummy label opcode. Label will be patched after
26767                        the end of the switch body. Do not use emit_label(s, 0)
26768                        because it would clobber label 0 address, preventing
26769                        proper optimizer operation.
26770                      */
26771                     emit_op(s, OP_label);
26772                     emit_u32(s, 0);
26773                     default_label_pos = s->cur_func->byte_code.size - 4;
26774                 } else {
26775                     if (label_case < 0) {
26776                         /* falling thru direct from switch expression */
26777                         js_parse_error(s, "invalid switch statement");
26778                         goto fail;
26779                     }
26780                     if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
26781                         goto fail;
26782                 }
26783             }
26784             if (js_parse_expect(s, '}'))
26785                 goto fail;
26786             if (default_label_pos >= 0) {
26787                 /* Ugly patch for the the `default` label, shameful and risky */
26788                 put_u32(s->cur_func->byte_code.buf + default_label_pos,
26789                         label_case);
26790                 s->cur_func->label_slots[label_case].pos = default_label_pos + 4;
26791             } else {
26792                 emit_label(s, label_case);
26793             }
26794             emit_label(s, label_break);
26795             emit_op(s, OP_drop); /* drop the switch expression */
26796 
26797             pop_break_entry(s->cur_func);
26798             pop_scope(s);
26799         }
26800         break;
26801     case TOK_TRY:
26802         {
26803             int label_catch, label_catch2, label_finally, label_end;
26804             JSAtom name;
26805             BlockEnv block_env;
26806 
26807             set_eval_ret_undefined(s);
26808             if (next_token(s))
26809                 goto fail;
26810             label_catch = new_label(s);
26811             label_catch2 = new_label(s);
26812             label_finally = new_label(s);
26813             label_end = new_label(s);
26814 
26815             emit_goto(s, OP_catch, label_catch);
26816 
26817             push_break_entry(s->cur_func, &block_env,
26818                              JS_ATOM_NULL, -1, -1, 1);
26819             block_env.label_finally = label_finally;
26820 
26821             if (js_parse_block(s))
26822                 goto fail;
26823 
26824             pop_break_entry(s->cur_func);
26825 
26826             if (js_is_live_code(s)) {
26827                 /* drop the catch offset */
26828                 emit_op(s, OP_drop);
26829                 /* must push dummy value to keep same stack size */
26830                 emit_op(s, OP_undefined);
26831                 emit_goto(s, OP_gosub, label_finally);
26832                 emit_op(s, OP_drop);
26833 
26834                 emit_goto(s, OP_goto, label_end);
26835             }
26836 
26837             if (s->token.val == TOK_CATCH) {
26838                 if (next_token(s))
26839                     goto fail;
26840 
26841                 push_scope(s);  /* catch variable */
26842                 emit_label(s, label_catch);
26843 
26844                 if (s->token.val == '{') {
26845                     /* support optional-catch-binding feature */
26846                     emit_op(s, OP_drop);    /* pop the exception object */
26847                 } else {
26848                     if (js_parse_expect(s, '('))
26849                         goto fail;
26850                     if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
26851                         if (s->token.val == '[' || s->token.val == '{') {
26852                             /* XXX: TOK_LET is not completely correct */
26853                             if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0)
26854                                 goto fail;
26855                         } else {
26856                             js_parse_error(s, "identifier expected");
26857                             goto fail;
26858                         }
26859                     } else {
26860                         name = JS_DupAtom(ctx, s->token.u.ident.atom);
26861                         if (next_token(s)
26862                         ||  js_define_var(s, name, TOK_CATCH) < 0) {
26863                             JS_FreeAtom(ctx, name);
26864                             goto fail;
26865                         }
26866                         /* store the exception value in the catch variable */
26867                         emit_op(s, OP_scope_put_var);
26868                         emit_u32(s, name);
26869                         emit_u16(s, s->cur_func->scope_level);
26870                     }
26871                     if (js_parse_expect(s, ')'))
26872                         goto fail;
26873                 }
26874                 /* XXX: should keep the address to nop it out if there is no finally block */
26875                 emit_goto(s, OP_catch, label_catch2);
26876 
26877                 push_scope(s);  /* catch block */
26878                 push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
26879                                  -1, -1, 1);
26880                 block_env.label_finally = label_finally;
26881 
26882                 if (js_parse_block(s))
26883                     goto fail;
26884 
26885                 pop_break_entry(s->cur_func);
26886                 pop_scope(s);  /* catch block */
26887                 pop_scope(s);  /* catch variable */
26888 
26889                 if (js_is_live_code(s)) {
26890                     /* drop the catch2 offset */
26891                     emit_op(s, OP_drop);
26892                     /* XXX: should keep the address to nop it out if there is no finally block */
26893                     /* must push dummy value to keep same stack size */
26894                     emit_op(s, OP_undefined);
26895                     emit_goto(s, OP_gosub, label_finally);
26896                     emit_op(s, OP_drop);
26897                     emit_goto(s, OP_goto, label_end);
26898                 }
26899                 /* catch exceptions thrown in the catch block to execute the
26900                  * finally clause and rethrow the exception */
26901                 emit_label(s, label_catch2);
26902                 /* catch value is at TOS, no need to push undefined */
26903                 emit_goto(s, OP_gosub, label_finally);
26904                 emit_op(s, OP_throw);
26905 
26906             } else if (s->token.val == TOK_FINALLY) {
26907                 /* finally without catch : execute the finally clause
26908                  * and rethrow the exception */
26909                 emit_label(s, label_catch);
26910                 /* catch value is at TOS, no need to push undefined */
26911                 emit_goto(s, OP_gosub, label_finally);
26912                 emit_op(s, OP_throw);
26913             } else {
26914                 js_parse_error(s, "expecting catch or finally");
26915                 goto fail;
26916             }
26917             emit_label(s, label_finally);
26918             if (s->token.val == TOK_FINALLY) {
26919                 int saved_eval_ret_idx = 0; /* avoid warning */
26920 
26921                 if (next_token(s))
26922                     goto fail;
26923                 /* on the stack: ret_value gosub_ret_value */
26924                 push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
26925                                  -1, -1, 2);
26926 
26927                 if (s->cur_func->eval_ret_idx >= 0) {
26928                     /* 'finally' updates eval_ret only if not a normal
26929                        termination */
26930                     saved_eval_ret_idx =
26931                         add_var(s->ctx, s->cur_func, JS_ATOM__ret_);
26932                     if (saved_eval_ret_idx < 0)
26933                         goto fail;
26934                     emit_op(s, OP_get_loc);
26935                     emit_u16(s, s->cur_func->eval_ret_idx);
26936                     emit_op(s, OP_put_loc);
26937                     emit_u16(s, saved_eval_ret_idx);
26938                     set_eval_ret_undefined(s);
26939                 }
26940 
26941                 if (js_parse_block(s))
26942                     goto fail;
26943 
26944                 if (s->cur_func->eval_ret_idx >= 0) {
26945                     emit_op(s, OP_get_loc);
26946                     emit_u16(s, saved_eval_ret_idx);
26947                     emit_op(s, OP_put_loc);
26948                     emit_u16(s, s->cur_func->eval_ret_idx);
26949                 }
26950                 pop_break_entry(s->cur_func);
26951             }
26952             emit_op(s, OP_ret);
26953             emit_label(s, label_end);
26954         }
26955         break;
26956     case ';':
26957         /* empty statement */
26958         if (next_token(s))
26959             goto fail;
26960         break;
26961     case TOK_WITH:
26962         if (s->cur_func->js_mode & JS_MODE_STRICT) {
26963             js_parse_error(s, "invalid keyword: with");
26964             goto fail;
26965         } else {
26966             int with_idx;
26967 
26968             if (next_token(s))
26969                 goto fail;
26970 
26971             if (js_parse_expr_paren(s))
26972                 goto fail;
26973 
26974             push_scope(s);
26975             with_idx = define_var(s, s->cur_func, JS_ATOM__with_,
26976                                   JS_VAR_DEF_WITH);
26977             if (with_idx < 0)
26978                 goto fail;
26979             emit_op(s, OP_to_object);
26980             emit_op(s, OP_put_loc);
26981             emit_u16(s, with_idx);
26982 
26983             set_eval_ret_undefined(s);
26984             if (js_parse_statement(s))
26985                 goto fail;
26986 
26987             /* Popping scope drops lexical context for the with object variable */
26988             pop_scope(s);
26989         }
26990         break;
26991     case TOK_FUNCTION:
26992         /* ES6 Annex B.3.2 and B.3.3 semantics */
26993         if (!(decl_mask & DECL_MASK_FUNC))
26994             goto func_decl_error;
26995         if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*')
26996             goto func_decl_error;
26997         goto parse_func_var;
26998     case TOK_IDENT:
26999         if (s->token.u.ident.is_reserved) {
27000             js_parse_error_reserved_identifier(s);
27001             goto fail;
27002         }
27003         /* Determine if `let` introduces a Declaration or an ExpressionStatement */
27004         switch (is_let(s, decl_mask)) {
27005         case TRUE:
27006             tok = TOK_LET;
27007             goto haslet;
27008         case FALSE:
27009             break;
27010         default:
27011             goto fail;
27012         }
27013         if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
27014             peek_token(s, TRUE) == TOK_FUNCTION) {
27015             if (!(decl_mask & DECL_MASK_OTHER)) {
27016             func_decl_error:
27017                 js_parse_error(s, "function declarations can't appear in single-statement context");
27018                 goto fail;
27019             }
27020         parse_func_var:
27021             if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR,
27022                                        JS_FUNC_NORMAL, JS_ATOM_NULL,
27023                                        s->token.ptr, s->token.line_num))
27024                 goto fail;
27025             break;
27026         }
27027         goto hasexpr;
27028 
27029     case TOK_CLASS:
27030         if (!(decl_mask & DECL_MASK_OTHER)) {
27031             js_parse_error(s, "class declarations can't appear in single-statement context");
27032             goto fail;
27033         }
27034         if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE))
27035             return -1;
27036         break;
27037 
27038     case TOK_DEBUGGER:
27039         /* currently no debugger, so just skip the keyword */
27040         if (next_token(s))
27041             goto fail;
27042         if (js_parse_expect_semi(s))
27043             goto fail;
27044         break;
27045 
27046     case TOK_ENUM:
27047     case TOK_EXPORT:
27048     case TOK_EXTENDS:
27049         js_unsupported_keyword(s, s->token.u.ident.atom);
27050         goto fail;
27051 
27052     default:
27053     hasexpr:
27054         if (js_parse_expr(s))
27055             goto fail;
27056         if (s->cur_func->eval_ret_idx >= 0) {
27057             /* store the expression value so that it can be returned
27058                by eval() */
27059             emit_op(s, OP_put_loc);
27060             emit_u16(s, s->cur_func->eval_ret_idx);
27061         } else {
27062             emit_op(s, OP_drop); /* drop the result */
27063         }
27064         if (js_parse_expect_semi(s))
27065             goto fail;
27066         break;
27067     }
27068 done:
27069     JS_FreeAtom(ctx, label_name);
27070     return 0;
27071 fail:
27072     JS_FreeAtom(ctx, label_name);
27073     return -1;
27074 }
27075 
27076 /* 'name' is freed */
js_new_module_def(JSContext * ctx,JSAtom name)27077 static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)
27078 {
27079     JSModuleDef *m;
27080     m = js_mallocz(ctx, sizeof(*m));
27081     if (!m) {
27082         JS_FreeAtom(ctx, name);
27083         return NULL;
27084     }
27085     m->header.ref_count = 1;
27086     m->module_name = name;
27087     m->module_ns = JS_UNDEFINED;
27088     m->func_obj = JS_UNDEFINED;
27089     m->eval_exception = JS_UNDEFINED;
27090     m->meta_obj = JS_UNDEFINED;
27091     list_add_tail(&m->link, &ctx->loaded_modules);
27092     return m;
27093 }
27094 
js_mark_module_def(JSRuntime * rt,JSModuleDef * m,JS_MarkFunc * mark_func)27095 static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
27096                                JS_MarkFunc *mark_func)
27097 {
27098     int i;
27099 
27100     for(i = 0; i < m->export_entries_count; i++) {
27101         JSExportEntry *me = &m->export_entries[i];
27102         if (me->export_type == JS_EXPORT_TYPE_LOCAL &&
27103             me->u.local.var_ref) {
27104             mark_func(rt, &me->u.local.var_ref->header);
27105         }
27106     }
27107 
27108     JS_MarkValue(rt, m->module_ns, mark_func);
27109     JS_MarkValue(rt, m->func_obj, mark_func);
27110     JS_MarkValue(rt, m->eval_exception, mark_func);
27111     JS_MarkValue(rt, m->meta_obj, mark_func);
27112 }
27113 
js_free_module_def(JSContext * ctx,JSModuleDef * m)27114 static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
27115 {
27116     int i;
27117 
27118     JS_FreeAtom(ctx, m->module_name);
27119 
27120     for(i = 0; i < m->req_module_entries_count; i++) {
27121         JSReqModuleEntry *rme = &m->req_module_entries[i];
27122         JS_FreeAtom(ctx, rme->module_name);
27123     }
27124     js_free(ctx, m->req_module_entries);
27125 
27126     for(i = 0; i < m->export_entries_count; i++) {
27127         JSExportEntry *me = &m->export_entries[i];
27128         if (me->export_type == JS_EXPORT_TYPE_LOCAL)
27129             free_var_ref(ctx->rt, me->u.local.var_ref);
27130         JS_FreeAtom(ctx, me->export_name);
27131         JS_FreeAtom(ctx, me->local_name);
27132     }
27133     js_free(ctx, m->export_entries);
27134 
27135     js_free(ctx, m->star_export_entries);
27136 
27137     for(i = 0; i < m->import_entries_count; i++) {
27138         JSImportEntry *mi = &m->import_entries[i];
27139         JS_FreeAtom(ctx, mi->import_name);
27140     }
27141     js_free(ctx, m->import_entries);
27142 
27143     JS_FreeValue(ctx, m->module_ns);
27144     JS_FreeValue(ctx, m->func_obj);
27145     JS_FreeValue(ctx, m->eval_exception);
27146     JS_FreeValue(ctx, m->meta_obj);
27147     list_del(&m->link);
27148     js_free(ctx, m);
27149 }
27150 
add_req_module_entry(JSContext * ctx,JSModuleDef * m,JSAtom module_name)27151 static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
27152                                 JSAtom module_name)
27153 {
27154     JSReqModuleEntry *rme;
27155     int i;
27156 
27157     /* no need to add the module request if it is already present */
27158     for(i = 0; i < m->req_module_entries_count; i++) {
27159         rme = &m->req_module_entries[i];
27160         if (rme->module_name == module_name)
27161             return i;
27162     }
27163 
27164     if (js_resize_array(ctx, (void **)&m->req_module_entries,
27165                         sizeof(JSReqModuleEntry),
27166                         &m->req_module_entries_size,
27167                         m->req_module_entries_count + 1))
27168         return -1;
27169     rme = &m->req_module_entries[m->req_module_entries_count++];
27170     rme->module_name = JS_DupAtom(ctx, module_name);
27171     rme->module = NULL;
27172     return i;
27173 }
27174 
find_export_entry(JSContext * ctx,JSModuleDef * m,JSAtom export_name)27175 static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m,
27176                                         JSAtom export_name)
27177 {
27178     JSExportEntry *me;
27179     int i;
27180     for(i = 0; i < m->export_entries_count; i++) {
27181         me = &m->export_entries[i];
27182         if (me->export_name == export_name)
27183             return me;
27184     }
27185     return NULL;
27186 }
27187 
add_export_entry2(JSContext * ctx,JSParseState * s,JSModuleDef * m,JSAtom local_name,JSAtom export_name,JSExportTypeEnum export_type)27188 static JSExportEntry *add_export_entry2(JSContext *ctx,
27189                                         JSParseState *s, JSModuleDef *m,
27190                                        JSAtom local_name, JSAtom export_name,
27191                                        JSExportTypeEnum export_type)
27192 {
27193     JSExportEntry *me;
27194 
27195     if (find_export_entry(ctx, m, export_name)) {
27196         char buf1[ATOM_GET_STR_BUF_SIZE];
27197         if (s) {
27198             js_parse_error(s, "duplicate exported name '%s'",
27199                            JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name));
27200         } else {
27201             JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name);
27202         }
27203         return NULL;
27204     }
27205 
27206     if (js_resize_array(ctx, (void **)&m->export_entries,
27207                         sizeof(JSExportEntry),
27208                         &m->export_entries_size,
27209                         m->export_entries_count + 1))
27210         return NULL;
27211     me = &m->export_entries[m->export_entries_count++];
27212     memset(me, 0, sizeof(*me));
27213     me->local_name = JS_DupAtom(ctx, local_name);
27214     me->export_name = JS_DupAtom(ctx, export_name);
27215     me->export_type = export_type;
27216     return me;
27217 }
27218 
add_export_entry(JSParseState * s,JSModuleDef * m,JSAtom local_name,JSAtom export_name,JSExportTypeEnum export_type)27219 static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
27220                                        JSAtom local_name, JSAtom export_name,
27221                                        JSExportTypeEnum export_type)
27222 {
27223     return add_export_entry2(s->ctx, s, m, local_name, export_name,
27224                              export_type);
27225 }
27226 
add_star_export_entry(JSContext * ctx,JSModuleDef * m,int req_module_idx)27227 static int add_star_export_entry(JSContext *ctx, JSModuleDef *m,
27228                                  int req_module_idx)
27229 {
27230     JSStarExportEntry *se;
27231 
27232     if (js_resize_array(ctx, (void **)&m->star_export_entries,
27233                         sizeof(JSStarExportEntry),
27234                         &m->star_export_entries_size,
27235                         m->star_export_entries_count + 1))
27236         return -1;
27237     se = &m->star_export_entries[m->star_export_entries_count++];
27238     se->req_module_idx = req_module_idx;
27239     return 0;
27240 }
27241 
27242 /* create a C module */
JS_NewCModule(JSContext * ctx,const char * name_str,JSModuleInitFunc * func)27243 JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,
27244                            JSModuleInitFunc *func)
27245 {
27246     JSModuleDef *m;
27247     JSAtom name;
27248     name = JS_NewAtom(ctx, name_str);
27249     if (name == JS_ATOM_NULL)
27250         return NULL;
27251     m = js_new_module_def(ctx, name);
27252     m->init_func = func;
27253     return m;
27254 }
27255 
JS_AddModuleExport(JSContext * ctx,JSModuleDef * m,const char * export_name)27256 int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name)
27257 {
27258     JSExportEntry *me;
27259     JSAtom name;
27260     name = JS_NewAtom(ctx, export_name);
27261     if (name == JS_ATOM_NULL)
27262         return -1;
27263     me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name,
27264                            JS_EXPORT_TYPE_LOCAL);
27265     JS_FreeAtom(ctx, name);
27266     if (!me)
27267         return -1;
27268     else
27269         return 0;
27270 }
27271 
JS_SetModuleExport(JSContext * ctx,JSModuleDef * m,const char * export_name,JSValue val)27272 int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
27273                        JSValue val)
27274 {
27275     JSExportEntry *me;
27276     JSAtom name;
27277     name = JS_NewAtom(ctx, export_name);
27278     if (name == JS_ATOM_NULL)
27279         goto fail;
27280     me = find_export_entry(ctx, m, name);
27281     JS_FreeAtom(ctx, name);
27282     if (!me)
27283         goto fail;
27284     set_value(ctx, me->u.local.var_ref->pvalue, val);
27285     return 0;
27286  fail:
27287     JS_FreeValue(ctx, val);
27288     return -1;
27289 }
27290 
JS_SetModuleLoaderFunc(JSRuntime * rt,JSModuleNormalizeFunc * module_normalize,JSModuleLoaderFunc * module_loader,void * opaque)27291 void JS_SetModuleLoaderFunc(JSRuntime *rt,
27292                             JSModuleNormalizeFunc *module_normalize,
27293                             JSModuleLoaderFunc *module_loader, void *opaque)
27294 {
27295     rt->module_normalize_func = module_normalize;
27296     rt->module_loader_func = module_loader;
27297     rt->module_loader_opaque = opaque;
27298 }
27299 
27300 /* default module filename normalizer */
js_default_module_normalize_name(JSContext * ctx,const char * base_name,const char * name)27301 static char *js_default_module_normalize_name(JSContext *ctx,
27302                                               const char *base_name,
27303                                               const char *name)
27304 {
27305     char *filename, *p;
27306     const char *r;
27307     int len;
27308 
27309     if (name[0] != '.') {
27310         /* if no initial dot, the module name is not modified */
27311         return js_strdup(ctx, name);
27312     }
27313 
27314     p = strrchr(base_name, '/');
27315     if (p)
27316         len = p - base_name;
27317     else
27318         len = 0;
27319 
27320     filename = js_malloc(ctx, len + strlen(name) + 1 + 1);
27321     if (!filename)
27322         return NULL;
27323     memcpy(filename, base_name, len);
27324     filename[len] = '\0';
27325 
27326     /* we only normalize the leading '..' or '.' */
27327     r = name;
27328     for(;;) {
27329         if (r[0] == '.' && r[1] == '/') {
27330             r += 2;
27331         } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') {
27332             /* remove the last path element of filename, except if "."
27333                or ".." */
27334             if (filename[0] == '\0')
27335                 break;
27336             p = strrchr(filename, '/');
27337             if (!p)
27338                 p = filename;
27339             else
27340                 p++;
27341             if (!strcmp(p, ".") || !strcmp(p, ".."))
27342                 break;
27343             if (p > filename)
27344                 p--;
27345             *p = '\0';
27346             r += 3;
27347         } else {
27348             break;
27349         }
27350     }
27351     if (filename[0] != '\0')
27352         strcat(filename, "/");
27353     strcat(filename, r);
27354     //    printf("normalize: %s %s -> %s\n", base_name, name, filename);
27355     return filename;
27356 }
27357 
js_find_loaded_module(JSContext * ctx,JSAtom name)27358 static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name)
27359 {
27360     struct list_head *el;
27361     JSModuleDef *m;
27362 
27363     /* first look at the loaded modules */
27364     list_for_each(el, &ctx->loaded_modules) {
27365         m = list_entry(el, JSModuleDef, link);
27366         if (m->module_name == name)
27367             return m;
27368     }
27369     return NULL;
27370 }
27371 
27372 /* return NULL in case of exception (e.g. module could not be loaded) */
js_host_resolve_imported_module(JSContext * ctx,const char * base_cname,const char * cname1)27373 static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,
27374                                                     const char *base_cname,
27375                                                     const char *cname1)
27376 {
27377     JSRuntime *rt = ctx->rt;
27378     JSModuleDef *m;
27379     char *cname;
27380     JSAtom module_name;
27381 
27382     if (!rt->module_normalize_func) {
27383         cname = js_default_module_normalize_name(ctx, base_cname, cname1);
27384     } else {
27385         cname = rt->module_normalize_func(ctx, base_cname, cname1,
27386                                           rt->module_loader_opaque);
27387     }
27388     if (!cname)
27389         return NULL;
27390 
27391     module_name = JS_NewAtom(ctx, cname);
27392     if (module_name == JS_ATOM_NULL) {
27393         js_free(ctx, cname);
27394         return NULL;
27395     }
27396 
27397     /* first look at the loaded modules */
27398     m = js_find_loaded_module(ctx, module_name);
27399     if (m) {
27400         js_free(ctx, cname);
27401         JS_FreeAtom(ctx, module_name);
27402         return m;
27403     }
27404 
27405     JS_FreeAtom(ctx, module_name);
27406 
27407     /* load the module */
27408     if (!rt->module_loader_func) {
27409         /* XXX: use a syntax error ? */
27410         JS_ThrowReferenceError(ctx, "could not load module '%s'",
27411                                cname);
27412         js_free(ctx, cname);
27413         return NULL;
27414     }
27415 
27416     m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque);
27417     js_free(ctx, cname);
27418     return m;
27419 }
27420 
js_host_resolve_imported_module_atom(JSContext * ctx,JSAtom base_module_name,JSAtom module_name1)27421 static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx,
27422                                                     JSAtom base_module_name,
27423                                                     JSAtom module_name1)
27424 {
27425     const char *base_cname, *cname;
27426     JSModuleDef *m;
27427 
27428     base_cname = JS_AtomToCString(ctx, base_module_name);
27429     if (!base_cname)
27430         return NULL;
27431     cname = JS_AtomToCString(ctx, module_name1);
27432     if (!cname) {
27433         JS_FreeCString(ctx, base_cname);
27434         return NULL;
27435     }
27436     m = js_host_resolve_imported_module(ctx, base_cname, cname);
27437     JS_FreeCString(ctx, base_cname);
27438     JS_FreeCString(ctx, cname);
27439     return m;
27440 }
27441 
27442 typedef struct JSResolveEntry {
27443     JSModuleDef *module;
27444     JSAtom name;
27445 } JSResolveEntry;
27446 
27447 typedef struct JSResolveState {
27448     JSResolveEntry *array;
27449     int size;
27450     int count;
27451 } JSResolveState;
27452 
find_resolve_entry(JSResolveState * s,JSModuleDef * m,JSAtom name)27453 static int find_resolve_entry(JSResolveState *s,
27454                               JSModuleDef *m, JSAtom name)
27455 {
27456     int i;
27457     for(i = 0; i < s->count; i++) {
27458         JSResolveEntry *re = &s->array[i];
27459         if (re->module == m && re->name == name)
27460             return i;
27461     }
27462     return -1;
27463 }
27464 
add_resolve_entry(JSContext * ctx,JSResolveState * s,JSModuleDef * m,JSAtom name)27465 static int add_resolve_entry(JSContext *ctx, JSResolveState *s,
27466                              JSModuleDef *m, JSAtom name)
27467 {
27468     JSResolveEntry *re;
27469 
27470     if (js_resize_array(ctx, (void **)&s->array,
27471                         sizeof(JSResolveEntry),
27472                         &s->size, s->count + 1))
27473         return -1;
27474     re = &s->array[s->count++];
27475     re->module = m;
27476     re->name = JS_DupAtom(ctx, name);
27477     return 0;
27478 }
27479 
27480 typedef enum JSResolveResultEnum {
27481     JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */
27482     JS_RESOLVE_RES_FOUND = 0,
27483     JS_RESOLVE_RES_NOT_FOUND,
27484     JS_RESOLVE_RES_CIRCULAR,
27485     JS_RESOLVE_RES_AMBIGUOUS,
27486 } JSResolveResultEnum;
27487 
js_resolve_export1(JSContext * ctx,JSModuleDef ** pmodule,JSExportEntry ** pme,JSModuleDef * m,JSAtom export_name,JSResolveState * s)27488 static JSResolveResultEnum js_resolve_export1(JSContext *ctx,
27489                                               JSModuleDef **pmodule,
27490                                               JSExportEntry **pme,
27491                                               JSModuleDef *m,
27492                                               JSAtom export_name,
27493                                               JSResolveState *s)
27494 {
27495     JSExportEntry *me;
27496 
27497     *pmodule = NULL;
27498     *pme = NULL;
27499     if (find_resolve_entry(s, m, export_name) >= 0)
27500         return JS_RESOLVE_RES_CIRCULAR;
27501     if (add_resolve_entry(ctx, s, m, export_name) < 0)
27502         return JS_RESOLVE_RES_EXCEPTION;
27503     me = find_export_entry(ctx, m, export_name);
27504     if (me) {
27505         if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
27506             /* local export */
27507             *pmodule = m;
27508             *pme = me;
27509             return JS_RESOLVE_RES_FOUND;
27510         } else {
27511             /* indirect export */
27512             JSModuleDef *m1;
27513             m1 = m->req_module_entries[me->u.req_module_idx].module;
27514             if (me->local_name == JS_ATOM__star_) {
27515                 /* export ns from */
27516                 *pmodule = m;
27517                 *pme = me;
27518                 return JS_RESOLVE_RES_FOUND;
27519             } else {
27520                 return js_resolve_export1(ctx, pmodule, pme, m1,
27521                                           me->local_name, s);
27522             }
27523         }
27524     } else {
27525         if (export_name != JS_ATOM_default) {
27526             /* not found in direct or indirect exports: try star exports */
27527             int i;
27528 
27529             for(i = 0; i < m->star_export_entries_count; i++) {
27530                 JSStarExportEntry *se = &m->star_export_entries[i];
27531                 JSModuleDef *m1, *res_m;
27532                 JSExportEntry *res_me;
27533                 JSResolveResultEnum ret;
27534 
27535                 m1 = m->req_module_entries[se->req_module_idx].module;
27536                 ret = js_resolve_export1(ctx, &res_m, &res_me, m1,
27537                                          export_name, s);
27538                 if (ret == JS_RESOLVE_RES_AMBIGUOUS ||
27539                     ret == JS_RESOLVE_RES_EXCEPTION) {
27540                     return ret;
27541                 } else if (ret == JS_RESOLVE_RES_FOUND) {
27542                     if (*pme != NULL) {
27543                         if (*pmodule != res_m ||
27544                             res_me->local_name != (*pme)->local_name) {
27545                             *pmodule = NULL;
27546                             *pme = NULL;
27547                             return JS_RESOLVE_RES_AMBIGUOUS;
27548                         }
27549                     } else {
27550                         *pmodule = res_m;
27551                         *pme = res_me;
27552                     }
27553                 }
27554             }
27555             if (*pme != NULL)
27556                 return JS_RESOLVE_RES_FOUND;
27557         }
27558         return JS_RESOLVE_RES_NOT_FOUND;
27559     }
27560 }
27561 
27562 /* If the return value is JS_RESOLVE_RES_FOUND, return the module
27563   (*pmodule) and the corresponding local export entry
27564   (*pme). Otherwise return (NULL, NULL) */
js_resolve_export(JSContext * ctx,JSModuleDef ** pmodule,JSExportEntry ** pme,JSModuleDef * m,JSAtom export_name)27565 static JSResolveResultEnum js_resolve_export(JSContext *ctx,
27566                                              JSModuleDef **pmodule,
27567                                              JSExportEntry **pme,
27568                                              JSModuleDef *m,
27569                                              JSAtom export_name)
27570 {
27571     JSResolveState ss, *s = &ss;
27572     int i;
27573     JSResolveResultEnum ret;
27574 
27575     s->array = NULL;
27576     s->size = 0;
27577     s->count = 0;
27578 
27579     ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s);
27580 
27581     for(i = 0; i < s->count; i++)
27582         JS_FreeAtom(ctx, s->array[i].name);
27583     js_free(ctx, s->array);
27584 
27585     return ret;
27586 }
27587 
js_resolve_export_throw_error(JSContext * ctx,JSResolveResultEnum res,JSModuleDef * m,JSAtom export_name)27588 static void js_resolve_export_throw_error(JSContext *ctx,
27589                                           JSResolveResultEnum res,
27590                                           JSModuleDef *m, JSAtom export_name)
27591 {
27592     char buf1[ATOM_GET_STR_BUF_SIZE];
27593     char buf2[ATOM_GET_STR_BUF_SIZE];
27594     switch(res) {
27595     case JS_RESOLVE_RES_EXCEPTION:
27596         break;
27597     default:
27598     case JS_RESOLVE_RES_NOT_FOUND:
27599         JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'",
27600                             JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
27601                             JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
27602         break;
27603     case JS_RESOLVE_RES_CIRCULAR:
27604         JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'",
27605                             JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
27606                             JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
27607         break;
27608     case JS_RESOLVE_RES_AMBIGUOUS:
27609         JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous",
27610                             JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
27611                             JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
27612         break;
27613     }
27614 }
27615 
27616 
27617 typedef enum {
27618     EXPORTED_NAME_AMBIGUOUS,
27619     EXPORTED_NAME_NORMAL,
27620     EXPORTED_NAME_NS,
27621 } ExportedNameEntryEnum;
27622 
27623 typedef struct ExportedNameEntry {
27624     JSAtom export_name;
27625     ExportedNameEntryEnum export_type;
27626     union {
27627         JSExportEntry *me; /* using when the list is built */
27628         JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */
27629         JSModuleDef *module; /* for EXPORTED_NAME_NS */
27630     } u;
27631 } ExportedNameEntry;
27632 
27633 typedef struct GetExportNamesState {
27634     JSModuleDef **modules;
27635     int modules_size;
27636     int modules_count;
27637 
27638     ExportedNameEntry *exported_names;
27639     int exported_names_size;
27640     int exported_names_count;
27641 } GetExportNamesState;
27642 
find_exported_name(GetExportNamesState * s,JSAtom name)27643 static int find_exported_name(GetExportNamesState *s, JSAtom name)
27644 {
27645     int i;
27646     for(i = 0; i < s->exported_names_count; i++) {
27647         if (s->exported_names[i].export_name == name)
27648             return i;
27649     }
27650     return -1;
27651 }
27652 
get_exported_names(JSContext * ctx,GetExportNamesState * s,JSModuleDef * m,BOOL from_star)27653 static __exception int get_exported_names(JSContext *ctx,
27654                                           GetExportNamesState *s,
27655                                           JSModuleDef *m, BOOL from_star)
27656 {
27657     ExportedNameEntry *en;
27658     int i, j;
27659 
27660     /* check circular reference */
27661     for(i = 0; i < s->modules_count; i++) {
27662         if (s->modules[i] == m)
27663             return 0;
27664     }
27665     if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]),
27666                         &s->modules_size, s->modules_count + 1))
27667         return -1;
27668     s->modules[s->modules_count++] = m;
27669 
27670     for(i = 0; i < m->export_entries_count; i++) {
27671         JSExportEntry *me = &m->export_entries[i];
27672         if (from_star && me->export_name == JS_ATOM_default)
27673             continue;
27674         j = find_exported_name(s, me->export_name);
27675         if (j < 0) {
27676             if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]),
27677                                 &s->exported_names_size,
27678                                 s->exported_names_count + 1))
27679                 return -1;
27680             en = &s->exported_names[s->exported_names_count++];
27681             en->export_name = me->export_name;
27682             /* avoid a second lookup for simple module exports */
27683             if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL)
27684                 en->u.me = NULL;
27685             else
27686                 en->u.me = me;
27687         } else {
27688             en = &s->exported_names[j];
27689             en->u.me = NULL;
27690         }
27691     }
27692     for(i = 0; i < m->star_export_entries_count; i++) {
27693         JSStarExportEntry *se = &m->star_export_entries[i];
27694         JSModuleDef *m1;
27695         m1 = m->req_module_entries[se->req_module_idx].module;
27696         if (get_exported_names(ctx, s, m1, TRUE))
27697             return -1;
27698     }
27699     return 0;
27700 }
27701 
27702 /* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */
js_module_ns_has(JSContext * ctx,JSValueConst obj,JSAtom atom)27703 static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
27704 {
27705     return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL);
27706 }
27707 
27708 static const JSClassExoticMethods js_module_ns_exotic_methods = {
27709     .has_property = js_module_ns_has,
27710 };
27711 
exported_names_cmp(const void * p1,const void * p2,void * opaque)27712 static int exported_names_cmp(const void *p1, const void *p2, void *opaque)
27713 {
27714     JSContext *ctx = opaque;
27715     const ExportedNameEntry *me1 = p1;
27716     const ExportedNameEntry *me2 = p2;
27717     JSValue str1, str2;
27718     int ret;
27719 
27720     /* XXX: should avoid allocation memory in atom comparison */
27721     str1 = JS_AtomToString(ctx, me1->export_name);
27722     str2 = JS_AtomToString(ctx, me2->export_name);
27723     if (JS_IsException(str1) || JS_IsException(str2)) {
27724         /* XXX: raise an error ? */
27725         ret = 0;
27726     } else {
27727         ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1),
27728                                 JS_VALUE_GET_STRING(str2));
27729     }
27730     JS_FreeValue(ctx, str1);
27731     JS_FreeValue(ctx, str2);
27732     return ret;
27733 }
27734 
27735 static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m);
27736 
js_module_ns_autoinit(JSContext * ctx,JSObject * p,JSAtom atom,void * opaque)27737 static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
27738                                      void *opaque)
27739 {
27740     JSModuleDef *m = opaque;
27741     return js_get_module_ns(ctx, m);
27742 }
27743 
js_build_module_ns(JSContext * ctx,JSModuleDef * m)27744 static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
27745 {
27746     JSValue obj;
27747     JSObject *p;
27748     GetExportNamesState s_s, *s = &s_s;
27749     int i, ret;
27750     JSProperty *pr;
27751 
27752     obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS);
27753     if (JS_IsException(obj))
27754         return obj;
27755     p = JS_VALUE_GET_OBJ(obj);
27756 
27757     memset(s, 0, sizeof(*s));
27758     ret = get_exported_names(ctx, s, m, FALSE);
27759     js_free(ctx, s->modules);
27760     if (ret)
27761         goto fail;
27762 
27763     /* Resolve the exported names. The ambiguous exports are removed */
27764     for(i = 0; i < s->exported_names_count; i++) {
27765         ExportedNameEntry *en = &s->exported_names[i];
27766         JSResolveResultEnum res;
27767         JSExportEntry *res_me;
27768         JSModuleDef *res_m;
27769 
27770         if (en->u.me) {
27771             res_me = en->u.me; /* fast case: no resolution needed */
27772             res_m = m;
27773             res = JS_RESOLVE_RES_FOUND;
27774         } else {
27775             res = js_resolve_export(ctx, &res_m, &res_me, m,
27776                                     en->export_name);
27777         }
27778         if (res != JS_RESOLVE_RES_FOUND) {
27779             if (res != JS_RESOLVE_RES_AMBIGUOUS) {
27780                 js_resolve_export_throw_error(ctx, res, m, en->export_name);
27781                 goto fail;
27782             }
27783             en->export_type = EXPORTED_NAME_AMBIGUOUS;
27784         } else {
27785             if (res_me->local_name == JS_ATOM__star_) {
27786                 en->export_type = EXPORTED_NAME_NS;
27787                 en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module;
27788             } else {
27789                 en->export_type = EXPORTED_NAME_NORMAL;
27790                 if (res_me->u.local.var_ref) {
27791                     en->u.var_ref = res_me->u.local.var_ref;
27792                 } else {
27793                     JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
27794                     p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
27795                     en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
27796                 }
27797             }
27798         }
27799     }
27800 
27801     /* sort the exported names */
27802     rqsort(s->exported_names, s->exported_names_count,
27803            sizeof(s->exported_names[0]), exported_names_cmp, ctx);
27804 
27805     for(i = 0; i < s->exported_names_count; i++) {
27806         ExportedNameEntry *en = &s->exported_names[i];
27807         switch(en->export_type) {
27808         case EXPORTED_NAME_NORMAL:
27809             {
27810                 JSVarRef *var_ref = en->u.var_ref;
27811                 pr = add_property(ctx, p, en->export_name,
27812                                   JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
27813                                   JS_PROP_VARREF);
27814                 if (!pr)
27815                     goto fail;
27816                 var_ref->header.ref_count++;
27817                 pr->u.var_ref = var_ref;
27818             }
27819             break;
27820         case EXPORTED_NAME_NS:
27821             /* the exported namespace must be created on demand */
27822             if (JS_DefineAutoInitProperty(ctx, obj,
27823                                           en->export_name,
27824                                           JS_AUTOINIT_ID_MODULE_NS,
27825                                           en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
27826                 goto fail;
27827             break;
27828         default:
27829             break;
27830         }
27831     }
27832 
27833     js_free(ctx, s->exported_names);
27834 
27835     JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag,
27836                            JS_AtomToString(ctx, JS_ATOM_Module),
27837                            0);
27838 
27839     p->extensible = FALSE;
27840     return obj;
27841  fail:
27842     js_free(ctx, s->exported_names);
27843     JS_FreeValue(ctx, obj);
27844     return JS_EXCEPTION;
27845 }
27846 
js_get_module_ns(JSContext * ctx,JSModuleDef * m)27847 static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m)
27848 {
27849     if (JS_IsUndefined(m->module_ns)) {
27850         JSValue val;
27851         val = js_build_module_ns(ctx, m);
27852         if (JS_IsException(val))
27853             return JS_EXCEPTION;
27854         m->module_ns = val;
27855     }
27856     return JS_DupValue(ctx, m->module_ns);
27857 }
27858 
27859 /* Load all the required modules for module 'm' */
js_resolve_module(JSContext * ctx,JSModuleDef * m)27860 static int js_resolve_module(JSContext *ctx, JSModuleDef *m)
27861 {
27862     int i;
27863     JSModuleDef *m1;
27864 
27865     if (m->resolved)
27866         return 0;
27867 #ifdef DUMP_MODULE_RESOLVE
27868     {
27869         char buf1[ATOM_GET_STR_BUF_SIZE];
27870         printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
27871     }
27872 #endif
27873     m->resolved = TRUE;
27874     /* resolve each requested module */
27875     for(i = 0; i < m->req_module_entries_count; i++) {
27876         JSReqModuleEntry *rme = &m->req_module_entries[i];
27877         m1 = js_host_resolve_imported_module_atom(ctx, m->module_name,
27878                                                   rme->module_name);
27879         if (!m1)
27880             return -1;
27881         rme->module = m1;
27882         /* already done in js_host_resolve_imported_module() except if
27883            the module was loaded with JS_EvalBinary() */
27884         if (js_resolve_module(ctx, m1) < 0)
27885             return -1;
27886     }
27887     return 0;
27888 }
27889 
js_create_module_var(JSContext * ctx,BOOL is_lexical)27890 static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical)
27891 {
27892     JSVarRef *var_ref;
27893     var_ref = js_malloc(ctx, sizeof(JSVarRef));
27894     if (!var_ref)
27895         return NULL;
27896     var_ref->header.ref_count = 1;
27897     if (is_lexical)
27898         var_ref->value = JS_UNINITIALIZED;
27899     else
27900         var_ref->value = JS_UNDEFINED;
27901     var_ref->pvalue = &var_ref->value;
27902     var_ref->is_detached = TRUE;
27903     add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
27904     return var_ref;
27905 }
27906 
27907 /* Create the <eval> function associated with the module */
js_create_module_bytecode_function(JSContext * ctx,JSModuleDef * m)27908 static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)
27909 {
27910     JSFunctionBytecode *b;
27911     int i;
27912     JSVarRef **var_refs;
27913     JSValue func_obj, bfunc;
27914     JSObject *p;
27915 
27916     bfunc = m->func_obj;
27917     func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
27918                                       JS_CLASS_BYTECODE_FUNCTION);
27919 
27920     if (JS_IsException(func_obj))
27921         return -1;
27922     b = JS_VALUE_GET_PTR(bfunc);
27923 
27924     p = JS_VALUE_GET_OBJ(func_obj);
27925     p->u.func.function_bytecode = b;
27926     b->header.ref_count++;
27927     p->u.func.home_object = NULL;
27928     p->u.func.var_refs = NULL;
27929     if (b->closure_var_count) {
27930         var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
27931         if (!var_refs)
27932             goto fail;
27933         p->u.func.var_refs = var_refs;
27934 
27935         /* create the global variables. The other variables are
27936            imported from other modules */
27937         for(i = 0; i < b->closure_var_count; i++) {
27938             JSClosureVar *cv = &b->closure_var[i];
27939             JSVarRef *var_ref;
27940             if (cv->is_local) {
27941                 var_ref = js_create_module_var(ctx, cv->is_lexical);
27942                 if (!var_ref)
27943                     goto fail;
27944 #ifdef DUMP_MODULE_RESOLVE
27945                 printf("local %d: %p\n", i, var_ref);
27946 #endif
27947                 var_refs[i] = var_ref;
27948             }
27949         }
27950     }
27951     m->func_obj = func_obj;
27952     JS_FreeValue(ctx, bfunc);
27953     return 0;
27954  fail:
27955     JS_FreeValue(ctx, func_obj);
27956     return -1;
27957 }
27958 
27959 /* must be done before js_link_module() because of cyclic references */
js_create_module_function(JSContext * ctx,JSModuleDef * m)27960 static int js_create_module_function(JSContext *ctx, JSModuleDef *m)
27961 {
27962     BOOL is_c_module;
27963     int i;
27964     JSVarRef *var_ref;
27965 
27966     if (m->func_created)
27967         return 0;
27968 
27969     is_c_module = (m->init_func != NULL);
27970 
27971     if (is_c_module) {
27972         /* initialize the exported variables */
27973         for(i = 0; i < m->export_entries_count; i++) {
27974             JSExportEntry *me = &m->export_entries[i];
27975             if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
27976                 var_ref = js_create_module_var(ctx, FALSE);
27977                 if (!var_ref)
27978                     return -1;
27979                 me->u.local.var_ref = var_ref;
27980             }
27981         }
27982     } else {
27983         if (js_create_module_bytecode_function(ctx, m))
27984             return -1;
27985     }
27986     m->func_created = TRUE;
27987 
27988     /* do it on the dependencies */
27989 
27990     for(i = 0; i < m->req_module_entries_count; i++) {
27991         JSReqModuleEntry *rme = &m->req_module_entries[i];
27992         if (js_create_module_function(ctx, rme->module) < 0)
27993             return -1;
27994     }
27995 
27996     return 0;
27997 }
27998 
27999 
28000 /* Prepare a module to be executed by resolving all the imported
28001    variables. */
js_link_module(JSContext * ctx,JSModuleDef * m)28002 static int js_link_module(JSContext *ctx, JSModuleDef *m)
28003 {
28004     int i;
28005     JSImportEntry *mi;
28006     JSModuleDef *m1;
28007     JSVarRef **var_refs, *var_ref;
28008     JSObject *p;
28009     BOOL is_c_module;
28010     JSValue ret_val;
28011 
28012     if (m->instantiated)
28013         return 0;
28014     m->instantiated = TRUE;
28015 
28016 #ifdef DUMP_MODULE_RESOLVE
28017     {
28018         char buf1[ATOM_GET_STR_BUF_SIZE];
28019         printf("start instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
28020     }
28021 #endif
28022 
28023     for(i = 0; i < m->req_module_entries_count; i++) {
28024         JSReqModuleEntry *rme = &m->req_module_entries[i];
28025         if (js_link_module(ctx, rme->module) < 0)
28026             goto fail;
28027     }
28028 
28029 #ifdef DUMP_MODULE_RESOLVE
28030     {
28031         char buf1[ATOM_GET_STR_BUF_SIZE];
28032         printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
28033     }
28034 #endif
28035     /* check the indirect exports */
28036     for(i = 0; i < m->export_entries_count; i++) {
28037         JSExportEntry *me = &m->export_entries[i];
28038         if (me->export_type == JS_EXPORT_TYPE_INDIRECT &&
28039             me->local_name != JS_ATOM__star_) {
28040             JSResolveResultEnum ret;
28041             JSExportEntry *res_me;
28042             JSModuleDef *res_m, *m1;
28043             m1 = m->req_module_entries[me->u.req_module_idx].module;
28044             ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name);
28045             if (ret != JS_RESOLVE_RES_FOUND) {
28046                 js_resolve_export_throw_error(ctx, ret, m, me->export_name);
28047                 goto fail;
28048             }
28049         }
28050     }
28051 
28052 #ifdef DUMP_MODULE_RESOLVE
28053     {
28054         printf("exported bindings:\n");
28055         for(i = 0; i < m->export_entries_count; i++) {
28056             JSExportEntry *me = &m->export_entries[i];
28057             printf(" name="); print_atom(ctx, me->export_name);
28058             printf(" local="); print_atom(ctx, me->local_name);
28059             printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx);
28060         }
28061     }
28062 #endif
28063 
28064     is_c_module = (m->init_func != NULL);
28065 
28066     if (!is_c_module) {
28067         p = JS_VALUE_GET_OBJ(m->func_obj);
28068         var_refs = p->u.func.var_refs;
28069 
28070         for(i = 0; i < m->import_entries_count; i++) {
28071             mi = &m->import_entries[i];
28072 #ifdef DUMP_MODULE_RESOLVE
28073             printf("import var_idx=%d name=", mi->var_idx);
28074             print_atom(ctx, mi->import_name);
28075             printf(": ");
28076 #endif
28077             m1 = m->req_module_entries[mi->req_module_idx].module;
28078             if (mi->import_name == JS_ATOM__star_) {
28079                 JSValue val;
28080                 /* name space import */
28081                 val = js_get_module_ns(ctx, m1);
28082                 if (JS_IsException(val))
28083                     goto fail;
28084                 set_value(ctx, &var_refs[mi->var_idx]->value, val);
28085 #ifdef DUMP_MODULE_RESOLVE
28086                 printf("namespace\n");
28087 #endif
28088             } else {
28089                 JSResolveResultEnum ret;
28090                 JSExportEntry *res_me;
28091                 JSModuleDef *res_m;
28092                 JSObject *p1;
28093 
28094                 ret = js_resolve_export(ctx, &res_m,
28095                                         &res_me, m1, mi->import_name);
28096                 if (ret != JS_RESOLVE_RES_FOUND) {
28097                     js_resolve_export_throw_error(ctx, ret, m1, mi->import_name);
28098                     goto fail;
28099                 }
28100                 if (res_me->local_name == JS_ATOM__star_) {
28101                     JSValue val;
28102                     JSModuleDef *m2;
28103                     /* name space import from */
28104                     m2 = res_m->req_module_entries[res_me->u.req_module_idx].module;
28105                     val = js_get_module_ns(ctx, m2);
28106                     if (JS_IsException(val))
28107                         goto fail;
28108                     var_ref = js_create_module_var(ctx, TRUE);
28109                     if (!var_ref) {
28110                         JS_FreeValue(ctx, val);
28111                         goto fail;
28112                     }
28113                     set_value(ctx, &var_ref->value, val);
28114                     var_refs[mi->var_idx] = var_ref;
28115 #ifdef DUMP_MODULE_RESOLVE
28116                     printf("namespace from\n");
28117 #endif
28118                 } else {
28119                     var_ref = res_me->u.local.var_ref;
28120                     if (!var_ref) {
28121                         p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
28122                         var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
28123                     }
28124                     var_ref->header.ref_count++;
28125                     var_refs[mi->var_idx] = var_ref;
28126 #ifdef DUMP_MODULE_RESOLVE
28127                     printf("local export (var_ref=%p)\n", var_ref);
28128 #endif
28129                 }
28130             }
28131         }
28132 
28133         /* keep the exported variables in the module export entries (they
28134            are used when the eval function is deleted and cannot be
28135            initialized before in case imports are exported) */
28136         for(i = 0; i < m->export_entries_count; i++) {
28137             JSExportEntry *me = &m->export_entries[i];
28138             if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
28139                 var_ref = var_refs[me->u.local.var_idx];
28140                 var_ref->header.ref_count++;
28141                 me->u.local.var_ref = var_ref;
28142             }
28143         }
28144 
28145         /* initialize the global variables */
28146         ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL);
28147         if (JS_IsException(ret_val))
28148             goto fail;
28149         JS_FreeValue(ctx, ret_val);
28150     }
28151 
28152 #ifdef DUMP_MODULE_RESOLVE
28153     printf("done instantiate\n");
28154 #endif
28155     return 0;
28156  fail:
28157     return -1;
28158 }
28159 
28160 /* return JS_ATOM_NULL if the name cannot be found. Only works with
28161    not striped bytecode functions. */
JS_GetScriptOrModuleName(JSContext * ctx,int n_stack_levels)28162 JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels)
28163 {
28164     JSStackFrame *sf;
28165     JSFunctionBytecode *b;
28166     JSObject *p;
28167     /* XXX: currently we just use the filename of the englobing
28168        function. It does not work for eval(). Need to add a
28169        ScriptOrModule info in JSFunctionBytecode */
28170     sf = ctx->rt->current_stack_frame;
28171     if (!sf)
28172         return JS_ATOM_NULL;
28173     while (n_stack_levels-- > 0) {
28174         sf = sf->prev_frame;
28175         if (!sf)
28176             return JS_ATOM_NULL;
28177     }
28178     if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT)
28179         return JS_ATOM_NULL;
28180     p = JS_VALUE_GET_OBJ(sf->cur_func);
28181     if (!js_class_has_bytecode(p->class_id))
28182         return JS_ATOM_NULL;
28183     b = p->u.func.function_bytecode;
28184     if (!b->has_debug)
28185         return JS_ATOM_NULL;
28186     return JS_DupAtom(ctx, b->debug.filename);
28187 }
28188 
JS_GetModuleName(JSContext * ctx,JSModuleDef * m)28189 JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m)
28190 {
28191     return JS_DupAtom(ctx, m->module_name);
28192 }
28193 
JS_GetImportMeta(JSContext * ctx,JSModuleDef * m)28194 JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m)
28195 {
28196     JSValue obj;
28197     /* allocate meta_obj only if requested to save memory */
28198     obj = m->meta_obj;
28199     if (JS_IsUndefined(obj)) {
28200         obj = JS_NewObjectProto(ctx, JS_NULL);
28201         if (JS_IsException(obj))
28202             return JS_EXCEPTION;
28203         m->meta_obj = obj;
28204     }
28205     return JS_DupValue(ctx, obj);
28206 }
28207 
js_import_meta(JSContext * ctx)28208 static JSValue js_import_meta(JSContext *ctx)
28209 {
28210     JSAtom filename;
28211     JSModuleDef *m;
28212 
28213     filename = JS_GetScriptOrModuleName(ctx, 0);
28214     if (filename == JS_ATOM_NULL)
28215         goto fail;
28216 
28217     /* XXX: inefficient, need to add a module or script pointer in
28218        JSFunctionBytecode */
28219     m = js_find_loaded_module(ctx, filename);
28220     JS_FreeAtom(ctx, filename);
28221     if (!m) {
28222     fail:
28223         JS_ThrowTypeError(ctx, "import.meta not supported in this context");
28224         return JS_EXCEPTION;
28225     }
28226     return JS_GetImportMeta(ctx, m);
28227 }
28228 
28229 /* used by os.Worker() and import() */
JS_RunModule(JSContext * ctx,const char * basename,const char * filename)28230 JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,
28231                           const char *filename)
28232 {
28233     JSModuleDef *m;
28234     JSValue ret, func_obj;
28235 
28236     m = js_host_resolve_imported_module(ctx, basename, filename);
28237     if (!m)
28238         return NULL;
28239 
28240     if (js_resolve_module(ctx, m) < 0) {
28241         js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
28242         return NULL;
28243     }
28244 
28245     /* Evaluate the module code */
28246     func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
28247     ret = JS_EvalFunction(ctx, func_obj);
28248     if (JS_IsException(ret))
28249         return NULL;
28250     JS_FreeValue(ctx, ret);
28251     return m;
28252 }
28253 
js_dynamic_import_job(JSContext * ctx,int argc,JSValueConst * argv)28254 static JSValue js_dynamic_import_job(JSContext *ctx,
28255                                      int argc, JSValueConst *argv)
28256 {
28257     JSValueConst *resolving_funcs = argv;
28258     JSValueConst basename_val = argv[2];
28259     JSValueConst specifier = argv[3];
28260     JSModuleDef *m;
28261     const char *basename = NULL, *filename;
28262     JSValue ret, err, ns;
28263 
28264     if (!JS_IsString(basename_val)) {
28265         JS_ThrowTypeError(ctx, "no function filename for import()");
28266         goto exception;
28267     }
28268     basename = JS_ToCString(ctx, basename_val);
28269     if (!basename)
28270         goto exception;
28271 
28272     filename = JS_ToCString(ctx, specifier);
28273     if (!filename)
28274         goto exception;
28275 
28276     m = JS_RunModule(ctx, basename, filename);
28277     JS_FreeCString(ctx, filename);
28278     if (!m)
28279         goto exception;
28280 
28281     /* return the module namespace */
28282     ns = js_get_module_ns(ctx, m);
28283     if (JS_IsException(ns))
28284         goto exception;
28285 
28286     ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
28287                    1, (JSValueConst *)&ns);
28288     JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
28289     JS_FreeValue(ctx, ns);
28290     JS_FreeCString(ctx, basename);
28291     return JS_UNDEFINED;
28292  exception:
28293 
28294     err = JS_GetException(ctx);
28295     ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
28296                    1, (JSValueConst *)&err);
28297     JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
28298     JS_FreeValue(ctx, err);
28299     JS_FreeCString(ctx, basename);
28300     return JS_UNDEFINED;
28301 }
28302 
js_dynamic_import(JSContext * ctx,JSValueConst specifier)28303 static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)
28304 {
28305     JSAtom basename;
28306     JSValue promise, resolving_funcs[2], basename_val;
28307     JSValueConst args[4];
28308 
28309     basename = JS_GetScriptOrModuleName(ctx, 0);
28310     if (basename == JS_ATOM_NULL)
28311         basename_val = JS_NULL;
28312     else
28313         basename_val = JS_AtomToValue(ctx, basename);
28314     JS_FreeAtom(ctx, basename);
28315     if (JS_IsException(basename_val))
28316         return basename_val;
28317 
28318     promise = JS_NewPromiseCapability(ctx, resolving_funcs);
28319     if (JS_IsException(promise)) {
28320         JS_FreeValue(ctx, basename_val);
28321         return promise;
28322     }
28323 
28324     args[0] = resolving_funcs[0];
28325     args[1] = resolving_funcs[1];
28326     args[2] = basename_val;
28327     args[3] = specifier;
28328 
28329     JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args);
28330 
28331     JS_FreeValue(ctx, basename_val);
28332     JS_FreeValue(ctx, resolving_funcs[0]);
28333     JS_FreeValue(ctx, resolving_funcs[1]);
28334     return promise;
28335 }
28336 
28337 /* Run the <eval> function of the module and of all its requested
28338    modules. */
js_evaluate_module(JSContext * ctx,JSModuleDef * m)28339 static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
28340 {
28341     JSModuleDef *m1;
28342     int i;
28343     JSValue ret_val;
28344 
28345     if (m->eval_mark)
28346         return JS_UNDEFINED; /* avoid cycles */
28347 
28348     if (m->evaluated) {
28349         /* if the module was already evaluated, rethrow the exception
28350            it raised */
28351         if (m->eval_has_exception) {
28352             return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception));
28353         } else {
28354             return JS_UNDEFINED;
28355         }
28356     }
28357 
28358     m->eval_mark = TRUE;
28359 
28360     for(i = 0; i < m->req_module_entries_count; i++) {
28361         JSReqModuleEntry *rme = &m->req_module_entries[i];
28362         m1 = rme->module;
28363         if (!m1->eval_mark) {
28364             ret_val = js_evaluate_module(ctx, m1);
28365             if (JS_IsException(ret_val)) {
28366                 m->eval_mark = FALSE;
28367                 return ret_val;
28368             }
28369             JS_FreeValue(ctx, ret_val);
28370         }
28371     }
28372 
28373     if (m->init_func) {
28374         /* C module init */
28375         if (m->init_func(ctx, m) < 0)
28376             ret_val = JS_EXCEPTION;
28377         else
28378             ret_val = JS_UNDEFINED;
28379     } else {
28380         ret_val = JS_CallFree(ctx, m->func_obj, JS_UNDEFINED, 0, NULL);
28381         m->func_obj = JS_UNDEFINED;
28382     }
28383     if (JS_IsException(ret_val)) {
28384         /* save the thrown exception value */
28385         m->eval_has_exception = TRUE;
28386         m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception);
28387     }
28388     m->eval_mark = FALSE;
28389     m->evaluated = TRUE;
28390     return ret_val;
28391 }
28392 
js_parse_from_clause(JSParseState * s)28393 static __exception JSAtom js_parse_from_clause(JSParseState *s)
28394 {
28395     JSAtom module_name;
28396     if (!token_is_pseudo_keyword(s, JS_ATOM_from)) {
28397         js_parse_error(s, "from clause expected");
28398         return JS_ATOM_NULL;
28399     }
28400     if (next_token(s))
28401         return JS_ATOM_NULL;
28402     if (s->token.val != TOK_STRING) {
28403         js_parse_error(s, "string expected");
28404         return JS_ATOM_NULL;
28405     }
28406     module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
28407     if (module_name == JS_ATOM_NULL)
28408         return JS_ATOM_NULL;
28409     if (next_token(s)) {
28410         JS_FreeAtom(s->ctx, module_name);
28411         return JS_ATOM_NULL;
28412     }
28413     return module_name;
28414 }
28415 
js_parse_export(JSParseState * s)28416 static __exception int js_parse_export(JSParseState *s)
28417 {
28418     JSContext *ctx = s->ctx;
28419     JSModuleDef *m = s->cur_func->module;
28420     JSAtom local_name, export_name;
28421     int first_export, idx, i, tok;
28422     JSAtom module_name;
28423     JSExportEntry *me;
28424 
28425     if (next_token(s))
28426         return -1;
28427 
28428     tok = s->token.val;
28429     if (tok == TOK_CLASS) {
28430         return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED);
28431     } else if (tok == TOK_FUNCTION ||
28432                (token_is_pseudo_keyword(s, JS_ATOM_async) &&
28433                 peek_token(s, TRUE) == TOK_FUNCTION)) {
28434         return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
28435                                        JS_FUNC_NORMAL, JS_ATOM_NULL,
28436                                        s->token.ptr, s->token.line_num,
28437                                        JS_PARSE_EXPORT_NAMED, NULL);
28438     }
28439 
28440     if (next_token(s))
28441         return -1;
28442 
28443     switch(tok) {
28444     case '{':
28445         first_export = m->export_entries_count;
28446         while (s->token.val != '}') {
28447             if (!token_is_ident(s->token.val)) {
28448                 js_parse_error(s, "identifier expected");
28449                 return -1;
28450             }
28451             local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28452             export_name = JS_ATOM_NULL;
28453             if (next_token(s))
28454                 goto fail;
28455             if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
28456                 if (next_token(s))
28457                     goto fail;
28458                 if (!token_is_ident(s->token.val)) {
28459                     js_parse_error(s, "identifier expected");
28460                     goto fail;
28461                 }
28462                 export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28463                 if (next_token(s)) {
28464                 fail:
28465                     JS_FreeAtom(ctx, local_name);
28466                 fail1:
28467                     JS_FreeAtom(ctx, export_name);
28468                     return -1;
28469                 }
28470             } else {
28471                 export_name = JS_DupAtom(ctx, local_name);
28472             }
28473             me = add_export_entry(s, m, local_name, export_name,
28474                                   JS_EXPORT_TYPE_LOCAL);
28475             JS_FreeAtom(ctx, local_name);
28476             JS_FreeAtom(ctx, export_name);
28477             if (!me)
28478                 return -1;
28479             if (s->token.val != ',')
28480                 break;
28481             if (next_token(s))
28482                 return -1;
28483         }
28484         if (js_parse_expect(s, '}'))
28485             return -1;
28486         if (token_is_pseudo_keyword(s, JS_ATOM_from)) {
28487             module_name = js_parse_from_clause(s);
28488             if (module_name == JS_ATOM_NULL)
28489                 return -1;
28490             idx = add_req_module_entry(ctx, m, module_name);
28491             JS_FreeAtom(ctx, module_name);
28492             if (idx < 0)
28493                 return -1;
28494             for(i = first_export; i < m->export_entries_count; i++) {
28495                 me = &m->export_entries[i];
28496                 me->export_type = JS_EXPORT_TYPE_INDIRECT;
28497                 me->u.req_module_idx = idx;
28498             }
28499         }
28500         break;
28501     case '*':
28502         if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
28503             /* export ns from */
28504             if (next_token(s))
28505                 return -1;
28506             if (!token_is_ident(s->token.val)) {
28507                 js_parse_error(s, "identifier expected");
28508                 return -1;
28509             }
28510             export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28511             if (next_token(s))
28512                 goto fail1;
28513             module_name = js_parse_from_clause(s);
28514             if (module_name == JS_ATOM_NULL)
28515                 goto fail1;
28516             idx = add_req_module_entry(ctx, m, module_name);
28517             JS_FreeAtom(ctx, module_name);
28518             if (idx < 0)
28519                 goto fail1;
28520             me = add_export_entry(s, m, JS_ATOM__star_, export_name,
28521                                   JS_EXPORT_TYPE_INDIRECT);
28522             JS_FreeAtom(ctx, export_name);
28523             if (!me)
28524                 return -1;
28525             me->u.req_module_idx = idx;
28526         } else {
28527             module_name = js_parse_from_clause(s);
28528             if (module_name == JS_ATOM_NULL)
28529                 return -1;
28530             idx = add_req_module_entry(ctx, m, module_name);
28531             JS_FreeAtom(ctx, module_name);
28532             if (idx < 0)
28533                 return -1;
28534             if (add_star_export_entry(ctx, m, idx) < 0)
28535                 return -1;
28536         }
28537         break;
28538     case TOK_DEFAULT:
28539         if (s->token.val == TOK_CLASS) {
28540             return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT);
28541         } else if (s->token.val == TOK_FUNCTION ||
28542                    (token_is_pseudo_keyword(s, JS_ATOM_async) &&
28543                     peek_token(s, TRUE) == TOK_FUNCTION)) {
28544             return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
28545                                            JS_FUNC_NORMAL, JS_ATOM_NULL,
28546                                            s->token.ptr, s->token.line_num,
28547                                            JS_PARSE_EXPORT_DEFAULT, NULL);
28548         } else {
28549             if (js_parse_assign_expr(s))
28550                 return -1;
28551         }
28552         /* set the name of anonymous functions */
28553         set_object_name(s, JS_ATOM_default);
28554 
28555         /* store the value in the _default_ global variable and export
28556            it */
28557         local_name = JS_ATOM__default_;
28558         if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0)
28559             return -1;
28560         emit_op(s, OP_scope_put_var_init);
28561         emit_atom(s, local_name);
28562         emit_u16(s, 0);
28563 
28564         if (!add_export_entry(s, m, local_name, JS_ATOM_default,
28565                               JS_EXPORT_TYPE_LOCAL))
28566             return -1;
28567         break;
28568     case TOK_VAR:
28569     case TOK_LET:
28570     case TOK_CONST:
28571         return js_parse_var(s, TRUE, tok, TRUE);
28572     default:
28573         return js_parse_error(s, "invalid export syntax");
28574     }
28575     return js_parse_expect_semi(s);
28576 }
28577 
28578 static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
28579                            BOOL is_local, BOOL is_arg,
28580                            int var_idx, JSAtom var_name,
28581                            BOOL is_const, BOOL is_lexical,
28582                            JSVarKindEnum var_kind);
28583 
add_import(JSParseState * s,JSModuleDef * m,JSAtom local_name,JSAtom import_name)28584 static int add_import(JSParseState *s, JSModuleDef *m,
28585                       JSAtom local_name, JSAtom import_name)
28586 {
28587     JSContext *ctx = s->ctx;
28588     int i, var_idx;
28589     JSImportEntry *mi;
28590     BOOL is_local;
28591 
28592     if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval)
28593         return js_parse_error(s, "invalid import binding");
28594 
28595     if (local_name != JS_ATOM_default) {
28596         for (i = 0; i < s->cur_func->closure_var_count; i++) {
28597             if (s->cur_func->closure_var[i].var_name == local_name)
28598                 return js_parse_error(s, "duplicate import binding");
28599         }
28600     }
28601 
28602     is_local = (import_name == JS_ATOM__star_);
28603     var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE,
28604                               m->import_entries_count,
28605                               local_name, TRUE, TRUE, FALSE);
28606     if (var_idx < 0)
28607         return -1;
28608     if (js_resize_array(ctx, (void **)&m->import_entries,
28609                         sizeof(JSImportEntry),
28610                         &m->import_entries_size,
28611                         m->import_entries_count + 1))
28612         return -1;
28613     mi = &m->import_entries[m->import_entries_count++];
28614     mi->import_name = JS_DupAtom(ctx, import_name);
28615     mi->var_idx = var_idx;
28616     return 0;
28617 }
28618 
js_parse_import(JSParseState * s)28619 static __exception int js_parse_import(JSParseState *s)
28620 {
28621     JSContext *ctx = s->ctx;
28622     JSModuleDef *m = s->cur_func->module;
28623     JSAtom local_name, import_name, module_name;
28624     int first_import, i, idx;
28625 
28626     if (next_token(s))
28627         return -1;
28628 
28629     first_import = m->import_entries_count;
28630     if (s->token.val == TOK_STRING) {
28631         module_name = JS_ValueToAtom(ctx, s->token.u.str.str);
28632         if (module_name == JS_ATOM_NULL)
28633             return -1;
28634         if (next_token(s)) {
28635             JS_FreeAtom(ctx, module_name);
28636             return -1;
28637         }
28638     } else {
28639         if (s->token.val == TOK_IDENT) {
28640             if (s->token.u.ident.is_reserved) {
28641                 return js_parse_error_reserved_identifier(s);
28642             }
28643             /* "default" import */
28644             local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28645             import_name = JS_ATOM_default;
28646             if (next_token(s))
28647                 goto fail;
28648             if (add_import(s, m, local_name, import_name))
28649                 goto fail;
28650             JS_FreeAtom(ctx, local_name);
28651 
28652             if (s->token.val != ',')
28653                 goto end_import_clause;
28654             if (next_token(s))
28655                 return -1;
28656         }
28657 
28658         if (s->token.val == '*') {
28659             /* name space import */
28660             if (next_token(s))
28661                 return -1;
28662             if (!token_is_pseudo_keyword(s, JS_ATOM_as))
28663                 return js_parse_error(s, "expecting 'as'");
28664             if (next_token(s))
28665                 return -1;
28666             if (!token_is_ident(s->token.val)) {
28667                 js_parse_error(s, "identifier expected");
28668                 return -1;
28669             }
28670             local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28671             import_name = JS_ATOM__star_;
28672             if (next_token(s))
28673                 goto fail;
28674             if (add_import(s, m, local_name, import_name))
28675                 goto fail;
28676             JS_FreeAtom(ctx, local_name);
28677         } else if (s->token.val == '{') {
28678             if (next_token(s))
28679                 return -1;
28680 
28681             while (s->token.val != '}') {
28682                 if (!token_is_ident(s->token.val)) {
28683                     js_parse_error(s, "identifier expected");
28684                     return -1;
28685                 }
28686                 import_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28687                 local_name = JS_ATOM_NULL;
28688                 if (next_token(s))
28689                     goto fail;
28690                 if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
28691                     if (next_token(s))
28692                         goto fail;
28693                     if (!token_is_ident(s->token.val)) {
28694                         js_parse_error(s, "identifier expected");
28695                         goto fail;
28696                     }
28697                     local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28698                     if (next_token(s)) {
28699                     fail:
28700                         JS_FreeAtom(ctx, local_name);
28701                         JS_FreeAtom(ctx, import_name);
28702                         return -1;
28703                     }
28704                 } else {
28705                     local_name = JS_DupAtom(ctx, import_name);
28706                 }
28707                 if (add_import(s, m, local_name, import_name))
28708                     goto fail;
28709                 JS_FreeAtom(ctx, local_name);
28710                 JS_FreeAtom(ctx, import_name);
28711                 if (s->token.val != ',')
28712                     break;
28713                 if (next_token(s))
28714                     return -1;
28715             }
28716             if (js_parse_expect(s, '}'))
28717                 return -1;
28718         }
28719     end_import_clause:
28720         module_name = js_parse_from_clause(s);
28721         if (module_name == JS_ATOM_NULL)
28722             return -1;
28723     }
28724     idx = add_req_module_entry(ctx, m, module_name);
28725     JS_FreeAtom(ctx, module_name);
28726     if (idx < 0)
28727         return -1;
28728     for(i = first_import; i < m->import_entries_count; i++)
28729         m->import_entries[i].req_module_idx = idx;
28730 
28731     return js_parse_expect_semi(s);
28732 }
28733 
js_parse_source_element(JSParseState * s)28734 static __exception int js_parse_source_element(JSParseState *s)
28735 {
28736     JSFunctionDef *fd = s->cur_func;
28737     int tok;
28738 
28739     if (s->token.val == TOK_FUNCTION ||
28740         (token_is_pseudo_keyword(s, JS_ATOM_async) &&
28741          peek_token(s, TRUE) == TOK_FUNCTION)) {
28742         if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT,
28743                                    JS_FUNC_NORMAL, JS_ATOM_NULL,
28744                                    s->token.ptr, s->token.line_num))
28745             return -1;
28746     } else if (s->token.val == TOK_EXPORT && fd->module) {
28747         if (js_parse_export(s))
28748             return -1;
28749     } else if (s->token.val == TOK_IMPORT && fd->module &&
28750                ((tok = peek_token(s, FALSE)) != '(' && tok != '.'))  {
28751         /* the peek_token is needed to avoid confusion with ImportCall
28752            (dynamic import) or import.meta */
28753         if (js_parse_import(s))
28754             return -1;
28755     } else {
28756         if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
28757             return -1;
28758     }
28759     return 0;
28760 }
28761 
js_new_function_def(JSContext * ctx,JSFunctionDef * parent,BOOL is_eval,BOOL is_func_expr,const char * filename,int line_num)28762 static JSFunctionDef *js_new_function_def(JSContext *ctx,
28763                                           JSFunctionDef *parent,
28764                                           BOOL is_eval,
28765                                           BOOL is_func_expr,
28766                                           const char *filename, int line_num)
28767 {
28768     JSFunctionDef *fd;
28769 
28770     fd = js_mallocz(ctx, sizeof(*fd));
28771     if (!fd)
28772         return NULL;
28773 
28774     fd->ctx = ctx;
28775     init_list_head(&fd->child_list);
28776 
28777     /* insert in parent list */
28778     fd->parent = parent;
28779     fd->parent_cpool_idx = -1;
28780     if (parent) {
28781         list_add_tail(&fd->link, &parent->child_list);
28782         fd->js_mode = parent->js_mode;
28783         fd->parent_scope_level = parent->scope_level;
28784     }
28785 
28786     fd->is_eval = is_eval;
28787     fd->is_func_expr = is_func_expr;
28788     js_dbuf_init(ctx, &fd->byte_code);
28789     fd->last_opcode_pos = -1;
28790     fd->func_name = JS_ATOM_NULL;
28791     fd->var_object_idx = -1;
28792     fd->arg_var_object_idx = -1;
28793     fd->arguments_var_idx = -1;
28794     fd->arguments_arg_idx = -1;
28795     fd->func_var_idx = -1;
28796     fd->eval_ret_idx = -1;
28797     fd->this_var_idx = -1;
28798     fd->new_target_var_idx = -1;
28799     fd->this_active_func_var_idx = -1;
28800     fd->home_object_var_idx = -1;
28801 
28802     /* XXX: should distinguish arg, var and var object and body scopes */
28803     fd->scopes = fd->def_scope_array;
28804     fd->scope_size = countof(fd->def_scope_array);
28805     fd->scope_count = 1;
28806     fd->scopes[0].first = -1;
28807     fd->scopes[0].parent = -1;
28808     fd->scope_level = 0;  /* 0: var/arg scope */
28809     fd->scope_first = -1;
28810     fd->body_scope = -1;
28811 
28812     fd->filename = JS_NewAtom(ctx, filename);
28813     fd->line_num = line_num;
28814 
28815     js_dbuf_init(ctx, &fd->pc2line);
28816     //fd->pc2line_last_line_num = line_num;
28817     //fd->pc2line_last_pc = 0;
28818     fd->last_opcode_line_num = line_num;
28819 
28820     return fd;
28821 }
28822 
free_bytecode_atoms(JSRuntime * rt,const uint8_t * bc_buf,int bc_len,BOOL use_short_opcodes)28823 static void free_bytecode_atoms(JSRuntime *rt,
28824                                 const uint8_t *bc_buf, int bc_len,
28825                                 BOOL use_short_opcodes)
28826 {
28827     int pos, len, op;
28828     JSAtom atom;
28829     const JSOpCode *oi;
28830 
28831     pos = 0;
28832     while (pos < bc_len) {
28833         op = bc_buf[pos];
28834         if (use_short_opcodes)
28835             oi = &short_opcode_info(op);
28836         else
28837             oi = &opcode_info[op];
28838 
28839         len = oi->size;
28840         switch(oi->fmt) {
28841         case OP_FMT_atom:
28842         case OP_FMT_atom_u8:
28843         case OP_FMT_atom_u16:
28844         case OP_FMT_atom_label_u8:
28845         case OP_FMT_atom_label_u16:
28846             atom = get_u32(bc_buf + pos + 1);
28847             JS_FreeAtomRT(rt, atom);
28848             break;
28849         default:
28850             break;
28851         }
28852         pos += len;
28853     }
28854 }
28855 
js_free_function_def(JSContext * ctx,JSFunctionDef * fd)28856 static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)
28857 {
28858     int i;
28859     struct list_head *el, *el1;
28860 
28861     /* free the child functions */
28862     list_for_each_safe(el, el1, &fd->child_list) {
28863         JSFunctionDef *fd1;
28864         fd1 = list_entry(el, JSFunctionDef, link);
28865         js_free_function_def(ctx, fd1);
28866     }
28867 
28868     free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size,
28869                         fd->use_short_opcodes);
28870     dbuf_free(&fd->byte_code);
28871     js_free(ctx, fd->jump_slots);
28872     js_free(ctx, fd->label_slots);
28873     js_free(ctx, fd->line_number_slots);
28874 
28875     for(i = 0; i < fd->cpool_count; i++) {
28876         JS_FreeValue(ctx, fd->cpool[i]);
28877     }
28878     js_free(ctx, fd->cpool);
28879 
28880     JS_FreeAtom(ctx, fd->func_name);
28881 
28882     for(i = 0; i < fd->var_count; i++) {
28883         JS_FreeAtom(ctx, fd->vars[i].var_name);
28884     }
28885     js_free(ctx, fd->vars);
28886     for(i = 0; i < fd->arg_count; i++) {
28887         JS_FreeAtom(ctx, fd->args[i].var_name);
28888     }
28889     js_free(ctx, fd->args);
28890 
28891     for(i = 0; i < fd->global_var_count; i++) {
28892         JS_FreeAtom(ctx, fd->global_vars[i].var_name);
28893     }
28894     js_free(ctx, fd->global_vars);
28895 
28896     for(i = 0; i < fd->closure_var_count; i++) {
28897         JSClosureVar *cv = &fd->closure_var[i];
28898         JS_FreeAtom(ctx, cv->var_name);
28899     }
28900     js_free(ctx, fd->closure_var);
28901 
28902     if (fd->scopes != fd->def_scope_array)
28903         js_free(ctx, fd->scopes);
28904 
28905     JS_FreeAtom(ctx, fd->filename);
28906     dbuf_free(&fd->pc2line);
28907 
28908     js_free(ctx, fd->source);
28909 
28910     if (fd->parent) {
28911         /* remove in parent list */
28912         list_del(&fd->link);
28913     }
28914     js_free(ctx, fd);
28915 }
28916 
28917 #ifdef DUMP_BYTECODE
skip_lines(const char * p,int n)28918 static const char *skip_lines(const char *p, int n) {
28919     while (n-- > 0 && *p) {
28920         while (*p && *p++ != '\n')
28921             continue;
28922     }
28923     return p;
28924 }
28925 
print_lines(const char * source,int line,int line1)28926 static void print_lines(const char *source, int line, int line1) {
28927     const char *s = source;
28928     const char *p = skip_lines(s, line);
28929     if (*p) {
28930         while (line++ < line1) {
28931             p = skip_lines(s = p, 1);
28932             printf(";; %.*s", (int)(p - s), s);
28933             if (!*p) {
28934                 if (p[-1] != '\n')
28935                     printf("\n");
28936                 break;
28937             }
28938         }
28939     }
28940 }
28941 
dump_byte_code(JSContext * ctx,int pass,const uint8_t * tab,int len,const JSVarDef * args,int arg_count,const JSVarDef * vars,int var_count,const JSClosureVar * closure_var,int closure_var_count,const JSValue * cpool,uint32_t cpool_count,const char * source,int line_num,const LabelSlot * label_slots,JSFunctionBytecode * b)28942 static void dump_byte_code(JSContext *ctx, int pass,
28943                            const uint8_t *tab, int len,
28944                            const JSVarDef *args, int arg_count,
28945                            const JSVarDef *vars, int var_count,
28946                            const JSClosureVar *closure_var, int closure_var_count,
28947                            const JSValue *cpool, uint32_t cpool_count,
28948                            const char *source, int line_num,
28949                            const LabelSlot *label_slots, JSFunctionBytecode *b)
28950 {
28951     const JSOpCode *oi;
28952     int pos, pos_next, op, size, idx, addr, line, line1, in_source;
28953     uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits));
28954     BOOL use_short_opcodes = (b != NULL);
28955 
28956     /* scan for jump targets */
28957     for (pos = 0; pos < len; pos = pos_next) {
28958         op = tab[pos];
28959         if (use_short_opcodes)
28960             oi = &short_opcode_info(op);
28961         else
28962             oi = &opcode_info[op];
28963         pos_next = pos + oi->size;
28964         if (op < OP_COUNT) {
28965             switch (oi->fmt) {
28966 #if SHORT_OPCODES
28967             case OP_FMT_label8:
28968                 pos++;
28969                 addr = (int8_t)tab[pos];
28970                 goto has_addr;
28971             case OP_FMT_label16:
28972                 pos++;
28973                 addr = (int16_t)get_u16(tab + pos);
28974                 goto has_addr;
28975 #endif
28976             case OP_FMT_atom_label_u8:
28977             case OP_FMT_atom_label_u16:
28978                 pos += 4;
28979                 /* fall thru */
28980             case OP_FMT_label:
28981             case OP_FMT_label_u16:
28982                 pos++;
28983                 addr = get_u32(tab + pos);
28984                 goto has_addr;
28985             has_addr:
28986                 if (pass == 1)
28987                     addr = label_slots[addr].pos;
28988                 if (pass == 2)
28989                     addr = label_slots[addr].pos2;
28990                 if (pass == 3)
28991                     addr += pos;
28992                 if (addr >= 0 && addr < len)
28993                     bits[addr] |= 1;
28994                 break;
28995             }
28996         }
28997     }
28998     in_source = 0;
28999     if (source) {
29000         /* Always print first line: needed if single line */
29001         print_lines(source, 0, 1);
29002         in_source = 1;
29003     }
29004     line1 = line = 1;
29005     pos = 0;
29006     while (pos < len) {
29007         op = tab[pos];
29008         if (source) {
29009             if (b) {
29010                 line1 = find_line_num(ctx, b, pos) - line_num + 1;
29011             } else if (op == OP_line_num) {
29012                 line1 = get_u32(tab + pos + 1) - line_num + 1;
29013             }
29014             if (line1 > line) {
29015                 if (!in_source)
29016                     printf("\n");
29017                 in_source = 1;
29018                 print_lines(source, line, line1);
29019                 line = line1;
29020                 //bits[pos] |= 2;
29021             }
29022         }
29023         if (in_source)
29024             printf("\n");
29025         in_source = 0;
29026         if (op >= OP_COUNT) {
29027             printf("invalid opcode (0x%02x)\n", op);
29028             pos++;
29029             continue;
29030         }
29031         if (use_short_opcodes)
29032             oi = &short_opcode_info(op);
29033         else
29034             oi = &opcode_info[op];
29035         size = oi->size;
29036         if (pos + size > len) {
29037             printf("truncated opcode (0x%02x)\n", op);
29038             break;
29039         }
29040 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 16)
29041         {
29042             int i, x, x0;
29043             x = x0 = printf("%5d ", pos);
29044             for (i = 0; i < size; i++) {
29045                 if (i == 6) {
29046                     printf("\n%*s", x = x0, "");
29047                 }
29048                 x += printf(" %02X", tab[pos + i]);
29049             }
29050             printf("%*s", x0 + 20 - x, "");
29051         }
29052 #endif
29053         if (bits[pos]) {
29054             printf("%5d:  ", pos);
29055         } else {
29056             printf("        ");
29057         }
29058         printf("%s", oi->name);
29059         pos++;
29060         switch(oi->fmt) {
29061         case OP_FMT_none_int:
29062             printf(" %d", op - OP_push_0);
29063             break;
29064         case OP_FMT_npopx:
29065             printf(" %d", op - OP_call0);
29066             break;
29067         case OP_FMT_u8:
29068             printf(" %u", get_u8(tab + pos));
29069             break;
29070         case OP_FMT_i8:
29071             printf(" %d", get_i8(tab + pos));
29072             break;
29073         case OP_FMT_u16:
29074         case OP_FMT_npop:
29075             printf(" %u", get_u16(tab + pos));
29076             break;
29077         case OP_FMT_npop_u16:
29078             printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2));
29079             break;
29080         case OP_FMT_i16:
29081             printf(" %d", get_i16(tab + pos));
29082             break;
29083         case OP_FMT_i32:
29084             printf(" %d", get_i32(tab + pos));
29085             break;
29086         case OP_FMT_u32:
29087             printf(" %u", get_u32(tab + pos));
29088             break;
29089 #if SHORT_OPCODES
29090         case OP_FMT_label8:
29091             addr = get_i8(tab + pos);
29092             goto has_addr1;
29093         case OP_FMT_label16:
29094             addr = get_i16(tab + pos);
29095             goto has_addr1;
29096 #endif
29097         case OP_FMT_label:
29098             addr = get_u32(tab + pos);
29099             goto has_addr1;
29100         has_addr1:
29101             if (pass == 1)
29102                 printf(" %u:%u", addr, label_slots[addr].pos);
29103             if (pass == 2)
29104                 printf(" %u:%u", addr, label_slots[addr].pos2);
29105             if (pass == 3)
29106                 printf(" %u", addr + pos);
29107             break;
29108         case OP_FMT_label_u16:
29109             addr = get_u32(tab + pos);
29110             if (pass == 1)
29111                 printf(" %u:%u", addr, label_slots[addr].pos);
29112             if (pass == 2)
29113                 printf(" %u:%u", addr, label_slots[addr].pos2);
29114             if (pass == 3)
29115                 printf(" %u", addr + pos);
29116             printf(",%u", get_u16(tab + pos + 4));
29117             break;
29118 #if SHORT_OPCODES
29119         case OP_FMT_const8:
29120             idx = get_u8(tab + pos);
29121             goto has_pool_idx;
29122 #endif
29123         case OP_FMT_const:
29124             idx = get_u32(tab + pos);
29125             goto has_pool_idx;
29126         has_pool_idx:
29127             printf(" %u: ", idx);
29128             if (idx < cpool_count) {
29129                 JS_DumpValue(ctx, cpool[idx]);
29130             }
29131             break;
29132         case OP_FMT_atom:
29133             printf(" ");
29134             print_atom(ctx, get_u32(tab + pos));
29135             break;
29136         case OP_FMT_atom_u8:
29137             printf(" ");
29138             print_atom(ctx, get_u32(tab + pos));
29139             printf(",%d", get_u8(tab + pos + 4));
29140             break;
29141         case OP_FMT_atom_u16:
29142             printf(" ");
29143             print_atom(ctx, get_u32(tab + pos));
29144             printf(",%d", get_u16(tab + pos + 4));
29145             break;
29146         case OP_FMT_atom_label_u8:
29147         case OP_FMT_atom_label_u16:
29148             printf(" ");
29149             print_atom(ctx, get_u32(tab + pos));
29150             addr = get_u32(tab + pos + 4);
29151             if (pass == 1)
29152                 printf(",%u:%u", addr, label_slots[addr].pos);
29153             if (pass == 2)
29154                 printf(",%u:%u", addr, label_slots[addr].pos2);
29155             if (pass == 3)
29156                 printf(",%u", addr + pos + 4);
29157             if (oi->fmt == OP_FMT_atom_label_u8)
29158                 printf(",%u", get_u8(tab + pos + 8));
29159             else
29160                 printf(",%u", get_u16(tab + pos + 8));
29161             break;
29162         case OP_FMT_none_loc:
29163             idx = (op - OP_get_loc0) % 4;
29164             goto has_loc;
29165         case OP_FMT_loc8:
29166             idx = get_u8(tab + pos);
29167             goto has_loc;
29168         case OP_FMT_loc:
29169             idx = get_u16(tab + pos);
29170         has_loc:
29171             printf(" %d: ", idx);
29172             if (idx < var_count) {
29173                 print_atom(ctx, vars[idx].var_name);
29174             }
29175             break;
29176         case OP_FMT_none_arg:
29177             idx = (op - OP_get_arg0) % 4;
29178             goto has_arg;
29179         case OP_FMT_arg:
29180             idx = get_u16(tab + pos);
29181         has_arg:
29182             printf(" %d: ", idx);
29183             if (idx < arg_count) {
29184                 print_atom(ctx, args[idx].var_name);
29185             }
29186             break;
29187         case OP_FMT_none_var_ref:
29188             idx = (op - OP_get_var_ref0) % 4;
29189             goto has_var_ref;
29190         case OP_FMT_var_ref:
29191             idx = get_u16(tab + pos);
29192         has_var_ref:
29193             printf(" %d: ", idx);
29194             if (idx < closure_var_count) {
29195                 print_atom(ctx, closure_var[idx].var_name);
29196             }
29197             break;
29198         default:
29199             break;
29200         }
29201         printf("\n");
29202         pos += oi->size - 1;
29203     }
29204     if (source) {
29205         if (!in_source)
29206             printf("\n");
29207         print_lines(source, line, INT32_MAX);
29208     }
29209     js_free(ctx, bits);
29210 }
29211 
dump_pc2line(JSContext * ctx,const uint8_t * buf,int len,int line_num)29212 static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len,
29213                                                  int line_num)
29214 {
29215     const uint8_t *p_end, *p_next, *p;
29216     int pc, v;
29217     unsigned int op;
29218 
29219     if (len <= 0)
29220         return;
29221 
29222     printf("%5s %5s\n", "PC", "LINE");
29223 
29224     p = buf;
29225     p_end = buf + len;
29226     pc = 0;
29227     while (p < p_end) {
29228         op = *p++;
29229         if (op == 0) {
29230             v = unicode_from_utf8(p, p_end - p, &p_next);
29231             if (v < 0)
29232                 goto fail;
29233             pc += v;
29234             p = p_next;
29235             v = unicode_from_utf8(p, p_end - p, &p_next);
29236             if (v < 0) {
29237             fail:
29238                 printf("invalid pc2line encode pos=%d\n", (int)(p - buf));
29239                 return;
29240             }
29241             if (!(v & 1)) {
29242                 v = v >> 1;
29243             } else {
29244                 v = -(v >> 1) - 1;
29245             }
29246             line_num += v;
29247             p = p_next;
29248         } else {
29249             op -= PC2LINE_OP_FIRST;
29250             pc += (op / PC2LINE_RANGE);
29251             line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE;
29252         }
29253         printf("%5d %5d\n", pc, line_num);
29254     }
29255 }
29256 
js_dump_function_bytecode(JSContext * ctx,JSFunctionBytecode * b)29257 static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b)
29258 {
29259     int i;
29260     char atom_buf[ATOM_GET_STR_BUF_SIZE];
29261     const char *str;
29262 
29263     if (b->has_debug && b->debug.filename != JS_ATOM_NULL) {
29264         str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename);
29265         printf("%s:%d: ", str, b->debug.line_num);
29266     }
29267 
29268     str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name);
29269     printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str);
29270     if (b->js_mode) {
29271         printf("  mode:");
29272         if (b->js_mode & JS_MODE_STRICT)
29273             printf(" strict");
29274 #ifdef CONFIG_BIGNUM
29275         if (b->js_mode & JS_MODE_MATH)
29276             printf(" math");
29277 #endif
29278         printf("\n");
29279     }
29280     if (b->arg_count && b->vardefs) {
29281         printf("  args:");
29282         for(i = 0; i < b->arg_count; i++) {
29283             printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf),
29284                                         b->vardefs[i].var_name));
29285         }
29286         printf("\n");
29287     }
29288     if (b->var_count && b->vardefs) {
29289         printf("  locals:\n");
29290         for(i = 0; i < b->var_count; i++) {
29291             JSVarDef *vd = &b->vardefs[b->arg_count + i];
29292             printf("%5d: %s %s", i,
29293                    vd->var_kind == JS_VAR_CATCH ? "catch" :
29294                    (vd->var_kind == JS_VAR_FUNCTION_DECL ||
29295                     vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" :
29296                    vd->is_const ? "const" :
29297                    vd->is_lexical ? "let" : "var",
29298                    JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name));
29299             if (vd->scope_level)
29300                 printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next);
29301             printf("\n");
29302         }
29303     }
29304     if (b->closure_var_count) {
29305         printf("  closure vars:\n");
29306         for(i = 0; i < b->closure_var_count; i++) {
29307             JSClosureVar *cv = &b->closure_var[i];
29308             printf("%5d: %s %s:%s%d %s\n", i,
29309                    JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name),
29310                    cv->is_local ? "local" : "parent",
29311                    cv->is_arg ? "arg" : "loc", cv->var_idx,
29312                    cv->is_const ? "const" :
29313                    cv->is_lexical ? "let" : "var");
29314         }
29315     }
29316     printf("  stack_size: %d\n", b->stack_size);
29317     printf("  opcodes:\n");
29318     dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len,
29319                    b->vardefs, b->arg_count,
29320                    b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count,
29321                    b->closure_var, b->closure_var_count,
29322                    b->cpool, b->cpool_count,
29323                    b->has_debug ? b->debug.source : NULL,
29324                    b->has_debug ? b->debug.line_num : -1, NULL, b);
29325 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32)
29326     if (b->has_debug)
29327         dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len, b->debug.line_num);
29328 #endif
29329     printf("\n");
29330 }
29331 #endif
29332 
add_closure_var(JSContext * ctx,JSFunctionDef * s,BOOL is_local,BOOL is_arg,int var_idx,JSAtom var_name,BOOL is_const,BOOL is_lexical,JSVarKindEnum var_kind)29333 static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
29334                            BOOL is_local, BOOL is_arg,
29335                            int var_idx, JSAtom var_name,
29336                            BOOL is_const, BOOL is_lexical,
29337                            JSVarKindEnum var_kind)
29338 {
29339     JSClosureVar *cv;
29340 
29341     /* the closure variable indexes are currently stored on 16 bits */
29342     if (s->closure_var_count >= JS_MAX_LOCAL_VARS) {
29343         JS_ThrowInternalError(ctx, "too many closure variables");
29344         return -1;
29345     }
29346 
29347     if (js_resize_array(ctx, (void **)&s->closure_var,
29348                         sizeof(s->closure_var[0]),
29349                         &s->closure_var_size, s->closure_var_count + 1))
29350         return -1;
29351     cv = &s->closure_var[s->closure_var_count++];
29352     cv->is_local = is_local;
29353     cv->is_arg = is_arg;
29354     cv->is_const = is_const;
29355     cv->is_lexical = is_lexical;
29356     cv->var_kind = var_kind;
29357     cv->var_idx = var_idx;
29358     cv->var_name = JS_DupAtom(ctx, var_name);
29359     return s->closure_var_count - 1;
29360 }
29361 
find_closure_var(JSContext * ctx,JSFunctionDef * s,JSAtom var_name)29362 static int find_closure_var(JSContext *ctx, JSFunctionDef *s,
29363                             JSAtom var_name)
29364 {
29365     int i;
29366     for(i = 0; i < s->closure_var_count; i++) {
29367         JSClosureVar *cv = &s->closure_var[i];
29368         if (cv->var_name == var_name)
29369             return i;
29370     }
29371     return -1;
29372 }
29373 
29374 /* 'fd' must be a parent of 's'. Create in 's' a closure referencing a
29375    local variable (is_local = TRUE) or a closure (is_local = FALSE) in
29376    'fd' */
get_closure_var2(JSContext * ctx,JSFunctionDef * s,JSFunctionDef * fd,BOOL is_local,BOOL is_arg,int var_idx,JSAtom var_name,BOOL is_const,BOOL is_lexical,JSVarKindEnum var_kind)29377 static int get_closure_var2(JSContext *ctx, JSFunctionDef *s,
29378                             JSFunctionDef *fd, BOOL is_local,
29379                             BOOL is_arg, int var_idx, JSAtom var_name,
29380                             BOOL is_const, BOOL is_lexical,
29381                             JSVarKindEnum var_kind)
29382 {
29383     int i;
29384 
29385     if (fd != s->parent) {
29386         var_idx = get_closure_var2(ctx, s->parent, fd, is_local,
29387                                    is_arg, var_idx, var_name,
29388                                    is_const, is_lexical, var_kind);
29389         if (var_idx < 0)
29390             return -1;
29391         is_local = FALSE;
29392     }
29393     for(i = 0; i < s->closure_var_count; i++) {
29394         JSClosureVar *cv = &s->closure_var[i];
29395         if (cv->var_idx == var_idx && cv->is_arg == is_arg &&
29396             cv->is_local == is_local)
29397             return i;
29398     }
29399     return add_closure_var(ctx, s, is_local, is_arg, var_idx, var_name,
29400                            is_const, is_lexical, var_kind);
29401 }
29402 
get_closure_var(JSContext * ctx,JSFunctionDef * s,JSFunctionDef * fd,BOOL is_arg,int var_idx,JSAtom var_name,BOOL is_const,BOOL is_lexical,JSVarKindEnum var_kind)29403 static int get_closure_var(JSContext *ctx, JSFunctionDef *s,
29404                            JSFunctionDef *fd, BOOL is_arg,
29405                            int var_idx, JSAtom var_name,
29406                            BOOL is_const, BOOL is_lexical,
29407                            JSVarKindEnum var_kind)
29408 {
29409     return get_closure_var2(ctx, s, fd, TRUE, is_arg,
29410                             var_idx, var_name, is_const, is_lexical,
29411                             var_kind);
29412 }
29413 
get_with_scope_opcode(int op)29414 static int get_with_scope_opcode(int op)
29415 {
29416     if (op == OP_scope_get_var_undef)
29417         return OP_with_get_var;
29418     else
29419         return OP_with_get_var + (op - OP_scope_get_var);
29420 }
29421 
can_opt_put_ref_value(const uint8_t * bc_buf,int pos)29422 static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos)
29423 {
29424     int opcode = bc_buf[pos];
29425     return (bc_buf[pos + 1] == OP_put_ref_value &&
29426             (opcode == OP_insert3 ||
29427              opcode == OP_perm4 ||
29428              opcode == OP_nop ||
29429              opcode == OP_rot3l));
29430 }
29431 
can_opt_put_global_ref_value(const uint8_t * bc_buf,int pos)29432 static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos)
29433 {
29434     int opcode = bc_buf[pos];
29435     return (bc_buf[pos + 1] == OP_put_ref_value &&
29436             (opcode == OP_insert3 ||
29437              opcode == OP_perm4 ||
29438              opcode == OP_nop ||
29439              opcode == OP_rot3l));
29440 }
29441 
optimize_scope_make_ref(JSContext * ctx,JSFunctionDef * s,DynBuf * bc,uint8_t * bc_buf,LabelSlot * ls,int pos_next,int get_op,int var_idx)29442 static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s,
29443                                    DynBuf *bc, uint8_t *bc_buf,
29444                                    LabelSlot *ls, int pos_next,
29445                                    int get_op, int var_idx)
29446 {
29447     int label_pos, end_pos, pos;
29448 
29449     /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)`
29450        but only if expr does not modify `a`.
29451        should scan the code between pos_next and label_pos
29452        for operations that can potentially change `a`:
29453        OP_scope_make_ref(a), function calls, jumps and gosub.
29454      */
29455     /* replace the reference get/put with normal variable
29456        accesses */
29457     if (bc_buf[pos_next] == OP_get_ref_value) {
29458         dbuf_putc(bc, get_op);
29459         dbuf_put_u16(bc, var_idx);
29460         pos_next++;
29461     }
29462     /* remove the OP_label to make room for replacement */
29463     /* label should have a refcount of 0 anyway */
29464     /* XXX: should avoid this patch by inserting nops in phase 1 */
29465     label_pos = ls->pos;
29466     pos = label_pos - 5;
29467     assert(bc_buf[pos] == OP_label);
29468     /* label points to an instruction pair:
29469        - insert3 / put_ref_value
29470        - perm4 / put_ref_value
29471        - rot3l / put_ref_value
29472        - nop / put_ref_value
29473      */
29474     end_pos = label_pos + 2;
29475     if (bc_buf[label_pos] == OP_insert3)
29476         bc_buf[pos++] = OP_dup;
29477     bc_buf[pos] = get_op + 1;
29478     put_u16(bc_buf + pos + 1, var_idx);
29479     pos += 3;
29480     /* pad with OP_nop */
29481     while (pos < end_pos)
29482         bc_buf[pos++] = OP_nop;
29483     return pos_next;
29484 }
29485 
optimize_scope_make_global_ref(JSContext * ctx,JSFunctionDef * s,DynBuf * bc,uint8_t * bc_buf,LabelSlot * ls,int pos_next,JSAtom var_name)29486 static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,
29487                                           DynBuf *bc, uint8_t *bc_buf,
29488                                           LabelSlot *ls, int pos_next,
29489                                           JSAtom var_name)
29490 {
29491     int label_pos, end_pos, pos, op;
29492     BOOL is_strict;
29493     is_strict = ((s->js_mode & JS_MODE_STRICT) != 0);
29494 
29495     /* replace the reference get/put with normal variable
29496        accesses */
29497     if (is_strict) {
29498         /* need to check if the variable exists before evaluating the right
29499            expression */
29500         /* XXX: need an extra OP_true if destructuring an array */
29501         dbuf_putc(bc, OP_check_var);
29502         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29503     } else {
29504         /* XXX: need 2 extra OP_true if destructuring an array */
29505     }
29506     if (bc_buf[pos_next] == OP_get_ref_value) {
29507         dbuf_putc(bc, OP_get_var);
29508         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29509         pos_next++;
29510     }
29511     /* remove the OP_label to make room for replacement */
29512     /* label should have a refcount of 0 anyway */
29513     /* XXX: should have emitted several OP_nop to avoid this kludge */
29514     label_pos = ls->pos;
29515     pos = label_pos - 5;
29516     assert(bc_buf[pos] == OP_label);
29517     end_pos = label_pos + 2;
29518     op = bc_buf[label_pos];
29519     if (is_strict) {
29520         if (op != OP_nop) {
29521             switch(op) {
29522             case OP_insert3:
29523                 op = OP_insert2;
29524                 break;
29525             case OP_perm4:
29526                 op = OP_perm3;
29527                 break;
29528             case OP_rot3l:
29529                 op = OP_swap;
29530                 break;
29531             default:
29532                 abort();
29533             }
29534             bc_buf[pos++] = op;
29535         }
29536     } else {
29537         if (op == OP_insert3)
29538             bc_buf[pos++] = OP_dup;
29539     }
29540     if (is_strict) {
29541         bc_buf[pos] = OP_put_var_strict;
29542         /* XXX: need 1 extra OP_drop if destructuring an array */
29543     } else {
29544         bc_buf[pos] = OP_put_var;
29545         /* XXX: need 2 extra OP_drop if destructuring an array */
29546     }
29547     put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name));
29548     pos += 5;
29549     /* pad with OP_nop */
29550     while (pos < end_pos)
29551         bc_buf[pos++] = OP_nop;
29552     return pos_next;
29553 }
29554 
add_var_this(JSContext * ctx,JSFunctionDef * fd)29555 static int add_var_this(JSContext *ctx, JSFunctionDef *fd)
29556 {
29557     int idx;
29558     idx = add_var(ctx, fd, JS_ATOM_this);
29559     if (idx >= 0 && fd->is_derived_class_constructor) {
29560         JSVarDef *vd = &fd->vars[idx];
29561         /* XXX: should have is_this flag or var type */
29562         vd->is_lexical = 1; /* used to trigger 'uninitialized' checks
29563                                in a derived class constructor */
29564     }
29565     return idx;
29566 }
29567 
resolve_pseudo_var(JSContext * ctx,JSFunctionDef * s,JSAtom var_name)29568 static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s,
29569                                JSAtom var_name)
29570 {
29571     int var_idx;
29572 
29573     if (!s->has_this_binding)
29574         return -1;
29575     switch(var_name) {
29576     case JS_ATOM_home_object:
29577         /* 'home_object' pseudo variable */
29578         if (s->home_object_var_idx < 0)
29579             s->home_object_var_idx = add_var(ctx, s, var_name);
29580         var_idx = s->home_object_var_idx;
29581         break;
29582     case JS_ATOM_this_active_func:
29583         /* 'this.active_func' pseudo variable */
29584         if (s->this_active_func_var_idx < 0)
29585             s->this_active_func_var_idx = add_var(ctx, s, var_name);
29586         var_idx = s->this_active_func_var_idx;
29587         break;
29588     case JS_ATOM_new_target:
29589         /* 'new.target' pseudo variable */
29590         if (s->new_target_var_idx < 0)
29591             s->new_target_var_idx = add_var(ctx, s, var_name);
29592         var_idx = s->new_target_var_idx;
29593         break;
29594     case JS_ATOM_this:
29595         /* 'this' pseudo variable */
29596         if (s->this_var_idx < 0)
29597             s->this_var_idx = add_var_this(ctx, s);
29598         var_idx = s->this_var_idx;
29599         break;
29600     default:
29601         var_idx = -1;
29602         break;
29603     }
29604     return var_idx;
29605 }
29606 
29607 /* test if 'var_name' is in the variable object on the stack. If is it
29608    the case, handle it and jump to 'label_done' */
var_object_test(JSContext * ctx,JSFunctionDef * s,JSAtom var_name,int op,DynBuf * bc,int * plabel_done,BOOL is_with)29609 static void var_object_test(JSContext *ctx, JSFunctionDef *s,
29610                             JSAtom var_name, int op, DynBuf *bc,
29611                             int *plabel_done, BOOL is_with)
29612 {
29613     dbuf_putc(bc, get_with_scope_opcode(op));
29614     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29615     *plabel_done = new_label_fd(s, *plabel_done);
29616     dbuf_put_u32(bc, *plabel_done);
29617     dbuf_putc(bc, is_with);
29618     update_label(s, *plabel_done, 1);
29619     s->jump_size++;
29620 }
29621 
29622 /* return the position of the next opcode */
resolve_scope_var(JSContext * ctx,JSFunctionDef * s,JSAtom var_name,int scope_level,int op,DynBuf * bc,uint8_t * bc_buf,LabelSlot * ls,int pos_next)29623 static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
29624                              JSAtom var_name, int scope_level, int op,
29625                              DynBuf *bc, uint8_t *bc_buf,
29626                              LabelSlot *ls, int pos_next)
29627 {
29628     int idx, var_idx, is_put;
29629     int label_done;
29630     JSFunctionDef *fd;
29631     JSVarDef *vd;
29632     BOOL is_pseudo_var, is_arg_scope;
29633 
29634     label_done = -1;
29635 
29636     /* XXX: could be simpler to use a specific function to
29637        resolve the pseudo variables */
29638     is_pseudo_var = (var_name == JS_ATOM_home_object ||
29639                      var_name == JS_ATOM_this_active_func ||
29640                      var_name == JS_ATOM_new_target ||
29641                      var_name == JS_ATOM_this);
29642 
29643     /* resolve local scoped variables */
29644     var_idx = -1;
29645     for (idx = s->scopes[scope_level].first; idx >= 0;) {
29646         vd = &s->vars[idx];
29647         if (vd->var_name == var_name) {
29648             if (op == OP_scope_put_var || op == OP_scope_make_ref) {
29649                 if (vd->is_const) {
29650                     dbuf_putc(bc, OP_throw_error);
29651                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29652                     dbuf_putc(bc, JS_THROW_VAR_RO);
29653                     goto done;
29654                 }
29655             }
29656             var_idx = idx;
29657             break;
29658         } else
29659         if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
29660             dbuf_putc(bc, OP_get_loc);
29661             dbuf_put_u16(bc, idx);
29662             var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
29663         }
29664         idx = vd->scope_next;
29665     }
29666     is_arg_scope = (idx == ARG_SCOPE_END);
29667     if (var_idx < 0) {
29668         /* argument scope: variables are not visible but pseudo
29669            variables are visible */
29670         if (!is_arg_scope) {
29671             var_idx = find_var(ctx, s, var_name);
29672         }
29673 
29674         if (var_idx < 0 && is_pseudo_var)
29675             var_idx = resolve_pseudo_var(ctx, s, var_name);
29676 
29677         if (var_idx < 0 && var_name == JS_ATOM_arguments &&
29678             s->has_arguments_binding) {
29679             /* 'arguments' pseudo variable */
29680             var_idx = add_arguments_var(ctx, s);
29681         }
29682         if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) {
29683             /* add a new variable with the function name */
29684             var_idx = add_func_var(ctx, s, var_name);
29685         }
29686     }
29687     if (var_idx >= 0) {
29688         if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
29689             !(var_idx & ARGUMENT_VAR_OFFSET) &&
29690             s->vars[var_idx].is_const) {
29691             /* only happens when assigning a function expression name
29692                in strict mode */
29693             dbuf_putc(bc, OP_throw_error);
29694             dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29695             dbuf_putc(bc, JS_THROW_VAR_RO);
29696             goto done;
29697         }
29698         /* OP_scope_put_var_init is only used to initialize a
29699            lexical variable, so it is never used in a with or var object. It
29700            can be used with a closure (module global variable case). */
29701         switch (op) {
29702         case OP_scope_make_ref:
29703             if (!(var_idx & ARGUMENT_VAR_OFFSET) &&
29704                 s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) {
29705                 /* Create a dummy object reference for the func_var */
29706                 dbuf_putc(bc, OP_object);
29707                 dbuf_putc(bc, OP_get_loc);
29708                 dbuf_put_u16(bc, var_idx);
29709                 dbuf_putc(bc, OP_define_field);
29710                 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29711                 dbuf_putc(bc, OP_push_atom_value);
29712                 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29713             } else
29714             if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) {
29715                 int get_op;
29716                 if (var_idx & ARGUMENT_VAR_OFFSET) {
29717                     get_op = OP_get_arg;
29718                     var_idx -= ARGUMENT_VAR_OFFSET;
29719                 } else {
29720                     if (s->vars[var_idx].is_lexical)
29721                         get_op = OP_get_loc_check;
29722                     else
29723                         get_op = OP_get_loc;
29724                 }
29725                 pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
29726                                                    pos_next, get_op, var_idx);
29727             } else {
29728                 /* Create a dummy object with a named slot that is
29729                    a reference to the local variable */
29730                 if (var_idx & ARGUMENT_VAR_OFFSET) {
29731                     dbuf_putc(bc, OP_make_arg_ref);
29732                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29733                     dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
29734                 } else {
29735                     dbuf_putc(bc, OP_make_loc_ref);
29736                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29737                     dbuf_put_u16(bc, var_idx);
29738                 }
29739             }
29740             break;
29741         case OP_scope_get_ref:
29742             dbuf_putc(bc, OP_undefined);
29743             /* fall thru */
29744         case OP_scope_get_var_undef:
29745         case OP_scope_get_var:
29746         case OP_scope_put_var:
29747         case OP_scope_put_var_init:
29748             is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init);
29749             if (var_idx & ARGUMENT_VAR_OFFSET) {
29750                 dbuf_putc(bc, OP_get_arg + is_put);
29751                 dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
29752             } else {
29753                 if (is_put) {
29754                     if (s->vars[var_idx].is_lexical) {
29755                         if (op == OP_scope_put_var_init) {
29756                             /* 'this' can only be initialized once */
29757                             if (var_name == JS_ATOM_this)
29758                                 dbuf_putc(bc, OP_put_loc_check_init);
29759                             else
29760                                 dbuf_putc(bc, OP_put_loc);
29761                         } else {
29762                             dbuf_putc(bc, OP_put_loc_check);
29763                         }
29764                     } else {
29765                         dbuf_putc(bc, OP_put_loc);
29766                     }
29767                 } else {
29768                     if (s->vars[var_idx].is_lexical) {
29769                         dbuf_putc(bc, OP_get_loc_check);
29770                     } else {
29771                         dbuf_putc(bc, OP_get_loc);
29772                     }
29773                 }
29774                 dbuf_put_u16(bc, var_idx);
29775             }
29776             break;
29777         case OP_scope_delete_var:
29778             dbuf_putc(bc, OP_push_false);
29779             break;
29780         }
29781         goto done;
29782     }
29783     /* check eval object */
29784     if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) {
29785         dbuf_putc(bc, OP_get_loc);
29786         dbuf_put_u16(bc, s->var_object_idx);
29787         var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
29788     }
29789     /* check eval object in argument scope */
29790     if (s->arg_var_object_idx >= 0 && !is_pseudo_var) {
29791         dbuf_putc(bc, OP_get_loc);
29792         dbuf_put_u16(bc, s->arg_var_object_idx);
29793         var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
29794     }
29795 
29796     /* check parent scopes */
29797     for (fd = s; fd->parent;) {
29798         scope_level = fd->parent_scope_level;
29799         fd = fd->parent;
29800         for (idx = fd->scopes[scope_level].first; idx >= 0;) {
29801             vd = &fd->vars[idx];
29802             if (vd->var_name == var_name) {
29803                 if (op == OP_scope_put_var || op == OP_scope_make_ref) {
29804                     if (vd->is_const) {
29805                         dbuf_putc(bc, OP_throw_error);
29806                         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29807                         dbuf_putc(bc, JS_THROW_VAR_RO);
29808                         goto done;
29809                     }
29810                 }
29811                 var_idx = idx;
29812                 break;
29813             } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
29814                 vd->is_captured = 1;
29815                 idx = get_closure_var(ctx, s, fd, FALSE, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL);
29816                 if (idx >= 0) {
29817                     dbuf_putc(bc, OP_get_var_ref);
29818                     dbuf_put_u16(bc, idx);
29819                     var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
29820                 }
29821             }
29822             idx = vd->scope_next;
29823         }
29824         is_arg_scope = (idx == ARG_SCOPE_END);
29825         if (var_idx >= 0)
29826             break;
29827 
29828         if (!is_arg_scope) {
29829             var_idx = find_var(ctx, fd, var_name);
29830             if (var_idx >= 0)
29831                 break;
29832         }
29833         if (is_pseudo_var) {
29834             var_idx = resolve_pseudo_var(ctx, fd, var_name);
29835             if (var_idx >= 0)
29836                 break;
29837         }
29838         if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) {
29839             var_idx = add_arguments_var(ctx, fd);
29840             break;
29841         }
29842         if (fd->is_func_expr && fd->func_name == var_name) {
29843             /* add a new variable with the function name */
29844             var_idx = add_func_var(ctx, fd, var_name);
29845             break;
29846         }
29847 
29848         /* check eval object */
29849         if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) {
29850             vd = &fd->vars[fd->var_object_idx];
29851             vd->is_captured = 1;
29852             idx = get_closure_var(ctx, s, fd, FALSE,
29853                                   fd->var_object_idx, vd->var_name,
29854                                   FALSE, FALSE, JS_VAR_NORMAL);
29855             dbuf_putc(bc, OP_get_var_ref);
29856             dbuf_put_u16(bc, idx);
29857             var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
29858         }
29859 
29860         /* check eval object in argument scope */
29861         if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) {
29862             vd = &fd->vars[fd->arg_var_object_idx];
29863             vd->is_captured = 1;
29864             idx = get_closure_var(ctx, s, fd, FALSE,
29865                                   fd->arg_var_object_idx, vd->var_name,
29866                                   FALSE, FALSE, JS_VAR_NORMAL);
29867             dbuf_putc(bc, OP_get_var_ref);
29868             dbuf_put_u16(bc, idx);
29869             var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
29870         }
29871 
29872         if (fd->is_eval)
29873             break; /* it it necessarily the top level function */
29874     }
29875 
29876     /* check direct eval scope (in the closure of the eval function
29877        which is necessarily at the top level) */
29878     if (!fd)
29879         fd = s;
29880     if (var_idx < 0 && fd->is_eval) {
29881         int idx1;
29882         for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) {
29883             JSClosureVar *cv = &fd->closure_var[idx1];
29884             if (var_name == cv->var_name) {
29885                 if (fd != s) {
29886                     idx = get_closure_var2(ctx, s, fd,
29887                                            FALSE,
29888                                            cv->is_arg, idx1,
29889                                            cv->var_name, cv->is_const,
29890                                            cv->is_lexical, cv->var_kind);
29891                 } else {
29892                     idx = idx1;
29893                 }
29894                 goto has_idx;
29895             } else if ((cv->var_name == JS_ATOM__var_ ||
29896                         cv->var_name == JS_ATOM__arg_var_ ||
29897                         cv->var_name == JS_ATOM__with_) && !is_pseudo_var) {
29898                 int is_with = (cv->var_name == JS_ATOM__with_);
29899                 if (fd != s) {
29900                     idx = get_closure_var2(ctx, s, fd,
29901                                            FALSE,
29902                                            cv->is_arg, idx1,
29903                                            cv->var_name, FALSE, FALSE,
29904                                            JS_VAR_NORMAL);
29905                 } else {
29906                     idx = idx1;
29907                 }
29908                 dbuf_putc(bc, OP_get_var_ref);
29909                 dbuf_put_u16(bc, idx);
29910                 var_object_test(ctx, s, var_name, op, bc, &label_done, is_with);
29911             }
29912         }
29913     }
29914 
29915     if (var_idx >= 0) {
29916         /* find the corresponding closure variable */
29917         if (var_idx & ARGUMENT_VAR_OFFSET) {
29918             fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1;
29919             idx = get_closure_var(ctx, s, fd,
29920                                   TRUE, var_idx - ARGUMENT_VAR_OFFSET,
29921                                   var_name, FALSE, FALSE, JS_VAR_NORMAL);
29922         } else {
29923             fd->vars[var_idx].is_captured = 1;
29924             idx = get_closure_var(ctx, s, fd,
29925                                   FALSE, var_idx,
29926                                   var_name,
29927                                   fd->vars[var_idx].is_const,
29928                                   fd->vars[var_idx].is_lexical,
29929                                   fd->vars[var_idx].var_kind);
29930         }
29931         if (idx >= 0) {
29932         has_idx:
29933             if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
29934                 s->closure_var[idx].is_const) {
29935                 dbuf_putc(bc, OP_throw_error);
29936                 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29937                 dbuf_putc(bc, JS_THROW_VAR_RO);
29938                 goto done;
29939             }
29940             switch (op) {
29941             case OP_scope_make_ref:
29942                 if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) {
29943                     /* Create a dummy object reference for the func_var */
29944                     dbuf_putc(bc, OP_object);
29945                     dbuf_putc(bc, OP_get_var_ref);
29946                     dbuf_put_u16(bc, idx);
29947                     dbuf_putc(bc, OP_define_field);
29948                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29949                     dbuf_putc(bc, OP_push_atom_value);
29950                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29951                 } else
29952                 if (label_done == -1 &&
29953                     can_opt_put_ref_value(bc_buf, ls->pos)) {
29954                     int get_op;
29955                     if (s->closure_var[idx].is_lexical)
29956                         get_op = OP_get_var_ref_check;
29957                     else
29958                         get_op = OP_get_var_ref;
29959                     pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
29960                                                        pos_next,
29961                                                        get_op, idx);
29962                 } else {
29963                     /* Create a dummy object with a named slot that is
29964                        a reference to the closure variable */
29965                     dbuf_putc(bc, OP_make_var_ref_ref);
29966                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29967                     dbuf_put_u16(bc, idx);
29968                 }
29969                 break;
29970             case OP_scope_get_ref:
29971                 /* XXX: should create a dummy object with a named slot that is
29972                    a reference to the closure variable */
29973                 dbuf_putc(bc, OP_undefined);
29974                 /* fall thru */
29975             case OP_scope_get_var_undef:
29976             case OP_scope_get_var:
29977             case OP_scope_put_var:
29978             case OP_scope_put_var_init:
29979                 is_put = (op == OP_scope_put_var ||
29980                           op == OP_scope_put_var_init);
29981                 if (is_put) {
29982                     if (s->closure_var[idx].is_lexical) {
29983                         if (op == OP_scope_put_var_init) {
29984                             /* 'this' can only be initialized once */
29985                             if (var_name == JS_ATOM_this)
29986                                 dbuf_putc(bc, OP_put_var_ref_check_init);
29987                             else
29988                                 dbuf_putc(bc, OP_put_var_ref);
29989                         } else {
29990                             dbuf_putc(bc, OP_put_var_ref_check);
29991                         }
29992                     } else {
29993                         dbuf_putc(bc, OP_put_var_ref);
29994                     }
29995                 } else {
29996                     if (s->closure_var[idx].is_lexical) {
29997                         dbuf_putc(bc, OP_get_var_ref_check);
29998                     } else {
29999                         dbuf_putc(bc, OP_get_var_ref);
30000                     }
30001                 }
30002                 dbuf_put_u16(bc, idx);
30003                 break;
30004             case OP_scope_delete_var:
30005                 dbuf_putc(bc, OP_push_false);
30006                 break;
30007             }
30008             goto done;
30009         }
30010     }
30011 
30012     /* global variable access */
30013 
30014     switch (op) {
30015     case OP_scope_make_ref:
30016         if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) {
30017             pos_next = optimize_scope_make_global_ref(ctx, s, bc, bc_buf, ls,
30018                                                       pos_next, var_name);
30019         } else {
30020             dbuf_putc(bc, OP_make_var_ref);
30021             dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30022         }
30023         break;
30024     case OP_scope_get_ref:
30025         /* XXX: should create a dummy object with a named slot that is
30026            a reference to the global variable */
30027         dbuf_putc(bc, OP_undefined);
30028         dbuf_putc(bc, OP_get_var);
30029         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30030         break;
30031     case OP_scope_get_var_undef:
30032     case OP_scope_get_var:
30033     case OP_scope_put_var:
30034         dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef));
30035         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30036         break;
30037     case OP_scope_put_var_init:
30038         dbuf_putc(bc, OP_put_var_init);
30039         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30040         break;
30041     case OP_scope_delete_var:
30042         dbuf_putc(bc, OP_delete_var);
30043         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30044         break;
30045     }
30046 done:
30047     if (label_done >= 0) {
30048         dbuf_putc(bc, OP_label);
30049         dbuf_put_u32(bc, label_done);
30050         s->label_slots[label_done].pos2 = bc->size;
30051     }
30052     return pos_next;
30053 }
30054 
30055 /* search in all scopes */
find_private_class_field_all(JSContext * ctx,JSFunctionDef * fd,JSAtom name,int scope_level)30056 static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd,
30057                                         JSAtom name, int scope_level)
30058 {
30059     int idx;
30060 
30061     idx = fd->scopes[scope_level].first;
30062     while (idx >= 0) {
30063         if (fd->vars[idx].var_name == name)
30064             return idx;
30065         idx = fd->vars[idx].scope_next;
30066     }
30067     return -1;
30068 }
30069 
get_loc_or_ref(DynBuf * bc,BOOL is_ref,int idx)30070 static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx)
30071 {
30072     /* if the field is not initialized, the error is catched when
30073        accessing it */
30074     if (is_ref)
30075         dbuf_putc(bc, OP_get_var_ref);
30076     else
30077         dbuf_putc(bc, OP_get_loc);
30078     dbuf_put_u16(bc, idx);
30079 }
30080 
resolve_scope_private_field1(JSContext * ctx,BOOL * pis_ref,int * pvar_kind,JSFunctionDef * s,JSAtom var_name,int scope_level)30081 static int resolve_scope_private_field1(JSContext *ctx,
30082                                         BOOL *pis_ref, int *pvar_kind,
30083                                         JSFunctionDef *s,
30084                                         JSAtom var_name, int scope_level)
30085 {
30086     int idx, var_kind;
30087     JSFunctionDef *fd;
30088     BOOL is_ref;
30089 
30090     fd = s;
30091     is_ref = FALSE;
30092     for(;;) {
30093         idx = find_private_class_field_all(ctx, fd, var_name, scope_level);
30094         if (idx >= 0) {
30095             var_kind = fd->vars[idx].var_kind;
30096             if (is_ref) {
30097                 idx = get_closure_var(ctx, s, fd, FALSE, idx, var_name,
30098                                       TRUE, TRUE, JS_VAR_NORMAL);
30099                 if (idx < 0)
30100                     return -1;
30101             }
30102             break;
30103         }
30104         scope_level = fd->parent_scope_level;
30105         if (!fd->parent) {
30106             if (fd->is_eval) {
30107                 /* closure of the eval function (top level) */
30108                 for (idx = 0; idx < fd->closure_var_count; idx++) {
30109                     JSClosureVar *cv = &fd->closure_var[idx];
30110                     if (cv->var_name == var_name) {
30111                         var_kind = cv->var_kind;
30112                         is_ref = TRUE;
30113                         if (fd != s) {
30114                             idx = get_closure_var2(ctx, s, fd,
30115                                                    FALSE,
30116                                                    cv->is_arg, idx,
30117                                                    cv->var_name, cv->is_const,
30118                                                    cv->is_lexical,
30119                                                    cv->var_kind);
30120                             if (idx < 0)
30121                                 return -1;
30122                         }
30123                         goto done;
30124                     }
30125                 }
30126             }
30127             /* XXX: no line number info */
30128             JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'",
30129                                     var_name);
30130             return -1;
30131         } else {
30132             fd = fd->parent;
30133         }
30134         is_ref = TRUE;
30135     }
30136  done:
30137     *pis_ref = is_ref;
30138     *pvar_kind = var_kind;
30139     return idx;
30140 }
30141 
30142 /* return 0 if OK or -1 if the private field could not be resolved */
resolve_scope_private_field(JSContext * ctx,JSFunctionDef * s,JSAtom var_name,int scope_level,int op,DynBuf * bc)30143 static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s,
30144                                        JSAtom var_name, int scope_level, int op,
30145                                        DynBuf *bc)
30146 {
30147     int idx, var_kind;
30148     BOOL is_ref;
30149 
30150     idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s,
30151                                        var_name, scope_level);
30152     if (idx < 0)
30153         return -1;
30154     assert(var_kind != JS_VAR_NORMAL);
30155     switch (op) {
30156     case OP_scope_get_private_field:
30157     case OP_scope_get_private_field2:
30158         switch(var_kind) {
30159         case JS_VAR_PRIVATE_FIELD:
30160             if (op == OP_scope_get_private_field2)
30161                 dbuf_putc(bc, OP_dup);
30162             get_loc_or_ref(bc, is_ref, idx);
30163             dbuf_putc(bc, OP_get_private_field);
30164             break;
30165         case JS_VAR_PRIVATE_METHOD:
30166             get_loc_or_ref(bc, is_ref, idx);
30167             dbuf_putc(bc, OP_check_brand);
30168             if (op != OP_scope_get_private_field2)
30169                 dbuf_putc(bc, OP_nip);
30170             break;
30171         case JS_VAR_PRIVATE_GETTER:
30172         case JS_VAR_PRIVATE_GETTER_SETTER:
30173             if (op == OP_scope_get_private_field2)
30174                 dbuf_putc(bc, OP_dup);
30175             get_loc_or_ref(bc, is_ref, idx);
30176             dbuf_putc(bc, OP_check_brand);
30177             dbuf_putc(bc, OP_call_method);
30178             dbuf_put_u16(bc, 0);
30179             break;
30180         case JS_VAR_PRIVATE_SETTER:
30181             /* XXX: add clearer error message */
30182             dbuf_putc(bc, OP_throw_error);
30183             dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30184             dbuf_putc(bc, JS_THROW_VAR_RO);
30185             break;
30186         default:
30187             abort();
30188         }
30189         break;
30190     case OP_scope_put_private_field:
30191         switch(var_kind) {
30192         case JS_VAR_PRIVATE_FIELD:
30193             get_loc_or_ref(bc, is_ref, idx);
30194             dbuf_putc(bc, OP_put_private_field);
30195             break;
30196         case JS_VAR_PRIVATE_METHOD:
30197         case JS_VAR_PRIVATE_GETTER:
30198             /* XXX: add clearer error message */
30199             dbuf_putc(bc, OP_throw_error);
30200             dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30201             dbuf_putc(bc, JS_THROW_VAR_RO);
30202             break;
30203         case JS_VAR_PRIVATE_SETTER:
30204         case JS_VAR_PRIVATE_GETTER_SETTER:
30205             {
30206                 JSAtom setter_name = get_private_setter_name(ctx, var_name);
30207                 if (setter_name == JS_ATOM_NULL)
30208                     return -1;
30209                 idx = resolve_scope_private_field1(ctx, &is_ref,
30210                                                    &var_kind, s,
30211                                                    setter_name, scope_level);
30212                 JS_FreeAtom(ctx, setter_name);
30213                 if (idx < 0)
30214                     return -1;
30215                 assert(var_kind == JS_VAR_PRIVATE_SETTER);
30216                 get_loc_or_ref(bc, is_ref, idx);
30217                 dbuf_putc(bc, OP_swap);
30218                 /* obj func value */
30219                 dbuf_putc(bc, OP_rot3r);
30220                 /* value obj func */
30221                 dbuf_putc(bc, OP_check_brand);
30222                 dbuf_putc(bc, OP_rot3l);
30223                 /* obj func value */
30224                 dbuf_putc(bc, OP_call_method);
30225                 dbuf_put_u16(bc, 1);
30226             }
30227             break;
30228         default:
30229             abort();
30230         }
30231         break;
30232     default:
30233         abort();
30234     }
30235     return 0;
30236 }
30237 
mark_eval_captured_variables(JSContext * ctx,JSFunctionDef * s,int scope_level)30238 static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s,
30239                                          int scope_level)
30240 {
30241     int idx;
30242     JSVarDef *vd;
30243 
30244     for (idx = s->scopes[scope_level].first; idx >= 0;) {
30245         vd = &s->vars[idx];
30246         vd->is_captured = 1;
30247         idx = vd->scope_next;
30248     }
30249 }
30250 
30251 /* XXX: should handle the argument scope generically */
is_var_in_arg_scope(const JSVarDef * vd)30252 static BOOL is_var_in_arg_scope(const JSVarDef *vd)
30253 {
30254     return (vd->var_name == JS_ATOM_home_object ||
30255             vd->var_name == JS_ATOM_this_active_func ||
30256             vd->var_name == JS_ATOM_new_target ||
30257             vd->var_name == JS_ATOM_this ||
30258             vd->var_name == JS_ATOM__arg_var_ ||
30259             vd->var_kind == JS_VAR_FUNCTION_NAME);
30260 }
30261 
add_eval_variables(JSContext * ctx,JSFunctionDef * s)30262 static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
30263 {
30264     JSFunctionDef *fd;
30265     JSVarDef *vd;
30266     int i, scope_level, scope_idx;
30267     BOOL has_arguments_binding, has_this_binding, is_arg_scope;
30268 
30269     /* in non strict mode, variables are created in the caller's
30270        environment object */
30271     if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) {
30272         s->var_object_idx = add_var(ctx, s, JS_ATOM__var_);
30273         if (s->has_parameter_expressions) {
30274             /* an additional variable object is needed for the
30275                argument scope */
30276             s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_);
30277         }
30278     }
30279 
30280     /* eval can potentially use 'arguments' so we must define it */
30281     has_this_binding = s->has_this_binding;
30282     if (has_this_binding) {
30283         if (s->this_var_idx < 0)
30284             s->this_var_idx = add_var_this(ctx, s);
30285         if (s->new_target_var_idx < 0)
30286             s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target);
30287         if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0)
30288             s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func);
30289         if (s->has_home_object && s->home_object_var_idx < 0)
30290             s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object);
30291     }
30292     has_arguments_binding = s->has_arguments_binding;
30293     if (has_arguments_binding) {
30294         add_arguments_var(ctx, s);
30295         /* also add an arguments binding in the argument scope to
30296            raise an error if a direct eval in the argument scope tries
30297            to redefine it */
30298         if (s->has_parameter_expressions && !(s->js_mode & JS_MODE_STRICT))
30299             add_arguments_arg(ctx, s);
30300     }
30301     if (s->is_func_expr && s->func_name != JS_ATOM_NULL)
30302         add_func_var(ctx, s, s->func_name);
30303 
30304     /* eval can use all the variables of the enclosing functions, so
30305        they must be all put in the closure. The closure variables are
30306        ordered by scope. It works only because no closure are created
30307        before. */
30308     assert(s->is_eval || s->closure_var_count == 0);
30309 
30310     /* XXX: inefficient, but eval performance is less critical */
30311     fd = s;
30312     for(;;) {
30313         scope_level = fd->parent_scope_level;
30314         fd = fd->parent;
30315         if (!fd)
30316             break;
30317         /* add 'this' if it was not previously added */
30318         if (!has_this_binding && fd->has_this_binding) {
30319             if (fd->this_var_idx < 0)
30320                 fd->this_var_idx = add_var_this(ctx, fd);
30321             if (fd->new_target_var_idx < 0)
30322                 fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target);
30323             if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0)
30324                 fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func);
30325             if (fd->has_home_object && fd->home_object_var_idx < 0)
30326                 fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object);
30327             has_this_binding = TRUE;
30328         }
30329         /* add 'arguments' if it was not previously added */
30330         if (!has_arguments_binding && fd->has_arguments_binding) {
30331             add_arguments_var(ctx, fd);
30332             has_arguments_binding = TRUE;
30333         }
30334         /* add function name */
30335         if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL)
30336             add_func_var(ctx, fd, fd->func_name);
30337 
30338         /* add lexical variables */
30339         scope_idx = fd->scopes[scope_level].first;
30340         while (scope_idx >= 0) {
30341             vd = &fd->vars[scope_idx];
30342             vd->is_captured = 1;
30343             get_closure_var(ctx, s, fd, FALSE, scope_idx,
30344                             vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind);
30345             scope_idx = vd->scope_next;
30346         }
30347         is_arg_scope = (scope_idx == ARG_SCOPE_END);
30348         if (!is_arg_scope) {
30349             /* add unscoped variables */
30350             for(i = 0; i < fd->arg_count; i++) {
30351                 vd = &fd->args[i];
30352                 if (vd->var_name != JS_ATOM_NULL) {
30353                     get_closure_var(ctx, s, fd,
30354                                     TRUE, i, vd->var_name, FALSE, FALSE,
30355                                     JS_VAR_NORMAL);
30356                 }
30357             }
30358             for(i = 0; i < fd->var_count; i++) {
30359                 vd = &fd->vars[i];
30360                 /* do not close top level last result */
30361                 if (vd->scope_level == 0 &&
30362                     vd->var_name != JS_ATOM__ret_ &&
30363                     vd->var_name != JS_ATOM_NULL) {
30364                     get_closure_var(ctx, s, fd,
30365                                     FALSE, i, vd->var_name, FALSE, FALSE,
30366                                     JS_VAR_NORMAL);
30367                 }
30368             }
30369         } else {
30370             for(i = 0; i < fd->var_count; i++) {
30371                 vd = &fd->vars[i];
30372                 /* do not close top level last result */
30373                 if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {
30374                     get_closure_var(ctx, s, fd,
30375                                     FALSE, i, vd->var_name, FALSE, FALSE,
30376                                     JS_VAR_NORMAL);
30377                 }
30378             }
30379         }
30380         if (fd->is_eval) {
30381             int idx;
30382             /* add direct eval variables (we are necessarily at the
30383                top level) */
30384             for (idx = 0; idx < fd->closure_var_count; idx++) {
30385                 JSClosureVar *cv = &fd->closure_var[idx];
30386                 get_closure_var2(ctx, s, fd,
30387                                  FALSE, cv->is_arg,
30388                                  idx, cv->var_name, cv->is_const,
30389                                  cv->is_lexical, cv->var_kind);
30390             }
30391         }
30392     }
30393 }
30394 
set_closure_from_var(JSContext * ctx,JSClosureVar * cv,JSVarDef * vd,int var_idx)30395 static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv,
30396                                  JSVarDef *vd, int var_idx)
30397 {
30398     cv->is_local = TRUE;
30399     cv->is_arg = FALSE;
30400     cv->is_const = vd->is_const;
30401     cv->is_lexical = vd->is_lexical;
30402     cv->var_kind = vd->var_kind;
30403     cv->var_idx = var_idx;
30404     cv->var_name = JS_DupAtom(ctx, vd->var_name);
30405 }
30406 
30407 /* for direct eval compilation: add references to the variables of the
30408    calling function */
add_closure_variables(JSContext * ctx,JSFunctionDef * s,JSFunctionBytecode * b,int scope_idx)30409 static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
30410                                              JSFunctionBytecode *b, int scope_idx)
30411 {
30412     int i, count;
30413     JSVarDef *vd;
30414     BOOL is_arg_scope;
30415 
30416     count = b->arg_count + b->var_count + b->closure_var_count;
30417     s->closure_var = NULL;
30418     s->closure_var_count = 0;
30419     s->closure_var_size = count;
30420     if (count == 0)
30421         return 0;
30422     s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count);
30423     if (!s->closure_var)
30424         return -1;
30425     /* Add lexical variables in scope at the point of evaluation */
30426     for (i = scope_idx; i >= 0;) {
30427         vd = &b->vardefs[b->arg_count + i];
30428         if (vd->scope_level > 0) {
30429             JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
30430             set_closure_from_var(ctx, cv, vd, i);
30431         }
30432         i = vd->scope_next;
30433     }
30434     is_arg_scope = (i == ARG_SCOPE_END);
30435     if (!is_arg_scope) {
30436         /* Add argument variables */
30437         for(i = 0; i < b->arg_count; i++) {
30438             JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
30439             vd = &b->vardefs[i];
30440             cv->is_local = TRUE;
30441             cv->is_arg = TRUE;
30442             cv->is_const = FALSE;
30443             cv->is_lexical = FALSE;
30444             cv->var_kind = JS_VAR_NORMAL;
30445             cv->var_idx = i;
30446             cv->var_name = JS_DupAtom(ctx, vd->var_name);
30447         }
30448         /* Add local non lexical variables */
30449         for(i = 0; i < b->var_count; i++) {
30450             vd = &b->vardefs[b->arg_count + i];
30451             if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) {
30452                 JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
30453                 set_closure_from_var(ctx, cv, vd, i);
30454             }
30455         }
30456     } else {
30457         /* only add pseudo variables */
30458         for(i = 0; i < b->var_count; i++) {
30459             vd = &b->vardefs[b->arg_count + i];
30460             if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {
30461                 JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
30462                 set_closure_from_var(ctx, cv, vd, i);
30463             }
30464         }
30465     }
30466     for(i = 0; i < b->closure_var_count; i++) {
30467         JSClosureVar *cv0 = &b->closure_var[i];
30468         JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
30469         cv->is_local = FALSE;
30470         cv->is_arg = cv0->is_arg;
30471         cv->is_const = cv0->is_const;
30472         cv->is_lexical = cv0->is_lexical;
30473         cv->var_kind = cv0->var_kind;
30474         cv->var_idx = i;
30475         cv->var_name = JS_DupAtom(ctx, cv0->var_name);
30476     }
30477     return 0;
30478 }
30479 
30480 typedef struct CodeContext {
30481     const uint8_t *bc_buf; /* code buffer */
30482     int bc_len;   /* length of the code buffer */
30483     int pos;      /* position past the matched code pattern */
30484     int line_num; /* last visited OP_line_num parameter or -1 */
30485     int op;
30486     int idx;
30487     int label;
30488     int val;
30489     JSAtom atom;
30490 } CodeContext;
30491 
30492 #define M2(op1, op2)            ((op1) | ((op2) << 8))
30493 #define M3(op1, op2, op3)       ((op1) | ((op2) << 8) | ((op3) << 16))
30494 #define M4(op1, op2, op3, op4)  ((op1) | ((op2) << 8) | ((op3) << 16) | ((op4) << 24))
30495 
code_match(CodeContext * s,int pos,...)30496 static BOOL code_match(CodeContext *s, int pos, ...)
30497 {
30498     const uint8_t *tab = s->bc_buf;
30499     int op, len, op1, line_num, pos_next;
30500     va_list ap;
30501     BOOL ret = FALSE;
30502 
30503     line_num = -1;
30504     va_start(ap, pos);
30505 
30506     for(;;) {
30507         op1 = va_arg(ap, int);
30508         if (op1 == -1) {
30509             s->pos = pos;
30510             s->line_num = line_num;
30511             ret = TRUE;
30512             break;
30513         }
30514         for (;;) {
30515             if (pos >= s->bc_len)
30516                 goto done;
30517             op = tab[pos];
30518             len = opcode_info[op].size;
30519             pos_next = pos + len;
30520             if (pos_next > s->bc_len)
30521                 goto done;
30522             if (op == OP_line_num) {
30523                 line_num = get_u32(tab + pos + 1);
30524                 pos = pos_next;
30525             } else {
30526                 break;
30527             }
30528         }
30529         if (op != op1) {
30530             if (op1 == (uint8_t)op1 || !op)
30531                 break;
30532             if (op != (uint8_t)op1
30533             &&  op != (uint8_t)(op1 >> 8)
30534             &&  op != (uint8_t)(op1 >> 16)
30535             &&  op != (uint8_t)(op1 >> 24)) {
30536                 break;
30537             }
30538             s->op = op;
30539         }
30540 
30541         pos++;
30542         switch(opcode_info[op].fmt) {
30543         case OP_FMT_loc8:
30544         case OP_FMT_u8:
30545             {
30546                 int idx = tab[pos];
30547                 int arg = va_arg(ap, int);
30548                 if (arg == -1) {
30549                     s->idx = idx;
30550                 } else {
30551                     if (arg != idx)
30552                         goto done;
30553                 }
30554                 break;
30555             }
30556         case OP_FMT_u16:
30557         case OP_FMT_npop:
30558         case OP_FMT_loc:
30559         case OP_FMT_arg:
30560         case OP_FMT_var_ref:
30561             {
30562                 int idx = get_u16(tab + pos);
30563                 int arg = va_arg(ap, int);
30564                 if (arg == -1) {
30565                     s->idx = idx;
30566                 } else {
30567                     if (arg != idx)
30568                         goto done;
30569                 }
30570                 break;
30571             }
30572         case OP_FMT_i32:
30573         case OP_FMT_u32:
30574         case OP_FMT_label:
30575         case OP_FMT_const:
30576             {
30577                 s->label = get_u32(tab + pos);
30578                 break;
30579             }
30580         case OP_FMT_label_u16:
30581             {
30582                 s->label = get_u32(tab + pos);
30583                 s->val = get_u16(tab + pos + 4);
30584                 break;
30585             }
30586         case OP_FMT_atom:
30587             {
30588                 s->atom = get_u32(tab + pos);
30589                 break;
30590             }
30591         case OP_FMT_atom_u8:
30592             {
30593                 s->atom = get_u32(tab + pos);
30594                 s->val = get_u8(tab + pos + 4);
30595                 break;
30596             }
30597         case OP_FMT_atom_u16:
30598             {
30599                 s->atom = get_u32(tab + pos);
30600                 s->val = get_u16(tab + pos + 4);
30601                 break;
30602             }
30603         case OP_FMT_atom_label_u8:
30604             {
30605                 s->atom = get_u32(tab + pos);
30606                 s->label = get_u32(tab + pos + 4);
30607                 s->val = get_u8(tab + pos + 8);
30608                 break;
30609             }
30610         default:
30611             break;
30612         }
30613         pos = pos_next;
30614     }
30615  done:
30616     va_end(ap);
30617     return ret;
30618 }
30619 
instantiate_hoisted_definitions(JSContext * ctx,JSFunctionDef * s,DynBuf * bc)30620 static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc)
30621 {
30622     int i, idx, label_next = -1;
30623 
30624     /* add the hoisted functions in arguments and local variables */
30625     for(i = 0; i < s->arg_count; i++) {
30626         JSVarDef *vd = &s->args[i];
30627         if (vd->func_pool_idx >= 0) {
30628             dbuf_putc(bc, OP_fclosure);
30629             dbuf_put_u32(bc, vd->func_pool_idx);
30630             dbuf_putc(bc, OP_put_arg);
30631             dbuf_put_u16(bc, i);
30632         }
30633     }
30634     for(i = 0; i < s->var_count; i++) {
30635         JSVarDef *vd = &s->vars[i];
30636         if (vd->scope_level == 0 && vd->func_pool_idx >= 0) {
30637             dbuf_putc(bc, OP_fclosure);
30638             dbuf_put_u32(bc, vd->func_pool_idx);
30639             dbuf_putc(bc, OP_put_loc);
30640             dbuf_put_u16(bc, i);
30641         }
30642     }
30643 
30644     /* the module global variables must be initialized before
30645        evaluating the module so that the exported functions are
30646        visible if there are cyclic module references */
30647     if (s->module) {
30648         label_next = new_label_fd(s, -1);
30649 
30650         /* if 'this' is true, initialize the global variables and return */
30651         dbuf_putc(bc, OP_push_this);
30652         dbuf_putc(bc, OP_if_false);
30653         dbuf_put_u32(bc, label_next);
30654         update_label(s, label_next, 1);
30655         s->jump_size++;
30656     }
30657 
30658     /* add the global variables (only happens if s->is_global_var is
30659        true) */
30660     for(i = 0; i < s->global_var_count; i++) {
30661         JSGlobalVar *hf = &s->global_vars[i];
30662         int has_closure = 0;
30663         BOOL force_init = hf->force_init;
30664         /* we are in an eval, so the closure contains all the
30665            enclosing variables */
30666         /* If the outer function has a variable environment,
30667            create a property for the variable there */
30668         for(idx = 0; idx < s->closure_var_count; idx++) {
30669             JSClosureVar *cv = &s->closure_var[idx];
30670             if (cv->var_name == hf->var_name) {
30671                 has_closure = 2;
30672                 force_init = FALSE;
30673                 break;
30674             }
30675             if (cv->var_name == JS_ATOM__var_ ||
30676                 cv->var_name == JS_ATOM__arg_var_) {
30677                 dbuf_putc(bc, OP_get_var_ref);
30678                 dbuf_put_u16(bc, idx);
30679                 has_closure = 1;
30680                 force_init = TRUE;
30681                 break;
30682             }
30683         }
30684         if (!has_closure) {
30685             int flags;
30686 
30687             flags = 0;
30688             if (s->eval_type != JS_EVAL_TYPE_GLOBAL)
30689                 flags |= JS_PROP_CONFIGURABLE;
30690             if (hf->cpool_idx >= 0 && !hf->is_lexical) {
30691                 /* global function definitions need a specific handling */
30692                 dbuf_putc(bc, OP_fclosure);
30693                 dbuf_put_u32(bc, hf->cpool_idx);
30694 
30695                 dbuf_putc(bc, OP_define_func);
30696                 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
30697                 dbuf_putc(bc, flags);
30698 
30699                 goto done_global_var;
30700             } else {
30701                 if (hf->is_lexical) {
30702                     flags |= DEFINE_GLOBAL_LEX_VAR;
30703                     if (!hf->is_const)
30704                         flags |= JS_PROP_WRITABLE;
30705                 }
30706                 dbuf_putc(bc, OP_define_var);
30707                 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
30708                 dbuf_putc(bc, flags);
30709             }
30710         }
30711         if (hf->cpool_idx >= 0 || force_init) {
30712             if (hf->cpool_idx >= 0) {
30713                 dbuf_putc(bc, OP_fclosure);
30714                 dbuf_put_u32(bc, hf->cpool_idx);
30715                 if (hf->var_name == JS_ATOM__default_) {
30716                     /* set default export function name */
30717                     dbuf_putc(bc, OP_set_name);
30718                     dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default));
30719                 }
30720             } else {
30721                 dbuf_putc(bc, OP_undefined);
30722             }
30723             if (has_closure == 2) {
30724                 dbuf_putc(bc, OP_put_var_ref);
30725                 dbuf_put_u16(bc, idx);
30726             } else if (has_closure == 1) {
30727                 dbuf_putc(bc, OP_define_field);
30728                 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
30729                 dbuf_putc(bc, OP_drop);
30730             } else {
30731                 /* XXX: Check if variable is writable and enumerable */
30732                 dbuf_putc(bc, OP_put_var);
30733                 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
30734             }
30735         }
30736     done_global_var:
30737         JS_FreeAtom(ctx, hf->var_name);
30738     }
30739 
30740     if (s->module) {
30741         dbuf_putc(bc, OP_return_undef);
30742 
30743         dbuf_putc(bc, OP_label);
30744         dbuf_put_u32(bc, label_next);
30745         s->label_slots[label_next].pos2 = bc->size;
30746     }
30747 
30748     js_free(ctx, s->global_vars);
30749     s->global_vars = NULL;
30750     s->global_var_count = 0;
30751     s->global_var_size = 0;
30752 }
30753 
skip_dead_code(JSFunctionDef * s,const uint8_t * bc_buf,int bc_len,int pos,int * linep)30754 static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len,
30755                           int pos, int *linep)
30756 {
30757     int op, len, label;
30758 
30759     for (; pos < bc_len; pos += len) {
30760         op = bc_buf[pos];
30761         len = opcode_info[op].size;
30762         if (op == OP_line_num) {
30763             *linep = get_u32(bc_buf + pos + 1);
30764         } else
30765         if (op == OP_label) {
30766             label = get_u32(bc_buf + pos + 1);
30767             if (update_label(s, label, 0) > 0)
30768                 break;
30769 #if 0
30770             if (s->label_slots[label].first_reloc) {
30771                 printf("line %d: unreferenced label %d:%d has relocations\n",
30772                        *linep, label, s->label_slots[label].pos2);
30773             }
30774 #endif
30775             assert(s->label_slots[label].first_reloc == NULL);
30776         } else {
30777             /* XXX: output a warning for unreachable code? */
30778             JSAtom atom;
30779             switch(opcode_info[op].fmt) {
30780             case OP_FMT_label:
30781             case OP_FMT_label_u16:
30782                 label = get_u32(bc_buf + pos + 1);
30783                 update_label(s, label, -1);
30784                 break;
30785             case OP_FMT_atom_label_u8:
30786             case OP_FMT_atom_label_u16:
30787                 label = get_u32(bc_buf + pos + 5);
30788                 update_label(s, label, -1);
30789                 /* fall thru */
30790             case OP_FMT_atom:
30791             case OP_FMT_atom_u8:
30792             case OP_FMT_atom_u16:
30793                 atom = get_u32(bc_buf + pos + 1);
30794                 JS_FreeAtom(s->ctx, atom);
30795                 break;
30796             default:
30797                 break;
30798             }
30799         }
30800     }
30801     return pos;
30802 }
30803 
get_label_pos(JSFunctionDef * s,int label)30804 static int get_label_pos(JSFunctionDef *s, int label)
30805 {
30806     int i, pos;
30807     for (i = 0; i < 20; i++) {
30808         pos = s->label_slots[label].pos;
30809         for (;;) {
30810             switch (s->byte_code.buf[pos]) {
30811             case OP_line_num:
30812             case OP_label:
30813                 pos += 5;
30814                 continue;
30815             case OP_goto:
30816                 label = get_u32(s->byte_code.buf + pos + 1);
30817                 break;
30818             default:
30819                 return pos;
30820             }
30821             break;
30822         }
30823     }
30824     return pos;
30825 }
30826 
30827 /* convert global variable accesses to local variables or closure
30828    variables when necessary */
resolve_variables(JSContext * ctx,JSFunctionDef * s)30829 static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
30830 {
30831     int pos, pos_next, bc_len, op, len, i, idx, line_num;
30832     uint8_t *bc_buf;
30833     JSAtom var_name;
30834     DynBuf bc_out;
30835     CodeContext cc;
30836     int scope;
30837 
30838     cc.bc_buf = bc_buf = s->byte_code.buf;
30839     cc.bc_len = bc_len = s->byte_code.size;
30840     js_dbuf_init(ctx, &bc_out);
30841 
30842     /* first pass for runtime checks (must be done before the
30843        variables are created) */
30844     for(i = 0; i < s->global_var_count; i++) {
30845         JSGlobalVar *hf = &s->global_vars[i];
30846         int flags;
30847 
30848         /* check if global variable (XXX: simplify) */
30849         for(idx = 0; idx < s->closure_var_count; idx++) {
30850             JSClosureVar *cv = &s->closure_var[idx];
30851             if (cv->var_name == hf->var_name) {
30852                 if (s->eval_type == JS_EVAL_TYPE_DIRECT &&
30853                     cv->is_lexical) {
30854                     /* Check if a lexical variable is
30855                        redefined as 'var'. XXX: Could abort
30856                        compilation here, but for consistency
30857                        with the other checks, we delay the
30858                        error generation. */
30859                     dbuf_putc(&bc_out, OP_throw_error);
30860                     dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
30861                     dbuf_putc(&bc_out, JS_THROW_VAR_REDECL);
30862                 }
30863                 goto next;
30864             }
30865             if (cv->var_name == JS_ATOM__var_ ||
30866                 cv->var_name == JS_ATOM__arg_var_)
30867                 goto next;
30868         }
30869 
30870         dbuf_putc(&bc_out, OP_check_define_var);
30871         dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
30872         flags = 0;
30873         if (hf->is_lexical)
30874             flags |= DEFINE_GLOBAL_LEX_VAR;
30875         if (hf->cpool_idx >= 0)
30876             flags |= DEFINE_GLOBAL_FUNC_VAR;
30877         dbuf_putc(&bc_out, flags);
30878     next: ;
30879     }
30880 
30881     line_num = 0; /* avoid warning */
30882     for (pos = 0; pos < bc_len; pos = pos_next) {
30883         op = bc_buf[pos];
30884         len = opcode_info[op].size;
30885         pos_next = pos + len;
30886         switch(op) {
30887         case OP_line_num:
30888             line_num = get_u32(bc_buf + pos + 1);
30889             s->line_number_size++;
30890             goto no_change;
30891 
30892         case OP_eval: /* convert scope index to adjusted variable index */
30893             {
30894                 int call_argc = get_u16(bc_buf + pos + 1);
30895                 scope = get_u16(bc_buf + pos + 1 + 2);
30896                 mark_eval_captured_variables(ctx, s, scope);
30897                 dbuf_putc(&bc_out, op);
30898                 dbuf_put_u16(&bc_out, call_argc);
30899                 dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);
30900             }
30901             break;
30902         case OP_apply_eval: /* convert scope index to adjusted variable index */
30903             scope = get_u16(bc_buf + pos + 1);
30904             mark_eval_captured_variables(ctx, s, scope);
30905             dbuf_putc(&bc_out, op);
30906             dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);
30907             break;
30908         case OP_scope_get_var_undef:
30909         case OP_scope_get_var:
30910         case OP_scope_put_var:
30911         case OP_scope_delete_var:
30912         case OP_scope_get_ref:
30913         case OP_scope_put_var_init:
30914             var_name = get_u32(bc_buf + pos + 1);
30915             scope = get_u16(bc_buf + pos + 5);
30916             pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
30917                                          NULL, NULL, pos_next);
30918             JS_FreeAtom(ctx, var_name);
30919             break;
30920         case OP_scope_make_ref:
30921             {
30922                 int label;
30923                 LabelSlot *ls;
30924                 var_name = get_u32(bc_buf + pos + 1);
30925                 label = get_u32(bc_buf + pos + 5);
30926                 scope = get_u16(bc_buf + pos + 9);
30927                 ls = &s->label_slots[label];
30928                 ls->ref_count--;  /* always remove label reference */
30929                 pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
30930                                              bc_buf, ls, pos_next);
30931                 JS_FreeAtom(ctx, var_name);
30932             }
30933             break;
30934         case OP_scope_get_private_field:
30935         case OP_scope_get_private_field2:
30936         case OP_scope_put_private_field:
30937             {
30938                 int ret;
30939                 var_name = get_u32(bc_buf + pos + 1);
30940                 scope = get_u16(bc_buf + pos + 5);
30941                 ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out);
30942                 if (ret < 0)
30943                     goto fail;
30944                 JS_FreeAtom(ctx, var_name);
30945             }
30946             break;
30947         case OP_gosub:
30948             s->jump_size++;
30949             if (OPTIMIZE) {
30950                 /* remove calls to empty finalizers  */
30951                 int label;
30952                 LabelSlot *ls;
30953 
30954                 label = get_u32(bc_buf + pos + 1);
30955                 assert(label >= 0 && label < s->label_count);
30956                 ls = &s->label_slots[label];
30957                 if (code_match(&cc, ls->pos, OP_ret, -1)) {
30958                     ls->ref_count--;
30959                     break;
30960                 }
30961             }
30962             goto no_change;
30963         case OP_drop:
30964             if (0) {
30965                 /* remove drops before return_undef */
30966                 /* do not perform this optimization in pass2 because
30967                    it breaks patterns recognised in resolve_labels */
30968                 int pos1 = pos_next;
30969                 int line1 = line_num;
30970                 while (code_match(&cc, pos1, OP_drop, -1)) {
30971                     if (cc.line_num >= 0) line1 = cc.line_num;
30972                     pos1 = cc.pos;
30973                 }
30974                 if (code_match(&cc, pos1, OP_return_undef, -1)) {
30975                     pos_next = pos1;
30976                     if (line1 != -1 && line1 != line_num) {
30977                         line_num = line1;
30978                         s->line_number_size++;
30979                         dbuf_putc(&bc_out, OP_line_num);
30980                         dbuf_put_u32(&bc_out, line_num);
30981                     }
30982                     break;
30983                 }
30984             }
30985             goto no_change;
30986         case OP_insert3:
30987             if (OPTIMIZE) {
30988                 /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */
30989                 if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) {
30990                     dbuf_putc(&bc_out, cc.op);
30991                     pos_next = cc.pos;
30992                     if (cc.line_num != -1 && cc.line_num != line_num) {
30993                         line_num = cc.line_num;
30994                         s->line_number_size++;
30995                         dbuf_putc(&bc_out, OP_line_num);
30996                         dbuf_put_u32(&bc_out, line_num);
30997                     }
30998                     break;
30999                 }
31000             }
31001             goto no_change;
31002 
31003         case OP_goto:
31004             s->jump_size++;
31005             /* fall thru */
31006         case OP_tail_call:
31007         case OP_tail_call_method:
31008         case OP_return:
31009         case OP_return_undef:
31010         case OP_throw:
31011         case OP_throw_error:
31012         case OP_ret:
31013             if (OPTIMIZE) {
31014                 /* remove dead code */
31015                 int line = -1;
31016                 dbuf_put(&bc_out, bc_buf + pos, len);
31017                 pos = skip_dead_code(s, bc_buf, bc_len, pos + len, &line);
31018                 pos_next = pos;
31019                 if (pos < bc_len && line >= 0 && line_num != line) {
31020                     line_num = line;
31021                     s->line_number_size++;
31022                     dbuf_putc(&bc_out, OP_line_num);
31023                     dbuf_put_u32(&bc_out, line_num);
31024                 }
31025                 break;
31026             }
31027             goto no_change;
31028 
31029         case OP_label:
31030             {
31031                 int label;
31032                 LabelSlot *ls;
31033 
31034                 label = get_u32(bc_buf + pos + 1);
31035                 assert(label >= 0 && label < s->label_count);
31036                 ls = &s->label_slots[label];
31037                 ls->pos2 = bc_out.size + opcode_info[op].size;
31038             }
31039             goto no_change;
31040 
31041         case OP_enter_scope:
31042             {
31043                 int scope_idx, scope = get_u16(bc_buf + pos + 1);
31044 
31045                 if (scope == s->body_scope) {
31046                     instantiate_hoisted_definitions(ctx, s, &bc_out);
31047                 }
31048 
31049                 for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
31050                     JSVarDef *vd = &s->vars[scope_idx];
31051                     if (vd->scope_level == scope) {
31052                         if (scope_idx != s->arguments_arg_idx) {
31053                             if (vd->var_kind == JS_VAR_FUNCTION_DECL ||
31054                                 vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) {
31055                                 /* Initialize lexical variable upon entering scope */
31056                                 dbuf_putc(&bc_out, OP_fclosure);
31057                                 dbuf_put_u32(&bc_out, vd->func_pool_idx);
31058                                 dbuf_putc(&bc_out, OP_put_loc);
31059                                 dbuf_put_u16(&bc_out, scope_idx);
31060                             } else {
31061                                 /* XXX: should check if variable can be used
31062                                    before initialization */
31063                                 dbuf_putc(&bc_out, OP_set_loc_uninitialized);
31064                                 dbuf_put_u16(&bc_out, scope_idx);
31065                             }
31066                         }
31067                         scope_idx = vd->scope_next;
31068                     } else {
31069                         break;
31070                     }
31071                 }
31072             }
31073             break;
31074 
31075         case OP_leave_scope:
31076             {
31077                 int scope_idx, scope = get_u16(bc_buf + pos + 1);
31078 
31079                 for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
31080                     JSVarDef *vd = &s->vars[scope_idx];
31081                     if (vd->scope_level == scope) {
31082                         if (vd->is_captured) {
31083                             dbuf_putc(&bc_out, OP_close_loc);
31084                             dbuf_put_u16(&bc_out, scope_idx);
31085                         }
31086                         scope_idx = vd->scope_next;
31087                     } else {
31088                         break;
31089                     }
31090                 }
31091             }
31092             break;
31093 
31094         case OP_set_name:
31095             {
31096                 /* remove dummy set_name opcodes */
31097                 JSAtom name = get_u32(bc_buf + pos + 1);
31098                 if (name == JS_ATOM_NULL)
31099                     break;
31100             }
31101             goto no_change;
31102 
31103         case OP_if_false:
31104         case OP_if_true:
31105         case OP_catch:
31106             s->jump_size++;
31107             goto no_change;
31108 
31109         case OP_dup:
31110             if (OPTIMIZE) {
31111                 /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */
31112                 /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */
31113                 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) {
31114                     int lab0, lab1, op1, pos1, line1, pos2;
31115                     lab0 = lab1 = cc.label;
31116                     assert(lab1 >= 0 && lab1 < s->label_count);
31117                     op1 = cc.op;
31118                     pos1 = cc.pos;
31119                     line1 = cc.line_num;
31120                     while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) {
31121                         lab1 = cc.label;
31122                     }
31123                     if (code_match(&cc, pos2, op1, -1)) {
31124                         s->jump_size++;
31125                         update_label(s, lab0, -1);
31126                         update_label(s, cc.label, +1);
31127                         dbuf_putc(&bc_out, op1);
31128                         dbuf_put_u32(&bc_out, cc.label);
31129                         pos_next = pos1;
31130                         if (line1 != -1 && line1 != line_num) {
31131                             line_num = line1;
31132                             s->line_number_size++;
31133                             dbuf_putc(&bc_out, OP_line_num);
31134                             dbuf_put_u32(&bc_out, line_num);
31135                         }
31136                         break;
31137                     }
31138                 }
31139             }
31140             goto no_change;
31141 
31142         case OP_nop:
31143             /* remove erased code */
31144             break;
31145         case OP_set_class_name:
31146             /* only used during parsing */
31147             break;
31148 
31149         default:
31150         no_change:
31151             dbuf_put(&bc_out, bc_buf + pos, len);
31152             break;
31153         }
31154     }
31155 
31156     /* set the new byte code */
31157     dbuf_free(&s->byte_code);
31158     s->byte_code = bc_out;
31159     if (dbuf_error(&s->byte_code)) {
31160         JS_ThrowOutOfMemory(ctx);
31161         return -1;
31162     }
31163     return 0;
31164  fail:
31165     /* continue the copy to keep the atom refcounts consistent */
31166     /* XXX: find a better solution ? */
31167     for (; pos < bc_len; pos = pos_next) {
31168         op = bc_buf[pos];
31169         len = opcode_info[op].size;
31170         pos_next = pos + len;
31171         dbuf_put(&bc_out, bc_buf + pos, len);
31172     }
31173     dbuf_free(&s->byte_code);
31174     s->byte_code = bc_out;
31175     return -1;
31176 }
31177 
31178 /* the pc2line table gives a line number for each PC value */
add_pc2line_info(JSFunctionDef * s,uint32_t pc,int line_num)31179 static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, int line_num)
31180 {
31181     if (s->line_number_slots != NULL
31182     &&  s->line_number_count < s->line_number_size
31183     &&  pc >= s->line_number_last_pc
31184     &&  line_num != s->line_number_last) {
31185         s->line_number_slots[s->line_number_count].pc = pc;
31186         s->line_number_slots[s->line_number_count].line_num = line_num;
31187         s->line_number_count++;
31188         s->line_number_last_pc = pc;
31189         s->line_number_last = line_num;
31190     }
31191 }
31192 
compute_pc2line_info(JSFunctionDef * s)31193 static void compute_pc2line_info(JSFunctionDef *s)
31194 {
31195     if (!(s->js_mode & JS_MODE_STRIP) && s->line_number_slots) {
31196         int last_line_num = s->line_num;
31197         uint32_t last_pc = 0;
31198         int i;
31199 
31200         js_dbuf_init(s->ctx, &s->pc2line);
31201         for (i = 0; i < s->line_number_count; i++) {
31202             uint32_t pc = s->line_number_slots[i].pc;
31203             int line_num = s->line_number_slots[i].line_num;
31204             int diff_pc, diff_line;
31205 
31206             if (line_num < 0)
31207                 continue;
31208 
31209             diff_pc = pc - last_pc;
31210             diff_line = line_num - last_line_num;
31211             if (diff_line == 0 || diff_pc < 0)
31212                 continue;
31213 
31214             if (diff_line >= PC2LINE_BASE &&
31215                 diff_line < PC2LINE_BASE + PC2LINE_RANGE &&
31216                 diff_pc <= PC2LINE_DIFF_PC_MAX) {
31217                 dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) +
31218                           diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST);
31219             } else {
31220                 /* longer encoding */
31221                 dbuf_putc(&s->pc2line, 0);
31222                 dbuf_put_leb128(&s->pc2line, diff_pc);
31223                 dbuf_put_sleb128(&s->pc2line, diff_line);
31224             }
31225             last_pc = pc;
31226             last_line_num = line_num;
31227         }
31228     }
31229 }
31230 
add_reloc(JSContext * ctx,LabelSlot * ls,uint32_t addr,int size)31231 static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size)
31232 {
31233     RelocEntry *re;
31234     re = js_malloc(ctx, sizeof(*re));
31235     if (!re)
31236         return NULL;
31237     re->addr = addr;
31238     re->size = size;
31239     re->next = ls->first_reloc;
31240     ls->first_reloc = re;
31241     return re;
31242 }
31243 
code_has_label(CodeContext * s,int pos,int label)31244 static BOOL code_has_label(CodeContext *s, int pos, int label)
31245 {
31246     while (pos < s->bc_len) {
31247         int op = s->bc_buf[pos];
31248         if (op == OP_line_num) {
31249             pos += 5;
31250             continue;
31251         }
31252         if (op == OP_label) {
31253             int lab = get_u32(s->bc_buf + pos + 1);
31254             if (lab == label)
31255                 return TRUE;
31256             pos += 5;
31257             continue;
31258         }
31259         if (op == OP_goto) {
31260             int lab = get_u32(s->bc_buf + pos + 1);
31261             if (lab == label)
31262                 return TRUE;
31263         }
31264         break;
31265     }
31266     return FALSE;
31267 }
31268 
31269 /* return the target label, following the OP_goto jumps
31270    the first opcode at destination is stored in *pop
31271  */
find_jump_target(JSFunctionDef * s,int label,int * pop,int * pline)31272 static int find_jump_target(JSFunctionDef *s, int label, int *pop, int *pline)
31273 {
31274     int i, pos, op;
31275 
31276     update_label(s, label, -1);
31277     for (i = 0; i < 10; i++) {
31278         assert(label >= 0 && label < s->label_count);
31279         pos = s->label_slots[label].pos2;
31280         for (;;) {
31281             switch(op = s->byte_code.buf[pos]) {
31282             case OP_line_num:
31283                 if (pline)
31284                     *pline = get_u32(s->byte_code.buf + pos + 1);
31285                 /* fall thru */
31286             case OP_label:
31287                 pos += opcode_info[op].size;
31288                 continue;
31289             case OP_goto:
31290                 label = get_u32(s->byte_code.buf + pos + 1);
31291                 break;
31292             case OP_drop:
31293                 /* ignore drop opcodes if followed by OP_return_undef */
31294                 while (s->byte_code.buf[++pos] == OP_drop)
31295                     continue;
31296                 if (s->byte_code.buf[pos] == OP_return_undef)
31297                     op = OP_return_undef;
31298                 /* fall thru */
31299             default:
31300                 goto done;
31301             }
31302             break;
31303         }
31304     }
31305     /* cycle detected, could issue a warning */
31306  done:
31307     *pop = op;
31308     update_label(s, label, +1);
31309     return label;
31310 }
31311 
push_short_int(DynBuf * bc_out,int val)31312 static void push_short_int(DynBuf *bc_out, int val)
31313 {
31314 #if SHORT_OPCODES
31315     if (val >= -1 && val <= 7) {
31316         dbuf_putc(bc_out, OP_push_0 + val);
31317         return;
31318     }
31319     if (val == (int8_t)val) {
31320         dbuf_putc(bc_out, OP_push_i8);
31321         dbuf_putc(bc_out, val);
31322         return;
31323     }
31324     if (val == (int16_t)val) {
31325         dbuf_putc(bc_out, OP_push_i16);
31326         dbuf_put_u16(bc_out, val);
31327         return;
31328     }
31329 #endif
31330     dbuf_putc(bc_out, OP_push_i32);
31331     dbuf_put_u32(bc_out, val);
31332 }
31333 
put_short_code(DynBuf * bc_out,int op,int idx)31334 static void put_short_code(DynBuf *bc_out, int op, int idx)
31335 {
31336 #if SHORT_OPCODES
31337     if (idx < 4) {
31338         switch (op) {
31339         case OP_get_loc:
31340             dbuf_putc(bc_out, OP_get_loc0 + idx);
31341             return;
31342         case OP_put_loc:
31343             dbuf_putc(bc_out, OP_put_loc0 + idx);
31344             return;
31345         case OP_set_loc:
31346             dbuf_putc(bc_out, OP_set_loc0 + idx);
31347             return;
31348         case OP_get_arg:
31349             dbuf_putc(bc_out, OP_get_arg0 + idx);
31350             return;
31351         case OP_put_arg:
31352             dbuf_putc(bc_out, OP_put_arg0 + idx);
31353             return;
31354         case OP_set_arg:
31355             dbuf_putc(bc_out, OP_set_arg0 + idx);
31356             return;
31357         case OP_get_var_ref:
31358             dbuf_putc(bc_out, OP_get_var_ref0 + idx);
31359             return;
31360         case OP_put_var_ref:
31361             dbuf_putc(bc_out, OP_put_var_ref0 + idx);
31362             return;
31363         case OP_set_var_ref:
31364             dbuf_putc(bc_out, OP_set_var_ref0 + idx);
31365             return;
31366         case OP_call:
31367             dbuf_putc(bc_out, OP_call0 + idx);
31368             return;
31369         }
31370     }
31371     if (idx < 256) {
31372         switch (op) {
31373         case OP_get_loc:
31374             dbuf_putc(bc_out, OP_get_loc8);
31375             dbuf_putc(bc_out, idx);
31376             return;
31377         case OP_put_loc:
31378             dbuf_putc(bc_out, OP_put_loc8);
31379             dbuf_putc(bc_out, idx);
31380             return;
31381         case OP_set_loc:
31382             dbuf_putc(bc_out, OP_set_loc8);
31383             dbuf_putc(bc_out, idx);
31384             return;
31385         }
31386     }
31387 #endif
31388     dbuf_putc(bc_out, op);
31389     dbuf_put_u16(bc_out, idx);
31390 }
31391 
31392 /* peephole optimizations and resolve goto/labels */
resolve_labels(JSContext * ctx,JSFunctionDef * s)31393 static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
31394 {
31395     int pos, pos_next, bc_len, op, op1, len, i, line_num;
31396     const uint8_t *bc_buf;
31397     DynBuf bc_out;
31398     LabelSlot *label_slots, *ls;
31399     RelocEntry *re, *re_next;
31400     CodeContext cc;
31401     int label;
31402 #if SHORT_OPCODES
31403     JumpSlot *jp;
31404 #endif
31405 
31406     label_slots = s->label_slots;
31407 
31408     line_num = s->line_num;
31409 
31410     cc.bc_buf = bc_buf = s->byte_code.buf;
31411     cc.bc_len = bc_len = s->byte_code.size;
31412     js_dbuf_init(ctx, &bc_out);
31413 
31414 #if SHORT_OPCODES
31415     if (s->jump_size) {
31416         s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size);
31417         if (s->jump_slots == NULL)
31418             return -1;
31419     }
31420 #endif
31421     /* XXX: Should skip this phase if not generating SHORT_OPCODES */
31422     if (s->line_number_size && !(s->js_mode & JS_MODE_STRIP)) {
31423         s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size);
31424         if (s->line_number_slots == NULL)
31425             return -1;
31426         s->line_number_last = s->line_num;
31427         s->line_number_last_pc = 0;
31428     }
31429 
31430     /* initialize the 'home_object' variable if needed */
31431     if (s->home_object_var_idx >= 0) {
31432         dbuf_putc(&bc_out, OP_special_object);
31433         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT);
31434         put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx);
31435     }
31436     /* initialize the 'this.active_func' variable if needed */
31437     if (s->this_active_func_var_idx >= 0) {
31438         dbuf_putc(&bc_out, OP_special_object);
31439         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
31440         put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx);
31441     }
31442     /* initialize the 'new.target' variable if needed */
31443     if (s->new_target_var_idx >= 0) {
31444         dbuf_putc(&bc_out, OP_special_object);
31445         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET);
31446         put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx);
31447     }
31448     /* initialize the 'this' variable if needed. In a derived class
31449        constructor, this is initially uninitialized. */
31450     if (s->this_var_idx >= 0) {
31451         if (s->is_derived_class_constructor) {
31452             dbuf_putc(&bc_out, OP_set_loc_uninitialized);
31453             dbuf_put_u16(&bc_out, s->this_var_idx);
31454         } else {
31455             dbuf_putc(&bc_out, OP_push_this);
31456             put_short_code(&bc_out, OP_put_loc, s->this_var_idx);
31457         }
31458     }
31459     /* initialize the 'arguments' variable if needed */
31460     if (s->arguments_var_idx >= 0) {
31461         if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) {
31462             dbuf_putc(&bc_out, OP_special_object);
31463             dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS);
31464         } else {
31465             dbuf_putc(&bc_out, OP_special_object);
31466             dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS);
31467         }
31468         if (s->arguments_arg_idx >= 0)
31469             put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx);
31470         put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx);
31471     }
31472     /* initialize a reference to the current function if needed */
31473     if (s->func_var_idx >= 0) {
31474         dbuf_putc(&bc_out, OP_special_object);
31475         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
31476         put_short_code(&bc_out, OP_put_loc, s->func_var_idx);
31477     }
31478     /* initialize the variable environment object if needed */
31479     if (s->var_object_idx >= 0) {
31480         dbuf_putc(&bc_out, OP_special_object);
31481         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
31482         put_short_code(&bc_out, OP_put_loc, s->var_object_idx);
31483     }
31484     if (s->arg_var_object_idx >= 0) {
31485         dbuf_putc(&bc_out, OP_special_object);
31486         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
31487         put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx);
31488     }
31489 
31490     for (pos = 0; pos < bc_len; pos = pos_next) {
31491         int val;
31492         op = bc_buf[pos];
31493         len = opcode_info[op].size;
31494         pos_next = pos + len;
31495         switch(op) {
31496         case OP_line_num:
31497             /* line number info (for debug). We put it in a separate
31498                compressed table to reduce memory usage and get better
31499                performance */
31500             line_num = get_u32(bc_buf + pos + 1);
31501             break;
31502 
31503         case OP_label:
31504             {
31505                 label = get_u32(bc_buf + pos + 1);
31506                 assert(label >= 0 && label < s->label_count);
31507                 ls = &label_slots[label];
31508                 assert(ls->addr == -1);
31509                 ls->addr = bc_out.size;
31510                 /* resolve the relocation entries */
31511                 for(re = ls->first_reloc; re != NULL; re = re_next) {
31512                     int diff = ls->addr - re->addr;
31513                     re_next = re->next;
31514                     switch (re->size) {
31515                     case 4:
31516                         put_u32(bc_out.buf + re->addr, diff);
31517                         break;
31518                     case 2:
31519                         assert(diff == (int16_t)diff);
31520                         put_u16(bc_out.buf + re->addr, diff);
31521                         break;
31522                     case 1:
31523                         assert(diff == (int8_t)diff);
31524                         put_u8(bc_out.buf + re->addr, diff);
31525                         break;
31526                     }
31527                     js_free(ctx, re);
31528                 }
31529                 ls->first_reloc = NULL;
31530             }
31531             break;
31532 
31533         case OP_call:
31534         case OP_call_method:
31535             {
31536                 /* detect and transform tail calls */
31537                 int argc;
31538                 argc = get_u16(bc_buf + pos + 1);
31539                 if (code_match(&cc, pos_next, OP_return, -1)) {
31540                     if (cc.line_num >= 0) line_num = cc.line_num;
31541                     add_pc2line_info(s, bc_out.size, line_num);
31542                     put_short_code(&bc_out, op + 1, argc);
31543                     pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num);
31544                     break;
31545                 }
31546                 add_pc2line_info(s, bc_out.size, line_num);
31547                 put_short_code(&bc_out, op, argc);
31548                 break;
31549             }
31550             goto no_change;
31551 
31552         case OP_return:
31553         case OP_return_undef:
31554         case OP_return_async:
31555         case OP_throw:
31556         case OP_throw_error:
31557             pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
31558             goto no_change;
31559 
31560         case OP_goto:
31561             label = get_u32(bc_buf + pos + 1);
31562         has_goto:
31563             if (OPTIMIZE) {
31564                 int line1 = -1;
31565                 /* Use custom matcher because multiple labels can follow */
31566                 label = find_jump_target(s, label, &op1, &line1);
31567                 if (code_has_label(&cc, pos_next, label)) {
31568                     /* jump to next instruction: remove jump */
31569                     update_label(s, label, -1);
31570                     break;
31571                 }
31572                 if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) {
31573                     /* jump to return/throw: remove jump, append return/throw */
31574                     /* updating the line number obfuscates assembly listing */
31575                     //if (line1 >= 0) line_num = line1;
31576                     update_label(s, label, -1);
31577                     add_pc2line_info(s, bc_out.size, line_num);
31578                     dbuf_putc(&bc_out, op1);
31579                     pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
31580                     break;
31581                 }
31582                 /* XXX: should duplicate single instructions followed by goto or return */
31583                 /* For example, can match one of these followed by return:
31584                    push_i32 / push_const / push_atom_value / get_var /
31585                    undefined / null / push_false / push_true / get_ref_value /
31586                    get_loc / get_arg / get_var_ref
31587                  */
31588             }
31589             goto has_label;
31590 
31591         case OP_gosub:
31592             label = get_u32(bc_buf + pos + 1);
31593             if (0 && OPTIMIZE) {
31594                 label = find_jump_target(s, label, &op1, NULL);
31595                 if (op1 == OP_ret) {
31596                     update_label(s, label, -1);
31597                     /* empty finally clause: remove gosub */
31598                     break;
31599                 }
31600             }
31601             goto has_label;
31602 
31603         case OP_catch:
31604             label = get_u32(bc_buf + pos + 1);
31605             goto has_label;
31606 
31607         case OP_if_true:
31608         case OP_if_false:
31609             label = get_u32(bc_buf + pos + 1);
31610             if (OPTIMIZE) {
31611                 label = find_jump_target(s, label, &op1, NULL);
31612                 /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */
31613                 if (code_has_label(&cc, pos_next, label)) {
31614                     update_label(s, label, -1);
31615                     dbuf_putc(&bc_out, OP_drop);
31616                     break;
31617                 }
31618                 /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */
31619                 if (code_match(&cc, pos_next, OP_goto, -1)) {
31620                     int pos1 = cc.pos;
31621                     int line1 = cc.line_num;
31622                     if (code_has_label(&cc, pos1, label)) {
31623                         if (line1 >= 0) line_num = line1;
31624                         pos_next = pos1;
31625                         update_label(s, label, -1);
31626                         label = cc.label;
31627                         op ^= OP_if_true ^ OP_if_false;
31628                     }
31629                 }
31630             }
31631         has_label:
31632             add_pc2line_info(s, bc_out.size, line_num);
31633             if (op == OP_goto) {
31634                 pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
31635             }
31636             assert(label >= 0 && label < s->label_count);
31637             ls = &label_slots[label];
31638 #if SHORT_OPCODES
31639             jp = &s->jump_slots[s->jump_count++];
31640             jp->op = op;
31641             jp->size = 4;
31642             jp->pos = bc_out.size + 1;
31643             jp->label = label;
31644 
31645             if (ls->addr == -1) {
31646                 int diff = ls->pos2 - pos - 1;
31647                 if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
31648                     jp->size = 1;
31649                     jp->op = OP_if_false8 + (op - OP_if_false);
31650                     dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
31651                     dbuf_putc(&bc_out, 0);
31652                     if (!add_reloc(ctx, ls, bc_out.size - 1, 1))
31653                         goto fail;
31654                     break;
31655                 }
31656                 if (diff < 32768 && op == OP_goto) {
31657                     jp->size = 2;
31658                     jp->op = OP_goto16;
31659                     dbuf_putc(&bc_out, OP_goto16);
31660                     dbuf_put_u16(&bc_out, 0);
31661                     if (!add_reloc(ctx, ls, bc_out.size - 2, 2))
31662                         goto fail;
31663                     break;
31664                 }
31665             } else {
31666                 int diff = ls->addr - bc_out.size - 1;
31667                 if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
31668                     jp->size = 1;
31669                     jp->op = OP_if_false8 + (op - OP_if_false);
31670                     dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
31671                     dbuf_putc(&bc_out, diff);
31672                     break;
31673                 }
31674                 if (diff == (int16_t)diff && op == OP_goto) {
31675                     jp->size = 2;
31676                     jp->op = OP_goto16;
31677                     dbuf_putc(&bc_out, OP_goto16);
31678                     dbuf_put_u16(&bc_out, diff);
31679                     break;
31680                 }
31681             }
31682 #endif
31683             dbuf_putc(&bc_out, op);
31684             dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
31685             if (ls->addr == -1) {
31686                 /* unresolved yet: create a new relocation entry */
31687                 if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
31688                     goto fail;
31689             }
31690             break;
31691         case OP_with_get_var:
31692         case OP_with_put_var:
31693         case OP_with_delete_var:
31694         case OP_with_make_ref:
31695         case OP_with_get_ref:
31696         case OP_with_get_ref_undef:
31697             {
31698                 JSAtom atom;
31699                 int is_with;
31700 
31701                 atom = get_u32(bc_buf + pos + 1);
31702                 label = get_u32(bc_buf + pos + 5);
31703                 is_with = bc_buf[pos + 9];
31704                 if (OPTIMIZE) {
31705                     label = find_jump_target(s, label, &op1, NULL);
31706                 }
31707                 assert(label >= 0 && label < s->label_count);
31708                 ls = &label_slots[label];
31709                 add_pc2line_info(s, bc_out.size, line_num);
31710 #if SHORT_OPCODES
31711                 jp = &s->jump_slots[s->jump_count++];
31712                 jp->op = op;
31713                 jp->size = 4;
31714                 jp->pos = bc_out.size + 5;
31715                 jp->label = label;
31716 #endif
31717                 dbuf_putc(&bc_out, op);
31718                 dbuf_put_u32(&bc_out, atom);
31719                 dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
31720                 if (ls->addr == -1) {
31721                     /* unresolved yet: create a new relocation entry */
31722                     if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
31723                         goto fail;
31724                 }
31725                 dbuf_putc(&bc_out, is_with);
31726             }
31727             break;
31728 
31729         case OP_drop:
31730             if (OPTIMIZE) {
31731                 /* remove useless drops before return */
31732                 if (code_match(&cc, pos_next, OP_return_undef, -1)) {
31733                     if (cc.line_num >= 0) line_num = cc.line_num;
31734                     break;
31735                 }
31736             }
31737             goto no_change;
31738 
31739         case OP_null:
31740 #if SHORT_OPCODES
31741             if (OPTIMIZE) {
31742                 /* transform null strict_eq into is_null */
31743                 if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
31744                     if (cc.line_num >= 0) line_num = cc.line_num;
31745                     add_pc2line_info(s, bc_out.size, line_num);
31746                     dbuf_putc(&bc_out, OP_is_null);
31747                     pos_next = cc.pos;
31748                     break;
31749                 }
31750                 /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */
31751                 if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
31752                     if (cc.line_num >= 0) line_num = cc.line_num;
31753                     add_pc2line_info(s, bc_out.size, line_num);
31754                     dbuf_putc(&bc_out, OP_is_null);
31755                     pos_next = cc.pos;
31756                     label = cc.label;
31757                     op = cc.op ^ OP_if_false ^ OP_if_true;
31758                     goto has_label;
31759                 }
31760             }
31761 #endif
31762             /* fall thru */
31763         case OP_push_false:
31764         case OP_push_true:
31765             if (OPTIMIZE) {
31766                 val = (op == OP_push_true);
31767                 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
31768                 has_constant_test:
31769                     if (cc.line_num >= 0) line_num = cc.line_num;
31770                     if (val == cc.op - OP_if_false) {
31771                         /* transform null if_false(l1) -> goto l1 */
31772                         /* transform false if_false(l1) -> goto l1 */
31773                         /* transform true if_true(l1) -> goto l1 */
31774                         pos_next = cc.pos;
31775                         op = OP_goto;
31776                         label = cc.label;
31777                         goto has_goto;
31778                     } else {
31779                         /* transform null if_true(l1) -> nop */
31780                         /* transform false if_true(l1) -> nop */
31781                         /* transform true if_false(l1) -> nop */
31782                         pos_next = cc.pos;
31783                         update_label(s, cc.label, -1);
31784                         break;
31785                     }
31786                 }
31787             }
31788             goto no_change;
31789 
31790         case OP_push_i32:
31791             if (OPTIMIZE) {
31792                 /* transform i32(val) neg -> i32(-val) */
31793                 val = get_i32(bc_buf + pos + 1);
31794                 if ((val != INT32_MIN && val != 0)
31795                 &&  code_match(&cc, pos_next, OP_neg, -1)) {
31796                     if (cc.line_num >= 0) line_num = cc.line_num;
31797                     if (code_match(&cc, cc.pos, OP_drop, -1)) {
31798                         if (cc.line_num >= 0) line_num = cc.line_num;
31799                     } else {
31800                         add_pc2line_info(s, bc_out.size, line_num);
31801                         push_short_int(&bc_out, -val);
31802                     }
31803                     pos_next = cc.pos;
31804                     break;
31805                 }
31806                 /* remove push/drop pairs generated by the parser */
31807                 if (code_match(&cc, pos_next, OP_drop, -1)) {
31808                     if (cc.line_num >= 0) line_num = cc.line_num;
31809                     pos_next = cc.pos;
31810                     break;
31811                 }
31812                 /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */
31813                 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
31814                     val = (val != 0);
31815                     goto has_constant_test;
31816                 }
31817                 add_pc2line_info(s, bc_out.size, line_num);
31818                 push_short_int(&bc_out, val);
31819                 break;
31820             }
31821             goto no_change;
31822 
31823 #if SHORT_OPCODES
31824         case OP_push_const:
31825         case OP_fclosure:
31826             if (OPTIMIZE) {
31827                 int idx = get_u32(bc_buf + pos + 1);
31828                 if (idx < 256) {
31829                     add_pc2line_info(s, bc_out.size, line_num);
31830                     dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const);
31831                     dbuf_putc(&bc_out, idx);
31832                     break;
31833                 }
31834             }
31835             goto no_change;
31836 
31837         case OP_get_field:
31838             if (OPTIMIZE) {
31839                 JSAtom atom = get_u32(bc_buf + pos + 1);
31840                 if (atom == JS_ATOM_length) {
31841                     JS_FreeAtom(ctx, atom);
31842                     add_pc2line_info(s, bc_out.size, line_num);
31843                     dbuf_putc(&bc_out, OP_get_length);
31844                     break;
31845                 }
31846             }
31847             goto no_change;
31848 #endif
31849         case OP_push_atom_value:
31850             if (OPTIMIZE) {
31851                 JSAtom atom = get_u32(bc_buf + pos + 1);
31852                 /* remove push/drop pairs generated by the parser */
31853                 if (code_match(&cc, pos_next, OP_drop, -1)) {
31854                     JS_FreeAtom(ctx, atom);
31855                     if (cc.line_num >= 0) line_num = cc.line_num;
31856                     pos_next = cc.pos;
31857                     break;
31858                 }
31859 #if SHORT_OPCODES
31860                 if (atom == JS_ATOM_empty_string) {
31861                     JS_FreeAtom(ctx, atom);
31862                     add_pc2line_info(s, bc_out.size, line_num);
31863                     dbuf_putc(&bc_out, OP_push_empty_string);
31864                     break;
31865                 }
31866 #endif
31867             }
31868             goto no_change;
31869 
31870         case OP_to_propkey:
31871         case OP_to_propkey2:
31872             if (OPTIMIZE) {
31873                 /* remove redundant to_propkey/to_propkey2 opcodes when storing simple data */
31874                 if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1)
31875                 ||  code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1)
31876                 ||  code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) {
31877                     break;
31878                 }
31879             }
31880             goto no_change;
31881 
31882         case OP_undefined:
31883             if (OPTIMIZE) {
31884                 /* remove push/drop pairs generated by the parser */
31885                 if (code_match(&cc, pos_next, OP_drop, -1)) {
31886                     if (cc.line_num >= 0) line_num = cc.line_num;
31887                     pos_next = cc.pos;
31888                     break;
31889                 }
31890                 /* transform undefined return -> return_undefined */
31891                 if (code_match(&cc, pos_next, OP_return, -1)) {
31892                     if (cc.line_num >= 0) line_num = cc.line_num;
31893                     add_pc2line_info(s, bc_out.size, line_num);
31894                     dbuf_putc(&bc_out, OP_return_undef);
31895                     pos_next = cc.pos;
31896                     break;
31897                 }
31898                 /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */
31899                 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
31900                     val = 0;
31901                     goto has_constant_test;
31902                 }
31903 #if SHORT_OPCODES
31904                 /* transform undefined strict_eq -> is_undefined */
31905                 if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
31906                     if (cc.line_num >= 0) line_num = cc.line_num;
31907                     add_pc2line_info(s, bc_out.size, line_num);
31908                     dbuf_putc(&bc_out, OP_is_undefined);
31909                     pos_next = cc.pos;
31910                     break;
31911                 }
31912                 /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */
31913                 if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
31914                     if (cc.line_num >= 0) line_num = cc.line_num;
31915                     add_pc2line_info(s, bc_out.size, line_num);
31916                     dbuf_putc(&bc_out, OP_is_undefined);
31917                     pos_next = cc.pos;
31918                     label = cc.label;
31919                     op = cc.op ^ OP_if_false ^ OP_if_true;
31920                     goto has_label;
31921                 }
31922 #endif
31923             }
31924             goto no_change;
31925 
31926         case OP_insert2:
31927             if (OPTIMIZE) {
31928                 /* Transformation:
31929                    insert2 put_field(a) drop -> put_field(a)
31930                    insert2 put_var_strict(a) drop -> put_var_strict(a)
31931                 */
31932                 if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
31933                     if (cc.line_num >= 0) line_num = cc.line_num;
31934                     add_pc2line_info(s, bc_out.size, line_num);
31935                     dbuf_putc(&bc_out, cc.op);
31936                     dbuf_put_u32(&bc_out, cc.atom);
31937                     pos_next = cc.pos;
31938                     break;
31939                 }
31940             }
31941             goto no_change;
31942 
31943         case OP_dup:
31944             if (OPTIMIZE) {
31945                 /* Transformation: dup put_x(n) drop -> put_x(n) */
31946                 int op1, line2 = -1;
31947                 /* Transformation: dup put_x(n) -> set_x(n) */
31948                 if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, -1)) {
31949                     if (cc.line_num >= 0) line_num = cc.line_num;
31950                     op1 = cc.op + 1;  /* put_x -> set_x */
31951                     pos_next = cc.pos;
31952                     if (code_match(&cc, cc.pos, OP_drop, -1)) {
31953                         if (cc.line_num >= 0) line_num = cc.line_num;
31954                         op1 -= 1; /* set_x drop -> put_x */
31955                         pos_next = cc.pos;
31956                         if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) {
31957                             line2 = cc.line_num; /* delay line number update */
31958                             op1 += 1;   /* put_x(n) get_x(n) -> set_x(n) */
31959                             pos_next = cc.pos;
31960                         }
31961                     }
31962                     add_pc2line_info(s, bc_out.size, line_num);
31963                     put_short_code(&bc_out, op1, cc.idx);
31964                     if (line2 >= 0) line_num = line2;
31965                     break;
31966                 }
31967             }
31968             goto no_change;
31969 
31970         case OP_get_loc:
31971             if (OPTIMIZE) {
31972                 /* transformation:
31973                    get_loc(n) post_dec put_loc(n) drop -> dec_loc(n)
31974                    get_loc(n) post_inc put_loc(n) drop -> inc_loc(n)
31975                    get_loc(n) dec dup put_loc(n) drop -> dec_loc(n)
31976                    get_loc(n) inc dup put_loc(n) drop -> inc_loc(n)
31977                  */
31978                 int idx;
31979                 idx = get_u16(bc_buf + pos + 1);
31980                 if (idx >= 256)
31981                     goto no_change;
31982                 if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) ||
31983                     code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) {
31984                     if (cc.line_num >= 0) line_num = cc.line_num;
31985                     add_pc2line_info(s, bc_out.size, line_num);
31986                     dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc);
31987                     dbuf_putc(&bc_out, idx);
31988                     pos_next = cc.pos;
31989                     break;
31990                 }
31991                 /* transformation:
31992                    get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n)
31993                  */
31994                 if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
31995                     if (cc.line_num >= 0) line_num = cc.line_num;
31996                     add_pc2line_info(s, bc_out.size, line_num);
31997 #if SHORT_OPCODES
31998                     if (cc.atom == JS_ATOM_empty_string) {
31999                         JS_FreeAtom(ctx, cc.atom);
32000                         dbuf_putc(&bc_out, OP_push_empty_string);
32001                     } else
32002 #endif
32003                     {
32004                         dbuf_putc(&bc_out, OP_push_atom_value);
32005                         dbuf_put_u32(&bc_out, cc.atom);
32006                     }
32007                     dbuf_putc(&bc_out, OP_add_loc);
32008                     dbuf_putc(&bc_out, idx);
32009                     pos_next = cc.pos;
32010                     break;
32011                 }
32012                 /* transformation:
32013                    get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n)
32014                  */
32015                 if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
32016                     if (cc.line_num >= 0) line_num = cc.line_num;
32017                     add_pc2line_info(s, bc_out.size, line_num);
32018                     push_short_int(&bc_out, cc.label);
32019                     dbuf_putc(&bc_out, OP_add_loc);
32020                     dbuf_putc(&bc_out, idx);
32021                     pos_next = cc.pos;
32022                     break;
32023                 }
32024                 /* transformation: XXX: also do these:
32025                    get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n)
32026                    get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n)
32027                    get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n)
32028                  */
32029                 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)) {
32030                     if (cc.line_num >= 0) line_num = cc.line_num;
32031                     add_pc2line_info(s, bc_out.size, line_num);
32032                     put_short_code(&bc_out, cc.op, cc.idx);
32033                     dbuf_putc(&bc_out, OP_add_loc);
32034                     dbuf_putc(&bc_out, idx);
32035                     pos_next = cc.pos;
32036                     break;
32037                 }
32038                 add_pc2line_info(s, bc_out.size, line_num);
32039                 put_short_code(&bc_out, op, idx);
32040                 break;
32041             }
32042             goto no_change;
32043 #if SHORT_OPCODES
32044         case OP_get_arg:
32045         case OP_get_var_ref:
32046             if (OPTIMIZE) {
32047                 int idx;
32048                 idx = get_u16(bc_buf + pos + 1);
32049                 add_pc2line_info(s, bc_out.size, line_num);
32050                 put_short_code(&bc_out, op, idx);
32051                 break;
32052             }
32053             goto no_change;
32054 #endif
32055         case OP_put_loc:
32056         case OP_put_arg:
32057         case OP_put_var_ref:
32058             if (OPTIMIZE) {
32059                 /* transformation: put_x(n) get_x(n) -> set_x(n) */
32060                 int idx;
32061                 idx = get_u16(bc_buf + pos + 1);
32062                 if (code_match(&cc, pos_next, op - 1, idx, -1)) {
32063                     if (cc.line_num >= 0) line_num = cc.line_num;
32064                     add_pc2line_info(s, bc_out.size, line_num);
32065                     put_short_code(&bc_out, op + 1, idx);
32066                     pos_next = cc.pos;
32067                     break;
32068                 }
32069                 add_pc2line_info(s, bc_out.size, line_num);
32070                 put_short_code(&bc_out, op, idx);
32071                 break;
32072             }
32073             goto no_change;
32074 
32075         case OP_post_inc:
32076         case OP_post_dec:
32077             if (OPTIMIZE) {
32078                 /* transformation:
32079                    post_inc put_x drop -> inc put_x
32080                    post_inc perm3 put_field drop -> inc put_field
32081                    post_inc perm3 put_var_strict drop -> inc put_var_strict
32082                    post_inc perm4 put_array_el drop -> inc put_array_el
32083                  */
32084                 int op1, idx;
32085                 if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) {
32086                     if (cc.line_num >= 0) line_num = cc.line_num;
32087                     op1 = cc.op;
32088                     idx = cc.idx;
32089                     pos_next = cc.pos;
32090                     if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) {
32091                         if (cc.line_num >= 0) line_num = cc.line_num;
32092                         op1 += 1;   /* put_x(n) get_x(n) -> set_x(n) */
32093                         pos_next = cc.pos;
32094                     }
32095                     add_pc2line_info(s, bc_out.size, line_num);
32096                     dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
32097                     put_short_code(&bc_out, op1, idx);
32098                     break;
32099                 }
32100                 if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
32101                     if (cc.line_num >= 0) line_num = cc.line_num;
32102                     add_pc2line_info(s, bc_out.size, line_num);
32103                     dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
32104                     dbuf_putc(&bc_out, cc.op);
32105                     dbuf_put_u32(&bc_out, cc.atom);
32106                     pos_next = cc.pos;
32107                     break;
32108                 }
32109                 if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) {
32110                     if (cc.line_num >= 0) line_num = cc.line_num;
32111                     add_pc2line_info(s, bc_out.size, line_num);
32112                     dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
32113                     dbuf_putc(&bc_out, OP_put_array_el);
32114                     pos_next = cc.pos;
32115                     break;
32116                 }
32117             }
32118             goto no_change;
32119 
32120 #if SHORT_OPCODES
32121         case OP_typeof:
32122             if (OPTIMIZE) {
32123                 /* simplify typeof tests */
32124                 if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) {
32125                     if (cc.line_num >= 0) line_num = cc.line_num;
32126                     int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq;
32127                     int op2 = -1;
32128                     switch (cc.atom) {
32129                     case JS_ATOM_undefined:
32130                         op2 = OP_typeof_is_undefined;
32131                         break;
32132                     case JS_ATOM_function:
32133                         op2 = OP_typeof_is_function;
32134                         break;
32135                     }
32136                     if (op2 >= 0) {
32137                         /* transform typeof(s) == "<type>" into is_<type> */
32138                         if (op1 == OP_strict_eq) {
32139                             add_pc2line_info(s, bc_out.size, line_num);
32140                             dbuf_putc(&bc_out, op2);
32141                             JS_FreeAtom(ctx, cc.atom);
32142                             pos_next = cc.pos;
32143                             break;
32144                         }
32145                         if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) {
32146                             /* transform typeof(s) != "<type>" if_false into is_<type> if_true */
32147                             if (cc.line_num >= 0) line_num = cc.line_num;
32148                             add_pc2line_info(s, bc_out.size, line_num);
32149                             dbuf_putc(&bc_out, op2);
32150                             JS_FreeAtom(ctx, cc.atom);
32151                             pos_next = cc.pos;
32152                             label = cc.label;
32153                             op = OP_if_true;
32154                             goto has_label;
32155                         }
32156                     }
32157                 }
32158             }
32159             goto no_change;
32160 #endif
32161 
32162         default:
32163         no_change:
32164             add_pc2line_info(s, bc_out.size, line_num);
32165             dbuf_put(&bc_out, bc_buf + pos, len);
32166             break;
32167         }
32168     }
32169 
32170     /* check that there were no missing labels */
32171     for(i = 0; i < s->label_count; i++) {
32172         assert(label_slots[i].first_reloc == NULL);
32173     }
32174 #if SHORT_OPCODES
32175     if (OPTIMIZE) {
32176         /* more jump optimizations */
32177         int patch_offsets = 0;
32178         for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) {
32179             LabelSlot *ls;
32180             JumpSlot *jp1;
32181             int j, pos, diff, delta;
32182 
32183             delta = 3;
32184             switch (op = jp->op) {
32185             case OP_goto16:
32186                 delta = 1;
32187                 /* fall thru */
32188             case OP_if_false:
32189             case OP_if_true:
32190             case OP_goto:
32191                 pos = jp->pos;
32192                 diff = s->label_slots[jp->label].addr - pos;
32193                 if (diff >= -128 && diff <= 127 + delta) {
32194                     //put_u8(bc_out.buf + pos, diff);
32195                     jp->size = 1;
32196                     if (op == OP_goto16) {
32197                         bc_out.buf[pos - 1] = jp->op = OP_goto8;
32198                     } else {
32199                         bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false);
32200                     }
32201                     goto shrink;
32202                 } else
32203                 if (diff == (int16_t)diff && op == OP_goto) {
32204                     //put_u16(bc_out.buf + pos, diff);
32205                     jp->size = 2;
32206                     delta = 2;
32207                     bc_out.buf[pos - 1] = jp->op = OP_goto16;
32208                 shrink:
32209                     /* XXX: should reduce complexity, using 2 finger copy scheme */
32210                     memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta,
32211                             bc_out.size - pos - jp->size - delta);
32212                     bc_out.size -= delta;
32213                     patch_offsets++;
32214                     for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) {
32215                         if (ls->addr > pos)
32216                             ls->addr -= delta;
32217                     }
32218                     for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) {
32219                         if (jp1->pos > pos)
32220                             jp1->pos -= delta;
32221                     }
32222                     for (j = 0; j < s->line_number_count; j++) {
32223                         if (s->line_number_slots[j].pc > pos)
32224                             s->line_number_slots[j].pc -= delta;
32225                     }
32226                     continue;
32227                 }
32228                 break;
32229             }
32230         }
32231         if (patch_offsets) {
32232             JumpSlot *jp1;
32233             int j;
32234             for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) {
32235                 int diff1 = s->label_slots[jp1->label].addr - jp1->pos;
32236                 switch (jp1->size) {
32237                 case 1:
32238                     put_u8(bc_out.buf + jp1->pos, diff1);
32239                     break;
32240                 case 2:
32241                     put_u16(bc_out.buf + jp1->pos, diff1);
32242                     break;
32243                 case 4:
32244                     put_u32(bc_out.buf + jp1->pos, diff1);
32245                     break;
32246                 }
32247             }
32248         }
32249     }
32250     js_free(ctx, s->jump_slots);
32251     s->jump_slots = NULL;
32252 #endif
32253     js_free(ctx, s->label_slots);
32254     s->label_slots = NULL;
32255     /* XXX: should delay until copying to runtime bytecode function */
32256     compute_pc2line_info(s);
32257     js_free(ctx, s->line_number_slots);
32258     s->line_number_slots = NULL;
32259     /* set the new byte code */
32260     dbuf_free(&s->byte_code);
32261     s->byte_code = bc_out;
32262     s->use_short_opcodes = TRUE;
32263     if (dbuf_error(&s->byte_code)) {
32264         JS_ThrowOutOfMemory(ctx);
32265         return -1;
32266     }
32267     return 0;
32268  fail:
32269     /* XXX: not safe */
32270     dbuf_free(&bc_out);
32271     return -1;
32272 }
32273 
32274 /* compute the maximum stack size needed by the function */
32275 
32276 typedef struct StackSizeState {
32277     int bc_len;
32278     int stack_len_max;
32279     uint16_t *stack_level_tab;
32280     int *pc_stack;
32281     int pc_stack_len;
32282     int pc_stack_size;
32283 } StackSizeState;
32284 
32285 /* 'op' is only used for error indication */
ss_check(JSContext * ctx,StackSizeState * s,int pos,int op,int stack_len)32286 static __exception int ss_check(JSContext *ctx, StackSizeState *s,
32287                                 int pos, int op, int stack_len)
32288 {
32289     if ((unsigned)pos >= s->bc_len) {
32290         JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
32291         return -1;
32292     }
32293     if (stack_len > s->stack_len_max) {
32294         s->stack_len_max = stack_len;
32295         if (s->stack_len_max > JS_STACK_SIZE_MAX) {
32296             JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
32297             return -1;
32298         }
32299     }
32300     if (s->stack_level_tab[pos] != 0xffff) {
32301         /* already explored: check that the stack size is consistent */
32302         if (s->stack_level_tab[pos] != stack_len) {
32303             JS_ThrowInternalError(ctx, "unconsistent stack size: %d %d (pc=%d)",
32304                                   s->stack_level_tab[pos], stack_len, pos);
32305             return -1;
32306         } else {
32307             return 0;
32308         }
32309     }
32310 
32311     /* mark as explored and store the stack size */
32312     s->stack_level_tab[pos] = stack_len;
32313 
32314     /* queue the new PC to explore */
32315     if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]),
32316                         &s->pc_stack_size, s->pc_stack_len + 1))
32317         return -1;
32318     s->pc_stack[s->pc_stack_len++] = pos;
32319     return 0;
32320 }
32321 
compute_stack_size(JSContext * ctx,JSFunctionDef * fd,int * pstack_size)32322 static __exception int compute_stack_size(JSContext *ctx,
32323                                           JSFunctionDef *fd,
32324                                           int *pstack_size)
32325 {
32326     StackSizeState s_s, *s = &s_s;
32327     int i, diff, n_pop, pos_next, stack_len, pos, op;
32328     const JSOpCode *oi;
32329     const uint8_t *bc_buf;
32330 
32331     bc_buf = fd->byte_code.buf;
32332     s->bc_len = fd->byte_code.size;
32333     /* bc_len > 0 */
32334     s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) *
32335                                    s->bc_len);
32336     if (!s->stack_level_tab)
32337         return -1;
32338     for(i = 0; i < s->bc_len; i++)
32339         s->stack_level_tab[i] = 0xffff;
32340     s->stack_len_max = 0;
32341     s->pc_stack = NULL;
32342     s->pc_stack_len = 0;
32343     s->pc_stack_size = 0;
32344 
32345     /* breadth-first graph exploration */
32346     if (ss_check(ctx, s, 0, OP_invalid, 0))
32347         goto fail;
32348 
32349     while (s->pc_stack_len > 0) {
32350         pos = s->pc_stack[--s->pc_stack_len];
32351         stack_len = s->stack_level_tab[pos];
32352         op = bc_buf[pos];
32353         if (op == 0 || op >= OP_COUNT) {
32354             JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos);
32355             goto fail;
32356         }
32357         oi = &short_opcode_info(op);
32358         pos_next = pos + oi->size;
32359         if (pos_next > s->bc_len) {
32360             JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
32361             goto fail;
32362         }
32363         n_pop = oi->n_pop;
32364         /* call pops a variable number of arguments */
32365         if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) {
32366             n_pop += get_u16(bc_buf + pos + 1);
32367         } else {
32368 #if SHORT_OPCODES
32369             if (oi->fmt == OP_FMT_npopx) {
32370                 n_pop += op - OP_call0;
32371             }
32372 #endif
32373         }
32374 
32375         if (stack_len < n_pop) {
32376             JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos);
32377             goto fail;
32378         }
32379         stack_len += oi->n_push - n_pop;
32380         if (stack_len > s->stack_len_max) {
32381             s->stack_len_max = stack_len;
32382             if (s->stack_len_max > JS_STACK_SIZE_MAX) {
32383                 JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
32384                 goto fail;
32385             }
32386         }
32387         switch(op) {
32388         case OP_tail_call:
32389         case OP_tail_call_method:
32390         case OP_return:
32391         case OP_return_undef:
32392         case OP_return_async:
32393         case OP_throw:
32394         case OP_throw_error:
32395         case OP_ret:
32396             goto done_insn;
32397         case OP_goto:
32398             diff = get_u32(bc_buf + pos + 1);
32399             pos_next = pos + 1 + diff;
32400             break;
32401 #if SHORT_OPCODES
32402         case OP_goto16:
32403             diff = (int16_t)get_u16(bc_buf + pos + 1);
32404             pos_next = pos + 1 + diff;
32405             break;
32406         case OP_goto8:
32407             diff = (int8_t)bc_buf[pos + 1];
32408             pos_next = pos + 1 + diff;
32409             break;
32410         case OP_if_true8:
32411         case OP_if_false8:
32412             diff = (int8_t)bc_buf[pos + 1];
32413             if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
32414                 goto fail;
32415             break;
32416 #endif
32417         case OP_if_true:
32418         case OP_if_false:
32419         case OP_catch:
32420             diff = get_u32(bc_buf + pos + 1);
32421             if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
32422                 goto fail;
32423             break;
32424         case OP_gosub:
32425             diff = get_u32(bc_buf + pos + 1);
32426             if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1))
32427                 goto fail;
32428             break;
32429         case OP_with_get_var:
32430         case OP_with_delete_var:
32431             diff = get_u32(bc_buf + pos + 5);
32432             if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1))
32433                 goto fail;
32434             break;
32435         case OP_with_make_ref:
32436         case OP_with_get_ref:
32437         case OP_with_get_ref_undef:
32438             diff = get_u32(bc_buf + pos + 5);
32439             if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2))
32440                 goto fail;
32441             break;
32442         case OP_with_put_var:
32443             diff = get_u32(bc_buf + pos + 5);
32444             if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1))
32445                 goto fail;
32446             break;
32447 
32448         default:
32449             break;
32450         }
32451         if (ss_check(ctx, s, pos_next, op, stack_len))
32452             goto fail;
32453     done_insn: ;
32454     }
32455     js_free(ctx, s->stack_level_tab);
32456     js_free(ctx, s->pc_stack);
32457     *pstack_size = s->stack_len_max;
32458     return 0;
32459  fail:
32460     js_free(ctx, s->stack_level_tab);
32461     js_free(ctx, s->pc_stack);
32462     *pstack_size = 0;
32463     return -1;
32464 }
32465 
add_module_variables(JSContext * ctx,JSFunctionDef * fd)32466 static int add_module_variables(JSContext *ctx, JSFunctionDef *fd)
32467 {
32468     int i, idx;
32469     JSModuleDef *m = fd->module;
32470     JSExportEntry *me;
32471     JSGlobalVar *hf;
32472 
32473     /* The imported global variables were added as closure variables
32474        in js_parse_import(). We add here the module global
32475        variables. */
32476 
32477     for(i = 0; i < fd->global_var_count; i++) {
32478         hf = &fd->global_vars[i];
32479         if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const,
32480                             hf->is_lexical, FALSE) < 0)
32481             return -1;
32482     }
32483 
32484     /* resolve the variable names of the local exports */
32485     for(i = 0; i < m->export_entries_count; i++) {
32486         me = &m->export_entries[i];
32487         if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
32488             idx = find_closure_var(ctx, fd, me->local_name);
32489             if (idx < 0) {
32490                 JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist",
32491                                         me->local_name);
32492                 return -1;
32493             }
32494             me->u.local.var_idx = idx;
32495         }
32496     }
32497     return 0;
32498 }
32499 
32500 /* create a function object from a function definition. The function
32501    definition is freed. All the child functions are also created. It
32502    must be done this way to resolve all the variables. */
js_create_function(JSContext * ctx,JSFunctionDef * fd)32503 static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
32504 {
32505     JSValue func_obj;
32506     JSFunctionBytecode *b;
32507     struct list_head *el, *el1;
32508     int stack_size, scope, idx;
32509     int function_size, byte_code_offset, cpool_offset;
32510     int closure_var_offset, vardefs_offset;
32511 
32512     /* recompute scope linkage */
32513     for (scope = 0; scope < fd->scope_count; scope++) {
32514         fd->scopes[scope].first = -1;
32515     }
32516     if (fd->has_parameter_expressions) {
32517         /* special end of variable list marker for the argument scope */
32518         fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END;
32519     }
32520     for (idx = 0; idx < fd->var_count; idx++) {
32521         JSVarDef *vd = &fd->vars[idx];
32522         vd->scope_next = fd->scopes[vd->scope_level].first;
32523         fd->scopes[vd->scope_level].first = idx;
32524     }
32525     for (scope = 2; scope < fd->scope_count; scope++) {
32526         JSVarScope *sd = &fd->scopes[scope];
32527         if (sd->first < 0)
32528             sd->first = fd->scopes[sd->parent].first;
32529     }
32530     for (idx = 0; idx < fd->var_count; idx++) {
32531         JSVarDef *vd = &fd->vars[idx];
32532         if (vd->scope_next < 0 && vd->scope_level > 1) {
32533             scope = fd->scopes[vd->scope_level].parent;
32534             vd->scope_next = fd->scopes[scope].first;
32535         }
32536     }
32537 
32538     /* if the function contains an eval call, the closure variables
32539        are used to compile the eval and they must be ordered by scope,
32540        so it is necessary to create the closure variables before any
32541        other variable lookup is done. */
32542     if (fd->has_eval_call)
32543         add_eval_variables(ctx, fd);
32544 
32545     /* add the module global variables in the closure */
32546     if (fd->module) {
32547         if (add_module_variables(ctx, fd))
32548             goto fail;
32549     }
32550 
32551     /* first create all the child functions */
32552     list_for_each_safe(el, el1, &fd->child_list) {
32553         JSFunctionDef *fd1;
32554         int cpool_idx;
32555 
32556         fd1 = list_entry(el, JSFunctionDef, link);
32557         cpool_idx = fd1->parent_cpool_idx;
32558         func_obj = js_create_function(ctx, fd1);
32559         if (JS_IsException(func_obj))
32560             goto fail;
32561         /* save it in the constant pool */
32562         assert(cpool_idx >= 0);
32563         fd->cpool[cpool_idx] = func_obj;
32564     }
32565 
32566 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4)
32567     if (!(fd->js_mode & JS_MODE_STRIP)) {
32568         printf("pass 1\n");
32569         dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size,
32570                        fd->args, fd->arg_count, fd->vars, fd->var_count,
32571                        fd->closure_var, fd->closure_var_count,
32572                        fd->cpool, fd->cpool_count, fd->source, fd->line_num,
32573                        fd->label_slots, NULL);
32574         printf("\n");
32575     }
32576 #endif
32577 
32578     if (resolve_variables(ctx, fd))
32579         goto fail;
32580 
32581 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)
32582     if (!(fd->js_mode & JS_MODE_STRIP)) {
32583         printf("pass 2\n");
32584         dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size,
32585                        fd->args, fd->arg_count, fd->vars, fd->var_count,
32586                        fd->closure_var, fd->closure_var_count,
32587                        fd->cpool, fd->cpool_count, fd->source, fd->line_num,
32588                        fd->label_slots, NULL);
32589         printf("\n");
32590     }
32591 #endif
32592 
32593     if (resolve_labels(ctx, fd))
32594         goto fail;
32595 
32596     if (compute_stack_size(ctx, fd, &stack_size) < 0)
32597         goto fail;
32598 
32599     if (fd->js_mode & JS_MODE_STRIP) {
32600         function_size = offsetof(JSFunctionBytecode, debug);
32601     } else {
32602         function_size = sizeof(*b);
32603     }
32604     cpool_offset = function_size;
32605     function_size += fd->cpool_count * sizeof(*fd->cpool);
32606     vardefs_offset = function_size;
32607     if (!(fd->js_mode & JS_MODE_STRIP) || fd->has_eval_call) {
32608         function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs);
32609     }
32610     closure_var_offset = function_size;
32611     function_size += fd->closure_var_count * sizeof(*fd->closure_var);
32612     byte_code_offset = function_size;
32613     function_size += fd->byte_code.size;
32614 
32615     b = js_mallocz(ctx, function_size);
32616     if (!b)
32617         goto fail;
32618     b->header.ref_count = 1;
32619 
32620     b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset);
32621     b->byte_code_len = fd->byte_code.size;
32622     memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size);
32623     js_free(ctx, fd->byte_code.buf);
32624     fd->byte_code.buf = NULL;
32625 
32626     b->func_name = fd->func_name;
32627     if (fd->arg_count + fd->var_count > 0) {
32628         if ((fd->js_mode & JS_MODE_STRIP) && !fd->has_eval_call) {
32629             /* Strip variable definitions not needed at runtime */
32630             int i;
32631             for(i = 0; i < fd->var_count; i++) {
32632                 JS_FreeAtom(ctx, fd->vars[i].var_name);
32633             }
32634             for(i = 0; i < fd->arg_count; i++) {
32635                 JS_FreeAtom(ctx, fd->args[i].var_name);
32636             }
32637             for(i = 0; i < fd->closure_var_count; i++) {
32638                 JS_FreeAtom(ctx, fd->closure_var[i].var_name);
32639                 fd->closure_var[i].var_name = JS_ATOM_NULL;
32640             }
32641         } else {
32642             b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
32643             memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));
32644             memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));
32645         }
32646         b->var_count = fd->var_count;
32647         b->arg_count = fd->arg_count;
32648         b->defined_arg_count = fd->defined_arg_count;
32649         js_free(ctx, fd->args);
32650         js_free(ctx, fd->vars);
32651     }
32652     b->cpool_count = fd->cpool_count;
32653     if (b->cpool_count) {
32654         b->cpool = (void *)((uint8_t*)b + cpool_offset);
32655         memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool));
32656     }
32657     js_free(ctx, fd->cpool);
32658     fd->cpool = NULL;
32659 
32660     b->stack_size = stack_size;
32661 
32662     if (fd->js_mode & JS_MODE_STRIP) {
32663         JS_FreeAtom(ctx, fd->filename);
32664         dbuf_free(&fd->pc2line);    // probably useless
32665     } else {
32666         /* XXX: source and pc2line info should be packed at the end of the
32667            JSFunctionBytecode structure, avoiding allocation overhead
32668          */
32669         b->has_debug = 1;
32670         b->debug.filename = fd->filename;
32671         b->debug.line_num = fd->line_num;
32672 
32673         //DynBuf pc2line;
32674         //compute_pc2line_info(fd, &pc2line);
32675         //js_free(ctx, fd->line_number_slots)
32676         b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size);
32677         if (!b->debug.pc2line_buf)
32678             b->debug.pc2line_buf = fd->pc2line.buf;
32679         b->debug.pc2line_len = fd->pc2line.size;
32680         b->debug.source = fd->source;
32681         b->debug.source_len = fd->source_len;
32682     }
32683     if (fd->scopes != fd->def_scope_array)
32684         js_free(ctx, fd->scopes);
32685 
32686     b->closure_var_count = fd->closure_var_count;
32687     if (b->closure_var_count) {
32688         b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
32689         memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var));
32690     }
32691     js_free(ctx, fd->closure_var);
32692     fd->closure_var = NULL;
32693 
32694     b->has_prototype = fd->has_prototype;
32695     b->has_simple_parameter_list = fd->has_simple_parameter_list;
32696     b->js_mode = fd->js_mode;
32697     b->is_derived_class_constructor = fd->is_derived_class_constructor;
32698     b->func_kind = fd->func_kind;
32699     b->need_home_object = (fd->home_object_var_idx >= 0 ||
32700                            fd->need_home_object);
32701     b->new_target_allowed = fd->new_target_allowed;
32702     b->super_call_allowed = fd->super_call_allowed;
32703     b->super_allowed = fd->super_allowed;
32704     b->arguments_allowed = fd->arguments_allowed;
32705     b->backtrace_barrier = fd->backtrace_barrier;
32706     b->realm = JS_DupContext(ctx);
32707 
32708     add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
32709 
32710 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1)
32711     if (!(fd->js_mode & JS_MODE_STRIP)) {
32712         js_dump_function_bytecode(ctx, b);
32713     }
32714 #endif
32715 
32716     if (fd->parent) {
32717         /* remove from parent list */
32718         list_del(&fd->link);
32719     }
32720 
32721     js_free(ctx, fd);
32722     return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
32723  fail:
32724     js_free_function_def(ctx, fd);
32725     return JS_EXCEPTION;
32726 }
32727 
free_function_bytecode(JSRuntime * rt,JSFunctionBytecode * b)32728 static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
32729 {
32730     int i;
32731 
32732 #if 0
32733     {
32734         char buf[ATOM_GET_STR_BUF_SIZE];
32735         printf("freeing %s\n",
32736                JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
32737     }
32738 #endif
32739     free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE);
32740 
32741     if (b->vardefs) {
32742         for(i = 0; i < b->arg_count + b->var_count; i++) {
32743             JS_FreeAtomRT(rt, b->vardefs[i].var_name);
32744         }
32745     }
32746     for(i = 0; i < b->cpool_count; i++)
32747         JS_FreeValueRT(rt, b->cpool[i]);
32748 
32749     for(i = 0; i < b->closure_var_count; i++) {
32750         JSClosureVar *cv = &b->closure_var[i];
32751         JS_FreeAtomRT(rt, cv->var_name);
32752     }
32753     if (b->realm)
32754         JS_FreeContext(b->realm);
32755 
32756     JS_FreeAtomRT(rt, b->func_name);
32757     if (b->has_debug) {
32758         JS_FreeAtomRT(rt, b->debug.filename);
32759         js_free_rt(rt, b->debug.pc2line_buf);
32760         js_free_rt(rt, b->debug.source);
32761     }
32762 
32763     remove_gc_object(&b->header);
32764     if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) {
32765         list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);
32766     } else {
32767         js_free_rt(rt, b);
32768     }
32769 }
32770 
js_parse_directives(JSParseState * s)32771 static __exception int js_parse_directives(JSParseState *s)
32772 {
32773     char str[20];
32774     JSParsePos pos;
32775     BOOL has_semi;
32776 
32777     if (s->token.val != TOK_STRING)
32778         return 0;
32779 
32780     js_parse_get_pos(s, &pos);
32781 
32782     while(s->token.val == TOK_STRING) {
32783         /* Copy actual source string representation */
32784         snprintf(str, sizeof str, "%.*s",
32785                  (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1);
32786 
32787         if (next_token(s))
32788             return -1;
32789 
32790         has_semi = FALSE;
32791         switch (s->token.val) {
32792         case ';':
32793             if (next_token(s))
32794                 return -1;
32795             has_semi = TRUE;
32796             break;
32797         case '}':
32798         case TOK_EOF:
32799             has_semi = TRUE;
32800             break;
32801         case TOK_NUMBER:
32802         case TOK_STRING:
32803         case TOK_TEMPLATE:
32804         case TOK_IDENT:
32805         case TOK_REGEXP:
32806         case TOK_DEC:
32807         case TOK_INC:
32808         case TOK_NULL:
32809         case TOK_FALSE:
32810         case TOK_TRUE:
32811         case TOK_IF:
32812         case TOK_RETURN:
32813         case TOK_VAR:
32814         case TOK_THIS:
32815         case TOK_DELETE:
32816         case TOK_TYPEOF:
32817         case TOK_NEW:
32818         case TOK_DO:
32819         case TOK_WHILE:
32820         case TOK_FOR:
32821         case TOK_SWITCH:
32822         case TOK_THROW:
32823         case TOK_TRY:
32824         case TOK_FUNCTION:
32825         case TOK_DEBUGGER:
32826         case TOK_WITH:
32827         case TOK_CLASS:
32828         case TOK_CONST:
32829         case TOK_ENUM:
32830         case TOK_EXPORT:
32831         case TOK_IMPORT:
32832         case TOK_SUPER:
32833         case TOK_INTERFACE:
32834         case TOK_LET:
32835         case TOK_PACKAGE:
32836         case TOK_PRIVATE:
32837         case TOK_PROTECTED:
32838         case TOK_PUBLIC:
32839         case TOK_STATIC:
32840             /* automatic insertion of ';' */
32841             if (s->got_lf)
32842                 has_semi = TRUE;
32843             break;
32844         default:
32845             break;
32846         }
32847         if (!has_semi)
32848             break;
32849         if (!strcmp(str, "use strict")) {
32850             s->cur_func->has_use_strict = TRUE;
32851             s->cur_func->js_mode |= JS_MODE_STRICT;
32852         }
32853 #if !defined(DUMP_BYTECODE) || !(DUMP_BYTECODE & 8)
32854         else if (!strcmp(str, "use strip")) {
32855             s->cur_func->js_mode |= JS_MODE_STRIP;
32856         }
32857 #endif
32858 #ifdef CONFIG_BIGNUM
32859         else if (s->ctx->bignum_ext && !strcmp(str, "use math")) {
32860             s->cur_func->js_mode |= JS_MODE_MATH;
32861         }
32862 #endif
32863     }
32864     return js_parse_seek_token(s, &pos);
32865 }
32866 
js_parse_function_check_names(JSParseState * s,JSFunctionDef * fd,JSAtom func_name)32867 static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd,
32868                                          JSAtom func_name)
32869 {
32870     JSAtom name;
32871     int i, idx;
32872 
32873     if (fd->js_mode & JS_MODE_STRICT) {
32874         if (!fd->has_simple_parameter_list && fd->has_use_strict) {
32875             return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter");
32876         }
32877         if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments) {
32878             return js_parse_error(s, "invalid function name in strict code");
32879         }
32880         for (idx = 0; idx < fd->arg_count; idx++) {
32881             name = fd->args[idx].var_name;
32882 
32883             if (name == JS_ATOM_eval || name == JS_ATOM_arguments) {
32884                 return js_parse_error(s, "invalid argument name in strict code");
32885             }
32886         }
32887     }
32888     /* check async_generator case */
32889     if ((fd->js_mode & JS_MODE_STRICT)
32890     ||  !fd->has_simple_parameter_list
32891     ||  (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC)
32892     ||  fd->func_type == JS_PARSE_FUNC_ARROW
32893     ||  fd->func_type == JS_PARSE_FUNC_METHOD) {
32894         for (idx = 0; idx < fd->arg_count; idx++) {
32895             name = fd->args[idx].var_name;
32896             if (name != JS_ATOM_NULL) {
32897                 for (i = 0; i < idx; i++) {
32898                     if (fd->args[i].var_name == name)
32899                         goto duplicate;
32900                 }
32901                 /* Check if argument name duplicates a destructuring parameter */
32902                 /* XXX: should have a flag for such variables */
32903                 for (i = 0; i < fd->var_count; i++) {
32904                     if (fd->vars[i].var_name == name &&
32905                         fd->vars[i].scope_level == 0)
32906                         goto duplicate;
32907                 }
32908             }
32909         }
32910     }
32911     return 0;
32912 
32913 duplicate:
32914     return js_parse_error(s, "duplicate argument names not allowed in this context");
32915 }
32916 
32917 /* create a function to initialize class fields */
js_parse_function_class_fields_init(JSParseState * s)32918 static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s)
32919 {
32920     JSFunctionDef *fd;
32921 
32922     fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE,
32923                              s->filename, 0);
32924     if (!fd)
32925         return NULL;
32926     fd->func_name = JS_ATOM_NULL;
32927     fd->has_prototype = FALSE;
32928     fd->has_home_object = TRUE;
32929 
32930     fd->has_arguments_binding = FALSE;
32931     fd->has_this_binding = TRUE;
32932     fd->is_derived_class_constructor = FALSE;
32933     fd->new_target_allowed = TRUE;
32934     fd->super_call_allowed = FALSE;
32935     fd->super_allowed = fd->has_home_object;
32936     fd->arguments_allowed = FALSE;
32937 
32938     fd->func_kind = JS_FUNC_NORMAL;
32939     fd->func_type = JS_PARSE_FUNC_METHOD;
32940     return fd;
32941 }
32942 
32943 /* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and
32944    JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */
js_parse_function_decl2(JSParseState * s,JSParseFunctionEnum func_type,JSFunctionKindEnum func_kind,JSAtom func_name,const uint8_t * ptr,int function_line_num,JSParseExportEnum export_flag,JSFunctionDef ** pfd)32945 static __exception int js_parse_function_decl2(JSParseState *s,
32946                                                JSParseFunctionEnum func_type,
32947                                                JSFunctionKindEnum func_kind,
32948                                                JSAtom func_name,
32949                                                const uint8_t *ptr,
32950                                                int function_line_num,
32951                                                JSParseExportEnum export_flag,
32952                                                JSFunctionDef **pfd)
32953 {
32954     JSContext *ctx = s->ctx;
32955     JSFunctionDef *fd = s->cur_func;
32956     BOOL is_expr;
32957     int func_idx, lexical_func_idx = -1;
32958     BOOL has_opt_arg;
32959     BOOL create_func_var = FALSE;
32960 
32961     is_expr = (func_type != JS_PARSE_FUNC_STATEMENT &&
32962                func_type != JS_PARSE_FUNC_VAR);
32963 
32964     if (func_type == JS_PARSE_FUNC_STATEMENT ||
32965         func_type == JS_PARSE_FUNC_VAR ||
32966         func_type == JS_PARSE_FUNC_EXPR) {
32967         if (func_kind == JS_FUNC_NORMAL &&
32968             token_is_pseudo_keyword(s, JS_ATOM_async) &&
32969             peek_token(s, TRUE) != '\n') {
32970             if (next_token(s))
32971                 return -1;
32972             func_kind = JS_FUNC_ASYNC;
32973         }
32974         if (next_token(s))
32975             return -1;
32976         if (s->token.val == '*') {
32977             if (next_token(s))
32978                 return -1;
32979             func_kind |= JS_FUNC_GENERATOR;
32980         }
32981 
32982         if (s->token.val == TOK_IDENT) {
32983             if (s->token.u.ident.is_reserved ||
32984                 (s->token.u.ident.atom == JS_ATOM_yield &&
32985                  func_type == JS_PARSE_FUNC_EXPR &&
32986                  (func_kind & JS_FUNC_GENERATOR)) ||
32987                 (s->token.u.ident.atom == JS_ATOM_await &&
32988                  func_type == JS_PARSE_FUNC_EXPR &&
32989                  (func_kind & JS_FUNC_ASYNC))) {
32990                 return js_parse_error_reserved_identifier(s);
32991             }
32992         }
32993         if (s->token.val == TOK_IDENT ||
32994             (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) ||
32995              (s->token.val == TOK_AWAIT && !s->is_module)) &&
32996              func_type == JS_PARSE_FUNC_EXPR)) {
32997             func_name = JS_DupAtom(ctx, s->token.u.ident.atom);
32998             if (next_token(s)) {
32999                 JS_FreeAtom(ctx, func_name);
33000                 return -1;
33001             }
33002         } else {
33003             if (func_type != JS_PARSE_FUNC_EXPR &&
33004                 export_flag != JS_PARSE_EXPORT_DEFAULT) {
33005                 return js_parse_error(s, "function name expected");
33006             }
33007         }
33008     } else if (func_type != JS_PARSE_FUNC_ARROW) {
33009         func_name = JS_DupAtom(ctx, func_name);
33010     }
33011 
33012     if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE &&
33013         (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) {
33014         JSGlobalVar *hf;
33015         hf = find_global_var(fd, func_name);
33016         /* XXX: should check scope chain */
33017         if (hf && hf->scope_level == fd->scope_level) {
33018             js_parse_error(s, "invalid redefinition of global identifier in module code");
33019             JS_FreeAtom(ctx, func_name);
33020             return -1;
33021         }
33022     }
33023 
33024     if (func_type == JS_PARSE_FUNC_VAR) {
33025         if (!(fd->js_mode & JS_MODE_STRICT)
33026         && func_kind == JS_FUNC_NORMAL
33027         &&  find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0
33028         &&  !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET))
33029         &&  !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) {
33030             create_func_var = TRUE;
33031         }
33032         /* Create the lexical name here so that the function closure
33033            contains it */
33034         if (fd->is_eval &&
33035             (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
33036              fd->eval_type == JS_EVAL_TYPE_MODULE) &&
33037             fd->scope_level == fd->body_scope) {
33038             /* avoid creating a lexical variable in the global
33039                scope. XXX: check annex B */
33040             JSGlobalVar *hf;
33041             hf = find_global_var(fd, func_name);
33042             /* XXX: should check scope chain */
33043             if (hf && hf->scope_level == fd->scope_level) {
33044                 js_parse_error(s, "invalid redefinition of global identifier");
33045                 JS_FreeAtom(ctx, func_name);
33046                 return -1;
33047             }
33048         } else {
33049             /* Always create a lexical name, fail if at the same scope as
33050                existing name */
33051             /* Lexical variable will be initialized upon entering scope */
33052             lexical_func_idx = define_var(s, fd, func_name,
33053                                           func_kind != JS_FUNC_NORMAL ?
33054                                           JS_VAR_DEF_NEW_FUNCTION_DECL :
33055                                           JS_VAR_DEF_FUNCTION_DECL);
33056             if (lexical_func_idx < 0) {
33057                 JS_FreeAtom(ctx, func_name);
33058                 return -1;
33059             }
33060         }
33061     }
33062 
33063     fd = js_new_function_def(ctx, fd, FALSE, is_expr,
33064                              s->filename, function_line_num);
33065     if (!fd) {
33066         JS_FreeAtom(ctx, func_name);
33067         return -1;
33068     }
33069     if (pfd)
33070         *pfd = fd;
33071     s->cur_func = fd;
33072     fd->func_name = func_name;
33073     /* XXX: test !fd->is_generator is always false */
33074     fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT ||
33075                          func_type == JS_PARSE_FUNC_VAR ||
33076                          func_type == JS_PARSE_FUNC_EXPR) &&
33077                         func_kind == JS_FUNC_NORMAL;
33078     fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD ||
33079                            func_type == JS_PARSE_FUNC_GETTER ||
33080                            func_type == JS_PARSE_FUNC_SETTER ||
33081                            func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
33082                            func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
33083     fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW);
33084     fd->has_this_binding = fd->has_arguments_binding;
33085     fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
33086     if (func_type == JS_PARSE_FUNC_ARROW) {
33087         fd->new_target_allowed = fd->parent->new_target_allowed;
33088         fd->super_call_allowed = fd->parent->super_call_allowed;
33089         fd->super_allowed = fd->parent->super_allowed;
33090         fd->arguments_allowed = fd->parent->arguments_allowed;
33091     } else {
33092         fd->new_target_allowed = TRUE;
33093         fd->super_call_allowed = fd->is_derived_class_constructor;
33094         fd->super_allowed = fd->has_home_object;
33095         fd->arguments_allowed = TRUE;
33096     }
33097 
33098     /* fd->in_function_body == FALSE prevents yield/await during the parsing
33099        of the arguments in generator/async functions. They are parsed as
33100        regular identifiers for other function kinds. */
33101     fd->func_kind = func_kind;
33102     fd->func_type = func_type;
33103 
33104     if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
33105         func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
33106         /* error if not invoked as a constructor */
33107         emit_op(s, OP_check_ctor);
33108     }
33109 
33110     if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
33111         emit_class_field_init(s);
33112     }
33113 
33114     /* parse arguments */
33115     fd->has_simple_parameter_list = TRUE;
33116     fd->has_parameter_expressions = FALSE;
33117     has_opt_arg = FALSE;
33118     if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) {
33119         JSAtom name;
33120         if (s->token.u.ident.is_reserved) {
33121             js_parse_error_reserved_identifier(s);
33122             goto fail;
33123         }
33124         name = s->token.u.ident.atom;
33125         if (add_arg(ctx, fd, name) < 0)
33126             goto fail;
33127         fd->defined_arg_count = 1;
33128     } else {
33129         if (s->token.val == '(') {
33130             int skip_bits;
33131             /* if there is an '=' inside the parameter list, we
33132                consider there is a parameter expression inside */
33133             js_parse_skip_parens_token(s, &skip_bits, FALSE);
33134             if (skip_bits & SKIP_HAS_ASSIGNMENT)
33135                 fd->has_parameter_expressions = TRUE;
33136             if (next_token(s))
33137                 goto fail;
33138         } else {
33139             if (js_parse_expect(s, '('))
33140                 goto fail;
33141         }
33142 
33143         if (fd->has_parameter_expressions) {
33144             fd->scope_level = -1; /* force no parent scope */
33145             if (push_scope(s) < 0)
33146                 return -1;
33147         }
33148 
33149         while (s->token.val != ')') {
33150             JSAtom name;
33151             BOOL rest = FALSE;
33152             int idx, has_initializer;
33153 
33154             if (s->token.val == TOK_ELLIPSIS) {
33155                 fd->has_simple_parameter_list = FALSE;
33156                 rest = TRUE;
33157                 if (next_token(s))
33158                     goto fail;
33159             }
33160             if (s->token.val == '[' || s->token.val == '{') {
33161                 fd->has_simple_parameter_list = FALSE;
33162                 if (rest) {
33163                     emit_op(s, OP_rest);
33164                     emit_u16(s, fd->arg_count);
33165                 } else {
33166                     /* unnamed arg for destructuring */
33167                     idx = add_arg(ctx, fd, JS_ATOM_NULL);
33168                     emit_op(s, OP_get_arg);
33169                     emit_u16(s, idx);
33170                 }
33171                 has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE);
33172                 if (has_initializer < 0)
33173                     goto fail;
33174                 if (has_initializer)
33175                     has_opt_arg = TRUE;
33176                 if (!has_opt_arg)
33177                     fd->defined_arg_count++;
33178             } else if (s->token.val == TOK_IDENT) {
33179                 if (s->token.u.ident.is_reserved) {
33180                     js_parse_error_reserved_identifier(s);
33181                     goto fail;
33182                 }
33183                 name = s->token.u.ident.atom;
33184                 if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
33185                     js_parse_error_reserved_identifier(s);
33186                     goto fail;
33187                 }
33188                 if (fd->has_parameter_expressions) {
33189                     if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0)
33190                         goto fail;
33191                 }
33192                 /* XXX: could avoid allocating an argument if rest is true */
33193                 idx = add_arg(ctx, fd, name);
33194                 if (idx < 0)
33195                     goto fail;
33196                 if (next_token(s))
33197                     goto fail;
33198                 if (rest) {
33199                     emit_op(s, OP_rest);
33200                     emit_u16(s, idx);
33201                     if (fd->has_parameter_expressions) {
33202                         emit_op(s, OP_dup);
33203                         emit_op(s, OP_scope_put_var_init);
33204                         emit_atom(s, name);
33205                         emit_u16(s, fd->scope_level);
33206                     }
33207                     emit_op(s, OP_put_arg);
33208                     emit_u16(s, idx);
33209                     fd->has_simple_parameter_list = FALSE;
33210                     has_opt_arg = TRUE;
33211                 } else if (s->token.val == '=') {
33212                     int label;
33213 
33214                     fd->has_simple_parameter_list = FALSE;
33215                     has_opt_arg = TRUE;
33216 
33217                     if (next_token(s))
33218                         goto fail;
33219 
33220                     label = new_label(s);
33221                     emit_op(s, OP_get_arg);
33222                     emit_u16(s, idx);
33223                     emit_op(s, OP_dup);
33224                     emit_op(s, OP_undefined);
33225                     emit_op(s, OP_strict_eq);
33226                     emit_goto(s, OP_if_false, label);
33227                     emit_op(s, OP_drop);
33228                     if (js_parse_assign_expr(s))
33229                         goto fail;
33230                     set_object_name(s, name);
33231                     emit_op(s, OP_dup);
33232                     emit_op(s, OP_put_arg);
33233                     emit_u16(s, idx);
33234                     emit_label(s, label);
33235                     emit_op(s, OP_scope_put_var_init);
33236                     emit_atom(s, name);
33237                     emit_u16(s, fd->scope_level);
33238                 } else {
33239                     if (!has_opt_arg) {
33240                         fd->defined_arg_count++;
33241                     }
33242                     if (fd->has_parameter_expressions) {
33243                         /* copy the argument to the argument scope */
33244                         emit_op(s, OP_get_arg);
33245                         emit_u16(s, idx);
33246                         emit_op(s, OP_scope_put_var_init);
33247                         emit_atom(s, name);
33248                         emit_u16(s, fd->scope_level);
33249                     }
33250                 }
33251             } else {
33252                 js_parse_error(s, "missing formal parameter");
33253                 goto fail;
33254             }
33255             if (rest && s->token.val != ')') {
33256                 js_parse_expect(s, ')');
33257                 goto fail;
33258             }
33259             if (s->token.val == ')')
33260                 break;
33261             if (js_parse_expect(s, ','))
33262                 goto fail;
33263         }
33264         if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) ||
33265             (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) {
33266             js_parse_error(s, "invalid number of arguments for getter or setter");
33267             goto fail;
33268         }
33269     }
33270 
33271     if (fd->has_parameter_expressions) {
33272         int idx;
33273 
33274         /* Copy the variables in the argument scope to the variable
33275            scope (see FunctionDeclarationInstantiation() in spec). The
33276            normal arguments are already present, so no need to copy
33277            them. */
33278         idx = fd->scopes[fd->scope_level].first;
33279         while (idx >= 0) {
33280             JSVarDef *vd = &fd->vars[idx];
33281             if (vd->scope_level != fd->scope_level)
33282                 break;
33283             if (find_var(ctx, fd, vd->var_name) < 0) {
33284                 if (add_var(ctx, fd, vd->var_name) < 0)
33285                     goto fail;
33286                 vd = &fd->vars[idx]; /* fd->vars may have been reallocated */
33287                 emit_op(s, OP_scope_get_var);
33288                 emit_atom(s, vd->var_name);
33289                 emit_u16(s, fd->scope_level);
33290                 emit_op(s, OP_scope_put_var);
33291                 emit_atom(s, vd->var_name);
33292                 emit_u16(s, 0);
33293             }
33294             idx = vd->scope_next;
33295         }
33296 
33297         /* the argument scope has no parent, hence we don't use pop_scope(s) */
33298         emit_op(s, OP_leave_scope);
33299         emit_u16(s, fd->scope_level);
33300 
33301         /* set the variable scope as the current scope */
33302         fd->scope_level = 0;
33303         fd->scope_first = fd->scopes[fd->scope_level].first;
33304     }
33305 
33306     if (next_token(s))
33307         goto fail;
33308 
33309     /* generator function: yield after the parameters are evaluated */
33310     if (func_kind == JS_FUNC_GENERATOR ||
33311         func_kind == JS_FUNC_ASYNC_GENERATOR)
33312         emit_op(s, OP_initial_yield);
33313 
33314     /* in generators, yield expression is forbidden during the parsing
33315        of the arguments */
33316     fd->in_function_body = TRUE;
33317     push_scope(s);  /* enter body scope */
33318     fd->body_scope = fd->scope_level;
33319 
33320     if (s->token.val == TOK_ARROW) {
33321         if (next_token(s))
33322             goto fail;
33323 
33324         if (s->token.val != '{') {
33325             if (js_parse_function_check_names(s, fd, func_name))
33326                 goto fail;
33327 
33328             if (js_parse_assign_expr(s))
33329                 goto fail;
33330 
33331             if (func_kind != JS_FUNC_NORMAL)
33332                 emit_op(s, OP_return_async);
33333             else
33334                 emit_op(s, OP_return);
33335 
33336             if (!(fd->js_mode & JS_MODE_STRIP)) {
33337                 /* save the function source code */
33338                 /* the end of the function source code is after the last
33339                    token of the function source stored into s->last_ptr */
33340                 fd->source_len = s->last_ptr - ptr;
33341                 fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
33342                 if (!fd->source)
33343                     goto fail;
33344             }
33345             goto done;
33346         }
33347     }
33348 
33349     if (js_parse_expect(s, '{'))
33350         goto fail;
33351 
33352     if (js_parse_directives(s))
33353         goto fail;
33354 
33355     /* in strict_mode, check function and argument names */
33356     if (js_parse_function_check_names(s, fd, func_name))
33357         goto fail;
33358 
33359     while (s->token.val != '}') {
33360         if (js_parse_source_element(s))
33361             goto fail;
33362     }
33363     if (!(fd->js_mode & JS_MODE_STRIP)) {
33364         /* save the function source code */
33365         fd->source_len = s->buf_ptr - ptr;
33366         fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
33367         if (!fd->source)
33368             goto fail;
33369     }
33370 
33371     if (next_token(s)) {
33372         /* consume the '}' */
33373         goto fail;
33374     }
33375 
33376     /* in case there is no return, add one */
33377     if (js_is_live_code(s)) {
33378         emit_return(s, FALSE);
33379     }
33380 done:
33381     s->cur_func = fd->parent;
33382 
33383     /* create the function object */
33384     {
33385         int idx;
33386         JSAtom func_name = fd->func_name;
33387 
33388         /* the real object will be set at the end of the compilation */
33389         idx = cpool_add(s, JS_NULL);
33390         fd->parent_cpool_idx = idx;
33391 
33392         if (is_expr) {
33393             /* for constructors, no code needs to be generated here */
33394             if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR &&
33395                 func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
33396                 /* OP_fclosure creates the function object from the bytecode
33397                    and adds the scope information */
33398                 emit_op(s, OP_fclosure);
33399                 emit_u32(s, idx);
33400                 if (func_name == JS_ATOM_NULL) {
33401                     emit_op(s, OP_set_name);
33402                     emit_u32(s, JS_ATOM_NULL);
33403                 }
33404             }
33405         } else if (func_type == JS_PARSE_FUNC_VAR) {
33406             emit_op(s, OP_fclosure);
33407             emit_u32(s, idx);
33408             if (create_func_var) {
33409                 if (s->cur_func->is_global_var) {
33410                     JSGlobalVar *hf;
33411                     /* the global variable must be defined at the start of the
33412                        function */
33413                     hf = add_global_var(ctx, s->cur_func, func_name);
33414                     if (!hf)
33415                         goto fail;
33416                     /* it is considered as defined at the top level
33417                        (needed for annex B.3.3.4 and B.3.3.5
33418                        checks) */
33419                     hf->scope_level = 0;
33420                     hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0);
33421                     /* store directly into global var, bypass lexical scope */
33422                     emit_op(s, OP_dup);
33423                     emit_op(s, OP_scope_put_var);
33424                     emit_atom(s, func_name);
33425                     emit_u16(s, 0);
33426                 } else {
33427                     /* do not call define_var to bypass lexical scope check */
33428                     func_idx = find_var(ctx, s->cur_func, func_name);
33429                     if (func_idx < 0) {
33430                         func_idx = add_var(ctx, s->cur_func, func_name);
33431                         if (func_idx < 0)
33432                             goto fail;
33433                     }
33434                     /* store directly into local var, bypass lexical catch scope */
33435                     emit_op(s, OP_dup);
33436                     emit_op(s, OP_scope_put_var);
33437                     emit_atom(s, func_name);
33438                     emit_u16(s, 0);
33439                 }
33440             }
33441             if (lexical_func_idx >= 0) {
33442                 /* lexical variable will be initialized upon entering scope */
33443                 s->cur_func->vars[lexical_func_idx].func_pool_idx = idx;
33444                 emit_op(s, OP_drop);
33445             } else {
33446                 /* store function object into its lexical name */
33447                 /* XXX: could use OP_put_loc directly */
33448                 emit_op(s, OP_scope_put_var_init);
33449                 emit_atom(s, func_name);
33450                 emit_u16(s, s->cur_func->scope_level);
33451             }
33452         } else {
33453             if (!s->cur_func->is_global_var) {
33454                 int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR);
33455 
33456                 if (var_idx < 0)
33457                     goto fail;
33458                 /* the variable will be assigned at the top of the function */
33459                 if (var_idx & ARGUMENT_VAR_OFFSET) {
33460                     s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx;
33461                 } else {
33462                     s->cur_func->vars[var_idx].func_pool_idx = idx;
33463                 }
33464             } else {
33465                 JSAtom func_var_name;
33466                 JSGlobalVar *hf;
33467                 if (func_name == JS_ATOM_NULL)
33468                     func_var_name = JS_ATOM__default_; /* export default */
33469                 else
33470                     func_var_name = func_name;
33471                 /* the variable will be assigned at the top of the function */
33472                 hf = add_global_var(ctx, s->cur_func, func_var_name);
33473                 if (!hf)
33474                     goto fail;
33475                 hf->cpool_idx = idx;
33476                 if (export_flag != JS_PARSE_EXPORT_NONE) {
33477                     if (!add_export_entry(s, s->cur_func->module, func_var_name,
33478                                           export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL))
33479                         goto fail;
33480                 }
33481             }
33482         }
33483     }
33484     return 0;
33485  fail:
33486     s->cur_func = fd->parent;
33487     js_free_function_def(ctx, fd);
33488     if (pfd)
33489         *pfd = NULL;
33490     return -1;
33491 }
33492 
js_parse_function_decl(JSParseState * s,JSParseFunctionEnum func_type,JSFunctionKindEnum func_kind,JSAtom func_name,const uint8_t * ptr,int function_line_num)33493 static __exception int js_parse_function_decl(JSParseState *s,
33494                                               JSParseFunctionEnum func_type,
33495                                               JSFunctionKindEnum func_kind,
33496                                               JSAtom func_name,
33497                                               const uint8_t *ptr,
33498                                               int function_line_num)
33499 {
33500     return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr,
33501                                    function_line_num, JS_PARSE_EXPORT_NONE,
33502                                    NULL);
33503 }
33504 
js_parse_program(JSParseState * s)33505 static __exception int js_parse_program(JSParseState *s)
33506 {
33507     JSFunctionDef *fd = s->cur_func;
33508     int idx;
33509 
33510     if (next_token(s))
33511         return -1;
33512 
33513     if (js_parse_directives(s))
33514         return -1;
33515 
33516     fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) ||
33517         (fd->eval_type == JS_EVAL_TYPE_MODULE) ||
33518         !(fd->js_mode & JS_MODE_STRICT);
33519 
33520     if (!s->is_module) {
33521         /* hidden variable for the return value */
33522         fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_);
33523         if (idx < 0)
33524             return -1;
33525     }
33526 
33527     while (s->token.val != TOK_EOF) {
33528         if (js_parse_source_element(s))
33529             return -1;
33530     }
33531 
33532     if (!s->is_module) {
33533         /* return the value of the hidden variable eval_ret_idx  */
33534         emit_op(s, OP_get_loc);
33535         emit_u16(s, fd->eval_ret_idx);
33536 
33537         emit_op(s, OP_return);
33538     } else {
33539         emit_op(s, OP_return_undef);
33540     }
33541 
33542     return 0;
33543 }
33544 
js_parse_init(JSContext * ctx,JSParseState * s,const char * input,size_t input_len,const char * filename)33545 static void js_parse_init(JSContext *ctx, JSParseState *s,
33546                           const char *input, size_t input_len,
33547                           const char *filename)
33548 {
33549     memset(s, 0, sizeof(*s));
33550     s->ctx = ctx;
33551     s->filename = filename;
33552     s->line_num = 1;
33553     s->buf_ptr = (const uint8_t *)input;
33554     s->buf_end = s->buf_ptr + input_len;
33555     s->token.val = ' ';
33556     s->token.line_num = 1;
33557 }
33558 
JS_EvalFunctionInternal(JSContext * ctx,JSValue fun_obj,JSValueConst this_obj,JSVarRef ** var_refs,JSStackFrame * sf)33559 static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,
33560                                        JSValueConst this_obj,
33561                                        JSVarRef **var_refs, JSStackFrame *sf)
33562 {
33563     JSValue ret_val;
33564     uint32_t tag;
33565 
33566     tag = JS_VALUE_GET_TAG(fun_obj);
33567     if (tag == JS_TAG_FUNCTION_BYTECODE) {
33568         fun_obj = js_closure(ctx, fun_obj, var_refs, sf);
33569         ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL);
33570     } else if (tag == JS_TAG_MODULE) {
33571         JSModuleDef *m;
33572         m = JS_VALUE_GET_PTR(fun_obj);
33573         /* the module refcount should be >= 2 */
33574         JS_FreeValue(ctx, fun_obj);
33575         if (js_create_module_function(ctx, m) < 0)
33576             goto fail;
33577         if (js_link_module(ctx, m) < 0)
33578             goto fail;
33579         ret_val = js_evaluate_module(ctx, m);
33580         if (JS_IsException(ret_val)) {
33581         fail:
33582             js_free_modules(ctx, JS_FREE_MODULE_NOT_EVALUATED);
33583             return JS_EXCEPTION;
33584         }
33585     } else {
33586         JS_FreeValue(ctx, fun_obj);
33587         ret_val = JS_ThrowTypeError(ctx, "bytecode function expected");
33588     }
33589     return ret_val;
33590 }
33591 
JS_EvalFunction(JSContext * ctx,JSValue fun_obj)33592 JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj)
33593 {
33594     return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL);
33595 }
33596 
skip_shebang(JSParseState * s)33597 static void skip_shebang(JSParseState *s)
33598 {
33599     const uint8_t *p = s->buf_ptr;
33600     int c;
33601 
33602     if (p[0] == '#' && p[1] == '!') {
33603         p += 2;
33604         while (p < s->buf_end) {
33605             if (*p == '\n' || *p == '\r') {
33606                 break;
33607             } else if (*p >= 0x80) {
33608                 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
33609                 if (c == CP_LS || c == CP_PS) {
33610                     break;
33611                 } else if (c == -1) {
33612                     p++; /* skip invalid UTF-8 */
33613                 }
33614             } else {
33615                 p++;
33616             }
33617         }
33618         s->buf_ptr = p;
33619     }
33620 }
33621 
33622 /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
__JS_EvalInternal(JSContext * ctx,JSValueConst this_obj,const char * input,size_t input_len,const char * filename,int flags,int scope_idx)33623 static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
33624                                  const char *input, size_t input_len,
33625                                  const char *filename, int flags, int scope_idx)
33626 {
33627     JSParseState s1, *s = &s1;
33628     int err, js_mode, eval_type;
33629     JSValue fun_obj, ret_val;
33630     JSStackFrame *sf;
33631     JSVarRef **var_refs;
33632     JSFunctionBytecode *b;
33633     JSFunctionDef *fd;
33634     JSModuleDef *m;
33635 
33636     js_parse_init(ctx, s, input, input_len, filename);
33637     skip_shebang(s);
33638 
33639     eval_type = flags & JS_EVAL_TYPE_MASK;
33640     m = NULL;
33641     if (eval_type == JS_EVAL_TYPE_DIRECT) {
33642         JSObject *p;
33643         sf = ctx->rt->current_stack_frame;
33644         assert(sf != NULL);
33645         assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT);
33646         p = JS_VALUE_GET_OBJ(sf->cur_func);
33647         assert(js_class_has_bytecode(p->class_id));
33648         b = p->u.func.function_bytecode;
33649         var_refs = p->u.func.var_refs;
33650         js_mode = b->js_mode;
33651     } else {
33652         sf = NULL;
33653         b = NULL;
33654         var_refs = NULL;
33655         js_mode = 0;
33656         if (flags & JS_EVAL_FLAG_STRICT)
33657             js_mode |= JS_MODE_STRICT;
33658         if (flags & JS_EVAL_FLAG_STRIP)
33659             js_mode |= JS_MODE_STRIP;
33660         if (eval_type == JS_EVAL_TYPE_MODULE) {
33661             JSAtom module_name = JS_NewAtom(ctx, filename);
33662             if (module_name == JS_ATOM_NULL)
33663                 return JS_EXCEPTION;
33664             m = js_new_module_def(ctx, module_name);
33665             if (!m)
33666                 return JS_EXCEPTION;
33667             js_mode |= JS_MODE_STRICT;
33668         }
33669     }
33670     fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1);
33671     if (!fd)
33672         goto fail1;
33673     s->cur_func = fd;
33674     fd->eval_type = eval_type;
33675     fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);
33676     fd->backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0);
33677     if (eval_type == JS_EVAL_TYPE_DIRECT) {
33678         fd->new_target_allowed = b->new_target_allowed;
33679         fd->super_call_allowed = b->super_call_allowed;
33680         fd->super_allowed = b->super_allowed;
33681         fd->arguments_allowed = b->arguments_allowed;
33682     } else {
33683         fd->new_target_allowed = FALSE;
33684         fd->super_call_allowed = FALSE;
33685         fd->super_allowed = FALSE;
33686         fd->arguments_allowed = TRUE;
33687     }
33688     fd->js_mode = js_mode;
33689     fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_);
33690     if (b) {
33691         if (add_closure_variables(ctx, fd, b, scope_idx))
33692             goto fail;
33693     }
33694     fd->module = m;
33695     s->is_module = (m != NULL);
33696     s->allow_html_comments = !s->is_module;
33697 
33698     push_scope(s); /* body scope */
33699     fd->body_scope = fd->scope_level;
33700 
33701     err = js_parse_program(s);
33702     if (err) {
33703     fail:
33704         free_token(s, &s->token);
33705         js_free_function_def(ctx, fd);
33706         goto fail1;
33707     }
33708 
33709     /* create the function object and all the enclosed functions */
33710     fun_obj = js_create_function(ctx, fd);
33711     if (JS_IsException(fun_obj))
33712         goto fail1;
33713     /* Could add a flag to avoid resolution if necessary */
33714     if (m) {
33715         m->func_obj = fun_obj;
33716         if (js_resolve_module(ctx, m) < 0)
33717             goto fail1;
33718         fun_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
33719     }
33720     if (flags & JS_EVAL_FLAG_COMPILE_ONLY) {
33721         ret_val = fun_obj;
33722     } else {
33723         ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf);
33724     }
33725     return ret_val;
33726  fail1:
33727     /* XXX: should free all the unresolved dependencies */
33728     if (m)
33729         js_free_module_def(ctx, m);
33730     return JS_EXCEPTION;
33731 }
33732 
33733 /* the indirection is needed to make 'eval' optional */
JS_EvalInternal(JSContext * ctx,JSValueConst this_obj,const char * input,size_t input_len,const char * filename,int flags,int scope_idx)33734 static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
33735                                const char *input, size_t input_len,
33736                                const char *filename, int flags, int scope_idx)
33737 {
33738     if (unlikely(!ctx->eval_internal)) {
33739         return JS_ThrowTypeError(ctx, "eval is not supported");
33740     }
33741     return ctx->eval_internal(ctx, this_obj, input, input_len, filename,
33742                               flags, scope_idx);
33743 }
33744 
JS_EvalObject(JSContext * ctx,JSValueConst this_obj,JSValueConst val,int flags,int scope_idx)33745 static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
33746                              JSValueConst val, int flags, int scope_idx)
33747 {
33748     JSValue ret;
33749     const char *str;
33750     size_t len;
33751 
33752     if (!JS_IsString(val))
33753         return JS_DupValue(ctx, val);
33754     str = JS_ToCStringLen(ctx, &len, val);
33755     if (!str)
33756         return JS_EXCEPTION;
33757     ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx);
33758     JS_FreeCString(ctx, str);
33759     return ret;
33760 
33761 }
33762 
JS_EvalThis(JSContext * ctx,JSValueConst this_obj,const char * input,size_t input_len,const char * filename,int eval_flags)33763 JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
33764                     const char *input, size_t input_len,
33765                     const char *filename, int eval_flags)
33766 {
33767     int eval_type = eval_flags & JS_EVAL_TYPE_MASK;
33768     JSValue ret;
33769 
33770     assert(eval_type == JS_EVAL_TYPE_GLOBAL ||
33771            eval_type == JS_EVAL_TYPE_MODULE);
33772     ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename,
33773                           eval_flags, -1);
33774     return ret;
33775 }
33776 
JS_Eval(JSContext * ctx,const char * input,size_t input_len,const char * filename,int eval_flags)33777 JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
33778                 const char *filename, int eval_flags)
33779 {
33780     return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename,
33781                        eval_flags);
33782 }
33783 
JS_ResolveModule(JSContext * ctx,JSValueConst obj)33784 int JS_ResolveModule(JSContext *ctx, JSValueConst obj)
33785 {
33786     if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
33787         JSModuleDef *m = JS_VALUE_GET_PTR(obj);
33788         if (js_resolve_module(ctx, m) < 0) {
33789             js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
33790             return -1;
33791         }
33792     }
33793     return 0;
33794 }
33795 
33796 /*******************************************************************/
33797 /* object list */
33798 
33799 typedef struct {
33800     JSObject *obj;
33801     uint32_t hash_next; /* -1 if no next entry */
33802 } JSObjectListEntry;
33803 
33804 /* XXX: reuse it to optimize weak references */
33805 typedef struct {
33806     JSObjectListEntry *object_tab;
33807     int object_count;
33808     int object_size;
33809     uint32_t *hash_table;
33810     uint32_t hash_size;
33811 } JSObjectList;
33812 
js_object_list_init(JSObjectList * s)33813 static void js_object_list_init(JSObjectList *s)
33814 {
33815     memset(s, 0, sizeof(*s));
33816 }
33817 
js_object_list_get_hash(JSObject * p,uint32_t hash_size)33818 static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size)
33819 {
33820     return ((uintptr_t)p * 3163) & (hash_size - 1);
33821 }
33822 
js_object_list_resize_hash(JSContext * ctx,JSObjectList * s,uint32_t new_hash_size)33823 static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s,
33824                                  uint32_t new_hash_size)
33825 {
33826     JSObjectListEntry *e;
33827     uint32_t i, h, *new_hash_table;
33828 
33829     new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);
33830     if (!new_hash_table)
33831         return -1;
33832     js_free(ctx, s->hash_table);
33833     s->hash_table = new_hash_table;
33834     s->hash_size = new_hash_size;
33835 
33836     for(i = 0; i < s->hash_size; i++) {
33837         s->hash_table[i] = -1;
33838     }
33839     for(i = 0; i < s->object_count; i++) {
33840         e = &s->object_tab[i];
33841         h = js_object_list_get_hash(e->obj, s->hash_size);
33842         e->hash_next = s->hash_table[h];
33843         s->hash_table[h] = i;
33844     }
33845     return 0;
33846 }
33847 
33848 /* the reference count of 'obj' is not modified. Return 0 if OK, -1 if
33849    memory error */
js_object_list_add(JSContext * ctx,JSObjectList * s,JSObject * obj)33850 static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj)
33851 {
33852     JSObjectListEntry *e;
33853     uint32_t h, new_hash_size;
33854 
33855     if (js_resize_array(ctx, (void *)&s->object_tab,
33856                         sizeof(s->object_tab[0]),
33857                         &s->object_size, s->object_count + 1))
33858         return -1;
33859     if (unlikely((s->object_count + 1) >= s->hash_size)) {
33860         new_hash_size = max_uint32(s->hash_size, 4);
33861         while (new_hash_size <= s->object_count)
33862             new_hash_size *= 2;
33863         if (js_object_list_resize_hash(ctx, s, new_hash_size))
33864             return -1;
33865     }
33866     e = &s->object_tab[s->object_count++];
33867     h = js_object_list_get_hash(obj, s->hash_size);
33868     e->obj = obj;
33869     e->hash_next = s->hash_table[h];
33870     s->hash_table[h] = s->object_count - 1;
33871     return 0;
33872 }
33873 
33874 /* return -1 if not present or the object index */
js_object_list_find(JSContext * ctx,JSObjectList * s,JSObject * obj)33875 static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj)
33876 {
33877     JSObjectListEntry *e;
33878     uint32_t h, p;
33879 
33880     /* must test empty size because there is no hash table */
33881     if (s->object_count == 0)
33882         return -1;
33883     h = js_object_list_get_hash(obj, s->hash_size);
33884     p = s->hash_table[h];
33885     while (p != -1) {
33886         e = &s->object_tab[p];
33887         if (e->obj == obj)
33888             return p;
33889         p = e->hash_next;
33890     }
33891     return -1;
33892 }
33893 
js_object_list_end(JSContext * ctx,JSObjectList * s)33894 static void js_object_list_end(JSContext *ctx, JSObjectList *s)
33895 {
33896     js_free(ctx, s->object_tab);
33897     js_free(ctx, s->hash_table);
33898 }
33899 
33900 /*******************************************************************/
33901 /* binary object writer & reader */
33902 
33903 typedef enum BCTagEnum {
33904     BC_TAG_NULL = 1,
33905     BC_TAG_UNDEFINED,
33906     BC_TAG_BOOL_FALSE,
33907     BC_TAG_BOOL_TRUE,
33908     BC_TAG_INT32,
33909     BC_TAG_FLOAT64,
33910     BC_TAG_STRING,
33911     BC_TAG_OBJECT,
33912     BC_TAG_ARRAY,
33913     BC_TAG_BIG_INT,
33914     BC_TAG_BIG_FLOAT,
33915     BC_TAG_BIG_DECIMAL,
33916     BC_TAG_TEMPLATE_OBJECT,
33917     BC_TAG_FUNCTION_BYTECODE,
33918     BC_TAG_MODULE,
33919     BC_TAG_TYPED_ARRAY,
33920     BC_TAG_ARRAY_BUFFER,
33921     BC_TAG_SHARED_ARRAY_BUFFER,
33922     BC_TAG_DATE,
33923     BC_TAG_OBJECT_VALUE,
33924     BC_TAG_OBJECT_REFERENCE,
33925 } BCTagEnum;
33926 
33927 #ifdef CONFIG_BIGNUM
33928 #define BC_BASE_VERSION 2
33929 #else
33930 #define BC_BASE_VERSION 1
33931 #endif
33932 #define BC_BE_VERSION 0x40
33933 #ifdef WORDS_BIGENDIAN
33934 #define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION)
33935 #else
33936 #define BC_VERSION BC_BASE_VERSION
33937 #endif
33938 
33939 typedef struct BCWriterState {
33940     JSContext *ctx;
33941     DynBuf dbuf;
33942     BOOL byte_swap : 8;
33943     BOOL allow_bytecode : 8;
33944     BOOL allow_sab : 8;
33945     BOOL allow_reference : 8;
33946     uint32_t first_atom;
33947     uint32_t *atom_to_idx;
33948     int atom_to_idx_size;
33949     JSAtom *idx_to_atom;
33950     int idx_to_atom_count;
33951     int idx_to_atom_size;
33952     uint8_t **sab_tab;
33953     int sab_tab_len;
33954     int sab_tab_size;
33955     /* list of referenced objects (used if allow_reference = TRUE) */
33956     JSObjectList object_list;
33957 } BCWriterState;
33958 
33959 #ifdef DUMP_READ_OBJECT
33960 static const char * const bc_tag_str[] = {
33961     "invalid",
33962     "null",
33963     "undefined",
33964     "false",
33965     "true",
33966     "int32",
33967     "float64",
33968     "string",
33969     "object",
33970     "array",
33971     "bigint",
33972     "bigfloat",
33973     "bigdecimal",
33974     "template",
33975     "function",
33976     "module",
33977     "TypedArray",
33978     "ArrayBuffer",
33979     "SharedArrayBuffer",
33980     "Date",
33981     "ObjectValue",
33982     "ObjectReference",
33983 };
33984 #endif
33985 
bc_put_u8(BCWriterState * s,uint8_t v)33986 static void bc_put_u8(BCWriterState *s, uint8_t v)
33987 {
33988     dbuf_putc(&s->dbuf, v);
33989 }
33990 
bc_put_u16(BCWriterState * s,uint16_t v)33991 static void bc_put_u16(BCWriterState *s, uint16_t v)
33992 {
33993     if (s->byte_swap)
33994         v = bswap16(v);
33995     dbuf_put_u16(&s->dbuf, v);
33996 }
33997 
bc_put_u32(BCWriterState * s,uint32_t v)33998 static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)
33999 {
34000     if (s->byte_swap)
34001         v = bswap32(v);
34002     dbuf_put_u32(&s->dbuf, v);
34003 }
34004 
bc_put_u64(BCWriterState * s,uint64_t v)34005 static void bc_put_u64(BCWriterState *s, uint64_t v)
34006 {
34007     if (s->byte_swap)
34008         v = bswap64(v);
34009     dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v));
34010 }
34011 
bc_put_leb128(BCWriterState * s,uint32_t v)34012 static void bc_put_leb128(BCWriterState *s, uint32_t v)
34013 {
34014     dbuf_put_leb128(&s->dbuf, v);
34015 }
34016 
bc_put_sleb128(BCWriterState * s,int32_t v)34017 static void bc_put_sleb128(BCWriterState *s, int32_t v)
34018 {
34019     dbuf_put_sleb128(&s->dbuf, v);
34020 }
34021 
bc_set_flags(uint32_t * pflags,int * pidx,uint32_t val,int n)34022 static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n)
34023 {
34024     *pflags = *pflags | (val << *pidx);
34025     *pidx += n;
34026 }
34027 
bc_atom_to_idx(BCWriterState * s,uint32_t * pres,JSAtom atom)34028 static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)
34029 {
34030     uint32_t v;
34031 
34032     if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) {
34033         *pres = atom;
34034         return 0;
34035     }
34036     atom -= s->first_atom;
34037     if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) {
34038         *pres = s->atom_to_idx[atom];
34039         return 0;
34040     }
34041     if (atom >= s->atom_to_idx_size) {
34042         int old_size, i;
34043         old_size = s->atom_to_idx_size;
34044         if (js_resize_array(s->ctx, (void **)&s->atom_to_idx,
34045                             sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size,
34046                             atom + 1))
34047             return -1;
34048         /* XXX: could add a specific js_resize_array() function to do it */
34049         for(i = old_size; i < s->atom_to_idx_size; i++)
34050             s->atom_to_idx[i] = 0;
34051     }
34052     if (js_resize_array(s->ctx, (void **)&s->idx_to_atom,
34053                         sizeof(s->idx_to_atom[0]),
34054                         &s->idx_to_atom_size, s->idx_to_atom_count + 1))
34055         goto fail;
34056 
34057     v = s->idx_to_atom_count++;
34058     s->idx_to_atom[v] = atom + s->first_atom;
34059     v += s->first_atom;
34060     s->atom_to_idx[atom] = v;
34061     *pres = v;
34062     return 0;
34063  fail:
34064     *pres = 0;
34065     return -1;
34066 }
34067 
bc_put_atom(BCWriterState * s,JSAtom atom)34068 static int bc_put_atom(BCWriterState *s, JSAtom atom)
34069 {
34070     uint32_t v;
34071 
34072     if (__JS_AtomIsTaggedInt(atom)) {
34073         v = (__JS_AtomToUInt32(atom) << 1) | 1;
34074     } else {
34075         if (bc_atom_to_idx(s, &v, atom))
34076             return -1;
34077         v <<= 1;
34078     }
34079     bc_put_leb128(s, v);
34080     return 0;
34081 }
34082 
bc_byte_swap(uint8_t * bc_buf,int bc_len)34083 static void bc_byte_swap(uint8_t *bc_buf, int bc_len)
34084 {
34085     int pos, len, op, fmt;
34086 
34087     pos = 0;
34088     while (pos < bc_len) {
34089         op = bc_buf[pos];
34090         len = short_opcode_info(op).size;
34091         fmt = short_opcode_info(op).fmt;
34092         switch(fmt) {
34093         case OP_FMT_u16:
34094         case OP_FMT_i16:
34095         case OP_FMT_label16:
34096         case OP_FMT_npop:
34097         case OP_FMT_loc:
34098         case OP_FMT_arg:
34099         case OP_FMT_var_ref:
34100             put_u16(bc_buf + pos + 1,
34101                     bswap16(get_u16(bc_buf + pos + 1)));
34102             break;
34103         case OP_FMT_i32:
34104         case OP_FMT_u32:
34105         case OP_FMT_const:
34106         case OP_FMT_label:
34107         case OP_FMT_atom:
34108         case OP_FMT_atom_u8:
34109             put_u32(bc_buf + pos + 1,
34110                     bswap32(get_u32(bc_buf + pos + 1)));
34111             break;
34112         case OP_FMT_atom_u16:
34113         case OP_FMT_label_u16:
34114             put_u32(bc_buf + pos + 1,
34115                     bswap32(get_u32(bc_buf + pos + 1)));
34116             put_u16(bc_buf + pos + 1 + 4,
34117                     bswap16(get_u16(bc_buf + pos + 1 + 4)));
34118             break;
34119         case OP_FMT_atom_label_u8:
34120         case OP_FMT_atom_label_u16:
34121             put_u32(bc_buf + pos + 1,
34122                     bswap32(get_u32(bc_buf + pos + 1)));
34123             put_u32(bc_buf + pos + 1 + 4,
34124                     bswap32(get_u32(bc_buf + pos + 1 + 4)));
34125             if (fmt == OP_FMT_atom_label_u16) {
34126                 put_u16(bc_buf + pos + 1 + 4 + 4,
34127                         bswap16(get_u16(bc_buf + pos + 1 + 4 + 4)));
34128             }
34129             break;
34130         case OP_FMT_npop_u16:
34131             put_u16(bc_buf + pos + 1,
34132                     bswap16(get_u16(bc_buf + pos + 1)));
34133             put_u16(bc_buf + pos + 1 + 2,
34134                     bswap16(get_u16(bc_buf + pos + 1 + 2)));
34135             break;
34136         default:
34137             break;
34138         }
34139         pos += len;
34140     }
34141 }
34142 
JS_WriteFunctionBytecode(BCWriterState * s,const uint8_t * bc_buf1,int bc_len)34143 static int JS_WriteFunctionBytecode(BCWriterState *s,
34144                                     const uint8_t *bc_buf1, int bc_len)
34145 {
34146     int pos, len, op;
34147     JSAtom atom;
34148     uint8_t *bc_buf;
34149     uint32_t val;
34150 
34151     bc_buf = js_malloc(s->ctx, bc_len);
34152     if (!bc_buf)
34153         return -1;
34154     memcpy(bc_buf, bc_buf1, bc_len);
34155 
34156     pos = 0;
34157     while (pos < bc_len) {
34158         op = bc_buf[pos];
34159         len = short_opcode_info(op).size;
34160         switch(short_opcode_info(op).fmt) {
34161         case OP_FMT_atom:
34162         case OP_FMT_atom_u8:
34163         case OP_FMT_atom_u16:
34164         case OP_FMT_atom_label_u8:
34165         case OP_FMT_atom_label_u16:
34166             atom = get_u32(bc_buf + pos + 1);
34167             if (bc_atom_to_idx(s, &val, atom))
34168                 goto fail;
34169             put_u32(bc_buf + pos + 1, val);
34170             break;
34171         default:
34172             break;
34173         }
34174         pos += len;
34175     }
34176 
34177     if (s->byte_swap)
34178         bc_byte_swap(bc_buf, bc_len);
34179 
34180     dbuf_put(&s->dbuf, bc_buf, bc_len);
34181 
34182     js_free(s->ctx, bc_buf);
34183     return 0;
34184  fail:
34185     js_free(s->ctx, bc_buf);
34186     return -1;
34187 }
34188 
JS_WriteString(BCWriterState * s,JSString * p)34189 static void JS_WriteString(BCWriterState *s, JSString *p)
34190 {
34191     int i;
34192     bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char);
34193     if (p->is_wide_char) {
34194         for(i = 0; i < p->len; i++)
34195             bc_put_u16(s, p->u.str16[i]);
34196     } else {
34197         dbuf_put(&s->dbuf, p->u.str8, p->len);
34198     }
34199 }
34200 
34201 #ifdef CONFIG_BIGNUM
JS_WriteBigNum(BCWriterState * s,JSValueConst obj)34202 static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj)
34203 {
34204     uint32_t tag, tag1;
34205     int64_t e;
34206     JSBigFloat *bf = JS_VALUE_GET_PTR(obj);
34207     bf_t *a = &bf->num;
34208     size_t len, i, n1, j;
34209     limb_t v;
34210 
34211     tag = JS_VALUE_GET_TAG(obj);
34212     switch(tag) {
34213     case JS_TAG_BIG_INT:
34214         tag1 = BC_TAG_BIG_INT;
34215         break;
34216     case JS_TAG_BIG_FLOAT:
34217         tag1 = BC_TAG_BIG_FLOAT;
34218         break;
34219     case JS_TAG_BIG_DECIMAL:
34220         tag1 = BC_TAG_BIG_DECIMAL;
34221         break;
34222     default:
34223         abort();
34224     }
34225     bc_put_u8(s, tag1);
34226 
34227     /* sign + exponent */
34228     if (a->expn == BF_EXP_ZERO)
34229         e = 0;
34230     else if (a->expn == BF_EXP_INF)
34231         e = 1;
34232     else if (a->expn == BF_EXP_NAN)
34233         e = 2;
34234     else if (a->expn >= 0)
34235         e = a->expn + 3;
34236     else
34237         e = a->expn;
34238     e = (e << 1) | a->sign;
34239     if (e < INT32_MIN || e > INT32_MAX) {
34240         JS_ThrowInternalError(s->ctx, "bignum exponent is too large");
34241         return -1;
34242     }
34243     bc_put_sleb128(s, e);
34244 
34245     /* mantissa */
34246     if (a->len != 0) {
34247         if (tag != JS_TAG_BIG_DECIMAL) {
34248             i = 0;
34249             while (i < a->len && a->tab[i] == 0)
34250                 i++;
34251             assert(i < a->len);
34252             v = a->tab[i];
34253             n1 = sizeof(limb_t);
34254             while ((v & 0xff) == 0) {
34255                 n1--;
34256                 v >>= 8;
34257             }
34258             i++;
34259             len = (a->len - i) * sizeof(limb_t) + n1;
34260             if (len > INT32_MAX) {
34261                 JS_ThrowInternalError(s->ctx, "bignum is too large");
34262                 return -1;
34263             }
34264             bc_put_leb128(s, len);
34265             /* always saved in byte based little endian representation */
34266             for(j = 0; j < n1; j++) {
34267                 dbuf_putc(&s->dbuf, v >> (j * 8));
34268             }
34269             for(; i < a->len; i++) {
34270                 limb_t v = a->tab[i];
34271 #if LIMB_BITS == 32
34272 #ifdef WORDS_BIGENDIAN
34273                 v = bswap32(v);
34274 #endif
34275                 dbuf_put_u32(&s->dbuf, v);
34276 #else
34277 #ifdef WORDS_BIGENDIAN
34278                 v = bswap64(v);
34279 #endif
34280                 dbuf_put_u64(&s->dbuf, v);
34281 #endif
34282             }
34283         } else {
34284             int bpos, d;
34285             uint8_t v8;
34286             size_t i0;
34287 
34288             /* little endian BCD */
34289             i = 0;
34290             while (i < a->len && a->tab[i] == 0)
34291                 i++;
34292             assert(i < a->len);
34293             len = a->len * LIMB_DIGITS;
34294             v = a->tab[i];
34295             j = 0;
34296             while ((v % 10) == 0) {
34297                 j++;
34298                 v /= 10;
34299             }
34300             len -= j;
34301             assert(len > 0);
34302             if (len > INT32_MAX) {
34303                 JS_ThrowInternalError(s->ctx, "bignum is too large");
34304                 return -1;
34305             }
34306             bc_put_leb128(s, len);
34307 
34308             bpos = 0;
34309             v8 = 0;
34310             i0 = i;
34311             for(; i < a->len; i++) {
34312                 if (i != i0) {
34313                     v = a->tab[i];
34314                     j = 0;
34315                 }
34316                 for(; j < LIMB_DIGITS; j++) {
34317                     d = v % 10;
34318                     v /= 10;
34319                     if (bpos == 0) {
34320                         v8 = d;
34321                         bpos = 1;
34322                     } else {
34323                         dbuf_putc(&s->dbuf, v8 | (d << 4));
34324                         bpos = 0;
34325                     }
34326                 }
34327             }
34328             /* flush the last digit */
34329             if (bpos) {
34330                 dbuf_putc(&s->dbuf, v8);
34331             }
34332         }
34333     }
34334     return 0;
34335 }
34336 #endif /* CONFIG_BIGNUM */
34337 
34338 static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj);
34339 
JS_WriteFunctionTag(BCWriterState * s,JSValueConst obj)34340 static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)
34341 {
34342     JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj);
34343     uint32_t flags;
34344     int idx, i;
34345 
34346     bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE);
34347     flags = idx = 0;
34348     bc_set_flags(&flags, &idx, b->has_prototype, 1);
34349     bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1);
34350     bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1);
34351     bc_set_flags(&flags, &idx, b->need_home_object, 1);
34352     bc_set_flags(&flags, &idx, b->func_kind, 2);
34353     bc_set_flags(&flags, &idx, b->new_target_allowed, 1);
34354     bc_set_flags(&flags, &idx, b->super_call_allowed, 1);
34355     bc_set_flags(&flags, &idx, b->super_allowed, 1);
34356     bc_set_flags(&flags, &idx, b->arguments_allowed, 1);
34357     bc_set_flags(&flags, &idx, b->has_debug, 1);
34358     bc_set_flags(&flags, &idx, b->backtrace_barrier, 1);
34359     assert(idx <= 16);
34360     bc_put_u16(s, flags);
34361     bc_put_u8(s, b->js_mode);
34362     bc_put_atom(s, b->func_name);
34363 
34364     bc_put_leb128(s, b->arg_count);
34365     bc_put_leb128(s, b->var_count);
34366     bc_put_leb128(s, b->defined_arg_count);
34367     bc_put_leb128(s, b->stack_size);
34368     bc_put_leb128(s, b->closure_var_count);
34369     bc_put_leb128(s, b->cpool_count);
34370     bc_put_leb128(s, b->byte_code_len);
34371     if (b->vardefs) {
34372         /* XXX: this field is redundant */
34373         bc_put_leb128(s, b->arg_count + b->var_count);
34374         for(i = 0; i < b->arg_count + b->var_count; i++) {
34375             JSVarDef *vd = &b->vardefs[i];
34376             bc_put_atom(s, vd->var_name);
34377             bc_put_leb128(s, vd->scope_level);
34378             bc_put_leb128(s, vd->scope_next + 1);
34379             flags = idx = 0;
34380             bc_set_flags(&flags, &idx, vd->var_kind, 4);
34381             bc_set_flags(&flags, &idx, vd->is_const, 1);
34382             bc_set_flags(&flags, &idx, vd->is_lexical, 1);
34383             bc_set_flags(&flags, &idx, vd->is_captured, 1);
34384             assert(idx <= 8);
34385             bc_put_u8(s, flags);
34386         }
34387     } else {
34388         bc_put_leb128(s, 0);
34389     }
34390 
34391     for(i = 0; i < b->closure_var_count; i++) {
34392         JSClosureVar *cv = &b->closure_var[i];
34393         bc_put_atom(s, cv->var_name);
34394         bc_put_leb128(s, cv->var_idx);
34395         flags = idx = 0;
34396         bc_set_flags(&flags, &idx, cv->is_local, 1);
34397         bc_set_flags(&flags, &idx, cv->is_arg, 1);
34398         bc_set_flags(&flags, &idx, cv->is_const, 1);
34399         bc_set_flags(&flags, &idx, cv->is_lexical, 1);
34400         bc_set_flags(&flags, &idx, cv->var_kind, 4);
34401         assert(idx <= 8);
34402         bc_put_u8(s, flags);
34403     }
34404 
34405     if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))
34406         goto fail;
34407 
34408     if (b->has_debug) {
34409         bc_put_atom(s, b->debug.filename);
34410         bc_put_leb128(s, b->debug.line_num);
34411         bc_put_leb128(s, b->debug.pc2line_len);
34412         dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);
34413     }
34414 
34415     for(i = 0; i < b->cpool_count; i++) {
34416         if (JS_WriteObjectRec(s, b->cpool[i]))
34417             goto fail;
34418     }
34419     return 0;
34420  fail:
34421     return -1;
34422 }
34423 
JS_WriteModule(BCWriterState * s,JSValueConst obj)34424 static int JS_WriteModule(BCWriterState *s, JSValueConst obj)
34425 {
34426     JSModuleDef *m = JS_VALUE_GET_PTR(obj);
34427     int i;
34428 
34429     bc_put_u8(s, BC_TAG_MODULE);
34430     bc_put_atom(s, m->module_name);
34431 
34432     bc_put_leb128(s, m->req_module_entries_count);
34433     for(i = 0; i < m->req_module_entries_count; i++) {
34434         JSReqModuleEntry *rme = &m->req_module_entries[i];
34435         bc_put_atom(s, rme->module_name);
34436     }
34437 
34438     bc_put_leb128(s, m->export_entries_count);
34439     for(i = 0; i < m->export_entries_count; i++) {
34440         JSExportEntry *me = &m->export_entries[i];
34441         bc_put_u8(s, me->export_type);
34442         if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
34443             bc_put_leb128(s, me->u.local.var_idx);
34444         } else {
34445             bc_put_leb128(s, me->u.req_module_idx);
34446             bc_put_atom(s, me->local_name);
34447         }
34448         bc_put_atom(s, me->export_name);
34449     }
34450 
34451     bc_put_leb128(s, m->star_export_entries_count);
34452     for(i = 0; i < m->star_export_entries_count; i++) {
34453         JSStarExportEntry *se = &m->star_export_entries[i];
34454         bc_put_leb128(s, se->req_module_idx);
34455     }
34456 
34457     bc_put_leb128(s, m->import_entries_count);
34458     for(i = 0; i < m->import_entries_count; i++) {
34459         JSImportEntry *mi = &m->import_entries[i];
34460         bc_put_leb128(s, mi->var_idx);
34461         bc_put_atom(s, mi->import_name);
34462         bc_put_leb128(s, mi->req_module_idx);
34463     }
34464 
34465     if (JS_WriteObjectRec(s, m->func_obj))
34466         goto fail;
34467     return 0;
34468  fail:
34469     return -1;
34470 }
34471 
JS_WriteArray(BCWriterState * s,JSValueConst obj)34472 static int JS_WriteArray(BCWriterState *s, JSValueConst obj)
34473 {
34474     JSObject *p = JS_VALUE_GET_OBJ(obj);
34475     uint32_t i, len;
34476     JSValue val;
34477     int ret;
34478     BOOL is_template;
34479 
34480     if (s->allow_bytecode && !p->extensible) {
34481         /* not extensible array: we consider it is a
34482            template when we are saving bytecode */
34483         bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT);
34484         is_template = TRUE;
34485     } else {
34486         bc_put_u8(s, BC_TAG_ARRAY);
34487         is_template = FALSE;
34488     }
34489     if (js_get_length32(s->ctx, &len, obj))
34490         goto fail1;
34491     bc_put_leb128(s, len);
34492     for(i = 0; i < len; i++) {
34493         val = JS_GetPropertyUint32(s->ctx, obj, i);
34494         if (JS_IsException(val))
34495             goto fail1;
34496         ret = JS_WriteObjectRec(s, val);
34497         JS_FreeValue(s->ctx, val);
34498         if (ret)
34499             goto fail1;
34500     }
34501     if (is_template) {
34502         val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw);
34503         if (JS_IsException(val))
34504             goto fail1;
34505         ret = JS_WriteObjectRec(s, val);
34506         JS_FreeValue(s->ctx, val);
34507         if (ret)
34508             goto fail1;
34509     }
34510     return 0;
34511  fail1:
34512     return -1;
34513 }
34514 
JS_WriteObjectTag(BCWriterState * s,JSValueConst obj)34515 static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj)
34516 {
34517     JSObject *p = JS_VALUE_GET_OBJ(obj);
34518     uint32_t i, prop_count;
34519     JSShape *sh;
34520     JSShapeProperty *pr;
34521     int pass;
34522     JSAtom atom;
34523 
34524     bc_put_u8(s, BC_TAG_OBJECT);
34525     prop_count = 0;
34526     sh = p->shape;
34527     for(pass = 0; pass < 2; pass++) {
34528         if (pass == 1)
34529             bc_put_leb128(s, prop_count);
34530         for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
34531             atom = pr->atom;
34532             if (atom != JS_ATOM_NULL &&
34533                 JS_AtomIsString(s->ctx, atom) &&
34534                 (pr->flags & JS_PROP_ENUMERABLE)) {
34535                 if (pr->flags & JS_PROP_TMASK) {
34536                     JS_ThrowTypeError(s->ctx, "only value properties are supported");
34537                     goto fail;
34538                 }
34539                 if (pass == 0) {
34540                     prop_count++;
34541                 } else {
34542                     bc_put_atom(s, atom);
34543                     if (JS_WriteObjectRec(s, p->prop[i].u.value))
34544                         goto fail;
34545                 }
34546             }
34547         }
34548     }
34549     return 0;
34550  fail:
34551     return -1;
34552 }
34553 
JS_WriteTypedArray(BCWriterState * s,JSValueConst obj)34554 static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj)
34555 {
34556     JSObject *p = JS_VALUE_GET_OBJ(obj);
34557     JSTypedArray *ta = p->u.typed_array;
34558 
34559     bc_put_u8(s, BC_TAG_TYPED_ARRAY);
34560     bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY);
34561     bc_put_leb128(s, p->u.array.count);
34562     bc_put_leb128(s, ta->offset);
34563     if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)))
34564         return -1;
34565     return 0;
34566 }
34567 
JS_WriteArrayBuffer(BCWriterState * s,JSValueConst obj)34568 static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj)
34569 {
34570     JSObject *p = JS_VALUE_GET_OBJ(obj);
34571     JSArrayBuffer *abuf = p->u.array_buffer;
34572     if (abuf->detached) {
34573         JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx);
34574         return -1;
34575     }
34576     bc_put_u8(s, BC_TAG_ARRAY_BUFFER);
34577     bc_put_leb128(s, abuf->byte_length);
34578     dbuf_put(&s->dbuf, abuf->data, abuf->byte_length);
34579     return 0;
34580 }
34581 
JS_WriteSharedArrayBuffer(BCWriterState * s,JSValueConst obj)34582 static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj)
34583 {
34584     JSObject *p = JS_VALUE_GET_OBJ(obj);
34585     JSArrayBuffer *abuf = p->u.array_buffer;
34586     assert(!abuf->detached); /* SharedArrayBuffer are never detached */
34587     bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER);
34588     bc_put_leb128(s, abuf->byte_length);
34589     bc_put_u64(s, (uintptr_t)abuf->data);
34590     if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]),
34591                         &s->sab_tab_size, s->sab_tab_len + 1))
34592         return -1;
34593     /* keep the SAB pointer so that the user can clone it or free it */
34594     s->sab_tab[s->sab_tab_len++] = abuf->data;
34595     return 0;
34596 }
34597 
JS_WriteObjectRec(BCWriterState * s,JSValueConst obj)34598 static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)
34599 {
34600     uint32_t tag;
34601 
34602     if (js_check_stack_overflow(s->ctx->rt, 0)) {
34603         JS_ThrowStackOverflow(s->ctx);
34604         return -1;
34605     }
34606 
34607     tag = JS_VALUE_GET_NORM_TAG(obj);
34608     switch(tag) {
34609     case JS_TAG_NULL:
34610         bc_put_u8(s, BC_TAG_NULL);
34611         break;
34612     case JS_TAG_UNDEFINED:
34613         bc_put_u8(s, BC_TAG_UNDEFINED);
34614         break;
34615     case JS_TAG_BOOL:
34616         bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj));
34617         break;
34618     case JS_TAG_INT:
34619         bc_put_u8(s, BC_TAG_INT32);
34620         bc_put_sleb128(s, JS_VALUE_GET_INT(obj));
34621         break;
34622     case JS_TAG_FLOAT64:
34623         {
34624             JSFloat64Union u;
34625             bc_put_u8(s, BC_TAG_FLOAT64);
34626             u.d = JS_VALUE_GET_FLOAT64(obj);
34627             bc_put_u64(s, u.u64);
34628         }
34629         break;
34630     case JS_TAG_STRING:
34631         {
34632             JSString *p = JS_VALUE_GET_STRING(obj);
34633             bc_put_u8(s, BC_TAG_STRING);
34634             JS_WriteString(s, p);
34635         }
34636         break;
34637     case JS_TAG_FUNCTION_BYTECODE:
34638         if (!s->allow_bytecode)
34639             goto invalid_tag;
34640         if (JS_WriteFunctionTag(s, obj))
34641             goto fail;
34642         break;
34643     case JS_TAG_MODULE:
34644         if (!s->allow_bytecode)
34645             goto invalid_tag;
34646         if (JS_WriteModule(s, obj))
34647             goto fail;
34648         break;
34649     case JS_TAG_OBJECT:
34650         {
34651             JSObject *p = JS_VALUE_GET_OBJ(obj);
34652             int ret, idx;
34653 
34654             if (s->allow_reference) {
34655                 idx = js_object_list_find(s->ctx, &s->object_list, p);
34656                 if (idx >= 0) {
34657                     bc_put_u8(s, BC_TAG_OBJECT_REFERENCE);
34658                     bc_put_leb128(s, idx);
34659                     break;
34660                 } else {
34661                     if (js_object_list_add(s->ctx, &s->object_list, p))
34662                         goto fail;
34663                 }
34664             } else {
34665                 if (p->tmp_mark) {
34666                     JS_ThrowTypeError(s->ctx, "circular reference");
34667                     goto fail;
34668                 }
34669                 p->tmp_mark = 1;
34670             }
34671             switch(p->class_id) {
34672             case JS_CLASS_ARRAY:
34673                 ret = JS_WriteArray(s, obj);
34674                 break;
34675             case JS_CLASS_OBJECT:
34676                 ret = JS_WriteObjectTag(s, obj);
34677                 break;
34678             case JS_CLASS_ARRAY_BUFFER:
34679                 ret = JS_WriteArrayBuffer(s, obj);
34680                 break;
34681             case JS_CLASS_SHARED_ARRAY_BUFFER:
34682                 if (!s->allow_sab)
34683                     goto invalid_tag;
34684                 ret = JS_WriteSharedArrayBuffer(s, obj);
34685                 break;
34686             case JS_CLASS_DATE:
34687                 bc_put_u8(s, BC_TAG_DATE);
34688                 ret = JS_WriteObjectRec(s, p->u.object_data);
34689                 break;
34690             case JS_CLASS_NUMBER:
34691             case JS_CLASS_STRING:
34692             case JS_CLASS_BOOLEAN:
34693 #ifdef CONFIG_BIGNUM
34694             case JS_CLASS_BIG_INT:
34695             case JS_CLASS_BIG_FLOAT:
34696             case JS_CLASS_BIG_DECIMAL:
34697 #endif
34698                 bc_put_u8(s, BC_TAG_OBJECT_VALUE);
34699                 ret = JS_WriteObjectRec(s, p->u.object_data);
34700                 break;
34701             default:
34702                 if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
34703                     p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
34704                     ret = JS_WriteTypedArray(s, obj);
34705                 } else {
34706                     JS_ThrowTypeError(s->ctx, "unsupported object class");
34707                     ret = -1;
34708                 }
34709                 break;
34710             }
34711             p->tmp_mark = 0;
34712             if (ret)
34713                 goto fail;
34714         }
34715         break;
34716 #ifdef CONFIG_BIGNUM
34717     case JS_TAG_BIG_INT:
34718     case JS_TAG_BIG_FLOAT:
34719     case JS_TAG_BIG_DECIMAL:
34720         if (JS_WriteBigNum(s, obj))
34721             goto fail;
34722         break;
34723 #endif
34724     default:
34725     invalid_tag:
34726         JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag);
34727         goto fail;
34728     }
34729     return 0;
34730 
34731  fail:
34732     return -1;
34733 }
34734 
34735 /* create the atom table */
JS_WriteObjectAtoms(BCWriterState * s)34736 static int JS_WriteObjectAtoms(BCWriterState *s)
34737 {
34738     JSRuntime *rt = s->ctx->rt;
34739     DynBuf dbuf1;
34740     int i, atoms_size;
34741     uint8_t version;
34742 
34743     dbuf1 = s->dbuf;
34744     js_dbuf_init(s->ctx, &s->dbuf);
34745 
34746     version = BC_VERSION;
34747     if (s->byte_swap)
34748         version ^= BC_BE_VERSION;
34749     bc_put_u8(s, version);
34750 
34751     bc_put_leb128(s, s->idx_to_atom_count);
34752     for(i = 0; i < s->idx_to_atom_count; i++) {
34753         JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]];
34754         JS_WriteString(s, p);
34755     }
34756     /* XXX: should check for OOM in above phase */
34757 
34758     /* move the atoms at the start */
34759     /* XXX: could just append dbuf1 data, but it uses more memory if
34760        dbuf1 is larger than dbuf */
34761     atoms_size = s->dbuf.size;
34762     if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size))
34763         goto fail;
34764     memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size);
34765     memcpy(dbuf1.buf, s->dbuf.buf, atoms_size);
34766     dbuf1.size += atoms_size;
34767     dbuf_free(&s->dbuf);
34768     s->dbuf = dbuf1;
34769     return 0;
34770  fail:
34771     dbuf_free(&dbuf1);
34772     return -1;
34773 }
34774 
JS_WriteObject2(JSContext * ctx,size_t * psize,JSValueConst obj,int flags,uint8_t *** psab_tab,size_t * psab_tab_len)34775 uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,
34776                          int flags, uint8_t ***psab_tab, size_t *psab_tab_len)
34777 {
34778     BCWriterState ss, *s = &ss;
34779 
34780     memset(s, 0, sizeof(*s));
34781     s->ctx = ctx;
34782     /* XXX: byte swapped output is untested */
34783     s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0);
34784     s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0);
34785     s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0);
34786     s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0);
34787     /* XXX: could use a different version when bytecode is included */
34788     if (s->allow_bytecode)
34789         s->first_atom = JS_ATOM_END;
34790     else
34791         s->first_atom = 1;
34792     js_dbuf_init(ctx, &s->dbuf);
34793     js_object_list_init(&s->object_list);
34794 
34795     if (JS_WriteObjectRec(s, obj))
34796         goto fail;
34797     if (JS_WriteObjectAtoms(s))
34798         goto fail;
34799     js_object_list_end(ctx, &s->object_list);
34800     js_free(ctx, s->atom_to_idx);
34801     js_free(ctx, s->idx_to_atom);
34802     *psize = s->dbuf.size;
34803     if (psab_tab)
34804         *psab_tab = s->sab_tab;
34805     if (psab_tab_len)
34806         *psab_tab_len = s->sab_tab_len;
34807     return s->dbuf.buf;
34808  fail:
34809     js_object_list_end(ctx, &s->object_list);
34810     js_free(ctx, s->atom_to_idx);
34811     js_free(ctx, s->idx_to_atom);
34812     dbuf_free(&s->dbuf);
34813     *psize = 0;
34814     if (psab_tab)
34815         *psab_tab = NULL;
34816     if (psab_tab_len)
34817         *psab_tab_len = 0;
34818     return NULL;
34819 }
34820 
JS_WriteObject(JSContext * ctx,size_t * psize,JSValueConst obj,int flags)34821 uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
34822                         int flags)
34823 {
34824     return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL);
34825 }
34826 
34827 typedef struct BCReaderState {
34828     JSContext *ctx;
34829     const uint8_t *buf_start, *ptr, *buf_end;
34830     uint32_t first_atom;
34831     uint32_t idx_to_atom_count;
34832     JSAtom *idx_to_atom;
34833     int error_state;
34834     BOOL allow_sab : 8;
34835     BOOL allow_bytecode : 8;
34836     BOOL is_rom_data : 8;
34837     BOOL allow_reference : 8;
34838     /* object references */
34839     JSObject **objects;
34840     int objects_count;
34841     int objects_size;
34842 
34843 #ifdef DUMP_READ_OBJECT
34844     const uint8_t *ptr_last;
34845     int level;
34846 #endif
34847 } BCReaderState;
34848 
34849 #ifdef DUMP_READ_OBJECT
bc_read_trace(BCReaderState * s,const char * fmt,...)34850 static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) {
34851     va_list ap;
34852     int i, n, n0;
34853 
34854     if (!s->ptr_last)
34855         s->ptr_last = s->buf_start;
34856 
34857     n = n0 = 0;
34858     if (s->ptr > s->ptr_last || s->ptr == s->buf_start) {
34859         n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start));
34860         n += n0;
34861     }
34862     for (i = 0; s->ptr_last < s->ptr; i++) {
34863         if ((i & 7) == 0 && i > 0) {
34864             printf("\n%*s", n0, "");
34865             n = n0;
34866         }
34867         n += printf(" %02x", *s->ptr_last++);
34868     }
34869     if (*fmt == '}')
34870         s->level--;
34871     if (n < 32 + s->level * 2) {
34872         printf("%*s", 32 + s->level * 2 - n, "");
34873     }
34874     va_start(ap, fmt);
34875     vfprintf(stdout, fmt, ap);
34876     va_end(ap);
34877     if (strchr(fmt, '{'))
34878         s->level++;
34879 }
34880 #else
34881 #define bc_read_trace(...)
34882 #endif
34883 
bc_read_error_end(BCReaderState * s)34884 static int bc_read_error_end(BCReaderState *s)
34885 {
34886     if (!s->error_state) {
34887         JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer");
34888     }
34889     return s->error_state = -1;
34890 }
34891 
bc_get_u8(BCReaderState * s,uint8_t * pval)34892 static int bc_get_u8(BCReaderState *s, uint8_t *pval)
34893 {
34894     if (unlikely(s->buf_end - s->ptr < 1)) {
34895         *pval = 0; /* avoid warning */
34896         return bc_read_error_end(s);
34897     }
34898     *pval = *s->ptr++;
34899     return 0;
34900 }
34901 
bc_get_u16(BCReaderState * s,uint16_t * pval)34902 static int bc_get_u16(BCReaderState *s, uint16_t *pval)
34903 {
34904     if (unlikely(s->buf_end - s->ptr < 2)) {
34905         *pval = 0; /* avoid warning */
34906         return bc_read_error_end(s);
34907     }
34908     *pval = get_u16(s->ptr);
34909     s->ptr += 2;
34910     return 0;
34911 }
34912 
bc_get_u32(BCReaderState * s,uint32_t * pval)34913 static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)
34914 {
34915     if (unlikely(s->buf_end - s->ptr < 4)) {
34916         *pval = 0; /* avoid warning */
34917         return bc_read_error_end(s);
34918     }
34919     *pval = get_u32(s->ptr);
34920     s->ptr += 4;
34921     return 0;
34922 }
34923 
bc_get_u64(BCReaderState * s,uint64_t * pval)34924 static int bc_get_u64(BCReaderState *s, uint64_t *pval)
34925 {
34926     if (unlikely(s->buf_end - s->ptr < 8)) {
34927         *pval = 0; /* avoid warning */
34928         return bc_read_error_end(s);
34929     }
34930     *pval = get_u64(s->ptr);
34931     s->ptr += 8;
34932     return 0;
34933 }
34934 
bc_get_leb128(BCReaderState * s,uint32_t * pval)34935 static int bc_get_leb128(BCReaderState *s, uint32_t *pval)
34936 {
34937     int ret;
34938     ret = get_leb128(pval, s->ptr, s->buf_end);
34939     if (unlikely(ret < 0))
34940         return bc_read_error_end(s);
34941     s->ptr += ret;
34942     return 0;
34943 }
34944 
bc_get_sleb128(BCReaderState * s,int32_t * pval)34945 static int bc_get_sleb128(BCReaderState *s, int32_t *pval)
34946 {
34947     int ret;
34948     ret = get_sleb128(pval, s->ptr, s->buf_end);
34949     if (unlikely(ret < 0))
34950         return bc_read_error_end(s);
34951     s->ptr += ret;
34952     return 0;
34953 }
34954 
34955 /* XXX: used to read an `int` with a positive value */
bc_get_leb128_int(BCReaderState * s,int * pval)34956 static int bc_get_leb128_int(BCReaderState *s, int *pval)
34957 {
34958     return bc_get_leb128(s, (uint32_t *)pval);
34959 }
34960 
bc_get_leb128_u16(BCReaderState * s,uint16_t * pval)34961 static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval)
34962 {
34963     uint32_t val;
34964     if (bc_get_leb128(s, &val)) {
34965         *pval = 0;
34966         return -1;
34967     }
34968     *pval = val;
34969     return 0;
34970 }
34971 
bc_get_buf(BCReaderState * s,uint8_t * buf,uint32_t buf_len)34972 static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len)
34973 {
34974     if (buf_len != 0) {
34975         if (unlikely(!buf || s->buf_end - s->ptr < buf_len))
34976             return bc_read_error_end(s);
34977         memcpy(buf, s->ptr, buf_len);
34978         s->ptr += buf_len;
34979     }
34980     return 0;
34981 }
34982 
bc_idx_to_atom(BCReaderState * s,JSAtom * patom,uint32_t idx)34983 static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx)
34984 {
34985     JSAtom atom;
34986 
34987     if (__JS_AtomIsTaggedInt(idx)) {
34988         atom = idx;
34989     } else if (idx < s->first_atom) {
34990         atom = JS_DupAtom(s->ctx, idx);
34991     } else {
34992         idx -= s->first_atom;
34993         if (idx >= s->idx_to_atom_count) {
34994             JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)",
34995                                 (unsigned int)(s->ptr - s->buf_start));
34996             *patom = JS_ATOM_NULL;
34997             return s->error_state = -1;
34998         }
34999         atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]);
35000     }
35001     *patom = atom;
35002     return 0;
35003 }
35004 
bc_get_atom(BCReaderState * s,JSAtom * patom)35005 static int bc_get_atom(BCReaderState *s, JSAtom *patom)
35006 {
35007     uint32_t v;
35008     if (bc_get_leb128(s, &v))
35009         return -1;
35010     if (v & 1) {
35011         *patom = __JS_AtomFromUInt32(v >> 1);
35012         return 0;
35013     } else {
35014         return bc_idx_to_atom(s, patom, v >> 1);
35015     }
35016 }
35017 
JS_ReadString(BCReaderState * s)35018 static JSString *JS_ReadString(BCReaderState *s)
35019 {
35020     uint32_t len;
35021     size_t size;
35022     BOOL is_wide_char;
35023     JSString *p;
35024 
35025     if (bc_get_leb128(s, &len))
35026         return NULL;
35027     is_wide_char = len & 1;
35028     len >>= 1;
35029     p = js_alloc_string(s->ctx, len, is_wide_char);
35030     if (!p) {
35031         s->error_state = -1;
35032         return NULL;
35033     }
35034     size = (size_t)len << is_wide_char;
35035     if ((s->buf_end - s->ptr) < size) {
35036         bc_read_error_end(s);
35037         js_free_string(s->ctx->rt, p);
35038         return NULL;
35039     }
35040     memcpy(p->u.str8, s->ptr, size);
35041     s->ptr += size;
35042     if (!is_wide_char) {
35043         p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */
35044     }
35045 #ifdef DUMP_READ_OBJECT
35046     JS_DumpString(s->ctx->rt, p); printf("\n");
35047 #endif
35048     return p;
35049 }
35050 
bc_get_flags(uint32_t flags,int * pidx,int n)35051 static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n)
35052 {
35053     uint32_t val;
35054     /* XXX: this does not work for n == 32 */
35055     val = (flags >> *pidx) & ((1U << n) - 1);
35056     *pidx += n;
35057     return val;
35058 }
35059 
JS_ReadFunctionBytecode(BCReaderState * s,JSFunctionBytecode * b,int byte_code_offset,uint32_t bc_len)35060 static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,
35061                                    int byte_code_offset, uint32_t bc_len)
35062 {
35063     uint8_t *bc_buf;
35064     int pos, len, op;
35065     JSAtom atom;
35066     uint32_t idx;
35067 
35068     if (s->is_rom_data) {
35069         /* directly use the input buffer */
35070         if (unlikely(s->buf_end - s->ptr < bc_len))
35071             return bc_read_error_end(s);
35072         bc_buf = (uint8_t *)s->ptr;
35073         s->ptr += bc_len;
35074     } else {
35075         bc_buf = (void *)((uint8_t*)b + byte_code_offset);
35076         if (bc_get_buf(s, bc_buf, bc_len))
35077             return -1;
35078     }
35079     b->byte_code_buf = bc_buf;
35080 
35081     pos = 0;
35082     while (pos < bc_len) {
35083         op = bc_buf[pos];
35084         len = short_opcode_info(op).size;
35085         switch(short_opcode_info(op).fmt) {
35086         case OP_FMT_atom:
35087         case OP_FMT_atom_u8:
35088         case OP_FMT_atom_u16:
35089         case OP_FMT_atom_label_u8:
35090         case OP_FMT_atom_label_u16:
35091             idx = get_u32(bc_buf + pos + 1);
35092             if (s->is_rom_data) {
35093                 /* just increment the reference count of the atom */
35094                 JS_DupAtom(s->ctx, (JSAtom)idx);
35095             } else {
35096                 if (bc_idx_to_atom(s, &atom, idx)) {
35097                     /* Note: the atoms will be freed up to this position */
35098                     b->byte_code_len = pos;
35099                     return -1;
35100                 }
35101                 put_u32(bc_buf + pos + 1, atom);
35102 #ifdef DUMP_READ_OBJECT
35103                 bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n");
35104 #endif
35105             }
35106             break;
35107         default:
35108             break;
35109         }
35110         pos += len;
35111     }
35112     return 0;
35113 }
35114 
35115 #ifdef CONFIG_BIGNUM
JS_ReadBigNum(BCReaderState * s,int tag)35116 static JSValue JS_ReadBigNum(BCReaderState *s, int tag)
35117 {
35118     JSValue obj = JS_UNDEFINED;
35119     uint8_t v8;
35120     int32_t e;
35121     uint32_t len;
35122     limb_t l, i, n, j;
35123     JSBigFloat *p;
35124     limb_t v;
35125     bf_t *a;
35126     int bpos, d;
35127 
35128     p = js_new_bf(s->ctx);
35129     if (!p)
35130         goto fail;
35131     switch(tag) {
35132     case BC_TAG_BIG_INT:
35133         obj = JS_MKPTR(JS_TAG_BIG_INT, p);
35134         break;
35135     case BC_TAG_BIG_FLOAT:
35136         obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
35137         break;
35138     case BC_TAG_BIG_DECIMAL:
35139         obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
35140         break;
35141     default:
35142         abort();
35143     }
35144 
35145     /* sign + exponent */
35146     if (bc_get_sleb128(s, &e))
35147         goto fail;
35148 
35149     a = &p->num;
35150     a->sign = e & 1;
35151     e >>= 1;
35152     if (e == 0)
35153         a->expn = BF_EXP_ZERO;
35154     else if (e == 1)
35155         a->expn = BF_EXP_INF;
35156     else if (e == 2)
35157         a->expn = BF_EXP_NAN;
35158     else if (e >= 3)
35159         a->expn = e - 3;
35160     else
35161         a->expn = e;
35162 
35163     /* mantissa */
35164     if (a->expn != BF_EXP_ZERO &&
35165         a->expn != BF_EXP_INF &&
35166         a->expn != BF_EXP_NAN) {
35167         if (bc_get_leb128(s, &len))
35168             goto fail;
35169         bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);
35170         if (len == 0) {
35171             JS_ThrowInternalError(s->ctx, "invalid bignum length");
35172             goto fail;
35173         }
35174         if (tag != BC_TAG_BIG_DECIMAL)
35175             l = (len + sizeof(limb_t) - 1) / sizeof(limb_t);
35176         else
35177             l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS;
35178         if (bf_resize(a, l)) {
35179             JS_ThrowOutOfMemory(s->ctx);
35180             goto fail;
35181         }
35182         if (tag != BC_TAG_BIG_DECIMAL) {
35183             n = len & (sizeof(limb_t) - 1);
35184             if (n != 0) {
35185                 v = 0;
35186                 for(i = 0; i < n; i++) {
35187                     if (bc_get_u8(s, &v8))
35188                         goto fail;
35189                     v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8);
35190                 }
35191                 a->tab[0] = v;
35192                 i = 1;
35193             } else {
35194                 i = 0;
35195             }
35196             for(; i < l; i++) {
35197 #if LIMB_BITS == 32
35198                 if (bc_get_u32(s, &v))
35199                     goto fail;
35200 #ifdef WORDS_BIGENDIAN
35201                 v = bswap32(v);
35202 #endif
35203 #else
35204                 if (bc_get_u64(s, &v))
35205                     goto fail;
35206 #ifdef WORDS_BIGENDIAN
35207                 v = bswap64(v);
35208 #endif
35209 #endif
35210                 a->tab[i] = v;
35211             }
35212         } else {
35213             bpos = 0;
35214             for(i = 0; i < l; i++) {
35215                 if (i == 0 && (n = len % LIMB_DIGITS) != 0) {
35216                     j = LIMB_DIGITS - n;
35217                 } else {
35218                     j = 0;
35219                 }
35220                 v = 0;
35221                 for(; j < LIMB_DIGITS; j++) {
35222                     if (bpos == 0) {
35223                         if (bc_get_u8(s, &v8))
35224                             goto fail;
35225                         d = v8 & 0xf;
35226                         bpos = 1;
35227                     } else {
35228                         d = v8 >> 4;
35229                         bpos = 0;
35230                     }
35231                     if (d >= 10) {
35232                         JS_ThrowInternalError(s->ctx, "invalid digit");
35233                         goto fail;
35234                     }
35235                     v += mp_pow_dec[j] * d;
35236                 }
35237                 a->tab[i] = v;
35238             }
35239         }
35240     }
35241     bc_read_trace(s, "}\n");
35242     return obj;
35243  fail:
35244     JS_FreeValue(s->ctx, obj);
35245     return JS_EXCEPTION;
35246 }
35247 #endif /* CONFIG_BIGNUM */
35248 
35249 static JSValue JS_ReadObjectRec(BCReaderState *s);
35250 
BC_add_object_ref1(BCReaderState * s,JSObject * p)35251 static int BC_add_object_ref1(BCReaderState *s, JSObject *p)
35252 {
35253     if (s->allow_reference) {
35254         if (js_resize_array(s->ctx, (void *)&s->objects,
35255                             sizeof(s->objects[0]),
35256                             &s->objects_size, s->objects_count + 1))
35257             return -1;
35258         s->objects[s->objects_count++] = p;
35259     }
35260     return 0;
35261 }
35262 
BC_add_object_ref(BCReaderState * s,JSValueConst obj)35263 static int BC_add_object_ref(BCReaderState *s, JSValueConst obj)
35264 {
35265     return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj));
35266 }
35267 
JS_ReadFunctionTag(BCReaderState * s)35268 static JSValue JS_ReadFunctionTag(BCReaderState *s)
35269 {
35270     JSContext *ctx = s->ctx;
35271     JSFunctionBytecode bc, *b;
35272     JSValue obj = JS_UNDEFINED;
35273     uint16_t v16;
35274     uint8_t v8;
35275     int idx, i, local_count;
35276     int function_size, cpool_offset, byte_code_offset;
35277     int closure_var_offset, vardefs_offset;
35278 
35279     memset(&bc, 0, sizeof(bc));
35280     bc.header.ref_count = 1;
35281     //bc.gc_header.mark = 0;
35282 
35283     if (bc_get_u16(s, &v16))
35284         goto fail;
35285     idx = 0;
35286     bc.has_prototype = bc_get_flags(v16, &idx, 1);
35287     bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1);
35288     bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1);
35289     bc.need_home_object = bc_get_flags(v16, &idx, 1);
35290     bc.func_kind = bc_get_flags(v16, &idx, 2);
35291     bc.new_target_allowed = bc_get_flags(v16, &idx, 1);
35292     bc.super_call_allowed = bc_get_flags(v16, &idx, 1);
35293     bc.super_allowed = bc_get_flags(v16, &idx, 1);
35294     bc.arguments_allowed = bc_get_flags(v16, &idx, 1);
35295     bc.has_debug = bc_get_flags(v16, &idx, 1);
35296     bc.backtrace_barrier = bc_get_flags(v16, &idx, 1);
35297     bc.read_only_bytecode = s->is_rom_data;
35298     if (bc_get_u8(s, &v8))
35299         goto fail;
35300     bc.js_mode = v8;
35301     if (bc_get_atom(s, &bc.func_name))  //@ atom leak if failure
35302         goto fail;
35303     if (bc_get_leb128_u16(s, &bc.arg_count))
35304         goto fail;
35305     if (bc_get_leb128_u16(s, &bc.var_count))
35306         goto fail;
35307     if (bc_get_leb128_u16(s, &bc.defined_arg_count))
35308         goto fail;
35309     if (bc_get_leb128_u16(s, &bc.stack_size))
35310         goto fail;
35311     if (bc_get_leb128_int(s, &bc.closure_var_count))
35312         goto fail;
35313     if (bc_get_leb128_int(s, &bc.cpool_count))
35314         goto fail;
35315     if (bc_get_leb128_int(s, &bc.byte_code_len))
35316         goto fail;
35317     if (bc_get_leb128_int(s, &local_count))
35318         goto fail;
35319 
35320     if (bc.has_debug) {
35321         function_size = sizeof(*b);
35322     } else {
35323         function_size = offsetof(JSFunctionBytecode, debug);
35324     }
35325     cpool_offset = function_size;
35326     function_size += bc.cpool_count * sizeof(*bc.cpool);
35327     vardefs_offset = function_size;
35328     function_size += local_count * sizeof(*bc.vardefs);
35329     closure_var_offset = function_size;
35330     function_size += bc.closure_var_count * sizeof(*bc.closure_var);
35331     byte_code_offset = function_size;
35332     if (!bc.read_only_bytecode) {
35333         function_size += bc.byte_code_len;
35334     }
35335 
35336     b = js_mallocz(ctx, function_size);
35337     if (!b)
35338         return JS_EXCEPTION;
35339 
35340     memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));
35341     b->header.ref_count = 1;
35342     if (local_count != 0) {
35343         b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
35344     }
35345     if (b->closure_var_count != 0) {
35346         b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
35347     }
35348     if (b->cpool_count != 0) {
35349         b->cpool = (void *)((uint8_t*)b + cpool_offset);
35350     }
35351 
35352     add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
35353 
35354     obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
35355 
35356 #ifdef DUMP_READ_OBJECT
35357     bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n");
35358 #endif
35359     bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n",
35360                   b->arg_count, b->var_count, b->defined_arg_count,
35361                   b->closure_var_count, b->cpool_count);
35362     bc_read_trace(s, "stack=%d bclen=%d locals=%d\n",
35363                   b->stack_size, b->byte_code_len, local_count);
35364 
35365     if (local_count != 0) {
35366         bc_read_trace(s, "vars {\n");
35367         for(i = 0; i < local_count; i++) {
35368             JSVarDef *vd = &b->vardefs[i];
35369             if (bc_get_atom(s, &vd->var_name))
35370                 goto fail;
35371             if (bc_get_leb128_int(s, &vd->scope_level))
35372                 goto fail;
35373             if (bc_get_leb128_int(s, &vd->scope_next))
35374                 goto fail;
35375             vd->scope_next--;
35376             if (bc_get_u8(s, &v8))
35377                 goto fail;
35378             idx = 0;
35379             vd->var_kind = bc_get_flags(v8, &idx, 4);
35380             vd->is_const = bc_get_flags(v8, &idx, 1);
35381             vd->is_lexical = bc_get_flags(v8, &idx, 1);
35382             vd->is_captured = bc_get_flags(v8, &idx, 1);
35383 #ifdef DUMP_READ_OBJECT
35384             bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");
35385 #endif
35386         }
35387         bc_read_trace(s, "}\n");
35388     }
35389     if (b->closure_var_count != 0) {
35390         bc_read_trace(s, "closure vars {\n");
35391         for(i = 0; i < b->closure_var_count; i++) {
35392             JSClosureVar *cv = &b->closure_var[i];
35393             int var_idx;
35394             if (bc_get_atom(s, &cv->var_name))
35395                 goto fail;
35396             if (bc_get_leb128_int(s, &var_idx))
35397                 goto fail;
35398             cv->var_idx = var_idx;
35399             if (bc_get_u8(s, &v8))
35400                 goto fail;
35401             idx = 0;
35402             cv->is_local = bc_get_flags(v8, &idx, 1);
35403             cv->is_arg = bc_get_flags(v8, &idx, 1);
35404             cv->is_const = bc_get_flags(v8, &idx, 1);
35405             cv->is_lexical = bc_get_flags(v8, &idx, 1);
35406             cv->var_kind = bc_get_flags(v8, &idx, 4);
35407 #ifdef DUMP_READ_OBJECT
35408             bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n");
35409 #endif
35410         }
35411         bc_read_trace(s, "}\n");
35412     }
35413     {
35414         bc_read_trace(s, "bytecode {\n");
35415         if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len))
35416             goto fail;
35417         bc_read_trace(s, "}\n");
35418     }
35419     if (b->has_debug) {
35420         /* read optional debug information */
35421         bc_read_trace(s, "debug {\n");
35422         if (bc_get_atom(s, &b->debug.filename))
35423             goto fail;
35424         if (bc_get_leb128_int(s, &b->debug.line_num))
35425             goto fail;
35426         if (bc_get_leb128_int(s, &b->debug.pc2line_len))
35427             goto fail;
35428         if (b->debug.pc2line_len) {
35429             b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len);
35430             if (!b->debug.pc2line_buf)
35431                 goto fail;
35432             if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))
35433                 goto fail;
35434         }
35435 #ifdef DUMP_READ_OBJECT
35436         bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");
35437 #endif
35438         bc_read_trace(s, "}\n");
35439     }
35440     if (b->cpool_count != 0) {
35441         bc_read_trace(s, "cpool {\n");
35442         for(i = 0; i < b->cpool_count; i++) {
35443             JSValue val;
35444             val = JS_ReadObjectRec(s);
35445             if (JS_IsException(val))
35446                 goto fail;
35447             b->cpool[i] = val;
35448         }
35449         bc_read_trace(s, "}\n");
35450     }
35451     b->realm = JS_DupContext(ctx);
35452     return obj;
35453  fail:
35454     JS_FreeValue(ctx, obj);
35455     return JS_EXCEPTION;
35456 }
35457 
JS_ReadModule(BCReaderState * s)35458 static JSValue JS_ReadModule(BCReaderState *s)
35459 {
35460     JSContext *ctx = s->ctx;
35461     JSValue obj;
35462     JSModuleDef *m = NULL;
35463     JSAtom module_name;
35464     int i;
35465     uint8_t v8;
35466 
35467     if (bc_get_atom(s, &module_name))
35468         goto fail;
35469 #ifdef DUMP_READ_OBJECT
35470     bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n");
35471 #endif
35472     m = js_new_module_def(ctx, module_name);
35473     if (!m)
35474         goto fail;
35475     obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
35476     if (bc_get_leb128_int(s, &m->req_module_entries_count))
35477         goto fail;
35478     if (m->req_module_entries_count != 0) {
35479         m->req_module_entries_size = m->req_module_entries_count;
35480         m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size);
35481         if (!m->req_module_entries)
35482             goto fail;
35483         for(i = 0; i < m->req_module_entries_count; i++) {
35484             JSReqModuleEntry *rme = &m->req_module_entries[i];
35485             if (bc_get_atom(s, &rme->module_name))
35486                 goto fail;
35487         }
35488     }
35489 
35490     if (bc_get_leb128_int(s, &m->export_entries_count))
35491         goto fail;
35492     if (m->export_entries_count != 0) {
35493         m->export_entries_size = m->export_entries_count;
35494         m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size);
35495         if (!m->export_entries)
35496             goto fail;
35497         for(i = 0; i < m->export_entries_count; i++) {
35498             JSExportEntry *me = &m->export_entries[i];
35499             if (bc_get_u8(s, &v8))
35500                 goto fail;
35501             me->export_type = v8;
35502             if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
35503                 if (bc_get_leb128_int(s, &me->u.local.var_idx))
35504                     goto fail;
35505             } else {
35506                 if (bc_get_leb128_int(s, &me->u.req_module_idx))
35507                     goto fail;
35508                 if (bc_get_atom(s, &me->local_name))
35509                     goto fail;
35510             }
35511             if (bc_get_atom(s, &me->export_name))
35512                 goto fail;
35513         }
35514     }
35515 
35516     if (bc_get_leb128_int(s, &m->star_export_entries_count))
35517         goto fail;
35518     if (m->star_export_entries_count != 0) {
35519         m->star_export_entries_size = m->star_export_entries_count;
35520         m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size);
35521         if (!m->star_export_entries)
35522             goto fail;
35523         for(i = 0; i < m->star_export_entries_count; i++) {
35524             JSStarExportEntry *se = &m->star_export_entries[i];
35525             if (bc_get_leb128_int(s, &se->req_module_idx))
35526                 goto fail;
35527         }
35528     }
35529 
35530     if (bc_get_leb128_int(s, &m->import_entries_count))
35531         goto fail;
35532     if (m->import_entries_count != 0) {
35533         m->import_entries_size = m->import_entries_count;
35534         m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size);
35535         if (!m->import_entries)
35536             goto fail;
35537         for(i = 0; i < m->import_entries_count; i++) {
35538             JSImportEntry *mi = &m->import_entries[i];
35539             if (bc_get_leb128_int(s, &mi->var_idx))
35540                 goto fail;
35541             if (bc_get_atom(s, &mi->import_name))
35542                 goto fail;
35543             if (bc_get_leb128_int(s, &mi->req_module_idx))
35544                 goto fail;
35545         }
35546     }
35547 
35548     m->func_obj = JS_ReadObjectRec(s);
35549     if (JS_IsException(m->func_obj))
35550         goto fail;
35551     return obj;
35552  fail:
35553     if (m) {
35554         js_free_module_def(ctx, m);
35555     }
35556     return JS_EXCEPTION;
35557 }
35558 
JS_ReadObjectTag(BCReaderState * s)35559 static JSValue JS_ReadObjectTag(BCReaderState *s)
35560 {
35561     JSContext *ctx = s->ctx;
35562     JSValue obj;
35563     uint32_t prop_count, i;
35564     JSAtom atom;
35565     JSValue val;
35566     int ret;
35567 
35568     obj = JS_NewObject(ctx);
35569     if (BC_add_object_ref(s, obj))
35570         goto fail;
35571     if (bc_get_leb128(s, &prop_count))
35572         goto fail;
35573     for(i = 0; i < prop_count; i++) {
35574         if (bc_get_atom(s, &atom))
35575             goto fail;
35576 #ifdef DUMP_READ_OBJECT
35577         bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n");
35578 #endif
35579         val = JS_ReadObjectRec(s);
35580         if (JS_IsException(val)) {
35581             JS_FreeAtom(ctx, atom);
35582             goto fail;
35583         }
35584         ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E);
35585         JS_FreeAtom(ctx, atom);
35586         if (ret < 0)
35587             goto fail;
35588     }
35589     return obj;
35590  fail:
35591     JS_FreeValue(ctx, obj);
35592     return JS_EXCEPTION;
35593 }
35594 
JS_ReadArray(BCReaderState * s,int tag)35595 static JSValue JS_ReadArray(BCReaderState *s, int tag)
35596 {
35597     JSContext *ctx = s->ctx;
35598     JSValue obj;
35599     uint32_t len, i;
35600     JSValue val;
35601     int ret, prop_flags;
35602     BOOL is_template;
35603 
35604     obj = JS_NewArray(ctx);
35605     if (BC_add_object_ref(s, obj))
35606         goto fail;
35607     is_template = (tag == BC_TAG_TEMPLATE_OBJECT);
35608     if (bc_get_leb128(s, &len))
35609         goto fail;
35610     for(i = 0; i < len; i++) {
35611         val = JS_ReadObjectRec(s);
35612         if (JS_IsException(val))
35613             goto fail;
35614         if (is_template)
35615             prop_flags = JS_PROP_ENUMERABLE;
35616         else
35617             prop_flags = JS_PROP_C_W_E;
35618         ret = JS_DefinePropertyValueUint32(ctx, obj, i, val,
35619                                            prop_flags);
35620         if (ret < 0)
35621             goto fail;
35622     }
35623     if (is_template) {
35624         val = JS_ReadObjectRec(s);
35625         if (JS_IsException(val))
35626             goto fail;
35627         if (!JS_IsUndefined(val)) {
35628             ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0);
35629             if (ret < 0)
35630                 goto fail;
35631         }
35632         JS_PreventExtensions(ctx, obj);
35633     }
35634     return obj;
35635  fail:
35636     JS_FreeValue(ctx, obj);
35637     return JS_EXCEPTION;
35638 }
35639 
JS_ReadTypedArray(BCReaderState * s)35640 static JSValue JS_ReadTypedArray(BCReaderState *s)
35641 {
35642     JSContext *ctx = s->ctx;
35643     JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED;
35644     uint8_t array_tag;
35645     JSValueConst args[3];
35646     uint32_t offset, len, idx;
35647 
35648     if (bc_get_u8(s, &array_tag))
35649         return JS_EXCEPTION;
35650     if (array_tag >= JS_TYPED_ARRAY_COUNT)
35651         return JS_ThrowTypeError(ctx, "invalid typed array");
35652     if (bc_get_leb128(s, &len))
35653         return JS_EXCEPTION;
35654     if (bc_get_leb128(s, &offset))
35655         return JS_EXCEPTION;
35656     /* XXX: this hack could be avoided if the typed array could be
35657        created before the array buffer */
35658     idx = s->objects_count;
35659     if (BC_add_object_ref1(s, NULL))
35660         goto fail;
35661     array_buffer = JS_ReadObjectRec(s);
35662     if (JS_IsException(array_buffer))
35663         return JS_EXCEPTION;
35664     if (!js_get_array_buffer(ctx, array_buffer)) {
35665         JS_FreeValue(ctx, array_buffer);
35666         return JS_EXCEPTION;
35667     }
35668     args[0] = array_buffer;
35669     args[1] = JS_NewInt64(ctx, offset);
35670     args[2] = JS_NewInt64(ctx, len);
35671     obj = js_typed_array_constructor(ctx, JS_UNDEFINED,
35672                                      3, args,
35673                                      JS_CLASS_UINT8C_ARRAY + array_tag);
35674     if (JS_IsException(obj))
35675         goto fail;
35676     if (s->allow_reference) {
35677         s->objects[idx] = JS_VALUE_GET_OBJ(obj);
35678     }
35679     JS_FreeValue(ctx, array_buffer);
35680     return obj;
35681  fail:
35682     JS_FreeValue(ctx, array_buffer);
35683     JS_FreeValue(ctx, obj);
35684     return JS_EXCEPTION;
35685 }
35686 
JS_ReadArrayBuffer(BCReaderState * s)35687 static JSValue JS_ReadArrayBuffer(BCReaderState *s)
35688 {
35689     JSContext *ctx = s->ctx;
35690     uint32_t byte_length;
35691     JSValue obj;
35692 
35693     if (bc_get_leb128(s, &byte_length))
35694         return JS_EXCEPTION;
35695     if (unlikely(s->buf_end - s->ptr < byte_length)) {
35696         bc_read_error_end(s);
35697         return JS_EXCEPTION;
35698     }
35699     obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length);
35700     if (JS_IsException(obj))
35701         goto fail;
35702     if (BC_add_object_ref(s, obj))
35703         goto fail;
35704     s->ptr += byte_length;
35705     return obj;
35706  fail:
35707     JS_FreeValue(ctx, obj);
35708     return JS_EXCEPTION;
35709 }
35710 
JS_ReadSharedArrayBuffer(BCReaderState * s)35711 static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s)
35712 {
35713     JSContext *ctx = s->ctx;
35714     uint32_t byte_length;
35715     uint8_t *data_ptr;
35716     JSValue obj;
35717     uint64_t u64;
35718 
35719     if (bc_get_leb128(s, &byte_length))
35720         return JS_EXCEPTION;
35721     if (bc_get_u64(s, &u64))
35722         return JS_EXCEPTION;
35723     data_ptr = (uint8_t *)(uintptr_t)u64;
35724     /* the SharedArrayBuffer is cloned */
35725     obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length,
35726                                        JS_CLASS_SHARED_ARRAY_BUFFER,
35727                                        data_ptr,
35728                                        NULL, NULL, FALSE);
35729     if (JS_IsException(obj))
35730         goto fail;
35731     if (BC_add_object_ref(s, obj))
35732         goto fail;
35733     return obj;
35734  fail:
35735     JS_FreeValue(ctx, obj);
35736     return JS_EXCEPTION;
35737 }
35738 
JS_ReadDate(BCReaderState * s)35739 static JSValue JS_ReadDate(BCReaderState *s)
35740 {
35741     JSContext *ctx = s->ctx;
35742     JSValue val, obj = JS_UNDEFINED;
35743 
35744     val = JS_ReadObjectRec(s);
35745     if (JS_IsException(val))
35746         goto fail;
35747     if (!JS_IsNumber(val)) {
35748         JS_ThrowTypeError(ctx, "Number tag expected for date");
35749         goto fail;
35750     }
35751     obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE],
35752                                  JS_CLASS_DATE);
35753     if (JS_IsException(obj))
35754         goto fail;
35755     if (BC_add_object_ref(s, obj))
35756         goto fail;
35757     JS_SetObjectData(ctx, obj, val);
35758     return obj;
35759  fail:
35760     JS_FreeValue(ctx, val);
35761     JS_FreeValue(ctx, obj);
35762     return JS_EXCEPTION;
35763 }
35764 
JS_ReadObjectValue(BCReaderState * s)35765 static JSValue JS_ReadObjectValue(BCReaderState *s)
35766 {
35767     JSContext *ctx = s->ctx;
35768     JSValue val, obj = JS_UNDEFINED;
35769 
35770     val = JS_ReadObjectRec(s);
35771     if (JS_IsException(val))
35772         goto fail;
35773     obj = JS_ToObject(ctx, val);
35774     if (JS_IsException(obj))
35775         goto fail;
35776     if (BC_add_object_ref(s, obj))
35777         goto fail;
35778     JS_FreeValue(ctx, val);
35779     return obj;
35780  fail:
35781     JS_FreeValue(ctx, val);
35782     JS_FreeValue(ctx, obj);
35783     return JS_EXCEPTION;
35784 }
35785 
JS_ReadObjectRec(BCReaderState * s)35786 static JSValue JS_ReadObjectRec(BCReaderState *s)
35787 {
35788     JSContext *ctx = s->ctx;
35789     uint8_t tag;
35790     JSValue obj = JS_UNDEFINED;
35791 
35792     if (js_check_stack_overflow(ctx->rt, 0))
35793         return JS_ThrowStackOverflow(ctx);
35794 
35795     if (bc_get_u8(s, &tag))
35796         return JS_EXCEPTION;
35797 
35798     bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
35799 
35800     switch(tag) {
35801     case BC_TAG_NULL:
35802         obj = JS_NULL;
35803         break;
35804     case BC_TAG_UNDEFINED:
35805         obj = JS_UNDEFINED;
35806         break;
35807     case BC_TAG_BOOL_FALSE:
35808     case BC_TAG_BOOL_TRUE:
35809         obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE);
35810         break;
35811     case BC_TAG_INT32:
35812         {
35813             int32_t val;
35814             if (bc_get_sleb128(s, &val))
35815                 return JS_EXCEPTION;
35816             bc_read_trace(s, "%d\n", val);
35817             obj = JS_NewInt32(ctx, val);
35818         }
35819         break;
35820     case BC_TAG_FLOAT64:
35821         {
35822             JSFloat64Union u;
35823             if (bc_get_u64(s, &u.u64))
35824                 return JS_EXCEPTION;
35825             bc_read_trace(s, "%g\n", u.d);
35826             obj = __JS_NewFloat64(ctx, u.d);
35827         }
35828         break;
35829     case BC_TAG_STRING:
35830         {
35831             JSString *p;
35832             p = JS_ReadString(s);
35833             if (!p)
35834                 return JS_EXCEPTION;
35835             obj = JS_MKPTR(JS_TAG_STRING, p);
35836         }
35837         break;
35838     case BC_TAG_FUNCTION_BYTECODE:
35839         if (!s->allow_bytecode)
35840             goto invalid_tag;
35841         obj = JS_ReadFunctionTag(s);
35842         break;
35843     case BC_TAG_MODULE:
35844         if (!s->allow_bytecode)
35845             goto invalid_tag;
35846         obj = JS_ReadModule(s);
35847         break;
35848     case BC_TAG_OBJECT:
35849         obj = JS_ReadObjectTag(s);
35850         break;
35851     case BC_TAG_ARRAY:
35852     case BC_TAG_TEMPLATE_OBJECT:
35853         obj = JS_ReadArray(s, tag);
35854         break;
35855     case BC_TAG_TYPED_ARRAY:
35856         obj = JS_ReadTypedArray(s);
35857         break;
35858     case BC_TAG_ARRAY_BUFFER:
35859         obj = JS_ReadArrayBuffer(s);
35860         break;
35861     case BC_TAG_SHARED_ARRAY_BUFFER:
35862         if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup)
35863             goto invalid_tag;
35864         obj = JS_ReadSharedArrayBuffer(s);
35865         break;
35866     case BC_TAG_DATE:
35867         obj = JS_ReadDate(s);
35868         break;
35869     case BC_TAG_OBJECT_VALUE:
35870         obj = JS_ReadObjectValue(s);
35871         break;
35872 #ifdef CONFIG_BIGNUM
35873     case BC_TAG_BIG_INT:
35874     case BC_TAG_BIG_FLOAT:
35875     case BC_TAG_BIG_DECIMAL:
35876         obj = JS_ReadBigNum(s, tag);
35877         break;
35878 #endif
35879     case BC_TAG_OBJECT_REFERENCE:
35880         {
35881             uint32_t val;
35882             if (!s->allow_reference)
35883                 return JS_ThrowSyntaxError(ctx, "object references are not allowed");
35884             if (bc_get_leb128(s, &val))
35885                 return JS_EXCEPTION;
35886             bc_read_trace(s, "%u\n", val);
35887             if (val >= s->objects_count) {
35888                 return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)",
35889                                            val, s->objects_count);
35890             }
35891             obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val]));
35892         }
35893         break;
35894     default:
35895     invalid_tag:
35896         return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)",
35897                                    tag, (unsigned int)(s->ptr - s->buf_start));
35898     }
35899     bc_read_trace(s, "}\n");
35900     return obj;
35901 }
35902 
JS_ReadObjectAtoms(BCReaderState * s)35903 static int JS_ReadObjectAtoms(BCReaderState *s)
35904 {
35905     uint8_t v8;
35906     JSString *p;
35907     int i;
35908     JSAtom atom;
35909 
35910     if (bc_get_u8(s, &v8))
35911         return -1;
35912     /* XXX: could support byte swapped input */
35913     if (v8 != BC_VERSION) {
35914         JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)",
35915                             v8, BC_VERSION);
35916         return -1;
35917     }
35918     if (bc_get_leb128(s, &s->idx_to_atom_count))
35919         return -1;
35920 
35921     bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count);
35922 
35923     if (s->idx_to_atom_count != 0) {
35924         s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count *
35925                                     sizeof(s->idx_to_atom[0]));
35926         if (!s->idx_to_atom)
35927             return s->error_state = -1;
35928     }
35929     for(i = 0; i < s->idx_to_atom_count; i++) {
35930         p = JS_ReadString(s);
35931         if (!p)
35932             return -1;
35933         atom = JS_NewAtomStr(s->ctx, p);
35934         if (atom == JS_ATOM_NULL)
35935             return s->error_state = -1;
35936         s->idx_to_atom[i] = atom;
35937         if (s->is_rom_data && (atom != (i + s->first_atom)))
35938             s->is_rom_data = FALSE; /* atoms must be relocated */
35939     }
35940     bc_read_trace(s, "}\n");
35941     return 0;
35942 }
35943 
bc_reader_free(BCReaderState * s)35944 static void bc_reader_free(BCReaderState *s)
35945 {
35946     int i;
35947     if (s->idx_to_atom) {
35948         for(i = 0; i < s->idx_to_atom_count; i++) {
35949             JS_FreeAtom(s->ctx, s->idx_to_atom[i]);
35950         }
35951         js_free(s->ctx, s->idx_to_atom);
35952     }
35953     js_free(s->ctx, s->objects);
35954 }
35955 
JS_ReadObject(JSContext * ctx,const uint8_t * buf,size_t buf_len,int flags)35956 JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
35957                        int flags)
35958 {
35959     BCReaderState ss, *s = &ss;
35960     JSValue obj;
35961 
35962     ctx->binary_object_count += 1;
35963     ctx->binary_object_size += buf_len;
35964 
35965     memset(s, 0, sizeof(*s));
35966     s->ctx = ctx;
35967     s->buf_start = buf;
35968     s->buf_end = buf + buf_len;
35969     s->ptr = buf;
35970     s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0);
35971     s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0);
35972     s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0);
35973     s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0);
35974     if (s->allow_bytecode)
35975         s->first_atom = JS_ATOM_END;
35976     else
35977         s->first_atom = 1;
35978     if (JS_ReadObjectAtoms(s)) {
35979         obj = JS_EXCEPTION;
35980     } else {
35981         obj = JS_ReadObjectRec(s);
35982     }
35983     bc_reader_free(s);
35984     return obj;
35985 }
35986 
35987 /*******************************************************************/
35988 /* runtime functions & objects */
35989 
35990 static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val,
35991                                      int argc, JSValueConst *argv);
35992 static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val,
35993                                       int argc, JSValueConst *argv);
35994 static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val,
35995                                      int argc, JSValueConst *argv);
35996 
check_function(JSContext * ctx,JSValueConst obj)35997 static int check_function(JSContext *ctx, JSValueConst obj)
35998 {
35999     if (likely(JS_IsFunction(ctx, obj)))
36000         return 0;
36001     JS_ThrowTypeError(ctx, "not a function");
36002     return -1;
36003 }
36004 
check_exception_free(JSContext * ctx,JSValue obj)36005 static int check_exception_free(JSContext *ctx, JSValue obj)
36006 {
36007     JS_FreeValue(ctx, obj);
36008     return JS_IsException(obj);
36009 }
36010 
find_atom(JSContext * ctx,const char * name)36011 static JSAtom find_atom(JSContext *ctx, const char *name)
36012 {
36013     JSAtom atom;
36014     int len;
36015 
36016     if (*name == '[') {
36017         name++;
36018         len = strlen(name) - 1;
36019         /* We assume 8 bit non null strings, which is the case for these
36020            symbols */
36021         for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) {
36022             JSAtomStruct *p = ctx->rt->atom_array[atom];
36023             JSString *str = p;
36024             if (str->len == len && !memcmp(str->u.str8, name, len))
36025                 return JS_DupAtom(ctx, atom);
36026         }
36027         abort();
36028     } else {
36029         atom = JS_NewAtom(ctx, name);
36030     }
36031     return atom;
36032 }
36033 
JS_InstantiateFunctionListItem2(JSContext * ctx,JSObject * p,JSAtom atom,void * opaque)36034 static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
36035                                                JSAtom atom, void *opaque)
36036 {
36037     const JSCFunctionListEntry *e = opaque;
36038     JSValue val;
36039 
36040     switch(e->def_type) {
36041     case JS_DEF_CFUNC:
36042         val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
36043                                e->name, e->u.func.length, e->u.func.cproto, e->magic);
36044         break;
36045     case JS_DEF_PROP_STRING:
36046         val = JS_NewAtomString(ctx, e->u.str);
36047         break;
36048     case JS_DEF_OBJECT:
36049         val = JS_NewObject(ctx);
36050         JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);
36051         break;
36052     default:
36053         abort();
36054     }
36055     return val;
36056 }
36057 
JS_InstantiateFunctionListItem(JSContext * ctx,JSValueConst obj,JSAtom atom,const JSCFunctionListEntry * e)36058 static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj,
36059                                           JSAtom atom,
36060                                           const JSCFunctionListEntry *e)
36061 {
36062     JSValue val;
36063     int prop_flags = e->prop_flags;
36064 
36065     switch(e->def_type) {
36066     case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */
36067         {
36068             JSAtom atom1 = find_atom(ctx, e->u.alias.name);
36069             switch (e->u.alias.base) {
36070             case -1:
36071                 val = JS_GetProperty(ctx, obj, atom1);
36072                 break;
36073             case 0:
36074                 val = JS_GetProperty(ctx, ctx->global_obj, atom1);
36075                 break;
36076             case 1:
36077                 val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1);
36078                 break;
36079             default:
36080                 abort();
36081             }
36082             JS_FreeAtom(ctx, atom1);
36083             if (atom == JS_ATOM_Symbol_toPrimitive) {
36084                 /* Symbol.toPrimitive functions are not writable */
36085                 prop_flags = JS_PROP_CONFIGURABLE;
36086             } else if (atom == JS_ATOM_Symbol_hasInstance) {
36087                 /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
36088                 prop_flags = 0;
36089             }
36090         }
36091         break;
36092     case JS_DEF_CFUNC:
36093         if (atom == JS_ATOM_Symbol_toPrimitive) {
36094             /* Symbol.toPrimitive functions are not writable */
36095             prop_flags = JS_PROP_CONFIGURABLE;
36096         } else if (atom == JS_ATOM_Symbol_hasInstance) {
36097             /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
36098             prop_flags = 0;
36099         }
36100         JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
36101                                   (void *)e, prop_flags);
36102         return 0;
36103     case JS_DEF_CGETSET: /* XXX: use autoinit again ? */
36104     case JS_DEF_CGETSET_MAGIC:
36105         {
36106             JSValue getter, setter;
36107             char buf[64];
36108 
36109             getter = JS_UNDEFINED;
36110             if (e->u.getset.get.generic) {
36111                 snprintf(buf, sizeof(buf), "get %s", e->name);
36112                 getter = JS_NewCFunction2(ctx, e->u.getset.get.generic,
36113                                           buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter,
36114                                           e->magic);
36115             }
36116             setter = JS_UNDEFINED;
36117             if (e->u.getset.set.generic) {
36118                 snprintf(buf, sizeof(buf), "set %s", e->name);
36119                 setter = JS_NewCFunction2(ctx, e->u.getset.set.generic,
36120                                           buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter,
36121                                           e->magic);
36122             }
36123             JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags);
36124             return 0;
36125         }
36126         break;
36127     case JS_DEF_PROP_INT32:
36128         val = JS_NewInt32(ctx, e->u.i32);
36129         break;
36130     case JS_DEF_PROP_INT64:
36131         val = JS_NewInt64(ctx, e->u.i64);
36132         break;
36133     case JS_DEF_PROP_DOUBLE:
36134         val = __JS_NewFloat64(ctx, e->u.f64);
36135         break;
36136     case JS_DEF_PROP_UNDEFINED:
36137         val = JS_UNDEFINED;
36138         break;
36139     case JS_DEF_PROP_STRING:
36140     case JS_DEF_OBJECT:
36141         JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
36142                                   (void *)e, prop_flags);
36143         return 0;
36144     default:
36145         abort();
36146     }
36147     JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags);
36148     return 0;
36149 }
36150 
JS_SetPropertyFunctionList(JSContext * ctx,JSValueConst obj,const JSCFunctionListEntry * tab,int len)36151 void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,
36152                                 const JSCFunctionListEntry *tab, int len)
36153 {
36154     int i;
36155 
36156     for (i = 0; i < len; i++) {
36157         const JSCFunctionListEntry *e = &tab[i];
36158         JSAtom atom = find_atom(ctx, e->name);
36159         JS_InstantiateFunctionListItem(ctx, obj, atom, e);
36160         JS_FreeAtom(ctx, atom);
36161     }
36162 }
36163 
JS_AddModuleExportList(JSContext * ctx,JSModuleDef * m,const JSCFunctionListEntry * tab,int len)36164 int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,
36165                            const JSCFunctionListEntry *tab, int len)
36166 {
36167     int i;
36168     for(i = 0; i < len; i++) {
36169         if (JS_AddModuleExport(ctx, m, tab[i].name))
36170             return -1;
36171     }
36172     return 0;
36173 }
36174 
JS_SetModuleExportList(JSContext * ctx,JSModuleDef * m,const JSCFunctionListEntry * tab,int len)36175 int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
36176                            const JSCFunctionListEntry *tab, int len)
36177 {
36178     int i;
36179     JSValue val;
36180 
36181     for(i = 0; i < len; i++) {
36182         const JSCFunctionListEntry *e = &tab[i];
36183         switch(e->def_type) {
36184         case JS_DEF_CFUNC:
36185             val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
36186                                    e->name, e->u.func.length, e->u.func.cproto, e->magic);
36187             break;
36188         case JS_DEF_PROP_STRING:
36189             val = JS_NewString(ctx, e->u.str);
36190             break;
36191         case JS_DEF_PROP_INT32:
36192             val = JS_NewInt32(ctx, e->u.i32);
36193             break;
36194         case JS_DEF_PROP_INT64:
36195             val = JS_NewInt64(ctx, e->u.i64);
36196             break;
36197         case JS_DEF_PROP_DOUBLE:
36198             val = __JS_NewFloat64(ctx, e->u.f64);
36199             break;
36200         case JS_DEF_OBJECT:
36201             val = JS_NewObject(ctx);
36202             JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);
36203             break;
36204         default:
36205             abort();
36206         }
36207         if (JS_SetModuleExport(ctx, m, e->name, val))
36208             return -1;
36209     }
36210     return 0;
36211 }
36212 
36213 /* Note: 'func_obj' is not necessarily a constructor */
JS_SetConstructor2(JSContext * ctx,JSValueConst func_obj,JSValueConst proto,int proto_flags,int ctor_flags)36214 static void JS_SetConstructor2(JSContext *ctx,
36215                                JSValueConst func_obj,
36216                                JSValueConst proto,
36217                                int proto_flags, int ctor_flags)
36218 {
36219     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype,
36220                            JS_DupValue(ctx, proto), proto_flags);
36221     JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
36222                            JS_DupValue(ctx, func_obj),
36223                            ctor_flags);
36224     set_cycle_flag(ctx, func_obj);
36225     set_cycle_flag(ctx, proto);
36226 }
36227 
JS_SetConstructor(JSContext * ctx,JSValueConst func_obj,JSValueConst proto)36228 void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
36229                        JSValueConst proto)
36230 {
36231     JS_SetConstructor2(ctx, func_obj, proto,
36232                        0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
36233 }
36234 
JS_NewGlobalCConstructor2(JSContext * ctx,JSValue func_obj,const char * name,JSValueConst proto)36235 static void JS_NewGlobalCConstructor2(JSContext *ctx,
36236                                       JSValue func_obj,
36237                                       const char *name,
36238                                       JSValueConst proto)
36239 {
36240     JS_DefinePropertyValueStr(ctx, ctx->global_obj, name,
36241                            JS_DupValue(ctx, func_obj),
36242                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
36243     JS_SetConstructor(ctx, func_obj, proto);
36244     JS_FreeValue(ctx, func_obj);
36245 }
36246 
JS_NewGlobalCConstructor(JSContext * ctx,const char * name,JSCFunction * func,int length,JSValueConst proto)36247 static JSValueConst JS_NewGlobalCConstructor(JSContext *ctx, const char *name,
36248                                              JSCFunction *func, int length,
36249                                              JSValueConst proto)
36250 {
36251     JSValue func_obj;
36252     func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0);
36253     JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
36254     return func_obj;
36255 }
36256 
JS_NewGlobalCConstructorOnly(JSContext * ctx,const char * name,JSCFunction * func,int length,JSValueConst proto)36257 static JSValueConst JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name,
36258                                                  JSCFunction *func, int length,
36259                                                  JSValueConst proto)
36260 {
36261     JSValue func_obj;
36262     func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0);
36263     JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
36264     return func_obj;
36265 }
36266 
js_global_eval(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36267 static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val,
36268                               int argc, JSValueConst *argv)
36269 {
36270     return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1);
36271 }
36272 
js_global_isNaN(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36273 static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val,
36274                                int argc, JSValueConst *argv)
36275 {
36276     double d;
36277 
36278     /* XXX: does this work for bigfloat? */
36279     if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
36280         return JS_EXCEPTION;
36281     return JS_NewBool(ctx, isnan(d));
36282 }
36283 
js_global_isFinite(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36284 static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val,
36285                                   int argc, JSValueConst *argv)
36286 {
36287     BOOL res;
36288     double d;
36289     if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
36290         return JS_EXCEPTION;
36291     res = isfinite(d);
36292     return JS_NewBool(ctx, res);
36293 }
36294 
36295 /* Object class */
36296 
JS_ToObject(JSContext * ctx,JSValueConst val)36297 static JSValue JS_ToObject(JSContext *ctx, JSValueConst val)
36298 {
36299     int tag = JS_VALUE_GET_NORM_TAG(val);
36300     JSValue obj;
36301 
36302     switch(tag) {
36303     default:
36304     case JS_TAG_NULL:
36305     case JS_TAG_UNDEFINED:
36306         return JS_ThrowTypeError(ctx, "cannot convert to object");
36307     case JS_TAG_OBJECT:
36308     case JS_TAG_EXCEPTION:
36309         return JS_DupValue(ctx, val);
36310 #ifdef CONFIG_BIGNUM
36311     case JS_TAG_BIG_INT:
36312         obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);
36313         goto set_value;
36314     case JS_TAG_BIG_FLOAT:
36315         obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT);
36316         goto set_value;
36317     case JS_TAG_BIG_DECIMAL:
36318         obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL);
36319         goto set_value;
36320 #endif
36321     case JS_TAG_INT:
36322     case JS_TAG_FLOAT64:
36323         obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
36324         goto set_value;
36325     case JS_TAG_STRING:
36326         /* XXX: should call the string constructor */
36327         {
36328             JSString *p1 = JS_VALUE_GET_STRING(val);
36329             obj = JS_NewObjectClass(ctx, JS_CLASS_STRING);
36330             JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
36331         }
36332         goto set_value;
36333     case JS_TAG_BOOL:
36334         obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN);
36335         goto set_value;
36336     case JS_TAG_SYMBOL:
36337         obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL);
36338     set_value:
36339         if (!JS_IsException(obj))
36340             JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val));
36341         return obj;
36342     }
36343 }
36344 
JS_ToObjectFree(JSContext * ctx,JSValue val)36345 static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val)
36346 {
36347     JSValue obj = JS_ToObject(ctx, val);
36348     JS_FreeValue(ctx, val);
36349     return obj;
36350 }
36351 
js_obj_to_desc(JSContext * ctx,JSPropertyDescriptor * d,JSValueConst desc)36352 static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
36353                           JSValueConst desc)
36354 {
36355     JSValue val, getter, setter;
36356     int flags;
36357 
36358     if (!JS_IsObject(desc)) {
36359         JS_ThrowTypeErrorNotAnObject(ctx);
36360         return -1;
36361     }
36362     flags = 0;
36363     val = JS_UNDEFINED;
36364     getter = JS_UNDEFINED;
36365     setter = JS_UNDEFINED;
36366     if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) {
36367         JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable);
36368         if (JS_IsException(prop))
36369             goto fail;
36370         flags |= JS_PROP_HAS_CONFIGURABLE;
36371         if (JS_ToBoolFree(ctx, prop))
36372             flags |= JS_PROP_CONFIGURABLE;
36373     }
36374     if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) {
36375         JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable);
36376         if (JS_IsException(prop))
36377             goto fail;
36378         flags |= JS_PROP_HAS_WRITABLE;
36379         if (JS_ToBoolFree(ctx, prop))
36380             flags |= JS_PROP_WRITABLE;
36381     }
36382     if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) {
36383         JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable);
36384         if (JS_IsException(prop))
36385             goto fail;
36386         flags |= JS_PROP_HAS_ENUMERABLE;
36387         if (JS_ToBoolFree(ctx, prop))
36388             flags |= JS_PROP_ENUMERABLE;
36389     }
36390     if (JS_HasProperty(ctx, desc, JS_ATOM_value)) {
36391         flags |= JS_PROP_HAS_VALUE;
36392         val = JS_GetProperty(ctx, desc, JS_ATOM_value);
36393         if (JS_IsException(val))
36394             goto fail;
36395     }
36396     if (JS_HasProperty(ctx, desc, JS_ATOM_get)) {
36397         flags |= JS_PROP_HAS_GET;
36398         getter = JS_GetProperty(ctx, desc, JS_ATOM_get);
36399         if (JS_IsException(getter) ||
36400             !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) {
36401             JS_ThrowTypeError(ctx, "invalid getter");
36402             goto fail;
36403         }
36404     }
36405     if (JS_HasProperty(ctx, desc, JS_ATOM_set)) {
36406         flags |= JS_PROP_HAS_SET;
36407         setter = JS_GetProperty(ctx, desc, JS_ATOM_set);
36408         if (JS_IsException(setter) ||
36409             !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) {
36410             JS_ThrowTypeError(ctx, "invalid setter");
36411             goto fail;
36412         }
36413     }
36414     if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) &&
36415         (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) {
36416         JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable");
36417         goto fail;
36418     }
36419     d->flags = flags;
36420     d->value = val;
36421     d->getter = getter;
36422     d->setter = setter;
36423     return 0;
36424  fail:
36425     JS_FreeValue(ctx, val);
36426     JS_FreeValue(ctx, getter);
36427     JS_FreeValue(ctx, setter);
36428     return -1;
36429 }
36430 
JS_DefinePropertyDesc(JSContext * ctx,JSValueConst obj,JSAtom prop,JSValueConst desc,int flags)36431 static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
36432                                              JSAtom prop, JSValueConst desc,
36433                                              int flags)
36434 {
36435     JSPropertyDescriptor d;
36436     int ret;
36437 
36438     if (js_obj_to_desc(ctx, &d, desc) < 0)
36439         return -1;
36440 
36441     ret = JS_DefineProperty(ctx, obj, prop,
36442                             d.value, d.getter, d.setter, d.flags | flags);
36443     js_free_desc(ctx, &d);
36444     return ret;
36445 }
36446 
JS_ObjectDefineProperties(JSContext * ctx,JSValueConst obj,JSValueConst properties)36447 static __exception int JS_ObjectDefineProperties(JSContext *ctx,
36448                                                  JSValueConst obj,
36449                                                  JSValueConst properties)
36450 {
36451     JSValue props, desc;
36452     JSObject *p;
36453     JSPropertyEnum *atoms;
36454     uint32_t len, i;
36455     int ret = -1;
36456 
36457     if (!JS_IsObject(obj)) {
36458         JS_ThrowTypeErrorNotAnObject(ctx);
36459         return -1;
36460     }
36461     desc = JS_UNDEFINED;
36462     props = JS_ToObject(ctx, properties);
36463     if (JS_IsException(props))
36464         return -1;
36465     p = JS_VALUE_GET_OBJ(props);
36466     if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0)
36467         goto exception;
36468     for(i = 0; i < len; i++) {
36469         JS_FreeValue(ctx, desc);
36470         desc = JS_GetProperty(ctx, props, atoms[i].atom);
36471         if (JS_IsException(desc))
36472             goto exception;
36473         if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0)
36474             goto exception;
36475     }
36476     ret = 0;
36477 
36478 exception:
36479     js_free_prop_enum(ctx, atoms, len);
36480     JS_FreeValue(ctx, props);
36481     JS_FreeValue(ctx, desc);
36482     return ret;
36483 }
36484 
js_object_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)36485 static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target,
36486                                      int argc, JSValueConst *argv)
36487 {
36488     JSValue ret;
36489     if (!JS_IsUndefined(new_target) &&
36490         JS_VALUE_GET_OBJ(new_target) !=
36491         JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) {
36492         ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
36493     } else {
36494         int tag = JS_VALUE_GET_NORM_TAG(argv[0]);
36495         switch(tag) {
36496         case JS_TAG_NULL:
36497         case JS_TAG_UNDEFINED:
36498             ret = JS_NewObject(ctx);
36499             break;
36500         default:
36501             ret = JS_ToObject(ctx, argv[0]);
36502             break;
36503         }
36504     }
36505     return ret;
36506 }
36507 
js_object_create(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36508 static JSValue js_object_create(JSContext *ctx, JSValueConst this_val,
36509                                 int argc, JSValueConst *argv)
36510 {
36511     JSValueConst proto, props;
36512     JSValue obj;
36513 
36514     proto = argv[0];
36515     if (!JS_IsObject(proto) && !JS_IsNull(proto))
36516         return JS_ThrowTypeError(ctx, "not a prototype");
36517     obj = JS_NewObjectProto(ctx, proto);
36518     if (JS_IsException(obj))
36519         return JS_EXCEPTION;
36520     props = argv[1];
36521     if (!JS_IsUndefined(props)) {
36522         if (JS_ObjectDefineProperties(ctx, obj, props)) {
36523             JS_FreeValue(ctx, obj);
36524             return JS_EXCEPTION;
36525         }
36526     }
36527     return obj;
36528 }
36529 
js_object_getPrototypeOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)36530 static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val,
36531                                         int argc, JSValueConst *argv, int magic)
36532 {
36533     JSValueConst val;
36534 
36535     val = argv[0];
36536     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) {
36537         /* ES6 feature non compatible with ES5.1: primitive types are
36538            accepted */
36539         if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL ||
36540             JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED)
36541             return JS_ThrowTypeErrorNotAnObject(ctx);
36542     }
36543     return JS_GetPrototype(ctx, val);
36544 }
36545 
js_object_setPrototypeOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36546 static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
36547                                         int argc, JSValueConst *argv)
36548 {
36549     JSValueConst obj;
36550     obj = argv[0];
36551     if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0)
36552         return JS_EXCEPTION;
36553     return JS_DupValue(ctx, obj);
36554 }
36555 
36556 /* magic = 1 if called as Reflect.defineProperty */
js_object_defineProperty(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)36557 static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val,
36558                                         int argc, JSValueConst *argv, int magic)
36559 {
36560     JSValueConst obj, prop, desc;
36561     int ret, flags;
36562     JSAtom atom;
36563 
36564     obj = argv[0];
36565     prop = argv[1];
36566     desc = argv[2];
36567 
36568     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
36569         return JS_ThrowTypeErrorNotAnObject(ctx);
36570     atom = JS_ValueToAtom(ctx, prop);
36571     if (unlikely(atom == JS_ATOM_NULL))
36572         return JS_EXCEPTION;
36573     flags = 0;
36574     if (!magic)
36575         flags |= JS_PROP_THROW;
36576     ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags);
36577     JS_FreeAtom(ctx, atom);
36578     if (ret < 0) {
36579         return JS_EXCEPTION;
36580     } else if (magic) {
36581         return JS_NewBool(ctx, ret);
36582     } else {
36583         return JS_DupValue(ctx, obj);
36584     }
36585 }
36586 
js_object_defineProperties(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36587 static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val,
36588                                           int argc, JSValueConst *argv)
36589 {
36590     // defineProperties(obj, properties)
36591     JSValueConst obj = argv[0];
36592 
36593     if (JS_ObjectDefineProperties(ctx, obj, argv[1]))
36594         return JS_EXCEPTION;
36595     else
36596         return JS_DupValue(ctx, obj);
36597 }
36598 
36599 /* magic = 1 if called as __defineSetter__ */
js_object___defineGetter__(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)36600 static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val,
36601                                           int argc, JSValueConst *argv, int magic)
36602 {
36603     JSValue obj;
36604     JSValueConst prop, value, get, set;
36605     int ret, flags;
36606     JSAtom atom;
36607 
36608     prop = argv[0];
36609     value = argv[1];
36610 
36611     obj = JS_ToObject(ctx, this_val);
36612     if (JS_IsException(obj))
36613         return JS_EXCEPTION;
36614 
36615     if (check_function(ctx, value)) {
36616         JS_FreeValue(ctx, obj);
36617         return JS_EXCEPTION;
36618     }
36619     atom = JS_ValueToAtom(ctx, prop);
36620     if (unlikely(atom == JS_ATOM_NULL)) {
36621         JS_FreeValue(ctx, obj);
36622         return JS_EXCEPTION;
36623     }
36624     flags = JS_PROP_THROW |
36625         JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE |
36626         JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE;
36627     if (magic) {
36628         get = JS_UNDEFINED;
36629         set = value;
36630         flags |= JS_PROP_HAS_SET;
36631     } else {
36632         get = value;
36633         set = JS_UNDEFINED;
36634         flags |= JS_PROP_HAS_GET;
36635     }
36636     ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags);
36637     JS_FreeValue(ctx, obj);
36638     JS_FreeAtom(ctx, atom);
36639     if (ret < 0) {
36640         return JS_EXCEPTION;
36641     } else {
36642         return JS_UNDEFINED;
36643     }
36644 }
36645 
js_object_getOwnPropertyDescriptor(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)36646 static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val,
36647                                                   int argc, JSValueConst *argv, int magic)
36648 {
36649     JSValueConst prop;
36650     JSAtom atom;
36651     JSValue ret, obj;
36652     JSPropertyDescriptor desc;
36653     int res, flags;
36654 
36655     if (magic) {
36656         /* Reflect.getOwnPropertyDescriptor case */
36657         if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
36658             return JS_ThrowTypeErrorNotAnObject(ctx);
36659         obj = JS_DupValue(ctx, argv[0]);
36660     } else {
36661         obj = JS_ToObject(ctx, argv[0]);
36662         if (JS_IsException(obj))
36663             return obj;
36664     }
36665     prop = argv[1];
36666     atom = JS_ValueToAtom(ctx, prop);
36667     if (unlikely(atom == JS_ATOM_NULL))
36668         goto exception;
36669     ret = JS_UNDEFINED;
36670     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
36671         res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom);
36672         if (res < 0)
36673             goto exception;
36674         if (res) {
36675             ret = JS_NewObject(ctx);
36676             if (JS_IsException(ret))
36677                 goto exception1;
36678             flags = JS_PROP_C_W_E | JS_PROP_THROW;
36679             if (desc.flags & JS_PROP_GETSET) {
36680                 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0
36681                 ||  JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0)
36682                     goto exception1;
36683             } else {
36684                 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0
36685                 ||  JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
36686                                            JS_NewBool(ctx, (desc.flags & JS_PROP_WRITABLE) != 0), flags) < 0)
36687                     goto exception1;
36688             }
36689             if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
36690                                        JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0), flags) < 0
36691             ||  JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
36692                                        JS_NewBool(ctx, (desc.flags & JS_PROP_CONFIGURABLE) != 0), flags) < 0)
36693                 goto exception1;
36694             js_free_desc(ctx, &desc);
36695         }
36696     }
36697     JS_FreeAtom(ctx, atom);
36698     JS_FreeValue(ctx, obj);
36699     return ret;
36700 
36701 exception1:
36702     js_free_desc(ctx, &desc);
36703     JS_FreeValue(ctx, ret);
36704 exception:
36705     JS_FreeAtom(ctx, atom);
36706     JS_FreeValue(ctx, obj);
36707     return JS_EXCEPTION;
36708 }
36709 
js_object_getOwnPropertyDescriptors(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36710 static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val,
36711                                                    int argc, JSValueConst *argv)
36712 {
36713     //getOwnPropertyDescriptors(obj)
36714     JSValue obj, r;
36715     JSObject *p;
36716     JSPropertyEnum *props;
36717     uint32_t len, i;
36718 
36719     r = JS_UNDEFINED;
36720     obj = JS_ToObject(ctx, argv[0]);
36721     if (JS_IsException(obj))
36722         return JS_EXCEPTION;
36723 
36724     p = JS_VALUE_GET_OBJ(obj);
36725     if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p,
36726                                JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
36727         goto exception;
36728     r = JS_NewObject(ctx);
36729     if (JS_IsException(r))
36730         goto exception;
36731     for(i = 0; i < len; i++) {
36732         JSValue atomValue, desc;
36733         JSValueConst args[2];
36734 
36735         atomValue = JS_AtomToValue(ctx, props[i].atom);
36736         if (JS_IsException(atomValue))
36737             goto exception;
36738         args[0] = obj;
36739         args[1] = atomValue;
36740         desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0);
36741         JS_FreeValue(ctx, atomValue);
36742         if (JS_IsException(desc))
36743             goto exception;
36744         if (!JS_IsUndefined(desc)) {
36745             if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc,
36746                                        JS_PROP_C_W_E | JS_PROP_THROW) < 0)
36747                 goto exception;
36748         }
36749     }
36750     js_free_prop_enum(ctx, props, len);
36751     JS_FreeValue(ctx, obj);
36752     return r;
36753 
36754 exception:
36755     js_free_prop_enum(ctx, props, len);
36756     JS_FreeValue(ctx, obj);
36757     JS_FreeValue(ctx, r);
36758     return JS_EXCEPTION;
36759 }
36760 
JS_GetOwnPropertyNames2(JSContext * ctx,JSValueConst obj1,int flags,int kind)36761 static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1,
36762                                        int flags, int kind)
36763 {
36764     JSValue obj, r, val, key, value;
36765     JSObject *p;
36766     JSPropertyEnum *atoms;
36767     uint32_t len, i, j;
36768 
36769     r = JS_UNDEFINED;
36770     val = JS_UNDEFINED;
36771     obj = JS_ToObject(ctx, obj1);
36772     if (JS_IsException(obj))
36773         return JS_EXCEPTION;
36774     p = JS_VALUE_GET_OBJ(obj);
36775     if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY))
36776         goto exception;
36777     r = JS_NewArray(ctx);
36778     if (JS_IsException(r))
36779         goto exception;
36780     for(j = i = 0; i < len; i++) {
36781         JSAtom atom = atoms[i].atom;
36782         if (flags & JS_GPN_ENUM_ONLY) {
36783             JSPropertyDescriptor desc;
36784             int res;
36785 
36786             /* Check if property is still enumerable */
36787             res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
36788             if (res < 0)
36789                 goto exception;
36790             if (!res)
36791                 continue;
36792             js_free_desc(ctx, &desc);
36793             if (!(desc.flags & JS_PROP_ENUMERABLE))
36794                 continue;
36795         }
36796         switch(kind) {
36797         default:
36798         case JS_ITERATOR_KIND_KEY:
36799             val = JS_AtomToValue(ctx, atom);
36800             if (JS_IsException(val))
36801                 goto exception;
36802             break;
36803         case JS_ITERATOR_KIND_VALUE:
36804             val = JS_GetProperty(ctx, obj, atom);
36805             if (JS_IsException(val))
36806                 goto exception;
36807             break;
36808         case JS_ITERATOR_KIND_KEY_AND_VALUE:
36809             val = JS_NewArray(ctx);
36810             if (JS_IsException(val))
36811                 goto exception;
36812             key = JS_AtomToValue(ctx, atom);
36813             if (JS_IsException(key))
36814                 goto exception1;
36815             if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0)
36816                 goto exception1;
36817             value = JS_GetProperty(ctx, obj, atom);
36818             if (JS_IsException(value))
36819                 goto exception1;
36820             if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0)
36821                 goto exception1;
36822             break;
36823         }
36824         if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0)
36825             goto exception;
36826     }
36827     goto done;
36828 
36829 exception1:
36830     JS_FreeValue(ctx, val);
36831 exception:
36832     JS_FreeValue(ctx, r);
36833     r = JS_EXCEPTION;
36834 done:
36835     js_free_prop_enum(ctx, atoms, len);
36836     JS_FreeValue(ctx, obj);
36837     return r;
36838 }
36839 
js_object_getOwnPropertyNames(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36840 static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val,
36841                                              int argc, JSValueConst *argv)
36842 {
36843     return JS_GetOwnPropertyNames2(ctx, argv[0],
36844                                    JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY);
36845 }
36846 
js_object_getOwnPropertySymbols(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36847 static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val,
36848                                              int argc, JSValueConst *argv)
36849 {
36850     return JS_GetOwnPropertyNames2(ctx, argv[0],
36851                                    JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY);
36852 }
36853 
js_object_keys(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int kind)36854 static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val,
36855                               int argc, JSValueConst *argv, int kind)
36856 {
36857     return JS_GetOwnPropertyNames2(ctx, argv[0],
36858                                    JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind);
36859 }
36860 
js_object_isExtensible(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int reflect)36861 static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val,
36862                                       int argc, JSValueConst *argv, int reflect)
36863 {
36864     JSValueConst obj;
36865     int ret;
36866 
36867     obj = argv[0];
36868     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
36869         if (reflect)
36870             return JS_ThrowTypeErrorNotAnObject(ctx);
36871         else
36872             return JS_FALSE;
36873     }
36874     ret = JS_IsExtensible(ctx, obj);
36875     if (ret < 0)
36876         return JS_EXCEPTION;
36877     else
36878         return JS_NewBool(ctx, ret);
36879 }
36880 
js_object_preventExtensions(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int reflect)36881 static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val,
36882                                            int argc, JSValueConst *argv, int reflect)
36883 {
36884     JSValueConst obj;
36885     int ret;
36886 
36887     obj = argv[0];
36888     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
36889         if (reflect)
36890             return JS_ThrowTypeErrorNotAnObject(ctx);
36891         else
36892             return JS_DupValue(ctx, obj);
36893     }
36894     ret = JS_PreventExtensions(ctx, obj);
36895     if (ret < 0)
36896         return JS_EXCEPTION;
36897     if (reflect) {
36898         return JS_NewBool(ctx, ret);
36899     } else {
36900         if (!ret)
36901             return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
36902         return JS_DupValue(ctx, obj);
36903     }
36904 }
36905 
js_object_hasOwnProperty(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36906 static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,
36907                                         int argc, JSValueConst *argv)
36908 {
36909     JSValue obj;
36910     JSAtom atom;
36911     JSObject *p;
36912     BOOL ret;
36913 
36914     atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */
36915     if (unlikely(atom == JS_ATOM_NULL))
36916         return JS_EXCEPTION;
36917     obj = JS_ToObject(ctx, this_val);
36918     if (JS_IsException(obj)) {
36919         JS_FreeAtom(ctx, atom);
36920         return obj;
36921     }
36922     p = JS_VALUE_GET_OBJ(obj);
36923     ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
36924     JS_FreeAtom(ctx, atom);
36925     JS_FreeValue(ctx, obj);
36926     if (ret < 0)
36927         return JS_EXCEPTION;
36928     else
36929         return JS_NewBool(ctx, ret);
36930 }
36931 
js_object_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36932 static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val,
36933                                  int argc, JSValueConst *argv)
36934 {
36935     return JS_ToObject(ctx, this_val);
36936 }
36937 
js_object_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36938 static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val,
36939                                   int argc, JSValueConst *argv)
36940 {
36941     JSValue obj, tag;
36942     int is_array;
36943     JSAtom atom;
36944     JSObject *p;
36945 
36946     if (JS_IsNull(this_val)) {
36947         tag = JS_NewString(ctx, "Null");
36948     } else if (JS_IsUndefined(this_val)) {
36949         tag = JS_NewString(ctx, "Undefined");
36950     } else {
36951         obj = JS_ToObject(ctx, this_val);
36952         if (JS_IsException(obj))
36953             return obj;
36954         is_array = JS_IsArray(ctx, obj);
36955         if (is_array < 0) {
36956             JS_FreeValue(ctx, obj);
36957             return JS_EXCEPTION;
36958         }
36959         if (is_array) {
36960             atom = JS_ATOM_Array;
36961         } else if (JS_IsFunction(ctx, obj)) {
36962             atom = JS_ATOM_Function;
36963         } else {
36964             p = JS_VALUE_GET_OBJ(obj);
36965             switch(p->class_id) {
36966             case JS_CLASS_STRING:
36967             case JS_CLASS_ARGUMENTS:
36968             case JS_CLASS_MAPPED_ARGUMENTS:
36969             case JS_CLASS_ERROR:
36970             case JS_CLASS_BOOLEAN:
36971             case JS_CLASS_NUMBER:
36972             case JS_CLASS_DATE:
36973             case JS_CLASS_REGEXP:
36974                 atom = ctx->rt->class_array[p->class_id].class_name;
36975                 break;
36976             default:
36977                 atom = JS_ATOM_Object;
36978                 break;
36979             }
36980         }
36981         tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag);
36982         JS_FreeValue(ctx, obj);
36983         if (JS_IsException(tag))
36984             return JS_EXCEPTION;
36985         if (!JS_IsString(tag)) {
36986             JS_FreeValue(ctx, tag);
36987             tag = JS_AtomToString(ctx, atom);
36988         }
36989     }
36990     return JS_ConcatString3(ctx, "[object ", tag, "]");
36991 }
36992 
js_object_toLocaleString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36993 static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val,
36994                                         int argc, JSValueConst *argv)
36995 {
36996     return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL);
36997 }
36998 
js_object_assign(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36999 static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val,
37000                                 int argc, JSValueConst *argv)
37001 {
37002     // Object.assign(obj, source1)
37003     JSValue obj, s;
37004     int i;
37005 
37006     s = JS_UNDEFINED;
37007     obj = JS_ToObject(ctx, argv[0]);
37008     if (JS_IsException(obj))
37009         goto exception;
37010     for (i = 1; i < argc; i++) {
37011         if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) {
37012             s = JS_ToObject(ctx, argv[i]);
37013             if (JS_IsException(s))
37014                 goto exception;
37015             if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE))
37016                 goto exception;
37017             JS_FreeValue(ctx, s);
37018         }
37019     }
37020     return obj;
37021 exception:
37022     JS_FreeValue(ctx, obj);
37023     JS_FreeValue(ctx, s);
37024     return JS_EXCEPTION;
37025 }
37026 
js_object_seal(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int freeze_flag)37027 static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val,
37028                               int argc, JSValueConst *argv, int freeze_flag)
37029 {
37030     JSValueConst obj = argv[0];
37031     JSObject *p;
37032     JSPropertyEnum *props;
37033     uint32_t len, i;
37034     int flags, desc_flags, res;
37035 
37036     if (!JS_IsObject(obj))
37037         return JS_DupValue(ctx, obj);
37038 
37039     res = JS_PreventExtensions(ctx, obj);
37040     if (res < 0)
37041         return JS_EXCEPTION;
37042     if (!res) {
37043         return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
37044     }
37045 
37046     p = JS_VALUE_GET_OBJ(obj);
37047     flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
37048     if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
37049         return JS_EXCEPTION;
37050 
37051     for(i = 0; i < len; i++) {
37052         JSPropertyDescriptor desc;
37053         JSAtom prop = props[i].atom;
37054 
37055         desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE;
37056         if (freeze_flag) {
37057             res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
37058             if (res < 0)
37059                 goto exception;
37060             if (res) {
37061                 if (desc.flags & JS_PROP_WRITABLE)
37062                     desc_flags |= JS_PROP_HAS_WRITABLE;
37063                 js_free_desc(ctx, &desc);
37064             }
37065         }
37066         if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED,
37067                               JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0)
37068             goto exception;
37069     }
37070     js_free_prop_enum(ctx, props, len);
37071     return JS_DupValue(ctx, obj);
37072 
37073  exception:
37074     js_free_prop_enum(ctx, props, len);
37075     return JS_EXCEPTION;
37076 }
37077 
js_object_isSealed(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int is_frozen)37078 static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val,
37079                                   int argc, JSValueConst *argv, int is_frozen)
37080 {
37081     JSValueConst obj = argv[0];
37082     JSObject *p;
37083     JSPropertyEnum *props;
37084     uint32_t len, i;
37085     int flags, res;
37086 
37087     if (!JS_IsObject(obj))
37088         return JS_TRUE;
37089 
37090     p = JS_VALUE_GET_OBJ(obj);
37091     flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
37092     if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
37093         return JS_EXCEPTION;
37094 
37095     for(i = 0; i < len; i++) {
37096         JSPropertyDescriptor desc;
37097         JSAtom prop = props[i].atom;
37098 
37099         res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
37100         if (res < 0)
37101             goto exception;
37102         if (res) {
37103             js_free_desc(ctx, &desc);
37104             if ((desc.flags & JS_PROP_CONFIGURABLE)
37105             ||  (is_frozen && (desc.flags & JS_PROP_WRITABLE))) {
37106                 res = FALSE;
37107                 goto done;
37108             }
37109         }
37110     }
37111     res = JS_IsExtensible(ctx, obj);
37112     if (res < 0)
37113         return JS_EXCEPTION;
37114     res ^= 1;
37115 done:
37116     js_free_prop_enum(ctx, props, len);
37117     return JS_NewBool(ctx, res);
37118 
37119 exception:
37120     js_free_prop_enum(ctx, props, len);
37121     return JS_EXCEPTION;
37122 }
37123 
js_object_fromEntries(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37124 static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val,
37125                                      int argc, JSValueConst *argv)
37126 {
37127     JSValue obj, iter, next_method = JS_UNDEFINED;
37128     JSValueConst iterable;
37129     BOOL done;
37130 
37131     /*  RequireObjectCoercible() not necessary because it is tested in
37132         JS_GetIterator() by JS_GetProperty() */
37133     iterable = argv[0];
37134 
37135     obj = JS_NewObject(ctx);
37136     if (JS_IsException(obj))
37137         return obj;
37138 
37139     iter = JS_GetIterator(ctx, iterable, FALSE);
37140     if (JS_IsException(iter))
37141         goto fail;
37142     next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
37143     if (JS_IsException(next_method))
37144         goto fail;
37145 
37146     for(;;) {
37147         JSValue key, value, item;
37148         item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
37149         if (JS_IsException(item))
37150             goto fail;
37151         if (done) {
37152             JS_FreeValue(ctx, item);
37153             break;
37154         }
37155 
37156         key = JS_UNDEFINED;
37157         value = JS_UNDEFINED;
37158         if (!JS_IsObject(item)) {
37159             JS_ThrowTypeErrorNotAnObject(ctx);
37160             goto fail1;
37161         }
37162         key = JS_GetPropertyUint32(ctx, item, 0);
37163         if (JS_IsException(key))
37164             goto fail1;
37165         value = JS_GetPropertyUint32(ctx, item, 1);
37166         if (JS_IsException(value)) {
37167             JS_FreeValue(ctx, key);
37168             goto fail1;
37169         }
37170         if (JS_DefinePropertyValueValue(ctx, obj, key, value,
37171                                         JS_PROP_C_W_E | JS_PROP_THROW) < 0) {
37172         fail1:
37173             JS_FreeValue(ctx, item);
37174             goto fail;
37175         }
37176         JS_FreeValue(ctx, item);
37177     }
37178     JS_FreeValue(ctx, next_method);
37179     JS_FreeValue(ctx, iter);
37180     return obj;
37181  fail:
37182     if (JS_IsObject(iter)) {
37183         /* close the iterator object, preserving pending exception */
37184         JS_IteratorClose(ctx, iter, TRUE);
37185     }
37186     JS_FreeValue(ctx, next_method);
37187     JS_FreeValue(ctx, iter);
37188     JS_FreeValue(ctx, obj);
37189     return JS_EXCEPTION;
37190 }
37191 
37192 #if 0
37193 /* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */
37194 static JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val,
37195                                           int argc, JSValueConst *argv)
37196 {
37197     int ret;
37198     ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]),
37199                                       JS_DupValue(ctx, argv[2]),
37200                                       JS_PROP_C_W_E | JS_PROP_THROW);
37201     if (ret < 0)
37202         return JS_EXCEPTION;
37203     else
37204         return JS_NewBool(ctx, ret);
37205 }
37206 
37207 static JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val,
37208                                     int argc, JSValueConst *argv)
37209 {
37210     return JS_ToObject(ctx, argv[0]);
37211 }
37212 
37213 static JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val,
37214                                        int argc, JSValueConst *argv)
37215 {
37216     int hint = HINT_NONE;
37217 
37218     if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT)
37219         hint = JS_VALUE_GET_INT(argv[1]);
37220 
37221     return JS_ToPrimitive(ctx, argv[0], hint);
37222 }
37223 #endif
37224 
37225 /* return an empty string if not an object */
js_object___getClass(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37226 static JSValue js_object___getClass(JSContext *ctx, JSValueConst this_val,
37227                                     int argc, JSValueConst *argv)
37228 {
37229     JSAtom atom;
37230     JSObject *p;
37231     uint32_t tag;
37232     int class_id;
37233 
37234     tag = JS_VALUE_GET_NORM_TAG(argv[0]);
37235     if (tag == JS_TAG_OBJECT) {
37236         p = JS_VALUE_GET_OBJ(argv[0]);
37237         class_id = p->class_id;
37238         if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0]))
37239             class_id = JS_CLASS_BYTECODE_FUNCTION;
37240         atom = ctx->rt->class_array[class_id].class_name;
37241     } else {
37242         atom = JS_ATOM_empty_string;
37243     }
37244     return JS_AtomToString(ctx, atom);
37245 }
37246 
js_object_is(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37247 static JSValue js_object_is(JSContext *ctx, JSValueConst this_val,
37248                             int argc, JSValueConst *argv)
37249 {
37250     return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1]));
37251 }
37252 
37253 #if 0
37254 static JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val,
37255                                          int argc, JSValueConst *argv)
37256 {
37257     return JS_GetObjectData(ctx, argv[0]);
37258 }
37259 
37260 static JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val,
37261                                          int argc, JSValueConst *argv)
37262 {
37263     if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1])))
37264         return JS_EXCEPTION;
37265     return JS_DupValue(ctx, argv[1]);
37266 }
37267 
37268 static JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val,
37269                                          int argc, JSValueConst *argv)
37270 {
37271     return JS_ToPropertyKey(ctx, argv[0]);
37272 }
37273 
37274 static JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val,
37275                                     int argc, JSValueConst *argv)
37276 {
37277     return JS_NewBool(ctx, JS_IsObject(argv[0]));
37278 }
37279 
37280 static JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val,
37281                                            int argc, JSValueConst *argv)
37282 {
37283     return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1]));
37284 }
37285 
37286 static JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val,
37287                                          int argc, JSValueConst *argv)
37288 {
37289     return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0]));
37290 }
37291 #endif
37292 
JS_SpeciesConstructor(JSContext * ctx,JSValueConst obj,JSValueConst defaultConstructor)37293 static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj,
37294                                      JSValueConst defaultConstructor)
37295 {
37296     JSValue ctor, species;
37297 
37298     if (!JS_IsObject(obj))
37299         return JS_ThrowTypeErrorNotAnObject(ctx);
37300     ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
37301     if (JS_IsException(ctor))
37302         return ctor;
37303     if (JS_IsUndefined(ctor))
37304         return JS_DupValue(ctx, defaultConstructor);
37305     if (!JS_IsObject(ctor)) {
37306         JS_FreeValue(ctx, ctor);
37307         return JS_ThrowTypeErrorNotAnObject(ctx);
37308     }
37309     species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
37310     JS_FreeValue(ctx, ctor);
37311     if (JS_IsException(species))
37312         return species;
37313     if (JS_IsUndefined(species) || JS_IsNull(species))
37314         return JS_DupValue(ctx, defaultConstructor);
37315     if (!JS_IsConstructor(ctx, species)) {
37316         JS_FreeValue(ctx, species);
37317         return JS_ThrowTypeError(ctx, "not a constructor");
37318     }
37319     return species;
37320 }
37321 
37322 #if 0
37323 static JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val,
37324                                               int argc, JSValueConst *argv)
37325 {
37326     return JS_SpeciesConstructor(ctx, argv[0], argv[1]);
37327 }
37328 #endif
37329 
js_object_get___proto__(JSContext * ctx,JSValueConst this_val)37330 static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val)
37331 {
37332     JSValue val, ret;
37333 
37334     val = JS_ToObject(ctx, this_val);
37335     if (JS_IsException(val))
37336         return val;
37337     ret = JS_GetPrototype(ctx, val);
37338     JS_FreeValue(ctx, val);
37339     return ret;
37340 }
37341 
js_object_set___proto__(JSContext * ctx,JSValueConst this_val,JSValueConst proto)37342 static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val,
37343                                        JSValueConst proto)
37344 {
37345     if (JS_IsUndefined(this_val) || JS_IsNull(this_val))
37346         return JS_ThrowTypeErrorNotAnObject(ctx);
37347     if (!JS_IsObject(proto) && !JS_IsNull(proto))
37348         return JS_UNDEFINED;
37349     if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0)
37350         return JS_EXCEPTION;
37351     else
37352         return JS_UNDEFINED;
37353 }
37354 
js_object_isPrototypeOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37355 static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val,
37356                                        int argc, JSValueConst *argv)
37357 {
37358     JSValue obj, v1;
37359     JSValueConst v;
37360     int res;
37361 
37362     v = argv[0];
37363     if (!JS_IsObject(v))
37364         return JS_FALSE;
37365     obj = JS_ToObject(ctx, this_val);
37366     if (JS_IsException(obj))
37367         return JS_EXCEPTION;
37368     v1 = JS_DupValue(ctx, v);
37369     for(;;) {
37370         v1 = JS_GetPrototypeFree(ctx, v1);
37371         if (JS_IsException(v1))
37372             goto exception;
37373         if (JS_IsNull(v1)) {
37374             res = FALSE;
37375             break;
37376         }
37377         if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) {
37378             res = TRUE;
37379             break;
37380         }
37381         /* avoid infinite loop (possible with proxies) */
37382         if (js_poll_interrupts(ctx))
37383             goto exception;
37384     }
37385     JS_FreeValue(ctx, v1);
37386     JS_FreeValue(ctx, obj);
37387     return JS_NewBool(ctx, res);
37388 
37389 exception:
37390     JS_FreeValue(ctx, v1);
37391     JS_FreeValue(ctx, obj);
37392     return JS_EXCEPTION;
37393 }
37394 
js_object_propertyIsEnumerable(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37395 static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val,
37396                                               int argc, JSValueConst *argv)
37397 {
37398     JSValue obj, res = JS_EXCEPTION;
37399     JSAtom prop = JS_ATOM_NULL;
37400     JSPropertyDescriptor desc;
37401     int has_prop;
37402 
37403     obj = JS_ToObject(ctx, this_val);
37404     if (JS_IsException(obj))
37405         goto exception;
37406     prop = JS_ValueToAtom(ctx, argv[0]);
37407     if (unlikely(prop == JS_ATOM_NULL))
37408         goto exception;
37409 
37410     has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
37411     if (has_prop < 0)
37412         goto exception;
37413     if (has_prop) {
37414         res = JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0);
37415         js_free_desc(ctx, &desc);
37416     } else {
37417         res = JS_FALSE;
37418     }
37419 
37420 exception:
37421     JS_FreeAtom(ctx, prop);
37422     JS_FreeValue(ctx, obj);
37423     return res;
37424 }
37425 
js_object___lookupGetter__(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int setter)37426 static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,
37427                                           int argc, JSValueConst *argv, int setter)
37428 {
37429     JSValue obj, res = JS_EXCEPTION;
37430     JSAtom prop = JS_ATOM_NULL;
37431     JSPropertyDescriptor desc;
37432     int has_prop;
37433 
37434     obj = JS_ToObject(ctx, this_val);
37435     if (JS_IsException(obj))
37436         goto exception;
37437     prop = JS_ValueToAtom(ctx, argv[0]);
37438     if (unlikely(prop == JS_ATOM_NULL))
37439         goto exception;
37440 
37441     for (;;) {
37442         has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
37443         if (has_prop < 0)
37444             goto exception;
37445         if (has_prop) {
37446             if (desc.flags & JS_PROP_GETSET)
37447                 res = JS_DupValue(ctx, setter ? desc.setter : desc.getter);
37448             else
37449                 res = JS_UNDEFINED;
37450             js_free_desc(ctx, &desc);
37451             break;
37452         }
37453         obj = JS_GetPrototypeFree(ctx, obj);
37454         if (JS_IsException(obj))
37455             goto exception;
37456         if (JS_IsNull(obj)) {
37457             res = JS_UNDEFINED;
37458             break;
37459         }
37460         /* avoid infinite loop (possible with proxies) */
37461         if (js_poll_interrupts(ctx))
37462             goto exception;
37463     }
37464 
37465 exception:
37466     JS_FreeAtom(ctx, prop);
37467     JS_FreeValue(ctx, obj);
37468     return res;
37469 }
37470 
37471 static const JSCFunctionListEntry js_object_funcs[] = {
37472     JS_CFUNC_DEF("create", 2, js_object_create ),
37473     JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ),
37474     JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ),
37475     JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ),
37476     JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ),
37477     JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ),
37478     JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ),
37479     JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ),
37480     JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ),
37481     JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ),
37482     JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ),
37483     JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ),
37484     JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ),
37485     JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ),
37486     JS_CFUNC_DEF("is", 2, js_object_is ),
37487     JS_CFUNC_DEF("assign", 2, js_object_assign ),
37488     JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ),
37489     JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ),
37490     JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ),
37491     JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ),
37492     JS_CFUNC_DEF("__getClass", 1, js_object___getClass ),
37493     //JS_CFUNC_DEF("__isObject", 1, js_object___isObject ),
37494     //JS_CFUNC_DEF("__isConstructor", 1, js_object___isConstructor ),
37495     //JS_CFUNC_DEF("__toObject", 1, js_object___toObject ),
37496     //JS_CFUNC_DEF("__setOwnProperty", 3, js_object___setOwnProperty ),
37497     //JS_CFUNC_DEF("__toPrimitive", 2, js_object___toPrimitive ),
37498     //JS_CFUNC_DEF("__toPropertyKey", 1, js_object___toPropertyKey ),
37499     //JS_CFUNC_DEF("__speciesConstructor", 2, js_object___speciesConstructor ),
37500     //JS_CFUNC_DEF("__isSameValueZero", 2, js_object___isSameValueZero ),
37501     //JS_CFUNC_DEF("__getObjectData", 1, js_object___getObjectData ),
37502     //JS_CFUNC_DEF("__setObjectData", 2, js_object___setObjectData ),
37503     JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ),
37504 };
37505 
37506 static const JSCFunctionListEntry js_object_proto_funcs[] = {
37507     JS_CFUNC_DEF("toString", 0, js_object_toString ),
37508     JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ),
37509     JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ),
37510     JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ),
37511     JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ),
37512     JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ),
37513     JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ),
37514     JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ),
37515     JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ),
37516     JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ),
37517     JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ),
37518 };
37519 
37520 /* Function class */
37521 
js_function_proto(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37522 static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val,
37523                                  int argc, JSValueConst *argv)
37524 {
37525     return JS_UNDEFINED;
37526 }
37527 
37528 /* XXX: add a specific eval mode so that Function("}), ({") is rejected */
js_function_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv,int magic)37529 static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,
37530                                        int argc, JSValueConst *argv, int magic)
37531 {
37532     JSFunctionKindEnum func_kind = magic;
37533     int i, n, ret;
37534     JSValue s, proto, obj = JS_UNDEFINED;
37535     StringBuffer b_s, *b = &b_s;
37536 
37537     string_buffer_init(ctx, b, 0);
37538     string_buffer_putc8(b, '(');
37539 
37540     if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) {
37541         string_buffer_puts8(b, "async ");
37542     }
37543     string_buffer_puts8(b, "function");
37544 
37545     if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) {
37546         string_buffer_putc8(b, '*');
37547     }
37548     string_buffer_puts8(b, " anonymous(");
37549 
37550     n = argc - 1;
37551     for(i = 0; i < n; i++) {
37552         if (i != 0) {
37553             string_buffer_putc8(b, ',');
37554         }
37555         if (string_buffer_concat_value(b, argv[i]))
37556             goto fail;
37557     }
37558     string_buffer_puts8(b, "\n) {\n");
37559     if (n >= 0) {
37560         if (string_buffer_concat_value(b, argv[n]))
37561             goto fail;
37562     }
37563     string_buffer_puts8(b, "\n})");
37564     s = string_buffer_end(b);
37565     if (JS_IsException(s))
37566         goto fail1;
37567 
37568     obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1);
37569     JS_FreeValue(ctx, s);
37570     if (JS_IsException(obj))
37571         goto fail1;
37572     if (!JS_IsUndefined(new_target)) {
37573         /* set the prototype */
37574         proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
37575         if (JS_IsException(proto))
37576             goto fail1;
37577         if (!JS_IsObject(proto)) {
37578             JSContext *realm;
37579             JS_FreeValue(ctx, proto);
37580             realm = JS_GetFunctionRealm(ctx, new_target);
37581             if (!realm)
37582                 goto fail1;
37583             proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]);
37584         }
37585         ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE);
37586         JS_FreeValue(ctx, proto);
37587         if (ret < 0)
37588             goto fail1;
37589     }
37590     return obj;
37591 
37592  fail:
37593     string_buffer_free(b);
37594  fail1:
37595     JS_FreeValue(ctx, obj);
37596     return JS_EXCEPTION;
37597 }
37598 
js_get_length32(JSContext * ctx,uint32_t * pres,JSValueConst obj)37599 static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
37600                                        JSValueConst obj)
37601 {
37602     JSValue len_val;
37603     len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
37604     if (JS_IsException(len_val)) {
37605         *pres = 0;
37606         return -1;
37607     }
37608     return JS_ToUint32Free(ctx, pres, len_val);
37609 }
37610 
js_get_length64(JSContext * ctx,int64_t * pres,JSValueConst obj)37611 static __exception int js_get_length64(JSContext *ctx, int64_t *pres,
37612                                        JSValueConst obj)
37613 {
37614     JSValue len_val;
37615     len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
37616     if (JS_IsException(len_val)) {
37617         *pres = 0;
37618         return -1;
37619     }
37620     return JS_ToLengthFree(ctx, pres, len_val);
37621 }
37622 
free_arg_list(JSContext * ctx,JSValue * tab,uint32_t len)37623 static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len)
37624 {
37625     uint32_t i;
37626     for(i = 0; i < len; i++) {
37627         JS_FreeValue(ctx, tab[i]);
37628     }
37629     js_free(ctx, tab);
37630 }
37631 
37632 /* XXX: should use ValueArray */
build_arg_list(JSContext * ctx,uint32_t * plen,JSValueConst array_arg)37633 static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
37634                                JSValueConst array_arg)
37635 {
37636     uint32_t len, i;
37637     JSValue *tab, ret;
37638     JSObject *p;
37639 
37640     if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) {
37641         JS_ThrowTypeError(ctx, "not a object");
37642         return NULL;
37643     }
37644     if (js_get_length32(ctx, &len, array_arg))
37645         return NULL;
37646     if (len > JS_MAX_LOCAL_VARS) {
37647         JS_ThrowInternalError(ctx, "too many arguments");
37648         return NULL;
37649     }
37650     /* avoid allocating 0 bytes */
37651     tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len));
37652     if (!tab)
37653         return NULL;
37654     p = JS_VALUE_GET_OBJ(array_arg);
37655     if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) &&
37656         p->fast_array &&
37657         len == p->u.array.count) {
37658         for(i = 0; i < len; i++) {
37659             tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]);
37660         }
37661     } else {
37662         for(i = 0; i < len; i++) {
37663             ret = JS_GetPropertyUint32(ctx, array_arg, i);
37664             if (JS_IsException(ret)) {
37665                 free_arg_list(ctx, tab, i);
37666                 return NULL;
37667             }
37668             tab[i] = ret;
37669         }
37670     }
37671     *plen = len;
37672     return tab;
37673 }
37674 
37675 /* magic value: 0 = normal apply, 1 = apply for constructor, 2 =
37676    Reflect.apply */
js_function_apply(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)37677 static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
37678                                  int argc, JSValueConst *argv, int magic)
37679 {
37680     JSValueConst this_arg, array_arg;
37681     uint32_t len;
37682     JSValue *tab, ret;
37683 
37684     if (check_function(ctx, this_val))
37685         return JS_EXCEPTION;
37686     this_arg = argv[0];
37687     array_arg = argv[1];
37688     if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED ||
37689          JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) {
37690         return JS_Call(ctx, this_val, this_arg, 0, NULL);
37691     }
37692     tab = build_arg_list(ctx, &len, array_arg);
37693     if (!tab)
37694         return JS_EXCEPTION;
37695     if (magic & 1) {
37696         ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab);
37697     } else {
37698         ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab);
37699     }
37700     free_arg_list(ctx, tab, len);
37701     return ret;
37702 }
37703 
js_function_call(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37704 static JSValue js_function_call(JSContext *ctx, JSValueConst this_val,
37705                                 int argc, JSValueConst *argv)
37706 {
37707     if (argc <= 0) {
37708         return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL);
37709     } else {
37710         return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1);
37711     }
37712 }
37713 
js_function_bind(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37714 static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val,
37715                                 int argc, JSValueConst *argv)
37716 {
37717     JSBoundFunction *bf;
37718     JSValue func_obj, name1, len_val;
37719     JSObject *p;
37720     int arg_count, i, ret;
37721 
37722     if (check_function(ctx, this_val))
37723         return JS_EXCEPTION;
37724 
37725     func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
37726                                  JS_CLASS_BOUND_FUNCTION);
37727     if (JS_IsException(func_obj))
37728         return JS_EXCEPTION;
37729     p = JS_VALUE_GET_OBJ(func_obj);
37730     p->is_constructor = JS_IsConstructor(ctx, this_val);
37731     arg_count = max_int(0, argc - 1);
37732     bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue));
37733     if (!bf)
37734         goto exception;
37735     bf->func_obj = JS_DupValue(ctx, this_val);
37736     bf->this_val = JS_DupValue(ctx, argv[0]);
37737     bf->argc = arg_count;
37738     for(i = 0; i < arg_count; i++) {
37739         bf->argv[i] = JS_DupValue(ctx, argv[i + 1]);
37740     }
37741     p->u.bound_function = bf;
37742 
37743     /* XXX: the spec could be simpler by only using GetOwnProperty */
37744     ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length);
37745     if (ret < 0)
37746         goto exception;
37747     if (!ret) {
37748         len_val = JS_NewInt32(ctx, 0);
37749     } else {
37750         len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length);
37751         if (JS_IsException(len_val))
37752             goto exception;
37753         if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) {
37754             /* most common case */
37755             int len1 = JS_VALUE_GET_INT(len_val);
37756             if (len1 <= arg_count)
37757                 len1 = 0;
37758             else
37759                 len1 -= arg_count;
37760             len_val = JS_NewInt32(ctx, len1);
37761         } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) {
37762             double d = JS_VALUE_GET_FLOAT64(len_val);
37763             if (isnan(d)) {
37764                 d = 0.0;
37765             } else {
37766                 d = trunc(d);
37767                 if (d <= (double)arg_count)
37768                     d = 0.0;
37769                 else
37770                     d -= (double)arg_count; /* also converts -0 to +0 */
37771             }
37772             len_val = JS_NewFloat64(ctx, d);
37773         } else {
37774             JS_FreeValue(ctx, len_val);
37775             len_val = JS_NewInt32(ctx, 0);
37776         }
37777     }
37778     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,
37779                            len_val, JS_PROP_CONFIGURABLE);
37780 
37781     name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name);
37782     if (JS_IsException(name1))
37783         goto exception;
37784     if (!JS_IsString(name1)) {
37785         JS_FreeValue(ctx, name1);
37786         name1 = JS_AtomToString(ctx, JS_ATOM_empty_string);
37787     }
37788     name1 = JS_ConcatString3(ctx, "bound ", name1, "");
37789     if (JS_IsException(name1))
37790         goto exception;
37791     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1,
37792                            JS_PROP_CONFIGURABLE);
37793     return func_obj;
37794  exception:
37795     JS_FreeValue(ctx, func_obj);
37796     return JS_EXCEPTION;
37797 }
37798 
js_function_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37799 static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val,
37800                                     int argc, JSValueConst *argv)
37801 {
37802     JSObject *p;
37803     JSFunctionKindEnum func_kind = JS_FUNC_NORMAL;
37804 
37805     if (check_function(ctx, this_val))
37806         return JS_EXCEPTION;
37807 
37808     p = JS_VALUE_GET_OBJ(this_val);
37809     if (js_class_has_bytecode(p->class_id)) {
37810         JSFunctionBytecode *b = p->u.func.function_bytecode;
37811         if (b->has_debug && b->debug.source) {
37812             return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len);
37813         }
37814         func_kind = b->func_kind;
37815     }
37816     {
37817         JSValue name;
37818         const char *pref, *suff;
37819 
37820         switch(func_kind) {
37821         default:
37822         case JS_FUNC_NORMAL:
37823             pref = "function ";
37824             break;
37825         case JS_FUNC_GENERATOR:
37826             pref = "function *";
37827             break;
37828         case JS_FUNC_ASYNC:
37829             pref = "async function ";
37830             break;
37831         case JS_FUNC_ASYNC_GENERATOR:
37832             pref = "async function *";
37833             break;
37834         }
37835         suff = "() {\n    [native code]\n}";
37836         name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
37837         if (JS_IsUndefined(name))
37838             name = JS_AtomToString(ctx, JS_ATOM_empty_string);
37839         return JS_ConcatString3(ctx, pref, name, suff);
37840     }
37841 }
37842 
js_function_hasInstance(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37843 static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val,
37844                                        int argc, JSValueConst *argv)
37845 {
37846     int ret;
37847     ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val);
37848     if (ret < 0)
37849         return JS_EXCEPTION;
37850     else
37851         return JS_NewBool(ctx, ret);
37852 }
37853 
37854 static const JSCFunctionListEntry js_function_proto_funcs[] = {
37855     JS_CFUNC_DEF("call", 1, js_function_call ),
37856     JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ),
37857     JS_CFUNC_DEF("bind", 1, js_function_bind ),
37858     JS_CFUNC_DEF("toString", 0, js_function_toString ),
37859     JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ),
37860     JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ),
37861     JS_CGETSET_DEF("lineNumber", js_function_proto_lineNumber, NULL ),
37862 };
37863 
37864 /* Error class */
37865 
iterator_to_array(JSContext * ctx,JSValueConst items)37866 static JSValue iterator_to_array(JSContext *ctx, JSValueConst items)
37867 {
37868     JSValue iter, next_method = JS_UNDEFINED;
37869     JSValue v, r = JS_UNDEFINED;
37870     int64_t k;
37871     BOOL done;
37872 
37873     iter = JS_GetIterator(ctx, items, FALSE);
37874     if (JS_IsException(iter))
37875         goto exception;
37876     next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
37877     if (JS_IsException(next_method))
37878         goto exception;
37879     r = JS_NewArray(ctx);
37880     if (JS_IsException(r))
37881         goto exception;
37882     for (k = 0;; k++) {
37883         v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
37884         if (JS_IsException(v))
37885             goto exception_close;
37886         if (done)
37887             break;
37888         if (JS_DefinePropertyValueInt64(ctx, r, k, v,
37889                                         JS_PROP_C_W_E | JS_PROP_THROW) < 0)
37890             goto exception_close;
37891     }
37892  done:
37893     JS_FreeValue(ctx, next_method);
37894     JS_FreeValue(ctx, iter);
37895     return r;
37896  exception_close:
37897     JS_IteratorClose(ctx, iter, TRUE);
37898  exception:
37899     JS_FreeValue(ctx, r);
37900     r = JS_EXCEPTION;
37901     goto done;
37902 }
37903 
js_error_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv,int magic)37904 static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
37905                                     int argc, JSValueConst *argv, int magic)
37906 {
37907     JSValue obj, msg, proto;
37908     JSValueConst message;
37909 
37910     if (JS_IsUndefined(new_target))
37911         new_target = JS_GetActiveFunction(ctx);
37912     proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
37913     if (JS_IsException(proto))
37914         return proto;
37915     if (!JS_IsObject(proto)) {
37916         JSContext *realm;
37917         JSValueConst proto1;
37918 
37919         JS_FreeValue(ctx, proto);
37920         realm = JS_GetFunctionRealm(ctx, new_target);
37921         if (!realm)
37922             return JS_EXCEPTION;
37923         if (magic < 0) {
37924             proto1 = realm->class_proto[JS_CLASS_ERROR];
37925         } else {
37926             proto1 = realm->native_error_proto[magic];
37927         }
37928         proto = JS_DupValue(ctx, proto1);
37929     }
37930     obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR);
37931     JS_FreeValue(ctx, proto);
37932     if (JS_IsException(obj))
37933         return obj;
37934     if (magic == JS_AGGREGATE_ERROR) {
37935         message = argv[1];
37936     } else {
37937         message = argv[0];
37938     }
37939 
37940     if (!JS_IsUndefined(message)) {
37941         msg = JS_ToString(ctx, message);
37942         if (unlikely(JS_IsException(msg)))
37943             goto exception;
37944         JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg,
37945                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
37946     }
37947 
37948     if (magic == JS_AGGREGATE_ERROR) {
37949         JSValue error_list = iterator_to_array(ctx, argv[0]);
37950         if (JS_IsException(error_list))
37951             goto exception;
37952         JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list,
37953                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
37954     }
37955 
37956     /* skip the Error() function in the backtrace */
37957     build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
37958     return obj;
37959  exception:
37960     JS_FreeValue(ctx, obj);
37961     return JS_EXCEPTION;
37962 }
37963 
js_error_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37964 static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
37965                                  int argc, JSValueConst *argv)
37966 {
37967     JSValue name, msg;
37968 
37969     if (!JS_IsObject(this_val))
37970         return JS_ThrowTypeErrorNotAnObject(ctx);
37971     name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
37972     if (JS_IsUndefined(name))
37973         name = JS_AtomToString(ctx, JS_ATOM_Error);
37974     else
37975         name = JS_ToStringFree(ctx, name);
37976     if (JS_IsException(name))
37977         return JS_EXCEPTION;
37978 
37979     msg = JS_GetProperty(ctx, this_val, JS_ATOM_message);
37980     if (JS_IsUndefined(msg))
37981         msg = JS_AtomToString(ctx, JS_ATOM_empty_string);
37982     else
37983         msg = JS_ToStringFree(ctx, msg);
37984     if (JS_IsException(msg)) {
37985         JS_FreeValue(ctx, name);
37986         return JS_EXCEPTION;
37987     }
37988     if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg))
37989         name = JS_ConcatString3(ctx, "", name, ": ");
37990     return JS_ConcatString(ctx, name, msg);
37991 }
37992 
37993 static const JSCFunctionListEntry js_error_proto_funcs[] = {
37994     JS_CFUNC_DEF("toString", 0, js_error_toString ),
37995     JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
37996     JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
37997 };
37998 
37999 /* AggregateError */
38000 
38001 /* used by C code. */
js_aggregate_error_constructor(JSContext * ctx,JSValueConst errors)38002 static JSValue js_aggregate_error_constructor(JSContext *ctx,
38003                                               JSValueConst errors)
38004 {
38005     JSValue obj;
38006 
38007     obj = JS_NewObjectProtoClass(ctx,
38008                                  ctx->native_error_proto[JS_AGGREGATE_ERROR],
38009                                  JS_CLASS_ERROR);
38010     if (JS_IsException(obj))
38011         return obj;
38012     JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors),
38013                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
38014     return obj;
38015 }
38016 
38017 /* Array */
38018 
JS_CopySubArray(JSContext * ctx,JSValueConst obj,int64_t to_pos,int64_t from_pos,int64_t count,int dir)38019 static int JS_CopySubArray(JSContext *ctx,
38020                            JSValueConst obj, int64_t to_pos,
38021                            int64_t from_pos, int64_t count, int dir)
38022 {
38023     int64_t i, from, to;
38024     JSValue val;
38025     int fromPresent;
38026 
38027     /* XXX: should special case fast arrays */
38028     for (i = 0; i < count; i++) {
38029         if (dir < 0) {
38030             from = from_pos + count - i - 1;
38031             to = to_pos + count - i - 1;
38032         } else {
38033             from = from_pos + i;
38034             to = to_pos + i;
38035         }
38036         fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val);
38037         if (fromPresent < 0)
38038             goto exception;
38039 
38040         if (fromPresent) {
38041             if (JS_SetPropertyInt64(ctx, obj, to, val) < 0)
38042                 goto exception;
38043         } else {
38044             if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0)
38045                 goto exception;
38046         }
38047     }
38048     return 0;
38049 
38050  exception:
38051     return -1;
38052 }
38053 
js_array_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)38054 static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target,
38055                                     int argc, JSValueConst *argv)
38056 {
38057     JSValue obj;
38058     int i;
38059 
38060     obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY);
38061     if (JS_IsException(obj))
38062         return obj;
38063     if (argc == 1 && JS_IsNumber(argv[0])) {
38064         uint32_t len;
38065         if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE))
38066             goto fail;
38067         if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0)
38068             goto fail;
38069     } else {
38070         for(i = 0; i < argc; i++) {
38071             if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0)
38072                 goto fail;
38073         }
38074     }
38075     return obj;
38076 fail:
38077     JS_FreeValue(ctx, obj);
38078     return JS_EXCEPTION;
38079 }
38080 
js_array_from(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38081 static JSValue js_array_from(JSContext *ctx, JSValueConst this_val,
38082                              int argc, JSValueConst *argv)
38083 {
38084     // from(items, mapfn = void 0, this_arg = void 0)
38085     JSValueConst items = argv[0], mapfn, this_arg;
38086     JSValueConst args[2];
38087     JSValue stack[2];
38088     JSValue iter, r, v, v2, arrayLike;
38089     int64_t k, len;
38090     int done, mapping;
38091 
38092     mapping = FALSE;
38093     mapfn = JS_UNDEFINED;
38094     this_arg = JS_UNDEFINED;
38095     r = JS_UNDEFINED;
38096     arrayLike = JS_UNDEFINED;
38097     stack[0] = JS_UNDEFINED;
38098     stack[1] = JS_UNDEFINED;
38099 
38100     if (argc > 1) {
38101         mapfn = argv[1];
38102         if (!JS_IsUndefined(mapfn)) {
38103             if (check_function(ctx, mapfn))
38104                 goto exception;
38105             mapping = 1;
38106             if (argc > 2)
38107                 this_arg = argv[2];
38108         }
38109     }
38110     iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
38111     if (JS_IsException(iter))
38112         goto exception;
38113     if (!JS_IsUndefined(iter)) {
38114         JS_FreeValue(ctx, iter);
38115         if (JS_IsConstructor(ctx, this_val))
38116             r = JS_CallConstructor(ctx, this_val, 0, NULL);
38117         else
38118             r = JS_NewArray(ctx);
38119         if (JS_IsException(r))
38120             goto exception;
38121         stack[0] = JS_DupValue(ctx, items);
38122         if (js_for_of_start(ctx, &stack[1], FALSE))
38123             goto exception;
38124         for (k = 0;; k++) {
38125             v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);
38126             if (JS_IsException(v))
38127                 goto exception_close;
38128             if (done)
38129                 break;
38130             if (mapping) {
38131                 args[0] = v;
38132                 args[1] = JS_NewInt32(ctx, k);
38133                 v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
38134                 JS_FreeValue(ctx, v);
38135                 v = v2;
38136                 if (JS_IsException(v))
38137                     goto exception_close;
38138             }
38139             if (JS_DefinePropertyValueInt64(ctx, r, k, v,
38140                                             JS_PROP_C_W_E | JS_PROP_THROW) < 0)
38141                 goto exception_close;
38142         }
38143     } else {
38144         arrayLike = JS_ToObject(ctx, items);
38145         if (JS_IsException(arrayLike))
38146             goto exception;
38147         if (js_get_length64(ctx, &len, arrayLike) < 0)
38148             goto exception;
38149         v = JS_NewInt64(ctx, len);
38150         args[0] = v;
38151         if (JS_IsConstructor(ctx, this_val)) {
38152             r = JS_CallConstructor(ctx, this_val, 1, args);
38153         } else {
38154             r = js_array_constructor(ctx, JS_UNDEFINED, 1, args);
38155         }
38156         JS_FreeValue(ctx, v);
38157         if (JS_IsException(r))
38158             goto exception;
38159         for(k = 0; k < len; k++) {
38160             v = JS_GetPropertyInt64(ctx, arrayLike, k);
38161             if (JS_IsException(v))
38162                 goto exception;
38163             if (mapping) {
38164                 args[0] = v;
38165                 args[1] = JS_NewInt32(ctx, k);
38166                 v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
38167                 JS_FreeValue(ctx, v);
38168                 v = v2;
38169                 if (JS_IsException(v))
38170                     goto exception;
38171             }
38172             if (JS_DefinePropertyValueInt64(ctx, r, k, v,
38173                                             JS_PROP_C_W_E | JS_PROP_THROW) < 0)
38174                 goto exception;
38175         }
38176     }
38177     if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0)
38178         goto exception;
38179     goto done;
38180 
38181  exception_close:
38182     if (!JS_IsUndefined(stack[0]))
38183         JS_IteratorClose(ctx, stack[0], TRUE);
38184  exception:
38185     JS_FreeValue(ctx, r);
38186     r = JS_EXCEPTION;
38187  done:
38188     JS_FreeValue(ctx, arrayLike);
38189     JS_FreeValue(ctx, stack[0]);
38190     JS_FreeValue(ctx, stack[1]);
38191     return r;
38192 }
38193 
js_array_of(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38194 static JSValue js_array_of(JSContext *ctx, JSValueConst this_val,
38195                            int argc, JSValueConst *argv)
38196 {
38197     JSValue obj, args[1];
38198     int i;
38199 
38200     if (JS_IsConstructor(ctx, this_val)) {
38201         args[0] = JS_NewInt32(ctx, argc);
38202         obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args);
38203     } else {
38204         obj = JS_NewArray(ctx);
38205     }
38206     if (JS_IsException(obj))
38207         return JS_EXCEPTION;
38208     for(i = 0; i < argc; i++) {
38209         if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]),
38210                                         JS_PROP_THROW) < 0) {
38211             goto fail;
38212         }
38213     }
38214     if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) {
38215     fail:
38216         JS_FreeValue(ctx, obj);
38217         return JS_EXCEPTION;
38218     }
38219     return obj;
38220 }
38221 
js_array_isArray(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38222 static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val,
38223                                 int argc, JSValueConst *argv)
38224 {
38225     int ret;
38226     ret = JS_IsArray(ctx, argv[0]);
38227     if (ret < 0)
38228         return JS_EXCEPTION;
38229     else
38230         return JS_NewBool(ctx, ret);
38231 }
38232 
js_get_this(JSContext * ctx,JSValueConst this_val)38233 static JSValue js_get_this(JSContext *ctx,
38234                            JSValueConst this_val)
38235 {
38236     return JS_DupValue(ctx, this_val);
38237 }
38238 
JS_ArraySpeciesCreate(JSContext * ctx,JSValueConst obj,JSValueConst len_val)38239 static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj,
38240                                      JSValueConst len_val)
38241 {
38242     JSValue ctor, ret, species;
38243     int res;
38244     JSContext *realm;
38245 
38246     res = JS_IsArray(ctx, obj);
38247     if (res < 0)
38248         return JS_EXCEPTION;
38249     if (!res)
38250         return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
38251     ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
38252     if (JS_IsException(ctor))
38253         return ctor;
38254     if (JS_IsConstructor(ctx, ctor)) {
38255         /* legacy web compatibility */
38256         realm = JS_GetFunctionRealm(ctx, ctor);
38257         if (!realm) {
38258             JS_FreeValue(ctx, ctor);
38259             return JS_EXCEPTION;
38260         }
38261         if (realm != ctx &&
38262             js_same_value(ctx, ctor, realm->array_ctor)) {
38263             JS_FreeValue(ctx, ctor);
38264             ctor = JS_UNDEFINED;
38265         }
38266     }
38267     if (JS_IsObject(ctor)) {
38268         species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
38269         JS_FreeValue(ctx, ctor);
38270         if (JS_IsException(species))
38271             return species;
38272         ctor = species;
38273         if (JS_IsNull(ctor))
38274             ctor = JS_UNDEFINED;
38275     }
38276     if (JS_IsUndefined(ctor)) {
38277         return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
38278     } else {
38279         ret = JS_CallConstructor(ctx, ctor, 1, &len_val);
38280         JS_FreeValue(ctx, ctor);
38281         return ret;
38282     }
38283 }
38284 
38285 static const JSCFunctionListEntry js_array_funcs[] = {
38286     JS_CFUNC_DEF("isArray", 1, js_array_isArray ),
38287     JS_CFUNC_DEF("from", 1, js_array_from ),
38288     JS_CFUNC_DEF("of", 0, js_array_of ),
38289     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
38290 };
38291 
JS_isConcatSpreadable(JSContext * ctx,JSValueConst obj)38292 static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj)
38293 {
38294     JSValue val;
38295 
38296     if (!JS_IsObject(obj))
38297         return FALSE;
38298     val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable);
38299     if (JS_IsException(val))
38300         return -1;
38301     if (!JS_IsUndefined(val))
38302         return JS_ToBoolFree(ctx, val);
38303     return JS_IsArray(ctx, obj);
38304 }
38305 
js_array_concat(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38306 static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val,
38307                                int argc, JSValueConst *argv)
38308 {
38309     JSValue obj, arr, val;
38310     JSValueConst e;
38311     int64_t len, k, n;
38312     int i, res;
38313 
38314     arr = JS_UNDEFINED;
38315     obj = JS_ToObject(ctx, this_val);
38316     if (JS_IsException(obj))
38317         goto exception;
38318 
38319     arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
38320     if (JS_IsException(arr))
38321         goto exception;
38322     n = 0;
38323     for (i = -1; i < argc; i++) {
38324         if (i < 0)
38325             e = obj;
38326         else
38327             e = argv[i];
38328 
38329         res = JS_isConcatSpreadable(ctx, e);
38330         if (res < 0)
38331             goto exception;
38332         if (res) {
38333             if (js_get_length64(ctx, &len, e))
38334                 goto exception;
38335             if (n + len > MAX_SAFE_INTEGER) {
38336                 JS_ThrowTypeError(ctx, "Array loo long");
38337                 goto exception;
38338             }
38339             for (k = 0; k < len; k++, n++) {
38340                 res = JS_TryGetPropertyInt64(ctx, e, k, &val);
38341                 if (res < 0)
38342                     goto exception;
38343                 if (res) {
38344                     if (JS_DefinePropertyValueInt64(ctx, arr, n, val,
38345                                                     JS_PROP_C_W_E | JS_PROP_THROW) < 0)
38346                         goto exception;
38347                 }
38348             }
38349         } else {
38350             if (n >= MAX_SAFE_INTEGER) {
38351                 JS_ThrowTypeError(ctx, "Array loo long");
38352                 goto exception;
38353             }
38354             if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e),
38355                                             JS_PROP_C_W_E | JS_PROP_THROW) < 0)
38356                 goto exception;
38357             n++;
38358         }
38359     }
38360     if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
38361         goto exception;
38362 
38363     JS_FreeValue(ctx, obj);
38364     return arr;
38365 
38366 exception:
38367     JS_FreeValue(ctx, arr);
38368     JS_FreeValue(ctx, obj);
38369     return JS_EXCEPTION;
38370 }
38371 
38372 #define special_every    0
38373 #define special_some     1
38374 #define special_forEach  2
38375 #define special_map      3
38376 #define special_filter   4
38377 #define special_TA       8
38378 
38379 static int js_typed_array_get_length_internal(JSContext *ctx, JSValueConst obj);
38380 
38381 static JSValue js_typed_array___speciesCreate(JSContext *ctx,
38382                                               JSValueConst this_val,
38383                                               int argc, JSValueConst *argv);
38384 
js_array_every(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int special)38385 static JSValue js_array_every(JSContext *ctx, JSValueConst this_val,
38386                               int argc, JSValueConst *argv, int special)
38387 {
38388     JSValue obj, val, index_val, res, ret;
38389     JSValueConst args[3];
38390     JSValueConst func, this_arg;
38391     int64_t len, k, n;
38392     int present;
38393 
38394     ret = JS_UNDEFINED;
38395     val = JS_UNDEFINED;
38396     if (special & special_TA) {
38397         obj = JS_DupValue(ctx, this_val);
38398         len = js_typed_array_get_length_internal(ctx, obj);
38399         if (len < 0)
38400             goto exception;
38401     } else {
38402         obj = JS_ToObject(ctx, this_val);
38403         if (js_get_length64(ctx, &len, obj))
38404             goto exception;
38405     }
38406     func = argv[0];
38407     this_arg = JS_UNDEFINED;
38408     if (argc > 1)
38409         this_arg = argv[1];
38410 
38411     if (check_function(ctx, func))
38412         goto exception;
38413 
38414     switch (special) {
38415     case special_every:
38416     case special_every | special_TA:
38417         ret = JS_TRUE;
38418         break;
38419     case special_some:
38420     case special_some | special_TA:
38421         ret = JS_FALSE;
38422         break;
38423     case special_map:
38424         /* XXX: JS_ArraySpeciesCreate should take int64_t */
38425         ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len));
38426         if (JS_IsException(ret))
38427             goto exception;
38428         break;
38429     case special_filter:
38430         ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
38431         if (JS_IsException(ret))
38432             goto exception;
38433         break;
38434     case special_map | special_TA:
38435         args[0] = obj;
38436         args[1] = JS_NewInt32(ctx, len);
38437         ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
38438         if (JS_IsException(ret))
38439             goto exception;
38440         break;
38441     case special_filter | special_TA:
38442         ret = JS_NewArray(ctx);
38443         if (JS_IsException(ret))
38444             goto exception;
38445         break;
38446     }
38447     n = 0;
38448 
38449     for(k = 0; k < len; k++) {
38450         if (special & special_TA) {
38451             val = JS_GetPropertyInt64(ctx, obj, k);
38452             if (JS_IsException(val))
38453                 goto exception;
38454             present = TRUE;
38455         } else {
38456             present = JS_TryGetPropertyInt64(ctx, obj, k, &val);
38457             if (present < 0)
38458                 goto exception;
38459         }
38460         if (present) {
38461             index_val = JS_NewInt64(ctx, k);
38462             if (JS_IsException(index_val))
38463                 goto exception;
38464             args[0] = val;
38465             args[1] = index_val;
38466             args[2] = obj;
38467             res = JS_Call(ctx, func, this_arg, 3, args);
38468             JS_FreeValue(ctx, index_val);
38469             if (JS_IsException(res))
38470                 goto exception;
38471             switch (special) {
38472             case special_every:
38473             case special_every | special_TA:
38474                 if (!JS_ToBoolFree(ctx, res)) {
38475                     ret = JS_FALSE;
38476                     goto done;
38477                 }
38478                 break;
38479             case special_some:
38480             case special_some | special_TA:
38481                 if (JS_ToBoolFree(ctx, res)) {
38482                     ret = JS_TRUE;
38483                     goto done;
38484                 }
38485                 break;
38486             case special_map:
38487                 if (JS_DefinePropertyValueInt64(ctx, ret, k, res,
38488                                                 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
38489                     goto exception;
38490                 break;
38491             case special_map | special_TA:
38492                 if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0)
38493                     goto exception;
38494                 break;
38495             case special_filter:
38496             case special_filter | special_TA:
38497                 if (JS_ToBoolFree(ctx, res)) {
38498                     if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val),
38499                                                     JS_PROP_C_W_E | JS_PROP_THROW) < 0)
38500                         goto exception;
38501                 }
38502                 break;
38503             default:
38504                 JS_FreeValue(ctx, res);
38505                 break;
38506             }
38507             JS_FreeValue(ctx, val);
38508             val = JS_UNDEFINED;
38509         }
38510     }
38511 done:
38512     if (special == (special_filter | special_TA)) {
38513         JSValue arr;
38514         args[0] = obj;
38515         args[1] = JS_NewInt32(ctx, n);
38516         arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
38517         if (JS_IsException(arr))
38518             goto exception;
38519         args[0] = ret;
38520         res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args);
38521         if (check_exception_free(ctx, res))
38522             goto exception;
38523         JS_FreeValue(ctx, ret);
38524         ret = arr;
38525     }
38526     JS_FreeValue(ctx, val);
38527     JS_FreeValue(ctx, obj);
38528     return ret;
38529 
38530 exception:
38531     JS_FreeValue(ctx, ret);
38532     JS_FreeValue(ctx, val);
38533     JS_FreeValue(ctx, obj);
38534     return JS_EXCEPTION;
38535 }
38536 
38537 #define special_reduce       0
38538 #define special_reduceRight  1
38539 
js_array_reduce(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int special)38540 static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val,
38541                                int argc, JSValueConst *argv, int special)
38542 {
38543     JSValue obj, val, index_val, acc, acc1;
38544     JSValueConst args[4];
38545     JSValueConst func;
38546     int64_t len, k, k1;
38547     int present;
38548 
38549     acc = JS_UNDEFINED;
38550     val = JS_UNDEFINED;
38551     if (special & special_TA) {
38552         obj = JS_DupValue(ctx, this_val);
38553         len = js_typed_array_get_length_internal(ctx, obj);
38554         if (len < 0)
38555             goto exception;
38556     } else {
38557         obj = JS_ToObject(ctx, this_val);
38558         if (js_get_length64(ctx, &len, obj))
38559             goto exception;
38560     }
38561     func = argv[0];
38562 
38563     if (check_function(ctx, func))
38564         goto exception;
38565 
38566     k = 0;
38567     if (argc > 1) {
38568         acc = JS_DupValue(ctx, argv[1]);
38569     } else {
38570         for(;;) {
38571             if (k >= len) {
38572                 JS_ThrowTypeError(ctx, "empty array");
38573                 goto exception;
38574             }
38575             k1 = (special & special_reduceRight) ? len - k - 1 : k;
38576             k++;
38577             if (special & special_TA) {
38578                 acc = JS_GetPropertyInt64(ctx, obj, k1);
38579                 if (JS_IsException(acc))
38580                     goto exception;
38581                 break;
38582             } else {
38583                 present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc);
38584                 if (present < 0)
38585                     goto exception;
38586                 if (present)
38587                     break;
38588             }
38589         }
38590     }
38591     for (; k < len; k++) {
38592         k1 = (special & special_reduceRight) ? len - k - 1 : k;
38593         if (special & special_TA) {
38594             val = JS_GetPropertyInt64(ctx, obj, k1);
38595             if (JS_IsException(val))
38596                 goto exception;
38597             present = TRUE;
38598         } else {
38599             present = JS_TryGetPropertyInt64(ctx, obj, k1, &val);
38600             if (present < 0)
38601                 goto exception;
38602         }
38603         if (present) {
38604             index_val = JS_NewInt64(ctx, k1);
38605             if (JS_IsException(index_val))
38606                 goto exception;
38607             args[0] = acc;
38608             args[1] = val;
38609             args[2] = index_val;
38610             args[3] = obj;
38611             acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args);
38612             JS_FreeValue(ctx, index_val);
38613             JS_FreeValue(ctx, val);
38614             val = JS_UNDEFINED;
38615             if (JS_IsException(acc1))
38616                 goto exception;
38617             JS_FreeValue(ctx, acc);
38618             acc = acc1;
38619         }
38620     }
38621     JS_FreeValue(ctx, obj);
38622     return acc;
38623 
38624 exception:
38625     JS_FreeValue(ctx, acc);
38626     JS_FreeValue(ctx, val);
38627     JS_FreeValue(ctx, obj);
38628     return JS_EXCEPTION;
38629 }
38630 
js_array_fill(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38631 static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val,
38632                              int argc, JSValueConst *argv)
38633 {
38634     JSValue obj;
38635     int64_t len, start, end;
38636 
38637     obj = JS_ToObject(ctx, this_val);
38638     if (js_get_length64(ctx, &len, obj))
38639         goto exception;
38640 
38641     start = 0;
38642     if (argc > 1 && !JS_IsUndefined(argv[1])) {
38643         if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len))
38644             goto exception;
38645     }
38646 
38647     end = len;
38648     if (argc > 2 && !JS_IsUndefined(argv[2])) {
38649         if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len))
38650             goto exception;
38651     }
38652 
38653     /* XXX: should special case fast arrays */
38654     while (start < end) {
38655         if (JS_SetPropertyInt64(ctx, obj, start,
38656                                 JS_DupValue(ctx, argv[0])) < 0)
38657             goto exception;
38658         start++;
38659     }
38660     return obj;
38661 
38662  exception:
38663     JS_FreeValue(ctx, obj);
38664     return JS_EXCEPTION;
38665 }
38666 
js_array_includes(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38667 static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val,
38668                                  int argc, JSValueConst *argv)
38669 {
38670     JSValue obj, val;
38671     int64_t len, n, res;
38672     JSValue *arrp;
38673     uint32_t count;
38674 
38675     obj = JS_ToObject(ctx, this_val);
38676     if (js_get_length64(ctx, &len, obj))
38677         goto exception;
38678 
38679     res = FALSE;
38680     if (len > 0) {
38681         n = 0;
38682         if (argc > 1) {
38683             if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
38684                 goto exception;
38685         }
38686         if (js_get_fast_array(ctx, obj, &arrp, &count)) {
38687             for (; n < count; n++) {
38688                 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
38689                                   JS_DupValue(ctx, arrp[n]),
38690                                   JS_EQ_SAME_VALUE_ZERO)) {
38691                     res = TRUE;
38692                     goto done;
38693                 }
38694             }
38695         }
38696         for (; n < len; n++) {
38697             val = JS_GetPropertyInt64(ctx, obj, n);
38698             if (JS_IsException(val))
38699                 goto exception;
38700             if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val,
38701                               JS_EQ_SAME_VALUE_ZERO)) {
38702                 res = TRUE;
38703                 break;
38704             }
38705         }
38706     }
38707  done:
38708     JS_FreeValue(ctx, obj);
38709     return JS_NewBool(ctx, res);
38710 
38711  exception:
38712     JS_FreeValue(ctx, obj);
38713     return JS_EXCEPTION;
38714 }
38715 
js_array_indexOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38716 static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val,
38717                                 int argc, JSValueConst *argv)
38718 {
38719     JSValue obj, val;
38720     int64_t len, n, res;
38721     JSValue *arrp;
38722     uint32_t count;
38723 
38724     obj = JS_ToObject(ctx, this_val);
38725     if (js_get_length64(ctx, &len, obj))
38726         goto exception;
38727 
38728     res = -1;
38729     if (len > 0) {
38730         n = 0;
38731         if (argc > 1) {
38732             if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
38733                 goto exception;
38734         }
38735         if (js_get_fast_array(ctx, obj, &arrp, &count)) {
38736             for (; n < count; n++) {
38737                 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
38738                                   JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) {
38739                     res = n;
38740                     goto done;
38741                 }
38742             }
38743         }
38744         for (; n < len; n++) {
38745             int present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
38746             if (present < 0)
38747                 goto exception;
38748             if (present) {
38749                 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
38750                     res = n;
38751                     break;
38752                 }
38753             }
38754         }
38755     }
38756  done:
38757     JS_FreeValue(ctx, obj);
38758     return JS_NewInt64(ctx, res);
38759 
38760  exception:
38761     JS_FreeValue(ctx, obj);
38762     return JS_EXCEPTION;
38763 }
38764 
js_array_lastIndexOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38765 static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val,
38766                                     int argc, JSValueConst *argv)
38767 {
38768     JSValue obj, val;
38769     int64_t len, n, res;
38770     int present;
38771 
38772     obj = JS_ToObject(ctx, this_val);
38773     if (js_get_length64(ctx, &len, obj))
38774         goto exception;
38775 
38776     res = -1;
38777     if (len > 0) {
38778         n = len - 1;
38779         if (argc > 1) {
38780             if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len))
38781                 goto exception;
38782         }
38783         /* XXX: should special case fast arrays */
38784         for (; n >= 0; n--) {
38785             present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
38786             if (present < 0)
38787                 goto exception;
38788             if (present) {
38789                 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
38790                     res = n;
38791                     break;
38792                 }
38793             }
38794         }
38795     }
38796     JS_FreeValue(ctx, obj);
38797     return JS_NewInt64(ctx, res);
38798 
38799  exception:
38800     JS_FreeValue(ctx, obj);
38801     return JS_EXCEPTION;
38802 }
38803 
js_array_find(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int findIndex)38804 static JSValue js_array_find(JSContext *ctx, JSValueConst this_val,
38805                              int argc, JSValueConst *argv, int findIndex)
38806 {
38807     JSValueConst func, this_arg;
38808     JSValueConst args[3];
38809     JSValue obj, val, index_val, res;
38810     int64_t len, k;
38811 
38812     index_val = JS_UNDEFINED;
38813     val = JS_UNDEFINED;
38814     obj = JS_ToObject(ctx, this_val);
38815     if (js_get_length64(ctx, &len, obj))
38816         goto exception;
38817 
38818     func = argv[0];
38819     if (check_function(ctx, func))
38820         goto exception;
38821 
38822     this_arg = JS_UNDEFINED;
38823     if (argc > 1)
38824         this_arg = argv[1];
38825 
38826     for(k = 0; k < len; k++) {
38827         index_val = JS_NewInt64(ctx, k);
38828         if (JS_IsException(index_val))
38829             goto exception;
38830         val = JS_GetPropertyValue(ctx, obj, index_val);
38831         if (JS_IsException(val))
38832             goto exception;
38833         args[0] = val;
38834         args[1] = index_val;
38835         args[2] = this_val;
38836         res = JS_Call(ctx, func, this_arg, 3, args);
38837         if (JS_IsException(res))
38838             goto exception;
38839         if (JS_ToBoolFree(ctx, res)) {
38840             if (findIndex) {
38841                 JS_FreeValue(ctx, val);
38842                 JS_FreeValue(ctx, obj);
38843                 return index_val;
38844             } else {
38845                 JS_FreeValue(ctx, index_val);
38846                 JS_FreeValue(ctx, obj);
38847                 return val;
38848             }
38849         }
38850         JS_FreeValue(ctx, val);
38851         JS_FreeValue(ctx, index_val);
38852     }
38853     JS_FreeValue(ctx, obj);
38854     if (findIndex)
38855         return JS_NewInt32(ctx, -1);
38856     else
38857         return JS_UNDEFINED;
38858 
38859 exception:
38860     JS_FreeValue(ctx, index_val);
38861     JS_FreeValue(ctx, val);
38862     JS_FreeValue(ctx, obj);
38863     return JS_EXCEPTION;
38864 }
38865 
js_array_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38866 static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val,
38867                                  int argc, JSValueConst *argv)
38868 {
38869     JSValue obj, method, ret;
38870 
38871     obj = JS_ToObject(ctx, this_val);
38872     if (JS_IsException(obj))
38873         return JS_EXCEPTION;
38874     method = JS_GetProperty(ctx, obj, JS_ATOM_join);
38875     if (JS_IsException(method)) {
38876         ret = JS_EXCEPTION;
38877     } else
38878     if (!JS_IsFunction(ctx, method)) {
38879         /* Use intrinsic Object.prototype.toString */
38880         JS_FreeValue(ctx, method);
38881         ret = js_object_toString(ctx, obj, 0, NULL);
38882     } else {
38883         ret = JS_CallFree(ctx, method, obj, 0, NULL);
38884     }
38885     JS_FreeValue(ctx, obj);
38886     return ret;
38887 }
38888 
js_array_join(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int toLocaleString)38889 static JSValue js_array_join(JSContext *ctx, JSValueConst this_val,
38890                              int argc, JSValueConst *argv, int toLocaleString)
38891 {
38892     JSValue obj, sep = JS_UNDEFINED, el;
38893     StringBuffer b_s, *b = &b_s;
38894     JSString *p = NULL;
38895     int64_t i, n;
38896     int c;
38897 
38898     obj = JS_ToObject(ctx, this_val);
38899     if (js_get_length64(ctx, &n, obj))
38900         goto exception;
38901 
38902     c = ',';    /* default separator */
38903     if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
38904         sep = JS_ToString(ctx, argv[0]);
38905         if (JS_IsException(sep))
38906             goto exception;
38907         p = JS_VALUE_GET_STRING(sep);
38908         if (p->len == 1 && !p->is_wide_char)
38909             c = p->u.str8[0];
38910         else
38911             c = -1;
38912     }
38913     string_buffer_init(ctx, b, 0);
38914 
38915     for(i = 0; i < n; i++) {
38916         if (i > 0) {
38917             if (c >= 0) {
38918                 string_buffer_putc8(b, c);
38919             } else {
38920                 string_buffer_concat(b, p, 0, p->len);
38921             }
38922         }
38923         el = JS_GetPropertyUint32(ctx, obj, i);
38924         if (JS_IsException(el))
38925             goto fail;
38926         if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
38927             if (toLocaleString) {
38928                 el = JS_ToLocaleStringFree(ctx, el);
38929             }
38930             if (string_buffer_concat_value_free(b, el))
38931                 goto fail;
38932         }
38933     }
38934     JS_FreeValue(ctx, sep);
38935     JS_FreeValue(ctx, obj);
38936     return string_buffer_end(b);
38937 
38938 fail:
38939     string_buffer_free(b);
38940     JS_FreeValue(ctx, sep);
38941 exception:
38942     JS_FreeValue(ctx, obj);
38943     return JS_EXCEPTION;
38944 }
38945 
js_array_pop(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int shift)38946 static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val,
38947                             int argc, JSValueConst *argv, int shift)
38948 {
38949     JSValue obj, res = JS_UNDEFINED;
38950     int64_t len, newLen;
38951     JSValue *arrp;
38952     uint32_t count32;
38953 
38954     obj = JS_ToObject(ctx, this_val);
38955     if (js_get_length64(ctx, &len, obj))
38956         goto exception;
38957     newLen = 0;
38958     if (len > 0) {
38959         newLen = len - 1;
38960         /* Special case fast arrays */
38961         if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
38962             JSObject *p = JS_VALUE_GET_OBJ(obj);
38963             if (shift) {
38964                 res = arrp[0];
38965                 memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp));
38966                 p->u.array.count--;
38967             } else {
38968                 res = arrp[count32 - 1];
38969                 p->u.array.count--;
38970             }
38971         } else {
38972             if (shift) {
38973                 res = JS_GetPropertyInt64(ctx, obj, 0);
38974                 if (JS_IsException(res))
38975                     goto exception;
38976                 if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1))
38977                     goto exception;
38978             } else {
38979                 res = JS_GetPropertyInt64(ctx, obj, newLen);
38980                 if (JS_IsException(res))
38981                     goto exception;
38982             }
38983             if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0)
38984                 goto exception;
38985         }
38986     }
38987     if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
38988         goto exception;
38989 
38990     JS_FreeValue(ctx, obj);
38991     return res;
38992 
38993  exception:
38994     JS_FreeValue(ctx, res);
38995     JS_FreeValue(ctx, obj);
38996     return JS_EXCEPTION;
38997 }
38998 
js_array_push(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int unshift)38999 static JSValue js_array_push(JSContext *ctx, JSValueConst this_val,
39000                              int argc, JSValueConst *argv, int unshift)
39001 {
39002     JSValue obj;
39003     int i;
39004     int64_t len, from, newLen;
39005 
39006     obj = JS_ToObject(ctx, this_val);
39007 
39008     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
39009         JSObject *p = JS_VALUE_GET_OBJ(obj);
39010         if (p->class_id != JS_CLASS_ARRAY ||
39011             !p->fast_array || !p->extensible)
39012             goto generic_case;
39013         /* length must be writable */
39014         if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE)))
39015             goto generic_case;
39016         /* check the length */
39017         if (unlikely(JS_VALUE_GET_TAG(p->prop[0].u.value) != JS_TAG_INT))
39018             goto generic_case;
39019         len = JS_VALUE_GET_INT(p->prop[0].u.value);
39020         /* we don't support holes */
39021         if (unlikely(len != p->u.array.count))
39022             goto generic_case;
39023         newLen = len + argc;
39024         if (unlikely(newLen > INT32_MAX))
39025             goto generic_case;
39026         if (newLen > p->u.array.u1.size) {
39027             if (expand_fast_array(ctx, p, newLen))
39028                 goto exception;
39029         }
39030         if (unshift && argc > 0) {
39031             memmove(p->u.array.u.values + argc, p->u.array.u.values,
39032                     len * sizeof(p->u.array.u.values[0]));
39033             from = 0;
39034         } else {
39035             from = len;
39036         }
39037         for(i = 0; i < argc; i++) {
39038             p->u.array.u.values[from + i] = JS_DupValue(ctx, argv[i]);
39039         }
39040         p->u.array.count = newLen;
39041         p->prop[0].u.value = JS_NewInt32(ctx, newLen);
39042     } else {
39043     generic_case:
39044         if (js_get_length64(ctx, &len, obj))
39045             goto exception;
39046         newLen = len + argc;
39047         if (newLen > MAX_SAFE_INTEGER) {
39048             JS_ThrowTypeError(ctx, "Array loo long");
39049             goto exception;
39050         }
39051         from = len;
39052         if (unshift && argc > 0) {
39053             if (JS_CopySubArray(ctx, obj, argc, 0, len, -1))
39054                 goto exception;
39055             from = 0;
39056         }
39057         for(i = 0; i < argc; i++) {
39058             if (JS_SetPropertyInt64(ctx, obj, from + i,
39059                                     JS_DupValue(ctx, argv[i])) < 0)
39060                 goto exception;
39061         }
39062         if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
39063             goto exception;
39064     }
39065     JS_FreeValue(ctx, obj);
39066     return JS_NewInt64(ctx, newLen);
39067 
39068  exception:
39069     JS_FreeValue(ctx, obj);
39070     return JS_EXCEPTION;
39071 }
39072 
js_array_reverse(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39073 static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val,
39074                                 int argc, JSValueConst *argv)
39075 {
39076     JSValue obj, lval, hval;
39077     JSValue *arrp;
39078     int64_t len, l, h;
39079     int l_present, h_present;
39080     uint32_t count32;
39081 
39082     lval = JS_UNDEFINED;
39083     obj = JS_ToObject(ctx, this_val);
39084     if (js_get_length64(ctx, &len, obj))
39085         goto exception;
39086 
39087     /* Special case fast arrays */
39088     if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
39089         uint32_t ll, hh;
39090 
39091         if (count32 > 1) {
39092             for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) {
39093                 lval = arrp[ll];
39094                 arrp[ll] = arrp[hh];
39095                 arrp[hh] = lval;
39096             }
39097         }
39098         return obj;
39099     }
39100 
39101     for (l = 0, h = len - 1; l < h; l++, h--) {
39102         l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval);
39103         if (l_present < 0)
39104             goto exception;
39105         h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval);
39106         if (h_present < 0)
39107             goto exception;
39108         if (h_present) {
39109             if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0)
39110                 goto exception;
39111 
39112             if (l_present) {
39113                 if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
39114                     lval = JS_UNDEFINED;
39115                     goto exception;
39116                 }
39117                 lval = JS_UNDEFINED;
39118             } else {
39119                 if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0)
39120                     goto exception;
39121             }
39122         } else {
39123             if (l_present) {
39124                 if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0)
39125                     goto exception;
39126                 if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
39127                     lval = JS_UNDEFINED;
39128                     goto exception;
39129                 }
39130                 lval = JS_UNDEFINED;
39131             }
39132         }
39133     }
39134     return obj;
39135 
39136  exception:
39137     JS_FreeValue(ctx, lval);
39138     JS_FreeValue(ctx, obj);
39139     return JS_EXCEPTION;
39140 }
39141 
js_array_slice(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int splice)39142 static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,
39143                               int argc, JSValueConst *argv, int splice)
39144 {
39145     JSValue obj, arr, val, len_val;
39146     int64_t len, start, k, final, n, count, del_count, new_len;
39147     int kPresent;
39148     JSValue *arrp;
39149     uint32_t count32, i, item_count;
39150 
39151     arr = JS_UNDEFINED;
39152     obj = JS_ToObject(ctx, this_val);
39153     if (js_get_length64(ctx, &len, obj))
39154         goto exception;
39155 
39156     if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
39157         goto exception;
39158 
39159     if (splice) {
39160         if (argc == 0) {
39161             item_count = 0;
39162             del_count = 0;
39163         } else
39164         if (argc == 1) {
39165             item_count = 0;
39166             del_count = len - start;
39167         } else {
39168             item_count = argc - 2;
39169             if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0))
39170                 goto exception;
39171         }
39172         if (len + item_count - del_count > MAX_SAFE_INTEGER) {
39173             JS_ThrowTypeError(ctx, "Array loo long");
39174             goto exception;
39175         }
39176         count = del_count;
39177     } else {
39178         item_count = 0; /* avoid warning */
39179         final = len;
39180         if (!JS_IsUndefined(argv[1])) {
39181             if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len))
39182                 goto exception;
39183         }
39184         count = max_int64(final - start, 0);
39185     }
39186     len_val = JS_NewInt64(ctx, count);
39187     arr = JS_ArraySpeciesCreate(ctx, obj, len_val);
39188     JS_FreeValue(ctx, len_val);
39189     if (JS_IsException(arr))
39190         goto exception;
39191 
39192     k = start;
39193     final = start + count;
39194     n = 0;
39195     /* The fast array test on arr ensures that
39196        JS_CreateDataPropertyUint32() won't modify obj in case arr is
39197        an exotic object */
39198     /* Special case fast arrays */
39199     if (js_get_fast_array(ctx, obj, &arrp, &count32) &&
39200         js_is_fast_array(ctx, arr)) {
39201         /* XXX: should share code with fast array constructor */
39202         for (; k < final && k < count32; k++, n++) {
39203             if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0)
39204                 goto exception;
39205         }
39206     }
39207     /* Copy the remaining elements if any (handle case of inherited properties) */
39208     for (; k < final; k++, n++) {
39209         kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val);
39210         if (kPresent < 0)
39211             goto exception;
39212         if (kPresent) {
39213             if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0)
39214                 goto exception;
39215         }
39216     }
39217     if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
39218         goto exception;
39219 
39220     if (splice) {
39221         new_len = len + item_count - del_count;
39222         if (item_count != del_count) {
39223             if (JS_CopySubArray(ctx, obj, start + item_count,
39224                                 start + del_count, len - (start + del_count),
39225                                 item_count <= del_count ? +1 : -1) < 0)
39226                 goto exception;
39227 
39228             for (k = len; k-- > new_len; ) {
39229                 if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0)
39230                     goto exception;
39231             }
39232         }
39233         for (i = 0; i < item_count; i++) {
39234             if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0)
39235                 goto exception;
39236         }
39237         if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0)
39238             goto exception;
39239     }
39240     JS_FreeValue(ctx, obj);
39241     return arr;
39242 
39243  exception:
39244     JS_FreeValue(ctx, obj);
39245     JS_FreeValue(ctx, arr);
39246     return JS_EXCEPTION;
39247 }
39248 
js_array_copyWithin(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39249 static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val,
39250                                    int argc, JSValueConst *argv)
39251 {
39252     JSValue obj;
39253     int64_t len, from, to, final, count;
39254 
39255     obj = JS_ToObject(ctx, this_val);
39256     if (js_get_length64(ctx, &len, obj))
39257         goto exception;
39258 
39259     if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len))
39260         goto exception;
39261 
39262     if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len))
39263         goto exception;
39264 
39265     final = len;
39266     if (argc > 2 && !JS_IsUndefined(argv[2])) {
39267         if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len))
39268             goto exception;
39269     }
39270 
39271     count = min_int64(final - from, len - to);
39272 
39273     if (JS_CopySubArray(ctx, obj, to, from, count,
39274                         (from < to && to < from + count) ? -1 : +1))
39275         goto exception;
39276 
39277     return obj;
39278 
39279  exception:
39280     JS_FreeValue(ctx, obj);
39281     return JS_EXCEPTION;
39282 }
39283 
JS_FlattenIntoArray(JSContext * ctx,JSValueConst target,JSValueConst source,int64_t sourceLen,int64_t targetIndex,int depth,JSValueConst mapperFunction,JSValueConst thisArg)39284 static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target,
39285                                    JSValueConst source, int64_t sourceLen,
39286                                    int64_t targetIndex, int depth,
39287                                    JSValueConst mapperFunction,
39288                                    JSValueConst thisArg)
39289 {
39290     JSValue element;
39291     int64_t sourceIndex, elementLen;
39292     int present, is_array;
39293 
39294     if (js_check_stack_overflow(ctx->rt, 0)) {
39295         JS_ThrowStackOverflow(ctx);
39296         return -1;
39297     }
39298 
39299     for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) {
39300         present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element);
39301         if (present < 0)
39302             return -1;
39303         if (!present)
39304             continue;
39305         if (!JS_IsUndefined(mapperFunction)) {
39306             JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source };
39307             element = JS_Call(ctx, mapperFunction, thisArg, 3, args);
39308             JS_FreeValue(ctx, JS_VALUE_CONST_CAST(args[0]));
39309             JS_FreeValue(ctx, JS_VALUE_CONST_CAST(args[1]));
39310             if (JS_IsException(element))
39311                 return -1;
39312         }
39313         if (depth > 0) {
39314             is_array = JS_IsArray(ctx, element);
39315             if (is_array < 0)
39316                 goto fail;
39317             if (is_array) {
39318                 if (js_get_length64(ctx, &elementLen, element) < 0)
39319                     goto fail;
39320                 targetIndex = JS_FlattenIntoArray(ctx, target, element,
39321                                                   elementLen, targetIndex,
39322                                                   depth - 1,
39323                                                   JS_UNDEFINED, JS_UNDEFINED);
39324                 if (targetIndex < 0)
39325                     goto fail;
39326                 JS_FreeValue(ctx, element);
39327                 continue;
39328             }
39329         }
39330         if (targetIndex >= MAX_SAFE_INTEGER) {
39331             JS_ThrowTypeError(ctx, "Array too long");
39332             goto fail;
39333         }
39334         if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element,
39335                                         JS_PROP_C_W_E | JS_PROP_THROW) < 0)
39336             return -1;
39337         targetIndex++;
39338     }
39339     return targetIndex;
39340 
39341 fail:
39342     JS_FreeValue(ctx, element);
39343     return -1;
39344 }
39345 
js_array_flatten(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int map)39346 static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val,
39347                                 int argc, JSValueConst *argv, int map)
39348 {
39349     JSValue obj, arr;
39350     JSValueConst mapperFunction, thisArg;
39351     int64_t sourceLen;
39352     int depthNum;
39353 
39354     arr = JS_UNDEFINED;
39355     obj = JS_ToObject(ctx, this_val);
39356     if (js_get_length64(ctx, &sourceLen, obj))
39357         goto exception;
39358 
39359     depthNum = 1;
39360     mapperFunction = JS_UNDEFINED;
39361     thisArg = JS_UNDEFINED;
39362     if (map) {
39363         mapperFunction = argv[0];
39364         if (argc > 1) {
39365             thisArg = argv[1];
39366         }
39367         if (check_function(ctx, mapperFunction))
39368             goto exception;
39369     } else {
39370         if (argc > 0 && !JS_IsUndefined(argv[0])) {
39371             if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0)
39372                 goto exception;
39373         }
39374     }
39375     arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
39376     if (JS_IsException(arr))
39377         goto exception;
39378     if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum,
39379                             mapperFunction, thisArg) < 0)
39380         goto exception;
39381     JS_FreeValue(ctx, obj);
39382     return arr;
39383 
39384 exception:
39385     JS_FreeValue(ctx, obj);
39386     JS_FreeValue(ctx, arr);
39387     return JS_EXCEPTION;
39388 }
39389 
39390 /* Array sort */
39391 
39392 typedef struct ValueSlot {
39393     JSValue val;
39394     JSString *str;
39395     int64_t pos;
39396 } ValueSlot;
39397 
39398 struct array_sort_context {
39399     JSContext *ctx;
39400     int exception;
39401     int has_method;
39402     JSValueConst method;
39403 };
39404 
js_array_cmp_generic(const void * a,const void * b,void * opaque)39405 static int js_array_cmp_generic(const void *a, const void *b, void *opaque) {
39406     struct array_sort_context *psc = opaque;
39407     JSContext *ctx = psc->ctx;
39408     JSValueConst argv[2];
39409     JSValue res;
39410     ValueSlot *ap = (ValueSlot *)(void *)a;
39411     ValueSlot *bp = (ValueSlot *)(void *)b;
39412     int cmp;
39413 
39414     if (psc->exception)
39415         return 0;
39416 
39417     if (psc->has_method) {
39418         /* custom sort function is specified as returning 0 for identical
39419          * objects: avoid method call overhead.
39420          */
39421         if (!memcmp(&ap->val, &bp->val, sizeof(ap->val)))
39422             goto cmp_same;
39423         argv[0] = ap->val;
39424         argv[1] = bp->val;
39425         res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv);
39426         if (JS_IsException(res))
39427             goto exception;
39428         if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
39429             int val = JS_VALUE_GET_INT(res);
39430             cmp = (val > 0) - (val < 0);
39431         } else {
39432             double val;
39433             if (JS_ToFloat64Free(ctx, &val, res) < 0)
39434                 goto exception;
39435             cmp = (val > 0) - (val < 0);
39436         }
39437     } else {
39438         /* Not supposed to bypass ToString even for identical objects as
39439          * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js
39440          */
39441         if (!ap->str) {
39442             JSValue str = JS_ToString(ctx, ap->val);
39443             if (JS_IsException(str))
39444                 goto exception;
39445             ap->str = JS_VALUE_GET_STRING(str);
39446         }
39447         if (!bp->str) {
39448             JSValue str = JS_ToString(ctx, bp->val);
39449             if (JS_IsException(str))
39450                 goto exception;
39451             bp->str = JS_VALUE_GET_STRING(str);
39452         }
39453         cmp = js_string_compare(ctx, ap->str, bp->str);
39454     }
39455     if (cmp != 0)
39456         return cmp;
39457 cmp_same:
39458     /* make sort stable: compare array offsets */
39459     return (ap->pos > bp->pos) - (ap->pos < bp->pos);
39460 
39461 exception:
39462     psc->exception = 1;
39463     return 0;
39464 }
39465 
js_array_sort(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39466 static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val,
39467                              int argc, JSValueConst *argv)
39468 {
39469     struct array_sort_context asc = { ctx, 0, 0, argv[0] };
39470     JSValue obj = JS_UNDEFINED;
39471     ValueSlot *array = NULL;
39472     size_t array_size = 0, pos = 0, n = 0;
39473     int64_t i, len, undefined_count = 0;
39474     int present;
39475 
39476     if (!JS_IsUndefined(asc.method)) {
39477         if (check_function(ctx, asc.method))
39478             goto exception;
39479         asc.has_method = 1;
39480     }
39481     obj = JS_ToObject(ctx, this_val);
39482     if (js_get_length64(ctx, &len, obj))
39483         goto exception;
39484 
39485     /* XXX: should special case fast arrays */
39486     for (i = 0; i < len; i++) {
39487         if (pos >= array_size) {
39488             size_t new_size, slack;
39489             ValueSlot *new_array;
39490             new_size = (array_size + (array_size >> 1) + 31) & ~15;
39491             new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack);
39492             if (new_array == NULL)
39493                 goto exception;
39494             new_size += slack / sizeof(*new_array);
39495             array = new_array;
39496             array_size = new_size;
39497         }
39498         present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val);
39499         if (present < 0)
39500             goto exception;
39501         if (present == 0)
39502             continue;
39503         if (JS_IsUndefined(array[pos].val)) {
39504             undefined_count++;
39505             continue;
39506         }
39507         array[pos].str = NULL;
39508         array[pos].pos = i;
39509         pos++;
39510     }
39511     rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc);
39512     if (asc.exception)
39513         goto exception;
39514 
39515     /* XXX: should special case fast arrays */
39516     while (n < pos) {
39517         if (array[n].str)
39518             JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
39519         if (array[n].pos == n) {
39520             JS_FreeValue(ctx, array[n].val);
39521         } else {
39522             if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) {
39523                 n++;
39524                 goto exception;
39525             }
39526         }
39527         n++;
39528     }
39529     js_free(ctx, array);
39530     for (i = n; undefined_count-- > 0; i++) {
39531         if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0)
39532             goto fail;
39533     }
39534     for (; i < len; i++) {
39535         if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0)
39536             goto fail;
39537     }
39538     return obj;
39539 
39540 exception:
39541     for (; n < pos; n++) {
39542         JS_FreeValue(ctx, array[n].val);
39543         if (array[n].str)
39544             JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
39545     }
39546     js_free(ctx, array);
39547 fail:
39548     JS_FreeValue(ctx, obj);
39549     return JS_EXCEPTION;
39550 }
39551 
39552 typedef struct JSArrayIteratorData {
39553     JSValue obj;
39554     JSIteratorKindEnum kind;
39555     uint32_t idx;
39556 } JSArrayIteratorData;
39557 
js_array_iterator_finalizer(JSRuntime * rt,JSValue val)39558 static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val)
39559 {
39560     JSObject *p = JS_VALUE_GET_OBJ(val);
39561     JSArrayIteratorData *it = p->u.array_iterator_data;
39562     if (it) {
39563         JS_FreeValueRT(rt, it->obj);
39564         js_free_rt(rt, it);
39565     }
39566 }
39567 
js_array_iterator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)39568 static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
39569                                    JS_MarkFunc *mark_func)
39570 {
39571     JSObject *p = JS_VALUE_GET_OBJ(val);
39572     JSArrayIteratorData *it = p->u.array_iterator_data;
39573     if (it) {
39574         JS_MarkValue(rt, it->obj, mark_func);
39575     }
39576 }
39577 
js_create_array(JSContext * ctx,int len,JSValueConst * tab)39578 static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab)
39579 {
39580     JSValue obj;
39581     int i;
39582 
39583     obj = JS_NewArray(ctx);
39584     if (JS_IsException(obj))
39585         return JS_EXCEPTION;
39586     for(i = 0; i < len; i++) {
39587         if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) {
39588             JS_FreeValue(ctx, obj);
39589             return JS_EXCEPTION;
39590         }
39591     }
39592     return obj;
39593 }
39594 
js_create_array_iterator(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)39595 static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
39596                                         int argc, JSValueConst *argv, int magic)
39597 {
39598     JSValue enum_obj, arr;
39599     JSArrayIteratorData *it;
39600     JSIteratorKindEnum kind;
39601     int class_id;
39602 
39603     kind = magic & 3;
39604     if (magic & 4) {
39605         /* string iterator case */
39606         arr = JS_ToStringCheckObject(ctx, this_val);
39607         class_id = JS_CLASS_STRING_ITERATOR;
39608     } else {
39609         arr = JS_ToObject(ctx, this_val);
39610         class_id = JS_CLASS_ARRAY_ITERATOR;
39611     }
39612     if (JS_IsException(arr))
39613         goto fail;
39614     enum_obj = JS_NewObjectClass(ctx, class_id);
39615     if (JS_IsException(enum_obj))
39616         goto fail;
39617     it = js_malloc(ctx, sizeof(*it));
39618     if (!it)
39619         goto fail1;
39620     it->obj = arr;
39621     it->kind = kind;
39622     it->idx = 0;
39623     JS_SetOpaque(enum_obj, it);
39624     return enum_obj;
39625  fail1:
39626     JS_FreeValue(ctx, enum_obj);
39627  fail:
39628     JS_FreeValue(ctx, arr);
39629     return JS_EXCEPTION;
39630 }
39631 
js_array_iterator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,BOOL * pdone,int magic)39632 static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
39633                                       int argc, JSValueConst *argv,
39634                                       BOOL *pdone, int magic)
39635 {
39636     JSArrayIteratorData *it;
39637     uint32_t len, idx;
39638     JSValue val, obj;
39639     JSObject *p;
39640 
39641     it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR);
39642     if (!it)
39643         goto fail1;
39644     if (JS_IsUndefined(it->obj))
39645         goto done;
39646     p = JS_VALUE_GET_OBJ(it->obj);
39647     if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
39648         p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
39649         if (typed_array_is_detached(ctx, p)) {
39650             JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
39651             goto fail1;
39652         }
39653         len = p->u.array.count;
39654     } else {
39655         if (js_get_length32(ctx, &len, it->obj)) {
39656         fail1:
39657             *pdone = FALSE;
39658             return JS_EXCEPTION;
39659         }
39660     }
39661     idx = it->idx;
39662     if (idx >= len) {
39663         JS_FreeValue(ctx, it->obj);
39664         it->obj = JS_UNDEFINED;
39665     done:
39666         *pdone = TRUE;
39667         return JS_UNDEFINED;
39668     }
39669     it->idx = idx + 1;
39670     *pdone = FALSE;
39671     if (it->kind == JS_ITERATOR_KIND_KEY) {
39672         return JS_NewUint32(ctx, idx);
39673     } else {
39674         val = JS_GetPropertyUint32(ctx, it->obj, idx);
39675         if (JS_IsException(val))
39676             return JS_EXCEPTION;
39677         if (it->kind == JS_ITERATOR_KIND_VALUE) {
39678             return val;
39679         } else {
39680             JSValueConst args[2];
39681             JSValue num;
39682             num = JS_NewUint32(ctx, idx);
39683             args[0] = num;
39684             args[1] = val;
39685             obj = js_create_array(ctx, 2, args);
39686             JS_FreeValue(ctx, val);
39687             JS_FreeValue(ctx, num);
39688             return obj;
39689         }
39690     }
39691 }
39692 
js_iterator_proto_iterator(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39693 static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val,
39694                                           int argc, JSValueConst *argv)
39695 {
39696     return JS_DupValue(ctx, this_val);
39697 }
39698 
39699 static const JSCFunctionListEntry js_iterator_proto_funcs[] = {
39700     JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ),
39701 };
39702 
39703 static const JSCFunctionListEntry js_array_proto_funcs[] = {
39704     JS_CFUNC_DEF("concat", 1, js_array_concat ),
39705     JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ),
39706     JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ),
39707     JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ),
39708     JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ),
39709     JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ),
39710     JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ),
39711     JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ),
39712     JS_CFUNC_DEF("fill", 1, js_array_fill ),
39713     JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, 0 ),
39714     JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, 1 ),
39715     JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ),
39716     JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ),
39717     JS_CFUNC_DEF("includes", 1, js_array_includes ),
39718     JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ),
39719     JS_CFUNC_DEF("toString", 0, js_array_toString ),
39720     JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ),
39721     JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ),
39722     JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ),
39723     JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ),
39724     JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ),
39725     JS_CFUNC_DEF("reverse", 0, js_array_reverse ),
39726     JS_CFUNC_DEF("sort", 1, js_array_sort ),
39727     JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ),
39728     JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ),
39729     JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ),
39730     JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ),
39731     JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ),
39732     JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ),
39733     JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
39734     JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ),
39735     JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
39736 };
39737 
39738 static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = {
39739     JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ),
39740     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ),
39741 };
39742 
39743 /* Number */
39744 
js_number_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)39745 static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,
39746                                      int argc, JSValueConst *argv)
39747 {
39748     JSValue val, obj;
39749     if (argc == 0) {
39750         val = JS_NewInt32(ctx, 0);
39751     } else {
39752         val = JS_ToNumeric(ctx, argv[0]);
39753         if (JS_IsException(val))
39754             return val;
39755         switch(JS_VALUE_GET_TAG(val)) {
39756 #ifdef CONFIG_BIGNUM
39757         case JS_TAG_BIG_INT:
39758         case JS_TAG_BIG_FLOAT:
39759             {
39760                 JSBigFloat *p = JS_VALUE_GET_PTR(val);
39761                 double d;
39762                 bf_get_float64(&p->num, &d, BF_RNDN);
39763                 JS_FreeValue(ctx, val);
39764                 val = __JS_NewFloat64(ctx, d);
39765             }
39766             break;
39767         case JS_TAG_BIG_DECIMAL:
39768             val = JS_ToStringFree(ctx, val);
39769             if (JS_IsException(val))
39770                 return val;
39771             val = JS_ToNumberFree(ctx, val);
39772             if (JS_IsException(val))
39773                 return val;
39774             break;
39775 #endif
39776         default:
39777             break;
39778         }
39779     }
39780     if (!JS_IsUndefined(new_target)) {
39781         obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER);
39782         if (!JS_IsException(obj))
39783             JS_SetObjectData(ctx, obj, val);
39784         return obj;
39785     } else {
39786         return val;
39787     }
39788 }
39789 
39790 #if 0
39791 static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val,
39792                                      int argc, JSValueConst *argv)
39793 {
39794     return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0]));
39795 }
39796 
39797 static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val,
39798                                     int argc, JSValueConst *argv)
39799 {
39800     int64_t v;
39801     if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0])))
39802         return JS_EXCEPTION;
39803     return JS_NewInt64(ctx, v);
39804 }
39805 #endif
39806 
js_number_isNaN(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39807 static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val,
39808                                int argc, JSValueConst *argv)
39809 {
39810     if (!JS_IsNumber(argv[0]))
39811         return JS_FALSE;
39812     return js_global_isNaN(ctx, this_val, argc, argv);
39813 }
39814 
js_number_isFinite(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39815 static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val,
39816                                   int argc, JSValueConst *argv)
39817 {
39818     if (!JS_IsNumber(argv[0]))
39819         return JS_FALSE;
39820     return js_global_isFinite(ctx, this_val, argc, argv);
39821 }
39822 
js_number_isInteger(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39823 static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val,
39824                                    int argc, JSValueConst *argv)
39825 {
39826     int ret;
39827     ret = JS_NumberIsInteger(ctx, argv[0]);
39828     if (ret < 0)
39829         return JS_EXCEPTION;
39830     else
39831         return JS_NewBool(ctx, ret);
39832 }
39833 
js_number_isSafeInteger(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39834 static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val,
39835                                        int argc, JSValueConst *argv)
39836 {
39837     double d;
39838     if (!JS_IsNumber(argv[0]))
39839         return JS_FALSE;
39840     if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
39841         return JS_EXCEPTION;
39842     return JS_NewBool(ctx, is_safe_integer(d));
39843 }
39844 
39845 static const JSCFunctionListEntry js_number_funcs[] = {
39846     /* global ParseInt and parseFloat should be defined already or delayed */
39847     JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ),
39848     JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ),
39849     JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ),
39850     JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ),
39851     JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ),
39852     JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ),
39853     JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ),
39854     JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ),
39855     JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
39856     JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ),
39857     JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ),
39858     JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */
39859     JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */
39860     JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */
39861     //JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ),
39862     //JS_CFUNC_DEF("__toLength", 1, js_number___toLength ),
39863 };
39864 
js_thisNumberValue(JSContext * ctx,JSValueConst this_val)39865 static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val)
39866 {
39867     if (JS_IsNumber(this_val))
39868         return JS_DupValue(ctx, this_val);
39869 
39870     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
39871         JSObject *p = JS_VALUE_GET_OBJ(this_val);
39872         if (p->class_id == JS_CLASS_NUMBER) {
39873             if (JS_IsNumber(p->u.object_data))
39874                 return JS_DupValue(ctx, p->u.object_data);
39875         }
39876     }
39877     return JS_ThrowTypeError(ctx, "not a number");
39878 }
39879 
js_number_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39880 static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val,
39881                                  int argc, JSValueConst *argv)
39882 {
39883     return js_thisNumberValue(ctx, this_val);
39884 }
39885 
js_get_radix(JSContext * ctx,JSValueConst val)39886 static int js_get_radix(JSContext *ctx, JSValueConst val)
39887 {
39888     int radix;
39889     if (JS_ToInt32Sat(ctx, &radix, val))
39890         return -1;
39891     if (radix < 2 || radix > 36) {
39892         JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
39893         return -1;
39894     }
39895     return radix;
39896 }
39897 
js_number_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)39898 static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,
39899                                   int argc, JSValueConst *argv, int magic)
39900 {
39901     JSValue val;
39902     int base;
39903     double d;
39904 
39905     val = js_thisNumberValue(ctx, this_val);
39906     if (JS_IsException(val))
39907         return val;
39908     if (magic || JS_IsUndefined(argv[0])) {
39909         base = 10;
39910     } else {
39911         base = js_get_radix(ctx, argv[0]);
39912         if (base < 0)
39913             goto fail;
39914     }
39915     if (JS_ToFloat64Free(ctx, &d, val))
39916         return JS_EXCEPTION;
39917     return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT);
39918  fail:
39919     JS_FreeValue(ctx, val);
39920     return JS_EXCEPTION;
39921 }
39922 
js_number_toFixed(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39923 static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val,
39924                                  int argc, JSValueConst *argv)
39925 {
39926     JSValue val;
39927     int f;
39928     double d;
39929 
39930     val = js_thisNumberValue(ctx, this_val);
39931     if (JS_IsException(val))
39932         return val;
39933     if (JS_ToFloat64Free(ctx, &d, val))
39934         return JS_EXCEPTION;
39935     if (JS_ToInt32Sat(ctx, &f, argv[0]))
39936         return JS_EXCEPTION;
39937     if (f < 0 || f > 100)
39938         return JS_ThrowRangeError(ctx, "invalid number of digits");
39939     if (fabs(d) >= 1e21) {
39940         return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
39941     } else {
39942         return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT);
39943     }
39944 }
39945 
js_number_toExponential(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39946 static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val,
39947                                        int argc, JSValueConst *argv)
39948 {
39949     JSValue val;
39950     int f, flags;
39951     double d;
39952 
39953     val = js_thisNumberValue(ctx, this_val);
39954     if (JS_IsException(val))
39955         return val;
39956     if (JS_ToFloat64Free(ctx, &d, val))
39957         return JS_EXCEPTION;
39958     if (JS_ToInt32Sat(ctx, &f, argv[0]))
39959         return JS_EXCEPTION;
39960     if (!isfinite(d)) {
39961         return JS_ToStringFree(ctx,  __JS_NewFloat64(ctx, d));
39962     }
39963     if (JS_IsUndefined(argv[0])) {
39964         flags = 0;
39965         f = 0;
39966     } else {
39967         if (f < 0 || f > 100)
39968             return JS_ThrowRangeError(ctx, "invalid number of digits");
39969         f++;
39970         flags = JS_DTOA_FIXED_FORMAT;
39971     }
39972     return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP);
39973 }
39974 
js_number_toPrecision(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39975 static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val,
39976                                      int argc, JSValueConst *argv)
39977 {
39978     JSValue val;
39979     int p;
39980     double d;
39981 
39982     val = js_thisNumberValue(ctx, this_val);
39983     if (JS_IsException(val))
39984         return val;
39985     if (JS_ToFloat64Free(ctx, &d, val))
39986         return JS_EXCEPTION;
39987     if (JS_IsUndefined(argv[0]))
39988         goto to_string;
39989     if (JS_ToInt32Sat(ctx, &p, argv[0]))
39990         return JS_EXCEPTION;
39991     if (!isfinite(d)) {
39992     to_string:
39993         return JS_ToStringFree(ctx,  __JS_NewFloat64(ctx, d));
39994     }
39995     if (p < 1 || p > 100)
39996         return JS_ThrowRangeError(ctx, "invalid number of digits");
39997     return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT);
39998 }
39999 
40000 static const JSCFunctionListEntry js_number_proto_funcs[] = {
40001     JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ),
40002     JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ),
40003     JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ),
40004     JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ),
40005     JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ),
40006     JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ),
40007 };
40008 
js_parseInt(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40009 static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val,
40010                            int argc, JSValueConst *argv)
40011 {
40012     const char *str, *p;
40013     int radix, flags;
40014     JSValue ret;
40015 
40016     str = JS_ToCString(ctx, argv[0]);
40017     if (!str)
40018         return JS_EXCEPTION;
40019     if (JS_ToInt32(ctx, &radix, argv[1])) {
40020         JS_FreeCString(ctx, str);
40021         return JS_EXCEPTION;
40022     }
40023     if (radix != 0 && (radix < 2 || radix > 36)) {
40024         ret = JS_NAN;
40025     } else {
40026         p = str;
40027         p += skip_spaces(p);
40028         flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN;
40029         ret = js_atof(ctx, p, NULL, radix, flags);
40030     }
40031     JS_FreeCString(ctx, str);
40032     return ret;
40033 }
40034 
js_parseFloat(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40035 static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val,
40036                              int argc, JSValueConst *argv)
40037 {
40038     const char *str, *p;
40039     JSValue ret;
40040 
40041     str = JS_ToCString(ctx, argv[0]);
40042     if (!str)
40043         return JS_EXCEPTION;
40044     p = str;
40045     p += skip_spaces(p);
40046     ret = js_atof(ctx, p, NULL, 10, 0);
40047     JS_FreeCString(ctx, str);
40048     return ret;
40049 }
40050 
40051 /* Boolean */
js_boolean_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)40052 static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target,
40053                                      int argc, JSValueConst *argv)
40054 {
40055     JSValue val, obj;
40056     val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0]));
40057     if (!JS_IsUndefined(new_target)) {
40058         obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN);
40059         if (!JS_IsException(obj))
40060             JS_SetObjectData(ctx, obj, val);
40061         return obj;
40062     } else {
40063         return val;
40064     }
40065 }
40066 
js_thisBooleanValue(JSContext * ctx,JSValueConst this_val)40067 static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val)
40068 {
40069     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL)
40070         return JS_DupValue(ctx, this_val);
40071 
40072     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
40073         JSObject *p = JS_VALUE_GET_OBJ(this_val);
40074         if (p->class_id == JS_CLASS_BOOLEAN) {
40075             if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL)
40076                 return p->u.object_data;
40077         }
40078     }
40079     return JS_ThrowTypeError(ctx, "not a boolean");
40080 }
40081 
js_boolean_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40082 static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val,
40083                                    int argc, JSValueConst *argv)
40084 {
40085     JSValue val = js_thisBooleanValue(ctx, this_val);
40086     if (JS_IsException(val))
40087         return val;
40088     return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
40089                        JS_ATOM_true : JS_ATOM_false);
40090 }
40091 
js_boolean_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40092 static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val,
40093                                   int argc, JSValueConst *argv)
40094 {
40095     return js_thisBooleanValue(ctx, this_val);
40096 }
40097 
40098 static const JSCFunctionListEntry js_boolean_proto_funcs[] = {
40099     JS_CFUNC_DEF("toString", 0, js_boolean_toString ),
40100     JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ),
40101 };
40102 
40103 /* String */
40104 
js_string_get_own_property(JSContext * ctx,JSPropertyDescriptor * desc,JSValueConst obj,JSAtom prop)40105 static int js_string_get_own_property(JSContext *ctx,
40106                                       JSPropertyDescriptor *desc,
40107                                       JSValueConst obj, JSAtom prop)
40108 {
40109     JSObject *p;
40110     JSString *p1;
40111     uint32_t idx, ch;
40112 
40113     /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
40114     if (__JS_AtomIsTaggedInt(prop)) {
40115         p = JS_VALUE_GET_OBJ(obj);
40116         if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
40117             p1 = JS_VALUE_GET_STRING(p->u.object_data);
40118             idx = __JS_AtomToUInt32(prop);
40119             if (idx < p1->len) {
40120                 if (desc) {
40121                     if (p1->is_wide_char)
40122                         ch = p1->u.str16[idx];
40123                     else
40124                         ch = p1->u.str8[idx];
40125                     desc->flags = JS_PROP_ENUMERABLE;
40126                     desc->value = js_new_string_char(ctx, ch);
40127                     desc->getter = JS_UNDEFINED;
40128                     desc->setter = JS_UNDEFINED;
40129                 }
40130                 return TRUE;
40131             }
40132         }
40133     }
40134     return FALSE;
40135 }
40136 
js_string_define_own_property(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSValueConst val,JSValueConst getter,JSValueConst setter,int flags)40137 static int js_string_define_own_property(JSContext *ctx,
40138                                          JSValueConst this_obj,
40139                                          JSAtom prop, JSValueConst val,
40140                                          JSValueConst getter,
40141                                          JSValueConst setter, int flags)
40142 {
40143     uint32_t idx;
40144     JSObject *p;
40145     JSString *p1, *p2;
40146 
40147     if (__JS_AtomIsTaggedInt(prop)) {
40148         idx = __JS_AtomToUInt32(prop);
40149         p = JS_VALUE_GET_OBJ(this_obj);
40150         if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING)
40151             goto def;
40152         p1 = JS_VALUE_GET_STRING(p->u.object_data);
40153         if (idx >= p1->len)
40154             goto def;
40155         if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags))
40156             goto fail;
40157         /* check that the same value is configured */
40158         if (flags & JS_PROP_HAS_VALUE) {
40159             if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
40160                 goto fail;
40161             p2 = JS_VALUE_GET_STRING(val);
40162             if (p2->len != 1)
40163                 goto fail;
40164             if (string_get(p1, idx) != string_get(p2, 0)) {
40165             fail:
40166                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
40167             }
40168         }
40169         return TRUE;
40170     } else {
40171     def:
40172         return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
40173                                  flags | JS_PROP_NO_EXOTIC);
40174     }
40175 }
40176 
js_string_delete_property(JSContext * ctx,JSValueConst obj,JSAtom prop)40177 static int js_string_delete_property(JSContext *ctx,
40178                                      JSValueConst obj, JSAtom prop)
40179 {
40180     uint32_t idx;
40181 
40182     if (__JS_AtomIsTaggedInt(prop)) {
40183         idx = __JS_AtomToUInt32(prop);
40184         if (idx < js_string_obj_get_length(ctx, obj)) {
40185             return FALSE;
40186         }
40187     }
40188     return TRUE;
40189 }
40190 
40191 static const JSClassExoticMethods js_string_exotic_methods = {
40192     .get_own_property = js_string_get_own_property,
40193     .define_own_property = js_string_define_own_property,
40194     .delete_property = js_string_delete_property,
40195 };
40196 
js_string_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)40197 static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target,
40198                                      int argc, JSValueConst *argv)
40199 {
40200     JSValue val, obj;
40201     if (argc == 0) {
40202         val = JS_AtomToString(ctx, JS_ATOM_empty_string);
40203     } else {
40204         if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) {
40205             JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]);
40206             val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")");
40207         } else {
40208             val = JS_ToString(ctx, argv[0]);
40209         }
40210         if (JS_IsException(val))
40211             return val;
40212     }
40213     if (!JS_IsUndefined(new_target)) {
40214         JSString *p1 = JS_VALUE_GET_STRING(val);
40215 
40216         obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING);
40217         if (!JS_IsException(obj)) {
40218             JS_SetObjectData(ctx, obj, val);
40219             JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
40220         }
40221         return obj;
40222     } else {
40223         return val;
40224     }
40225 }
40226 
js_thisStringValue(JSContext * ctx,JSValueConst this_val)40227 static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val)
40228 {
40229     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING)
40230         return JS_DupValue(ctx, this_val);
40231 
40232     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
40233         JSObject *p = JS_VALUE_GET_OBJ(this_val);
40234         if (p->class_id == JS_CLASS_STRING) {
40235             if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING)
40236                 return JS_DupValue(ctx, p->u.object_data);
40237         }
40238     }
40239     return JS_ThrowTypeError(ctx, "not a string");
40240 }
40241 
js_string_fromCharCode(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40242 static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val,
40243                                       int argc, JSValueConst *argv)
40244 {
40245     int i;
40246     StringBuffer b_s, *b = &b_s;
40247 
40248     string_buffer_init(ctx, b, argc);
40249 
40250     for(i = 0; i < argc; i++) {
40251         int32_t c;
40252         if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) {
40253             string_buffer_free(b);
40254             return JS_EXCEPTION;
40255         }
40256     }
40257     return string_buffer_end(b);
40258 }
40259 
js_string_fromCodePoint(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40260 static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val,
40261                                        int argc, JSValueConst *argv)
40262 {
40263     double d;
40264     int i, c;
40265     StringBuffer b_s, *b = &b_s;
40266 
40267     /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */
40268 
40269     if (string_buffer_init(ctx, b, argc))
40270         goto fail;
40271     for(i = 0; i < argc; i++) {
40272         if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) {
40273             c = JS_VALUE_GET_INT(argv[i]);
40274             if (c < 0 || c > 0x10ffff)
40275                 goto range_error;
40276         } else {
40277             if (JS_ToFloat64(ctx, &d, argv[i]))
40278                 goto fail;
40279             if (d < 0 || d > 0x10ffff || (c = (int)d) != d)
40280                 goto range_error;
40281         }
40282         if (string_buffer_putc(b, c))
40283             goto fail;
40284     }
40285     return string_buffer_end(b);
40286 
40287  range_error:
40288     JS_ThrowRangeError(ctx, "invalid code point");
40289  fail:
40290     string_buffer_free(b);
40291     return JS_EXCEPTION;
40292 }
40293 
js_string_raw(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40294 static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val,
40295                              int argc, JSValueConst *argv)
40296 {
40297     // raw(temp,...a)
40298     JSValue cooked, val, raw;
40299     StringBuffer b_s, *b = &b_s;
40300     int64_t i, n;
40301 
40302     string_buffer_init(ctx, b, 0);
40303     raw = JS_UNDEFINED;
40304     cooked = JS_ToObject(ctx, argv[0]);
40305     if (JS_IsException(cooked))
40306         goto exception;
40307     raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw));
40308     if (JS_IsException(raw))
40309         goto exception;
40310     if (js_get_length64(ctx, &n, raw) < 0)
40311         goto exception;
40312 
40313     for (i = 0; i < n; i++) {
40314         val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i));
40315         if (JS_IsException(val))
40316             goto exception;
40317         string_buffer_concat_value_free(b, val);
40318         if (i < n - 1 && i + 1 < argc) {
40319             if (string_buffer_concat_value(b, argv[i + 1]))
40320                 goto exception;
40321         }
40322     }
40323     JS_FreeValue(ctx, cooked);
40324     JS_FreeValue(ctx, raw);
40325     return string_buffer_end(b);
40326 
40327 exception:
40328     JS_FreeValue(ctx, cooked);
40329     JS_FreeValue(ctx, raw);
40330     string_buffer_free(b);
40331     return JS_EXCEPTION;
40332 }
40333 
40334 /* only used in test262 */
js_string_codePointRange(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40335 JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
40336                                  int argc, JSValueConst *argv)
40337 {
40338     uint32_t start, end, i, n;
40339     StringBuffer b_s, *b = &b_s;
40340 
40341     if (JS_ToUint32(ctx, &start, argv[0]) ||
40342         JS_ToUint32(ctx, &end, argv[1]))
40343         return JS_EXCEPTION;
40344     end = min_uint32(end, 0x10ffff + 1);
40345 
40346     if (start > end) {
40347         start = end;
40348     }
40349     n = end - start;
40350     if (end > 0x10000) {
40351         n += end - max_uint32(start, 0x10000);
40352     }
40353     if (string_buffer_init2(ctx, b, n, end >= 0x100))
40354         return JS_EXCEPTION;
40355     for(i = start; i < end; i++) {
40356         string_buffer_putc(b, i);
40357     }
40358     return string_buffer_end(b);
40359 }
40360 
40361 #if 0
40362 static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val,
40363                                    int argc, JSValueConst *argv)
40364 {
40365     int c;
40366     if (JS_ToInt32(ctx, &c, argv[0]))
40367         return JS_EXCEPTION;
40368     return JS_NewBool(ctx, lre_is_space(c));
40369 }
40370 #endif
40371 
js_string_charCodeAt(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40372 static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val,
40373                                      int argc, JSValueConst *argv)
40374 {
40375     JSValue val, ret;
40376     JSString *p;
40377     int idx, c;
40378 
40379     val = JS_ToStringCheckObject(ctx, this_val);
40380     if (JS_IsException(val))
40381         return val;
40382     p = JS_VALUE_GET_STRING(val);
40383     if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
40384         JS_FreeValue(ctx, val);
40385         return JS_EXCEPTION;
40386     }
40387     if (idx < 0 || idx >= p->len) {
40388         ret = JS_NAN;
40389     } else {
40390         if (p->is_wide_char)
40391             c = p->u.str16[idx];
40392         else
40393             c = p->u.str8[idx];
40394         ret = JS_NewInt32(ctx, c);
40395     }
40396     JS_FreeValue(ctx, val);
40397     return ret;
40398 }
40399 
js_string_charAt(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40400 static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val,
40401                                 int argc, JSValueConst *argv)
40402 {
40403     JSValue val, ret;
40404     JSString *p;
40405     int idx, c;
40406 
40407     val = JS_ToStringCheckObject(ctx, this_val);
40408     if (JS_IsException(val))
40409         return val;
40410     p = JS_VALUE_GET_STRING(val);
40411     if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
40412         JS_FreeValue(ctx, val);
40413         return JS_EXCEPTION;
40414     }
40415     if (idx < 0 || idx >= p->len) {
40416         ret = js_new_string8(ctx, NULL, 0);
40417     } else {
40418         if (p->is_wide_char)
40419             c = p->u.str16[idx];
40420         else
40421             c = p->u.str8[idx];
40422         ret = js_new_string_char(ctx, c);
40423     }
40424     JS_FreeValue(ctx, val);
40425     return ret;
40426 }
40427 
js_string_codePointAt(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40428 static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val,
40429                                      int argc, JSValueConst *argv)
40430 {
40431     JSValue val, ret;
40432     JSString *p;
40433     int idx, c;
40434 
40435     val = JS_ToStringCheckObject(ctx, this_val);
40436     if (JS_IsException(val))
40437         return val;
40438     p = JS_VALUE_GET_STRING(val);
40439     if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
40440         JS_FreeValue(ctx, val);
40441         return JS_EXCEPTION;
40442     }
40443     if (idx < 0 || idx >= p->len) {
40444         ret = JS_UNDEFINED;
40445     } else {
40446         c = string_getc(p, &idx);
40447         ret = JS_NewInt32(ctx, c);
40448     }
40449     JS_FreeValue(ctx, val);
40450     return ret;
40451 }
40452 
js_string_concat(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40453 static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val,
40454                                 int argc, JSValueConst *argv)
40455 {
40456     JSValue r;
40457     int i;
40458 
40459     /* XXX: Use more efficient method */
40460     /* XXX: This method is OK if r has a single refcount */
40461     /* XXX: should use string_buffer? */
40462     r = JS_ToStringCheckObject(ctx, this_val);
40463     for (i = 0; i < argc; i++) {
40464         if (JS_IsException(r))
40465             break;
40466         r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i]));
40467     }
40468     return r;
40469 }
40470 
string_cmp(JSString * p1,JSString * p2,int x1,int x2,int len)40471 static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len)
40472 {
40473     int i, c1, c2;
40474     for (i = 0; i < len; i++) {
40475         if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i)))
40476             return c1 - c2;
40477     }
40478     return 0;
40479 }
40480 
string_indexof_char(JSString * p,int c,int from)40481 static int string_indexof_char(JSString *p, int c, int from)
40482 {
40483     /* assuming 0 <= from <= p->len */
40484     int i, len = p->len;
40485     if (p->is_wide_char) {
40486         for (i = from; i < len; i++) {
40487             if (p->u.str16[i] == c)
40488                 return i;
40489         }
40490     } else {
40491         if ((c & ~0xff) == 0) {
40492             for (i = from; i < len; i++) {
40493                 if (p->u.str8[i] == (uint8_t)c)
40494                     return i;
40495             }
40496         }
40497     }
40498     return -1;
40499 }
40500 
string_indexof(JSString * p1,JSString * p2,int from)40501 static int string_indexof(JSString *p1, JSString *p2, int from)
40502 {
40503     /* assuming 0 <= from <= p1->len */
40504     int c, i, j, len1 = p1->len, len2 = p2->len;
40505     if (len2 == 0)
40506         return from;
40507     for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) {
40508         j = string_indexof_char(p1, c, i);
40509         if (j < 0 || j + len2 > len1)
40510             break;
40511         if (!string_cmp(p1, p2, j + 1, 1, len2 - 1))
40512             return j;
40513     }
40514     return -1;
40515 }
40516 
string_advance_index(JSString * p,int64_t index,BOOL unicode)40517 static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode)
40518 {
40519     if (!unicode || index >= p->len || !p->is_wide_char) {
40520         index++;
40521     } else {
40522         int index32 = (int)index;
40523         string_getc(p, &index32);
40524         index = index32;
40525     }
40526     return index;
40527 }
40528 
js_string_indexOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int lastIndexOf)40529 static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val,
40530                                  int argc, JSValueConst *argv, int lastIndexOf)
40531 {
40532     JSValue str, v;
40533     int i, len, v_len, pos, start, stop, ret, inc;
40534     JSString *p;
40535     JSString *p1;
40536 
40537     str = JS_ToStringCheckObject(ctx, this_val);
40538     if (JS_IsException(str))
40539         return str;
40540     v = JS_ToString(ctx, argv[0]);
40541     if (JS_IsException(v))
40542         goto fail;
40543     p = JS_VALUE_GET_STRING(str);
40544     p1 = JS_VALUE_GET_STRING(v);
40545     len = p->len;
40546     v_len = p1->len;
40547     if (lastIndexOf) {
40548         pos = len - v_len;
40549         if (argc > 1) {
40550             double d;
40551             if (JS_ToFloat64(ctx, &d, argv[1]))
40552                 goto fail;
40553             if (!isnan(d)) {
40554                 if (d <= 0)
40555                     pos = 0;
40556                 else if (d < pos)
40557                     pos = d;
40558             }
40559         }
40560         start = pos;
40561         stop = 0;
40562         inc = -1;
40563     } else {
40564         pos = 0;
40565         if (argc > 1) {
40566             if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
40567                 goto fail;
40568         }
40569         start = pos;
40570         stop = len - v_len;
40571         inc = 1;
40572     }
40573     ret = -1;
40574     if (len >= v_len && inc * (stop - start) >= 0) {
40575         for (i = start;; i += inc) {
40576             if (!string_cmp(p, p1, i, 0, v_len)) {
40577                 ret = i;
40578                 break;
40579             }
40580             if (i == stop)
40581                 break;
40582         }
40583     }
40584     JS_FreeValue(ctx, str);
40585     JS_FreeValue(ctx, v);
40586     return JS_NewInt32(ctx, ret);
40587 
40588 fail:
40589     JS_FreeValue(ctx, str);
40590     JS_FreeValue(ctx, v);
40591     return JS_EXCEPTION;
40592 }
40593 
40594 /* return < 0 if exception or TRUE/FALSE */
40595 static int js_is_regexp(JSContext *ctx, JSValueConst obj);
40596 
js_string_includes(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)40597 static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val,
40598                                   int argc, JSValueConst *argv, int magic)
40599 {
40600     JSValue str, v = JS_UNDEFINED;
40601     int i, len, v_len, pos, start, stop, ret;
40602     JSString *p;
40603     JSString *p1;
40604 
40605     str = JS_ToStringCheckObject(ctx, this_val);
40606     if (JS_IsException(str))
40607         return str;
40608     ret = js_is_regexp(ctx, argv[0]);
40609     if (ret) {
40610         if (ret > 0)
40611             JS_ThrowTypeError(ctx, "regex not supported");
40612         goto fail;
40613     }
40614     v = JS_ToString(ctx, argv[0]);
40615     if (JS_IsException(v))
40616         goto fail;
40617     p = JS_VALUE_GET_STRING(str);
40618     p1 = JS_VALUE_GET_STRING(v);
40619     len = p->len;
40620     v_len = p1->len;
40621     pos = (magic == 2) ? len : 0;
40622     if (argc > 1 && !JS_IsUndefined(argv[1])) {
40623         if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
40624             goto fail;
40625     }
40626     len -= v_len;
40627     ret = 0;
40628     if (magic == 0) {
40629         start = pos;
40630         stop = len;
40631     } else {
40632         if (magic == 1) {
40633             if (pos > len)
40634                 goto done;
40635         } else {
40636             pos -= v_len;
40637         }
40638         start = stop = pos;
40639     }
40640     if (start >= 0 && start <= stop) {
40641         for (i = start;; i++) {
40642             if (!string_cmp(p, p1, i, 0, v_len)) {
40643                 ret = 1;
40644                 break;
40645             }
40646             if (i == stop)
40647                 break;
40648         }
40649     }
40650  done:
40651     JS_FreeValue(ctx, str);
40652     JS_FreeValue(ctx, v);
40653     return JS_NewBool(ctx, ret);
40654 
40655 fail:
40656     JS_FreeValue(ctx, str);
40657     JS_FreeValue(ctx, v);
40658     return JS_EXCEPTION;
40659 }
40660 
check_regexp_g_flag(JSContext * ctx,JSValueConst regexp)40661 static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp)
40662 {
40663     int ret;
40664     JSValue flags;
40665 
40666     ret = js_is_regexp(ctx, regexp);
40667     if (ret < 0)
40668         return -1;
40669     if (ret) {
40670         flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags);
40671         if (JS_IsException(flags))
40672             return -1;
40673         if (JS_IsUndefined(flags) || JS_IsNull(flags)) {
40674             JS_ThrowTypeError(ctx, "cannot convert to object");
40675             return -1;
40676         }
40677         flags = JS_ToStringFree(ctx, flags);
40678         if (JS_IsException(flags))
40679             return -1;
40680         ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0);
40681         JS_FreeValue(ctx, flags);
40682         if (ret < 0) {
40683             JS_ThrowTypeError(ctx, "regexp must have the 'g' flag");
40684             return -1;
40685         }
40686     }
40687     return 0;
40688 }
40689 
js_string_match(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int atom)40690 static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,
40691                                int argc, JSValueConst *argv, int atom)
40692 {
40693     // match(rx), search(rx), matchAll(rx)
40694     // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll
40695     JSValueConst O = this_val, regexp = argv[0], args[2];
40696     JSValue matcher, S, rx, result, str;
40697     int args_len;
40698 
40699     if (JS_IsUndefined(O) || JS_IsNull(O))
40700         return JS_ThrowTypeError(ctx, "cannot convert to object");
40701 
40702     if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) {
40703         matcher = JS_GetProperty(ctx, regexp, atom);
40704         if (JS_IsException(matcher))
40705             return JS_EXCEPTION;
40706         if (atom == JS_ATOM_Symbol_matchAll) {
40707             if (check_regexp_g_flag(ctx, regexp) < 0) {
40708                 JS_FreeValue(ctx, matcher);
40709                 return JS_EXCEPTION;
40710             }
40711         }
40712         if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) {
40713             return JS_CallFree(ctx, matcher, regexp, 1, &O);
40714         }
40715     }
40716     S = JS_ToString(ctx, O);
40717     if (JS_IsException(S))
40718         return JS_EXCEPTION;
40719     args_len = 1;
40720     args[0] = regexp;
40721     str = JS_UNDEFINED;
40722     if (atom == JS_ATOM_Symbol_matchAll) {
40723         str = JS_NewString(ctx, "g");
40724         if (JS_IsException(str))
40725             goto fail;
40726         args[args_len++] = JS_VALUE_MAKE_CONST(str);
40727     }
40728     rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args);
40729     JS_FreeValue(ctx, str);
40730     if (JS_IsException(rx)) {
40731     fail:
40732         JS_FreeValue(ctx, S);
40733         return JS_EXCEPTION;
40734     }
40735     result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S);
40736     JS_FreeValue(ctx, S);
40737     return result;
40738 }
40739 
js_string___GetSubstitution(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40740 static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val,
40741                                            int argc, JSValueConst *argv)
40742 {
40743     // GetSubstitution(matched, str, position, captures, namedCaptures, rep)
40744     JSValueConst matched, str, captures, namedCaptures, rep;
40745     JSValue capture, name, s;
40746     uint32_t position, len, matched_len, captures_len;
40747     int i, j, j0, k, k1;
40748     int c, c1;
40749     StringBuffer b_s, *b = &b_s;
40750     JSString *sp, *rp;
40751 
40752     matched = argv[0];
40753     str = argv[1];
40754     captures = argv[3];
40755     namedCaptures = argv[4];
40756     rep = argv[5];
40757 
40758     if (!JS_IsString(rep) || !JS_IsString(str))
40759         return JS_ThrowTypeError(ctx, "not a string");
40760 
40761     sp = JS_VALUE_GET_STRING(str);
40762     rp = JS_VALUE_GET_STRING(rep);
40763 
40764     string_buffer_init(ctx, b, 0);
40765 
40766     captures_len = 0;
40767     if (!JS_IsUndefined(captures)) {
40768         if (js_get_length32(ctx, &captures_len, captures))
40769             goto exception;
40770     }
40771     if (js_get_length32(ctx, &matched_len, matched))
40772         goto exception;
40773     if (JS_ToUint32(ctx, &position, argv[2]) < 0)
40774         goto exception;
40775 
40776     len = rp->len;
40777     i = 0;
40778     for(;;) {
40779         j = string_indexof_char(rp, '$', i);
40780         if (j < 0 || j + 1 >= len)
40781             break;
40782         string_buffer_concat(b, rp, i, j);
40783         j0 = j++;
40784         c = string_get(rp, j++);
40785         if (c == '$') {
40786             string_buffer_putc8(b, '$');
40787         } else if (c == '&') {
40788             if (string_buffer_concat_value(b, matched))
40789                 goto exception;
40790         } else if (c == '`') {
40791             string_buffer_concat(b, sp, 0, position);
40792         } else if (c == '\'') {
40793             string_buffer_concat(b, sp, position + matched_len, sp->len);
40794         } else if (c >= '0' && c <= '9') {
40795             k = c - '0';
40796             if (j < len) {
40797                 c1 = string_get(rp, j);
40798                 if (c1 >= '0' && c1 <= '9') {
40799                     /* This behavior is specified in ES6 and refined in ECMA 2019 */
40800                     /* ECMA 2019 does not have the extra test, but
40801                        Test262 S15.5.4.11_A3_T1..3 require this behavior */
40802                     k1 = k * 10 + c1 - '0';
40803                     if (k1 >= 1 && k1 < captures_len) {
40804                         k = k1;
40805                         j++;
40806                     }
40807                 }
40808             }
40809             if (k >= 1 && k < captures_len) {
40810                 s = JS_GetPropertyInt64(ctx, captures, k);
40811                 if (JS_IsException(s))
40812                     goto exception;
40813                 if (!JS_IsUndefined(s)) {
40814                     if (string_buffer_concat_value_free(b, s))
40815                         goto exception;
40816                 }
40817             } else {
40818                 goto norep;
40819             }
40820         } else if (c == '<' && !JS_IsUndefined(namedCaptures)) {
40821             k = string_indexof_char(rp, '>', j);
40822             if (k < 0)
40823                 goto norep;
40824             name = js_sub_string(ctx, rp, j, k);
40825             if (JS_IsException(name))
40826                 goto exception;
40827             capture = JS_GetPropertyValue(ctx, namedCaptures, name);
40828             if (JS_IsException(capture))
40829                 goto exception;
40830             if (!JS_IsUndefined(capture)) {
40831                 if (string_buffer_concat_value_free(b, capture))
40832                     goto exception;
40833             }
40834             j = k + 1;
40835         } else {
40836         norep:
40837             string_buffer_concat(b, rp, j0, j);
40838         }
40839         i = j;
40840     }
40841     string_buffer_concat(b, rp, i, rp->len);
40842     return string_buffer_end(b);
40843 exception:
40844     string_buffer_free(b);
40845     return JS_EXCEPTION;
40846 }
40847 
js_string_replace(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int is_replaceAll)40848 static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val,
40849                                  int argc, JSValueConst *argv,
40850                                  int is_replaceAll)
40851 {
40852     // replace(rx, rep)
40853     JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1];
40854     JSValueConst args[6];
40855     JSValue str, search_str, replaceValue_str, repl_str;
40856     JSString *sp, *searchp;
40857     StringBuffer b_s, *b = &b_s;
40858     int pos, functionalReplace, endOfLastMatch;
40859     BOOL is_first;
40860 
40861     if (JS_IsUndefined(O) || JS_IsNull(O))
40862         return JS_ThrowTypeError(ctx, "cannot convert to object");
40863 
40864     search_str = JS_UNDEFINED;
40865     replaceValue_str = JS_UNDEFINED;
40866     repl_str = JS_UNDEFINED;
40867 
40868     if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) {
40869         JSValue replacer;
40870         if (is_replaceAll) {
40871             if (check_regexp_g_flag(ctx, searchValue) < 0)
40872                 return JS_EXCEPTION;
40873         }
40874         replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace);
40875         if (JS_IsException(replacer))
40876             return JS_EXCEPTION;
40877         if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) {
40878             args[0] = O;
40879             args[1] = replaceValue;
40880             return JS_CallFree(ctx, replacer, searchValue, 2, args);
40881         }
40882     }
40883     string_buffer_init(ctx, b, 0);
40884 
40885     str = JS_ToString(ctx, O);
40886     if (JS_IsException(str))
40887         goto exception;
40888     search_str = JS_ToString(ctx, searchValue);
40889     if (JS_IsException(search_str))
40890         goto exception;
40891     functionalReplace = JS_IsFunction(ctx, replaceValue);
40892     if (!functionalReplace) {
40893         replaceValue_str = JS_ToString(ctx, replaceValue);
40894         if (JS_IsException(replaceValue_str))
40895             goto exception;
40896     }
40897 
40898     sp = JS_VALUE_GET_STRING(str);
40899     searchp = JS_VALUE_GET_STRING(search_str);
40900     endOfLastMatch = 0;
40901     is_first = TRUE;
40902     for(;;) {
40903         if (unlikely(searchp->len == 0)) {
40904             if (is_first)
40905                 pos = 0;
40906             else if (endOfLastMatch >= sp->len)
40907                 pos = -1;
40908             else
40909                 pos = endOfLastMatch + 1;
40910         } else {
40911             pos = string_indexof(sp, searchp, endOfLastMatch);
40912         }
40913         if (pos < 0) {
40914             if (is_first) {
40915                 string_buffer_free(b);
40916                 JS_FreeValue(ctx, search_str);
40917                 JS_FreeValue(ctx, replaceValue_str);
40918                 return str;
40919             } else {
40920                 break;
40921             }
40922         }
40923         if (functionalReplace) {
40924             args[0] = search_str;
40925             args[1] = JS_NewInt32(ctx, pos);
40926             args[2] = str;
40927             repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args));
40928         } else {
40929             args[0] = search_str;
40930             args[1] = str;
40931             args[2] = JS_NewInt32(ctx, pos);
40932             args[3] = JS_UNDEFINED;
40933             args[4] = JS_UNDEFINED;
40934             args[5] = replaceValue_str;
40935             repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
40936         }
40937         if (JS_IsException(repl_str))
40938             goto exception;
40939 
40940         string_buffer_concat(b, sp, endOfLastMatch, pos);
40941         string_buffer_concat_value_free(b, repl_str);
40942         endOfLastMatch = pos + searchp->len;
40943         is_first = FALSE;
40944         if (!is_replaceAll)
40945             break;
40946     }
40947     string_buffer_concat(b, sp, endOfLastMatch, sp->len);
40948     JS_FreeValue(ctx, search_str);
40949     JS_FreeValue(ctx, replaceValue_str);
40950     JS_FreeValue(ctx, str);
40951     return string_buffer_end(b);
40952 
40953 exception:
40954     string_buffer_free(b);
40955     JS_FreeValue(ctx, search_str);
40956     JS_FreeValue(ctx, replaceValue_str);
40957     JS_FreeValue(ctx, str);
40958     return JS_EXCEPTION;
40959 }
40960 
js_string_split(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40961 static JSValue js_string_split(JSContext *ctx, JSValueConst this_val,
40962                                int argc, JSValueConst *argv)
40963 {
40964     // split(sep, limit)
40965     JSValueConst O = this_val, separator = argv[0], limit = argv[1];
40966     JSValueConst args[2];
40967     JSValue S, A, R, T;
40968     uint32_t lim, lengthA;
40969     int64_t p, q, s, r, e;
40970     JSString *sp, *rp;
40971 
40972     if (JS_IsUndefined(O) || JS_IsNull(O))
40973         return JS_ThrowTypeError(ctx, "cannot convert to object");
40974 
40975     S = JS_UNDEFINED;
40976     A = JS_UNDEFINED;
40977     R = JS_UNDEFINED;
40978 
40979     if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) {
40980         JSValue splitter;
40981         splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split);
40982         if (JS_IsException(splitter))
40983             return JS_EXCEPTION;
40984         if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) {
40985             args[0] = O;
40986             args[1] = limit;
40987             return JS_CallFree(ctx, splitter, separator, 2, args);
40988         }
40989     }
40990     S = JS_ToString(ctx, O);
40991     if (JS_IsException(S))
40992         goto exception;
40993     A = JS_NewArray(ctx);
40994     if (JS_IsException(A))
40995         goto exception;
40996     lengthA = 0;
40997     if (JS_IsUndefined(limit)) {
40998         lim = 0xffffffff;
40999     } else {
41000         if (JS_ToUint32(ctx, &lim, limit) < 0)
41001             goto exception;
41002     }
41003     sp = JS_VALUE_GET_STRING(S);
41004     s = sp->len;
41005     R = JS_ToString(ctx, separator);
41006     if (JS_IsException(R))
41007         goto exception;
41008     rp = JS_VALUE_GET_STRING(R);
41009     r = rp->len;
41010     p = 0;
41011     if (lim == 0)
41012         goto done;
41013     if (JS_IsUndefined(separator))
41014         goto add_tail;
41015     if (s == 0) {
41016         if (r != 0)
41017             goto add_tail;
41018         goto done;
41019     }
41020     q = p;
41021     for (q = p; (q += !r) <= s - r - !r; q = p = e + r) {
41022         e = string_indexof(sp, rp, q);
41023         if (e < 0)
41024             break;
41025         T = js_sub_string(ctx, sp, p, e);
41026         if (JS_IsException(T))
41027             goto exception;
41028         if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0)
41029             goto exception;
41030         if (lengthA == lim)
41031             goto done;
41032     }
41033 add_tail:
41034     T = js_sub_string(ctx, sp, p, s);
41035     if (JS_IsException(T))
41036         goto exception;
41037     if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0)
41038         goto exception;
41039 done:
41040     JS_FreeValue(ctx, S);
41041     JS_FreeValue(ctx, R);
41042     return A;
41043 
41044 exception:
41045     JS_FreeValue(ctx, A);
41046     JS_FreeValue(ctx, S);
41047     JS_FreeValue(ctx, R);
41048     return JS_EXCEPTION;
41049 }
41050 
js_string_substring(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41051 static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val,
41052                                    int argc, JSValueConst *argv)
41053 {
41054     JSValue str, ret;
41055     int a, b, start, end;
41056     JSString *p;
41057 
41058     str = JS_ToStringCheckObject(ctx, this_val);
41059     if (JS_IsException(str))
41060         return str;
41061     p = JS_VALUE_GET_STRING(str);
41062     if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) {
41063         JS_FreeValue(ctx, str);
41064         return JS_EXCEPTION;
41065     }
41066     b = p->len;
41067     if (!JS_IsUndefined(argv[1])) {
41068         if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) {
41069             JS_FreeValue(ctx, str);
41070             return JS_EXCEPTION;
41071         }
41072     }
41073     if (a < b) {
41074         start = a;
41075         end = b;
41076     } else {
41077         start = b;
41078         end = a;
41079     }
41080     ret = js_sub_string(ctx, p, start, end);
41081     JS_FreeValue(ctx, str);
41082     return ret;
41083 }
41084 
js_string_substr(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41085 static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val,
41086                                 int argc, JSValueConst *argv)
41087 {
41088     JSValue str, ret;
41089     int a, len, n;
41090     JSString *p;
41091 
41092     str = JS_ToStringCheckObject(ctx, this_val);
41093     if (JS_IsException(str))
41094         return str;
41095     p = JS_VALUE_GET_STRING(str);
41096     len = p->len;
41097     if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) {
41098         JS_FreeValue(ctx, str);
41099         return JS_EXCEPTION;
41100     }
41101     n = len - a;
41102     if (!JS_IsUndefined(argv[1])) {
41103         if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) {
41104             JS_FreeValue(ctx, str);
41105             return JS_EXCEPTION;
41106         }
41107     }
41108     ret = js_sub_string(ctx, p, a, a + n);
41109     JS_FreeValue(ctx, str);
41110     return ret;
41111 }
41112 
js_string_slice(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41113 static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val,
41114                                int argc, JSValueConst *argv)
41115 {
41116     JSValue str, ret;
41117     int len, start, end;
41118     JSString *p;
41119 
41120     str = JS_ToStringCheckObject(ctx, this_val);
41121     if (JS_IsException(str))
41122         return str;
41123     p = JS_VALUE_GET_STRING(str);
41124     len = p->len;
41125     if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) {
41126         JS_FreeValue(ctx, str);
41127         return JS_EXCEPTION;
41128     }
41129     end = len;
41130     if (!JS_IsUndefined(argv[1])) {
41131         if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) {
41132             JS_FreeValue(ctx, str);
41133             return JS_EXCEPTION;
41134         }
41135     }
41136     ret = js_sub_string(ctx, p, start, max_int(end, start));
41137     JS_FreeValue(ctx, str);
41138     return ret;
41139 }
41140 
js_string_pad(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int padEnd)41141 static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val,
41142                              int argc, JSValueConst *argv, int padEnd)
41143 {
41144     JSValue str, v = JS_UNDEFINED;
41145     StringBuffer b_s, *b = &b_s;
41146     JSString *p, *p1 = NULL;
41147     int n, len, c = ' ';
41148 
41149     str = JS_ToStringCheckObject(ctx, this_val);
41150     if (JS_IsException(str))
41151         goto fail1;
41152     if (JS_ToInt32Sat(ctx, &n, argv[0]))
41153         goto fail2;
41154     p = JS_VALUE_GET_STRING(str);
41155     len = p->len;
41156     if (len >= n)
41157         return str;
41158     if (argc > 1 && !JS_IsUndefined(argv[1])) {
41159         v = JS_ToString(ctx, argv[1]);
41160         if (JS_IsException(v))
41161             goto fail2;
41162         p1 = JS_VALUE_GET_STRING(v);
41163         if (p1->len == 0) {
41164             JS_FreeValue(ctx, v);
41165             return str;
41166         }
41167         if (p1->len == 1) {
41168             c = string_get(p1, 0);
41169             p1 = NULL;
41170         }
41171     }
41172     if (n > JS_STRING_LEN_MAX) {
41173         JS_ThrowInternalError(ctx, "string too long");
41174         goto fail2;
41175     }
41176     if (string_buffer_init(ctx, b, n))
41177         goto fail3;
41178     n -= len;
41179     if (padEnd) {
41180         if (string_buffer_concat(b, p, 0, len))
41181             goto fail;
41182     }
41183     if (p1) {
41184         while (n > 0) {
41185             int chunk = min_int(n, p1->len);
41186             if (string_buffer_concat(b, p1, 0, chunk))
41187                 goto fail;
41188             n -= chunk;
41189         }
41190     } else {
41191         if (string_buffer_fill(b, c, n))
41192             goto fail;
41193     }
41194     if (!padEnd) {
41195         if (string_buffer_concat(b, p, 0, len))
41196             goto fail;
41197     }
41198     JS_FreeValue(ctx, v);
41199     JS_FreeValue(ctx, str);
41200     return string_buffer_end(b);
41201 
41202 fail:
41203     string_buffer_free(b);
41204 fail3:
41205     JS_FreeValue(ctx, v);
41206 fail2:
41207     JS_FreeValue(ctx, str);
41208 fail1:
41209     return JS_EXCEPTION;
41210 }
41211 
js_string_repeat(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41212 static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val,
41213                                 int argc, JSValueConst *argv)
41214 {
41215     JSValue str;
41216     StringBuffer b_s, *b = &b_s;
41217     JSString *p;
41218     int64_t val;
41219     int n, len;
41220 
41221     str = JS_ToStringCheckObject(ctx, this_val);
41222     if (JS_IsException(str))
41223         goto fail;
41224     if (JS_ToInt64Sat(ctx, &val, argv[0]))
41225         goto fail;
41226     if (val < 0 || val > 2147483647) {
41227         JS_ThrowRangeError(ctx, "invalid repeat count");
41228         goto fail;
41229     }
41230     n = val;
41231     p = JS_VALUE_GET_STRING(str);
41232     len = p->len;
41233     if (len == 0 || n == 1)
41234         return str;
41235     if (val * len > JS_STRING_LEN_MAX) {
41236         JS_ThrowInternalError(ctx, "string too long");
41237         goto fail;
41238     }
41239     if (string_buffer_init2(ctx, b, n * len, p->is_wide_char))
41240         goto fail;
41241     if (len == 1) {
41242         string_buffer_fill(b, string_get(p, 0), n);
41243     } else {
41244         while (n-- > 0) {
41245             string_buffer_concat(b, p, 0, len);
41246         }
41247     }
41248     JS_FreeValue(ctx, str);
41249     return string_buffer_end(b);
41250 
41251 fail:
41252     JS_FreeValue(ctx, str);
41253     return JS_EXCEPTION;
41254 }
41255 
js_string_trim(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)41256 static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val,
41257                               int argc, JSValueConst *argv, int magic)
41258 {
41259     JSValue str, ret;
41260     int a, b, len;
41261     JSString *p;
41262 
41263     str = JS_ToStringCheckObject(ctx, this_val);
41264     if (JS_IsException(str))
41265         return str;
41266     p = JS_VALUE_GET_STRING(str);
41267     a = 0;
41268     b = len = p->len;
41269     if (magic & 1) {
41270         while (a < len && lre_is_space(string_get(p, a)))
41271             a++;
41272     }
41273     if (magic & 2) {
41274         while (b > a && lre_is_space(string_get(p, b - 1)))
41275             b--;
41276     }
41277     ret = js_sub_string(ctx, p, a, b);
41278     JS_FreeValue(ctx, str);
41279     return ret;
41280 }
41281 
js_string___quote(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41282 static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val,
41283                                  int argc, JSValueConst *argv)
41284 {
41285     return JS_ToQuotedString(ctx, this_val);
41286 }
41287 
41288 /* return 0 if before the first char */
string_prevc(JSString * p,int * pidx)41289 static int string_prevc(JSString *p, int *pidx)
41290 {
41291     int idx, c, c1;
41292 
41293     idx = *pidx;
41294     if (idx <= 0)
41295         return 0;
41296     idx--;
41297     if (p->is_wide_char) {
41298         c = p->u.str16[idx];
41299         if (c >= 0xdc00 && c < 0xe000 && idx > 0) {
41300             c1 = p->u.str16[idx - 1];
41301             if (c1 >= 0xd800 && c1 <= 0xdc00) {
41302                 c = (((c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000;
41303                 idx--;
41304             }
41305         }
41306     } else {
41307         c = p->u.str8[idx];
41308     }
41309     *pidx = idx;
41310     return c;
41311 }
41312 
test_final_sigma(JSString * p,int sigma_pos)41313 static BOOL test_final_sigma(JSString *p, int sigma_pos)
41314 {
41315     int k, c1;
41316 
41317     /* before C: skip case ignorable chars and check there is
41318        a cased letter */
41319     k = sigma_pos;
41320     for(;;) {
41321         c1 = string_prevc(p, &k);
41322         if (!lre_is_case_ignorable(c1))
41323             break;
41324     }
41325     if (!lre_is_cased(c1))
41326         return FALSE;
41327 
41328     /* after C: skip case ignorable chars and check there is
41329        no cased letter */
41330     k = sigma_pos + 1;
41331     for(;;) {
41332         if (k >= p->len)
41333             return TRUE;
41334         c1 = string_getc(p, &k);
41335         if (!lre_is_case_ignorable(c1))
41336             break;
41337     }
41338     return !lre_is_cased(c1);
41339 }
41340 
js_string_localeCompare(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41341 static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
41342                                        int argc, JSValueConst *argv)
41343 {
41344     JSValue a, b;
41345     int cmp;
41346 
41347     a = JS_ToStringCheckObject(ctx, this_val);
41348     if (JS_IsException(a))
41349         return JS_EXCEPTION;
41350     b = JS_ToString(ctx, argv[0]);
41351     if (JS_IsException(b)) {
41352         JS_FreeValue(ctx, a);
41353         return JS_EXCEPTION;
41354     }
41355     cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b));
41356     JS_FreeValue(ctx, a);
41357     JS_FreeValue(ctx, b);
41358     return JS_NewInt32(ctx, cmp);
41359 }
41360 
js_string_toLowerCase(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int to_lower)41361 static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val,
41362                                      int argc, JSValueConst *argv, int to_lower)
41363 {
41364     JSValue val;
41365     StringBuffer b_s, *b = &b_s;
41366     JSString *p;
41367     int i, c, j, l;
41368     uint32_t res[LRE_CC_RES_LEN_MAX];
41369 
41370     val = JS_ToStringCheckObject(ctx, this_val);
41371     if (JS_IsException(val))
41372         return val;
41373     p = JS_VALUE_GET_STRING(val);
41374     if (p->len == 0)
41375         return val;
41376     if (string_buffer_init(ctx, b, p->len))
41377         goto fail;
41378     for(i = 0; i < p->len;) {
41379         c = string_getc(p, &i);
41380         if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) {
41381             res[0] = 0x3c2; /* final sigma */
41382             l = 1;
41383         } else {
41384             l = lre_case_conv(res, c, to_lower);
41385         }
41386         for(j = 0; j < l; j++) {
41387             if (string_buffer_putc(b, res[j]))
41388                 goto fail;
41389         }
41390     }
41391     JS_FreeValue(ctx, val);
41392     return string_buffer_end(b);
41393  fail:
41394     JS_FreeValue(ctx, val);
41395     string_buffer_free(b);
41396     return JS_EXCEPTION;
41397 }
41398 
41399 #ifdef CONFIG_ALL_UNICODE
41400 
41401 /* return (-1, NULL) if exception, otherwise (len, buf) */
JS_ToUTF32String(JSContext * ctx,uint32_t ** pbuf,JSValueConst val1)41402 static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1)
41403 {
41404     JSValue val;
41405     JSString *p;
41406     uint32_t *buf;
41407     int i, j, len;
41408 
41409     val = JS_ToString(ctx, val1);
41410     if (JS_IsException(val))
41411         return -1;
41412     p = JS_VALUE_GET_STRING(val);
41413     len = p->len;
41414     /* UTF32 buffer length is len minus the number of correct surrogates pairs */
41415     buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1));
41416     if (!buf) {
41417         JS_FreeValue(ctx, val);
41418         goto fail;
41419     }
41420     for(i = j = 0; i < len;)
41421         buf[j++] = string_getc(p, &i);
41422     JS_FreeValue(ctx, val);
41423     *pbuf = buf;
41424     return j;
41425  fail:
41426     *pbuf = NULL;
41427     return -1;
41428 }
41429 
JS_NewUTF32String(JSContext * ctx,const uint32_t * buf,int len)41430 static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len)
41431 {
41432     int i;
41433     StringBuffer b_s, *b = &b_s;
41434     if (string_buffer_init(ctx, b, len))
41435         return JS_EXCEPTION;
41436     for(i = 0; i < len; i++) {
41437         if (string_buffer_putc(b, buf[i]))
41438             goto fail;
41439     }
41440     return string_buffer_end(b);
41441  fail:
41442     string_buffer_free(b);
41443     return JS_EXCEPTION;
41444 }
41445 
js_string_normalize(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41446 static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val,
41447                                    int argc, JSValueConst *argv)
41448 {
41449     const char *form, *p;
41450     size_t form_len;
41451     int is_compat, buf_len, out_len;
41452     UnicodeNormalizationEnum n_type;
41453     JSValue val;
41454     uint32_t *buf, *out_buf;
41455 
41456     val = JS_ToStringCheckObject(ctx, this_val);
41457     if (JS_IsException(val))
41458         return val;
41459     buf_len = JS_ToUTF32String(ctx, &buf, val);
41460     JS_FreeValue(ctx, val);
41461     if (buf_len < 0)
41462         return JS_EXCEPTION;
41463 
41464     if (argc == 0 || JS_IsUndefined(argv[0])) {
41465         n_type = UNICODE_NFC;
41466     } else {
41467         form = JS_ToCStringLen(ctx, &form_len, argv[0]);
41468         if (!form)
41469             goto fail1;
41470         p = form;
41471         if (p[0] != 'N' || p[1] != 'F')
41472             goto bad_form;
41473         p += 2;
41474         is_compat = FALSE;
41475         if (*p == 'K') {
41476             is_compat = TRUE;
41477             p++;
41478         }
41479         if (*p == 'C' || *p == 'D') {
41480             n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C');
41481             if ((p + 1 - form) != form_len)
41482                 goto bad_form;
41483         } else {
41484         bad_form:
41485             JS_FreeCString(ctx, form);
41486             JS_ThrowRangeError(ctx, "bad normalization form");
41487         fail1:
41488             js_free(ctx, buf);
41489             return JS_EXCEPTION;
41490         }
41491         JS_FreeCString(ctx, form);
41492     }
41493 
41494     out_len = unicode_normalize(&out_buf, buf, buf_len, n_type,
41495                                 ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
41496     js_free(ctx, buf);
41497     if (out_len < 0)
41498         return JS_EXCEPTION;
41499     val = JS_NewUTF32String(ctx, out_buf, out_len);
41500     js_free(ctx, out_buf);
41501     return val;
41502 }
41503 #endif /* CONFIG_ALL_UNICODE */
41504 
41505 /* also used for String.prototype.valueOf */
js_string_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41506 static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val,
41507                                   int argc, JSValueConst *argv)
41508 {
41509     return js_thisStringValue(ctx, this_val);
41510 }
41511 
41512 #if 0
41513 static JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val,
41514                                                int argc, JSValueConst *argv)
41515 {
41516     return JS_ToStringCheckObject(ctx, argv[0]);
41517 }
41518 
41519 static JSValue js_string___toString(JSContext *ctx, JSValueConst this_val,
41520                                     int argc, JSValueConst *argv)
41521 {
41522     return JS_ToString(ctx, argv[0]);
41523 }
41524 
41525 static JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst
41526                                               this_val,
41527                                               int argc, JSValueConst *argv)
41528 {
41529     JSValue str;
41530     int idx;
41531     BOOL is_unicode;
41532     JSString *p;
41533 
41534     str = JS_ToString(ctx, argv[0]);
41535     if (JS_IsException(str))
41536         return str;
41537     if (JS_ToInt32Sat(ctx, &idx, argv[1])) {
41538         JS_FreeValue(ctx, str);
41539         return JS_EXCEPTION;
41540     }
41541     is_unicode = JS_ToBool(ctx, argv[2]);
41542     p = JS_VALUE_GET_STRING(str);
41543     if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) {
41544         idx++;
41545     } else {
41546         string_getc(p, &idx);
41547     }
41548     JS_FreeValue(ctx, str);
41549     return JS_NewInt32(ctx, idx);
41550 }
41551 #endif
41552 
41553 /* String Iterator */
41554 
js_string_iterator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,BOOL * pdone,int magic)41555 static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val,
41556                                        int argc, JSValueConst *argv,
41557                                        BOOL *pdone, int magic)
41558 {
41559     JSArrayIteratorData *it;
41560     uint32_t idx, c, start;
41561     JSString *p;
41562 
41563     it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR);
41564     if (!it) {
41565         *pdone = FALSE;
41566         return JS_EXCEPTION;
41567     }
41568     if (JS_IsUndefined(it->obj))
41569         goto done;
41570     p = JS_VALUE_GET_STRING(it->obj);
41571     idx = it->idx;
41572     if (idx >= p->len) {
41573         JS_FreeValue(ctx, it->obj);
41574         it->obj = JS_UNDEFINED;
41575     done:
41576         *pdone = TRUE;
41577         return JS_UNDEFINED;
41578     }
41579 
41580     start = idx;
41581     c = string_getc(p, (int *)&idx);
41582     it->idx = idx;
41583     *pdone = FALSE;
41584     if (c <= 0xffff) {
41585         return js_new_string_char(ctx, c);
41586     } else {
41587         return js_new_string16(ctx, p->u.str16 + start, 2);
41588     }
41589 }
41590 
41591 /* ES6 Annex B 2.3.2 etc. */
41592 enum {
41593     magic_string_anchor,
41594     magic_string_big,
41595     magic_string_blink,
41596     magic_string_bold,
41597     magic_string_fixed,
41598     magic_string_fontcolor,
41599     magic_string_fontsize,
41600     magic_string_italics,
41601     magic_string_link,
41602     magic_string_small,
41603     magic_string_strike,
41604     magic_string_sub,
41605     magic_string_sup,
41606 };
41607 
js_string_CreateHTML(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)41608 static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val,
41609                                     int argc, JSValueConst *argv, int magic)
41610 {
41611     JSValue str;
41612     const JSString *p;
41613     StringBuffer b_s, *b = &b_s;
41614     static struct { const char *tag, *attr; } const defs[] = {
41615         { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL },
41616         { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL },
41617         { "a", "href" }, { "small", NULL }, { "strike", NULL },
41618         { "sub", NULL }, { "sup", NULL },
41619     };
41620 
41621     str = JS_ToStringCheckObject(ctx, this_val);
41622     if (JS_IsException(str))
41623         return JS_EXCEPTION;
41624     string_buffer_init(ctx, b, 7);
41625     string_buffer_putc8(b, '<');
41626     string_buffer_puts8(b, defs[magic].tag);
41627     if (defs[magic].attr) {
41628         // r += " " + attr + "=\"" + value + "\"";
41629         JSValue value;
41630         int i;
41631 
41632         string_buffer_putc8(b, ' ');
41633         string_buffer_puts8(b, defs[magic].attr);
41634         string_buffer_puts8(b, "=\"");
41635         value = JS_ToStringCheckObject(ctx, argv[0]);
41636         if (JS_IsException(value)) {
41637             JS_FreeValue(ctx, str);
41638             string_buffer_free(b);
41639             return JS_EXCEPTION;
41640         }
41641         p = JS_VALUE_GET_STRING(value);
41642         for (i = 0; i < p->len; i++) {
41643             int c = string_get(p, i);
41644             if (c == '"') {
41645                 string_buffer_puts8(b, "&quot;");
41646             } else {
41647                 string_buffer_putc16(b, c);
41648             }
41649         }
41650         JS_FreeValue(ctx, value);
41651         string_buffer_putc8(b, '\"');
41652     }
41653     // return r + ">" + str + "</" + tag + ">";
41654     string_buffer_putc8(b, '>');
41655     string_buffer_concat_value_free(b, str);
41656     string_buffer_puts8(b, "</");
41657     string_buffer_puts8(b, defs[magic].tag);
41658     string_buffer_putc8(b, '>');
41659     return string_buffer_end(b);
41660 }
41661 
41662 static const JSCFunctionListEntry js_string_funcs[] = {
41663     JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ),
41664     JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ),
41665     JS_CFUNC_DEF("raw", 1, js_string_raw ),
41666     //JS_CFUNC_DEF("__toString", 1, js_string___toString ),
41667     //JS_CFUNC_DEF("__isSpace", 1, js_string___isSpace ),
41668     //JS_CFUNC_DEF("__toStringCheckObject", 1, js_string___toStringCheckObject ),
41669     //JS_CFUNC_DEF("__advanceStringIndex", 3, js_string___advanceStringIndex ),
41670     //JS_CFUNC_DEF("__GetSubstitution", 6, js_string___GetSubstitution ),
41671 };
41672 
41673 static const JSCFunctionListEntry js_string_proto_funcs[] = {
41674     JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ),
41675     JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ),
41676     JS_CFUNC_DEF("charAt", 1, js_string_charAt ),
41677     JS_CFUNC_DEF("concat", 1, js_string_concat ),
41678     JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ),
41679     JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),
41680     JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),
41681     JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ),
41682     JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ),
41683     JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ),
41684     JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ),
41685     JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ),
41686     JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ),
41687     JS_CFUNC_DEF("split", 2, js_string_split ),
41688     JS_CFUNC_DEF("substring", 2, js_string_substring ),
41689     JS_CFUNC_DEF("substr", 2, js_string_substr ),
41690     JS_CFUNC_DEF("slice", 2, js_string_slice ),
41691     JS_CFUNC_DEF("repeat", 1, js_string_repeat ),
41692     JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),
41693     JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),
41694     JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ),
41695     JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ),
41696     JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ),
41697     JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ),
41698     JS_ALIAS_DEF("trimRight", "trimEnd" ),
41699     JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ),
41700     JS_ALIAS_DEF("trimLeft", "trimStart" ),
41701     JS_CFUNC_DEF("toString", 0, js_string_toString ),
41702     JS_CFUNC_DEF("valueOf", 0, js_string_toString ),
41703     JS_CFUNC_DEF("__quote", 1, js_string___quote ),
41704     JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ),
41705     JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),
41706     JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),
41707     JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ),
41708     JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ),
41709     JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),
41710     /* ES6 Annex B 2.3.2 etc. */
41711     JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ),
41712     JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ),
41713     JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ),
41714     JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ),
41715     JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ),
41716     JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ),
41717     JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ),
41718     JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ),
41719     JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ),
41720     JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ),
41721     JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ),
41722     JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ),
41723     JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ),
41724 };
41725 
41726 static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = {
41727     JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ),
41728     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ),
41729 };
41730 
41731 #ifdef CONFIG_ALL_UNICODE
41732 static const JSCFunctionListEntry js_string_proto_normalize[] = {
41733     JS_CFUNC_DEF("normalize", 0, js_string_normalize ),
41734 };
41735 #endif
41736 
JS_AddIntrinsicStringNormalize(JSContext * ctx)41737 void JS_AddIntrinsicStringNormalize(JSContext *ctx)
41738 {
41739 #ifdef CONFIG_ALL_UNICODE
41740     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize,
41741                                countof(js_string_proto_normalize));
41742 #endif
41743 }
41744 
41745 /* Math */
41746 
41747 /* precondition: a and b are not NaN */
js_fmin(double a,double b)41748 static double js_fmin(double a, double b)
41749 {
41750     if (a == 0 && b == 0) {
41751         JSFloat64Union a1, b1;
41752         a1.d = a;
41753         b1.d = b;
41754         a1.u64 |= b1.u64;
41755         return a1.d;
41756     } else {
41757         return fmin(a, b);
41758     }
41759 }
41760 
41761 /* precondition: a and b are not NaN */
js_fmax(double a,double b)41762 static double js_fmax(double a, double b)
41763 {
41764     if (a == 0 && b == 0) {
41765         JSFloat64Union a1, b1;
41766         a1.d = a;
41767         b1.d = b;
41768         a1.u64 &= b1.u64;
41769         return a1.d;
41770     } else {
41771         return fmax(a, b);
41772     }
41773 }
41774 
js_math_min_max(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)41775 static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
41776                                int argc, JSValueConst *argv, int magic)
41777 {
41778     BOOL is_max = magic;
41779     double r, a;
41780     int i;
41781     uint32_t tag;
41782 
41783     if (unlikely(argc == 0)) {
41784 #if defined(HUGE_VAL) || defined(_MSC_VER)
41785         return __JS_NewFloat64(ctx, is_max ? -HUGE_VAL : HUGE_VAL);
41786 #else
41787         return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);
41788 #endif
41789     }
41790 
41791     tag = JS_VALUE_GET_TAG(argv[0]);
41792     if (tag == JS_TAG_INT) {
41793         int a1, r1 = JS_VALUE_GET_INT(argv[0]);
41794         for(i = 1; i < argc; i++) {
41795             tag = JS_VALUE_GET_TAG(argv[i]);
41796             if (tag != JS_TAG_INT) {
41797                 r = r1;
41798                 goto generic_case;
41799             }
41800             a1 = JS_VALUE_GET_INT(argv[i]);
41801             if (is_max)
41802                 r1 = max_int(r1, a1);
41803             else
41804                 r1 = min_int(r1, a1);
41805 
41806         }
41807         return JS_NewInt32(ctx, r1);
41808     } else {
41809         if (JS_ToFloat64(ctx, &r, argv[0]))
41810             return JS_EXCEPTION;
41811         i = 1;
41812     generic_case:
41813         while (i < argc) {
41814             if (JS_ToFloat64(ctx, &a, argv[i]))
41815                 return JS_EXCEPTION;
41816             if (!isnan(r)) {
41817                 if (isnan(a)) {
41818                     r = a;
41819                 } else {
41820                     if (is_max)
41821                         r = js_fmax(r, a);
41822                     else
41823                         r = js_fmin(r, a);
41824                 }
41825             }
41826             i++;
41827         }
41828         return JS_NewFloat64(ctx, r);
41829     }
41830 }
41831 
js_math_sign(double a)41832 static double js_math_sign(double a)
41833 {
41834     if (isnan(a) || a == 0.0)
41835         return a;
41836     if (a < 0)
41837         return -1;
41838     else
41839         return 1;
41840 }
41841 
js_math_round(double a)41842 static double js_math_round(double a)
41843 {
41844     JSFloat64Union u;
41845     uint64_t frac_mask, one;
41846     unsigned int e, s;
41847 
41848     u.d = a;
41849     e = (u.u64 >> 52) & 0x7ff;
41850     if (e < 1023) {
41851         /* abs(a) < 1 */
41852         if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) {
41853             /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */
41854             u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52);
41855         } else {
41856             /* return +/-0.0 */
41857             u.u64 &= (uint64_t)1 << 63;
41858         }
41859     } else if (e < (1023 + 52)) {
41860         s = u.u64 >> 63;
41861         one = (uint64_t)1 << (52 - (e - 1023));
41862         frac_mask = one - 1;
41863         u.u64 += (one >> 1) - s;
41864         u.u64 &= ~frac_mask; /* truncate to an integer */
41865     }
41866     /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */
41867     return u.d;
41868 }
41869 
js_math_hypot(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41870 static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,
41871                              int argc, JSValueConst *argv)
41872 {
41873     double r, a;
41874     int i;
41875 
41876     r = 0;
41877     if (argc > 0) {
41878         if (JS_ToFloat64(ctx, &r, argv[0]))
41879             return JS_EXCEPTION;
41880         if (argc == 1) {
41881             r = fabs(r);
41882         } else {
41883             /* use the built-in function to minimize precision loss */
41884             for (i = 1; i < argc; i++) {
41885                 if (JS_ToFloat64(ctx, &a, argv[i]))
41886                     return JS_EXCEPTION;
41887                 r = hypot(r, a);
41888             }
41889         }
41890     }
41891     return JS_NewFloat64(ctx, r);
41892 }
41893 
js_math_fround(double a)41894 static double js_math_fround(double a)
41895 {
41896     return (float)a;
41897 }
41898 
js_math_imul(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41899 static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val,
41900                             int argc, JSValueConst *argv)
41901 {
41902     int a, b;
41903 
41904     if (JS_ToInt32(ctx, &a, argv[0]))
41905         return JS_EXCEPTION;
41906     if (JS_ToInt32(ctx, &b, argv[1]))
41907         return JS_EXCEPTION;
41908     /* purposely ignoring overflow */
41909     return JS_NewInt32(ctx, a * b);
41910 }
41911 
js_math_clz32(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41912 static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,
41913                              int argc, JSValueConst *argv)
41914 {
41915     uint32_t a, r;
41916 
41917     if (JS_ToUint32(ctx, &a, argv[0]))
41918         return JS_EXCEPTION;
41919     if (a == 0)
41920         r = 32;
41921     else
41922         r = clz32(a);
41923     return JS_NewInt32(ctx, r);
41924 }
41925 
41926 /* xorshift* random number generator by Marsaglia */
xorshift64star(uint64_t * pstate)41927 static uint64_t xorshift64star(uint64_t *pstate)
41928 {
41929     uint64_t x;
41930     x = *pstate;
41931     x ^= x >> 12;
41932     x ^= x << 25;
41933     x ^= x >> 27;
41934     *pstate = x;
41935     return x * 0x2545F4914F6CDD1D;
41936 }
41937 
js_random_init(JSContext * ctx)41938 static void js_random_init(JSContext *ctx)
41939 {
41940     struct timeval tv;
41941     gettimeofday(&tv, NULL);
41942     ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
41943     /* the state must be non zero */
41944     if (ctx->random_state == 0)
41945         ctx->random_state = 1;
41946 }
41947 
js_math_random(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41948 static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,
41949                               int argc, JSValueConst *argv)
41950 {
41951     JSFloat64Union u;
41952     uint64_t v;
41953 
41954     v = xorshift64star(&ctx->random_state);
41955     /* 1.0 <= u.d < 2 */
41956     u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12);
41957     return __JS_NewFloat64(ctx, u.d - 1.0);
41958 }
41959 
41960 #if defined(_MSC_VER)
41961   #pragma function (ceil)	// Fix MSVC error C2099: initializer is not a constant
41962   #pragma function (floor)	// Fix MSVC error C2099: initializer is not a constant
41963 #endif
41964 
41965 static const JSCFunctionListEntry js_math_funcs[] = {
41966     JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),
41967     JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),
41968     JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),
41969     JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ),
41970     JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ),
41971     JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),
41972     JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ),
41973 
41974     JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ),
41975     JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ),
41976     JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ),
41977     JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ),
41978     JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ),
41979     JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ),
41980     JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ),
41981     JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ),
41982     JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ),
41983     JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ),
41984     /* ES6 */
41985     JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ),
41986     JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ),
41987     JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ),
41988     JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ),
41989     JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ),
41990     JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ),
41991     JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ),
41992     JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ),
41993     JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ),
41994     JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ),
41995     JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ),
41996     JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ),
41997     JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),
41998     JS_CFUNC_DEF("hypot", 2, js_math_hypot ),
41999     JS_CFUNC_DEF("random", 0, js_math_random ),
42000     JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),
42001     JS_CFUNC_DEF("imul", 2, js_math_imul ),
42002     JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),
42003     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ),
42004     JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ),
42005     JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ),
42006     JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ),
42007     JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ),
42008     JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ),
42009     JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ),
42010     JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ),
42011     JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ),
42012 };
42013 
42014 static const JSCFunctionListEntry js_math_obj[] = {
42015     JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
42016 };
42017 
42018 /* Date */
42019 
42020 #if 0
42021 /* OS dependent: return the UTC time in ms since 1970. */
42022 static JSValue js___date_now(JSContext *ctx, JSValueConst this_val,
42023                              int argc, JSValueConst *argv)
42024 {
42025     int64_t d;
42026     struct timeval tv;
42027     gettimeofday(&tv, NULL);
42028     d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
42029     return JS_NewInt64(ctx, d);
42030 }
42031 #endif
42032 
42033 /* OS dependent: return the UTC time in microseconds since 1970. */
js___date_clock(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)42034 static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val,
42035                                int argc, JSValueConst *argv)
42036 {
42037     int64_t d;
42038     struct timeval tv;
42039     gettimeofday(&tv, NULL);
42040     d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
42041     return JS_NewInt64(ctx, d);
42042 }
42043 
42044 /* OS dependent. d = argv[0] is in ms from 1970. Return the difference
42045    between UTC time and local time 'd' in minutes */
getTimezoneOffset(int64_t time)42046 static int getTimezoneOffset(int64_t time) {
42047 #if defined(_WIN32)
42048     /* XXX: TODO */
42049     return 0;
42050 #else
42051     time_t ti;
42052     struct tm tm;
42053 
42054     time /= 1000; /* convert to seconds */
42055     if (sizeof(time_t) == 4) {
42056         /* on 32-bit systems, we need to clamp the time value to the
42057            range of `time_t`. This is better than truncating values to
42058            32 bits and hopefully provides the same result as 64-bit
42059            implementation of localtime_r.
42060          */
42061         if ((time_t)-1 < 0) {
42062             if (time < INT32_MIN) {
42063                 time = INT32_MIN;
42064             } else if (time > INT32_MAX) {
42065                 time = INT32_MAX;
42066             }
42067         } else {
42068             if (time < 0) {
42069                 time = 0;
42070             } else if (time > UINT32_MAX) {
42071                 time = UINT32_MAX;
42072             }
42073         }
42074     }
42075     ti = time;
42076     localtime_r(&ti, &tm);
42077     return -tm.tm_gmtoff / 60;
42078 #endif
42079 }
42080 
42081 #if 0
42082 static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
42083                                            int argc, JSValueConst *argv)
42084 {
42085     double dd;
42086 
42087     if (JS_ToFloat64(ctx, &dd, argv[0]))
42088         return JS_EXCEPTION;
42089     if (isnan(dd))
42090         return __JS_NewFloat64(ctx, dd);
42091     else
42092         return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd));
42093 }
42094 
42095 static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor,
42096                                           JSValueConst def_proto)
42097 {
42098     JSValue proto;
42099     proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
42100     if (JS_IsException(proto))
42101         return proto;
42102     if (!JS_IsObject(proto)) {
42103         JS_FreeValue(ctx, proto);
42104         proto = JS_DupValue(ctx, def_proto);
42105     }
42106     return proto;
42107 }
42108 
42109 /* create a new date object */
42110 static JSValue js___date_create(JSContext *ctx, JSValueConst this_val,
42111                                 int argc, JSValueConst *argv)
42112 {
42113     JSValue obj, proto;
42114     proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]);
42115     if (JS_IsException(proto))
42116         return proto;
42117     obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE);
42118     JS_FreeValue(ctx, proto);
42119     if (!JS_IsException(obj))
42120         JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2]));
42121     return obj;
42122 }
42123 #endif
42124 
42125 /* RegExp */
42126 
js_regexp_finalizer(JSRuntime * rt,JSValue val)42127 static void js_regexp_finalizer(JSRuntime *rt, JSValue val)
42128 {
42129     JSObject *p = JS_VALUE_GET_OBJ(val);
42130     JSRegExp *re = &p->u.regexp;
42131     JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode));
42132     JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern));
42133 }
42134 
42135 /* create a string containing the RegExp bytecode */
js_compile_regexp(JSContext * ctx,JSValueConst pattern,JSValueConst flags)42136 static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
42137                                  JSValueConst flags)
42138 {
42139     const char *str;
42140     int re_flags, mask;
42141     uint8_t *re_bytecode_buf;
42142     size_t i, len;
42143     int re_bytecode_len;
42144     JSValue ret;
42145     char error_msg[64];
42146 
42147     re_flags = 0;
42148     if (!JS_IsUndefined(flags)) {
42149         str = JS_ToCStringLen(ctx, &len, flags);
42150         if (!str)
42151             return JS_EXCEPTION;
42152         /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */
42153         for (i = 0; i < len; i++) {
42154             switch(str[i]) {
42155             case 'g':
42156                 mask = LRE_FLAG_GLOBAL;
42157                 break;
42158             case 'i':
42159                 mask = LRE_FLAG_IGNORECASE;
42160                 break;
42161             case 'm':
42162                 mask = LRE_FLAG_MULTILINE;
42163                 break;
42164             case 's':
42165                 mask = LRE_FLAG_DOTALL;
42166                 break;
42167             case 'u':
42168                 mask = LRE_FLAG_UTF16;
42169                 break;
42170             case 'y':
42171                 mask = LRE_FLAG_STICKY;
42172                 break;
42173             default:
42174                 goto bad_flags;
42175             }
42176             if ((re_flags & mask) != 0) {
42177             bad_flags:
42178                 JS_FreeCString(ctx, str);
42179                 return JS_ThrowSyntaxError(ctx, "invalid regular expression flags");
42180             }
42181             re_flags |= mask;
42182         }
42183         JS_FreeCString(ctx, str);
42184     }
42185 
42186     str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UTF16));
42187     if (!str)
42188         return JS_EXCEPTION;
42189     re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg,
42190                                   sizeof(error_msg), str, len, re_flags, ctx);
42191     JS_FreeCString(ctx, str);
42192     if (!re_bytecode_buf) {
42193         JS_ThrowSyntaxError(ctx, "%s", error_msg);
42194         return JS_EXCEPTION;
42195     }
42196 
42197     ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len);
42198     js_free(ctx, re_bytecode_buf);
42199     return ret;
42200 }
42201 
42202 /* create a RegExp object from a string containing the RegExp bytecode
42203    and the source pattern */
js_regexp_constructor_internal(JSContext * ctx,JSValueConst ctor,JSValue pattern,JSValue bc)42204 static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
42205                                               JSValue pattern, JSValue bc)
42206 {
42207     JSValue obj;
42208     JSObject *p;
42209     JSRegExp *re;
42210 
42211     /* sanity check */
42212     if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING ||
42213         JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) {
42214         JS_ThrowTypeError(ctx, "string expected");
42215     fail:
42216         JS_FreeValue(ctx, bc);
42217         JS_FreeValue(ctx, pattern);
42218         return JS_EXCEPTION;
42219     }
42220 
42221     obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP);
42222     if (JS_IsException(obj))
42223         goto fail;
42224     p = JS_VALUE_GET_OBJ(obj);
42225     re = &p->u.regexp;
42226     re->pattern = JS_VALUE_GET_STRING(pattern);
42227     re->bytecode = JS_VALUE_GET_STRING(bc);
42228     JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0),
42229                            JS_PROP_WRITABLE);
42230     return obj;
42231 }
42232 
js_get_regexp(JSContext * ctx,JSValueConst obj,BOOL throw_error)42233 static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error)
42234 {
42235     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
42236         JSObject *p = JS_VALUE_GET_OBJ(obj);
42237         if (p->class_id == JS_CLASS_REGEXP)
42238             return &p->u.regexp;
42239     }
42240     if (throw_error) {
42241         JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
42242     }
42243     return NULL;
42244 }
42245 
42246 /* return < 0 if exception or TRUE/FALSE */
js_is_regexp(JSContext * ctx,JSValueConst obj)42247 static int js_is_regexp(JSContext *ctx, JSValueConst obj)
42248 {
42249     JSValue m;
42250 
42251     if (!JS_IsObject(obj))
42252         return FALSE;
42253     m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match);
42254     if (JS_IsException(m))
42255         return -1;
42256     if (!JS_IsUndefined(m))
42257         return JS_ToBoolFree(ctx, m);
42258     return js_get_regexp(ctx, obj, FALSE) != NULL;
42259 }
42260 
js_regexp_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)42261 static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target,
42262                                      int argc, JSValueConst *argv)
42263 {
42264     JSValue pattern, flags, bc, val;
42265     JSValueConst pat, flags1;
42266     JSRegExp *re;
42267     int pat_is_regexp;
42268 
42269     pat = argv[0];
42270     flags1 = argv[1];
42271     pat_is_regexp = js_is_regexp(ctx, pat);
42272     if (pat_is_regexp < 0)
42273         return JS_EXCEPTION;
42274     if (JS_IsUndefined(new_target)) {
42275         /* called as a function */
42276         new_target = JS_GetActiveFunction(ctx);
42277         if (pat_is_regexp && JS_IsUndefined(flags1)) {
42278             JSValue ctor;
42279             BOOL res;
42280             ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor);
42281             if (JS_IsException(ctor))
42282                 return ctor;
42283             res = js_same_value(ctx, ctor, new_target);
42284             JS_FreeValue(ctx, ctor);
42285             if (res)
42286                 return JS_DupValue(ctx, pat);
42287         }
42288     }
42289     re = js_get_regexp(ctx, pat, FALSE);
42290     if (re) {
42291         pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
42292         if (JS_IsUndefined(flags1)) {
42293             bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
42294             goto no_compilation;
42295         } else {
42296             flags = JS_ToString(ctx, flags1);
42297             if (JS_IsException(flags))
42298                 goto fail;
42299         }
42300     } else {
42301         flags = JS_UNDEFINED;
42302         if (pat_is_regexp) {
42303             pattern = JS_GetProperty(ctx, pat, JS_ATOM_source);
42304             if (JS_IsException(pattern))
42305                 goto fail;
42306             if (JS_IsUndefined(flags1)) {
42307                 flags = JS_GetProperty(ctx, pat, JS_ATOM_flags);
42308                 if (JS_IsException(flags))
42309                     goto fail;
42310             } else {
42311                 flags = JS_DupValue(ctx, flags1);
42312             }
42313         } else {
42314             pattern = JS_DupValue(ctx, pat);
42315             flags = JS_DupValue(ctx, flags1);
42316         }
42317         if (JS_IsUndefined(pattern)) {
42318             pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
42319         } else {
42320             val = pattern;
42321             pattern = JS_ToString(ctx, val);
42322             JS_FreeValue(ctx, val);
42323             if (JS_IsException(pattern))
42324                 goto fail;
42325         }
42326     }
42327     bc = js_compile_regexp(ctx, pattern, flags);
42328     if (JS_IsException(bc))
42329         goto fail;
42330     JS_FreeValue(ctx, flags);
42331  no_compilation:
42332     return js_regexp_constructor_internal(ctx, new_target, pattern, bc);
42333  fail:
42334     JS_FreeValue(ctx, pattern);
42335     JS_FreeValue(ctx, flags);
42336     return JS_EXCEPTION;
42337 }
42338 
js_regexp_compile(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)42339 static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val,
42340                                  int argc, JSValueConst *argv)
42341 {
42342     JSRegExp *re1, *re;
42343     JSValueConst pattern1, flags1;
42344     JSValue bc, pattern;
42345 
42346     re = js_get_regexp(ctx, this_val, TRUE);
42347     if (!re)
42348         return JS_EXCEPTION;
42349     pattern1 = argv[0];
42350     flags1 = argv[1];
42351     re1 = js_get_regexp(ctx, pattern1, FALSE);
42352     if (re1) {
42353         if (!JS_IsUndefined(flags1))
42354             return JS_ThrowTypeError(ctx, "flags must be undefined");
42355         pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern));
42356         bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode));
42357     } else {
42358         bc = JS_UNDEFINED;
42359         if (JS_IsUndefined(pattern1))
42360             pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
42361         else
42362             pattern = JS_ToString(ctx, pattern1);
42363         if (JS_IsException(pattern))
42364             goto fail;
42365         bc = js_compile_regexp(ctx, pattern, flags1);
42366         if (JS_IsException(bc))
42367             goto fail;
42368     }
42369     JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
42370     JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
42371     re->pattern = JS_VALUE_GET_STRING(pattern);
42372     re->bytecode = JS_VALUE_GET_STRING(bc);
42373     if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
42374                        JS_NewInt32(ctx, 0)) < 0)
42375         return JS_EXCEPTION;
42376     return JS_DupValue(ctx, this_val);
42377  fail:
42378     JS_FreeValue(ctx, pattern);
42379     JS_FreeValue(ctx, bc);
42380     return JS_EXCEPTION;
42381 }
42382 
42383 #if 0
42384 static JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val)
42385 {
42386     JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
42387     if (!re)
42388         return JS_EXCEPTION;
42389     return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
42390 }
42391 
42392 static JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val)
42393 {
42394     JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
42395     int flags;
42396 
42397     if (!re)
42398         return JS_EXCEPTION;
42399     flags = lre_get_flags(re->bytecode->u.str8);
42400     return JS_NewInt32(ctx, flags);
42401 }
42402 #endif
42403 
js_regexp_get_source(JSContext * ctx,JSValueConst this_val)42404 static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val)
42405 {
42406     JSRegExp *re;
42407     JSString *p;
42408     StringBuffer b_s, *b = &b_s;
42409     int i, n, c, c2, bra;
42410 
42411     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
42412         return JS_ThrowTypeErrorNotAnObject(ctx);
42413 
42414     if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
42415         goto empty_regex;
42416 
42417     re = js_get_regexp(ctx, this_val, TRUE);
42418     if (!re)
42419         return JS_EXCEPTION;
42420 
42421     p = re->pattern;
42422 
42423     if (p->len == 0) {
42424     empty_regex:
42425         return JS_NewString(ctx, "(?:)");
42426     }
42427     string_buffer_init2(ctx, b, p->len, p->is_wide_char);
42428 
42429     /* Escape '/' and newline sequences as needed */
42430     bra = 0;
42431     for (i = 0, n = p->len; i < n;) {
42432         c2 = -1;
42433         switch (c = string_get(p, i++)) {
42434         case '\\':
42435             if (i < n)
42436                 c2 = string_get(p, i++);
42437             break;
42438         case ']':
42439             bra = 0;
42440             break;
42441         case '[':
42442             if (!bra) {
42443                 if (i < n && string_get(p, i) == ']')
42444                     c2 = string_get(p, i++);
42445                 bra = 1;
42446             }
42447             break;
42448         case '\n':
42449             c = '\\';
42450             c2 = 'n';
42451             break;
42452         case '\r':
42453             c = '\\';
42454             c2 = 'r';
42455             break;
42456         case '/':
42457             if (!bra) {
42458                 c = '\\';
42459                 c2 = '/';
42460             }
42461             break;
42462         }
42463         string_buffer_putc16(b, c);
42464         if (c2 >= 0)
42465             string_buffer_putc16(b, c2);
42466     }
42467     return string_buffer_end(b);
42468 }
42469 
js_regexp_get_flag(JSContext * ctx,JSValueConst this_val,int mask)42470 static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask)
42471 {
42472     JSRegExp *re;
42473     int flags;
42474 
42475     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
42476         return JS_ThrowTypeErrorNotAnObject(ctx);
42477 
42478     re = js_get_regexp(ctx, this_val, FALSE);
42479     if (!re) {
42480         if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
42481             return JS_UNDEFINED;
42482         else
42483             return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
42484     }
42485 
42486     flags = lre_get_flags(re->bytecode->u.str8);
42487     return JS_NewBool(ctx, (flags & mask) != 0);
42488 }
42489 
js_regexp_get_flags(JSContext * ctx,JSValueConst this_val)42490 static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val)
42491 {
42492     char str[8], *p = str;
42493     int res;
42494 
42495     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
42496         return JS_ThrowTypeErrorNotAnObject(ctx);
42497 
42498     res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global));
42499     if (res < 0)
42500         goto exception;
42501     if (res)
42502         *p++ = 'g';
42503     res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase"));
42504     if (res < 0)
42505         goto exception;
42506     if (res)
42507         *p++ = 'i';
42508     res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline"));
42509     if (res < 0)
42510         goto exception;
42511     if (res)
42512         *p++ = 'm';
42513     res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll"));
42514     if (res < 0)
42515         goto exception;
42516     if (res)
42517         *p++ = 's';
42518     res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode));
42519     if (res < 0)
42520         goto exception;
42521     if (res)
42522         *p++ = 'u';
42523     res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky"));
42524     if (res < 0)
42525         goto exception;
42526     if (res)
42527         *p++ = 'y';
42528     return JS_NewStringLen(ctx, str, p - str);
42529 
42530 exception:
42531     return JS_EXCEPTION;
42532 }
42533 
js_regexp_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)42534 static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,
42535                                   int argc, JSValueConst *argv)
42536 {
42537     JSValue pattern, flags;
42538     StringBuffer b_s, *b = &b_s;
42539 
42540     if (!JS_IsObject(this_val))
42541         return JS_ThrowTypeErrorNotAnObject(ctx);
42542 
42543     string_buffer_init(ctx, b, 0);
42544     string_buffer_putc8(b, '/');
42545     pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source);
42546     if (string_buffer_concat_value_free(b, pattern))
42547         goto fail;
42548     string_buffer_putc8(b, '/');
42549     flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags);
42550     if (string_buffer_concat_value_free(b, flags))
42551         goto fail;
42552     return string_buffer_end(b);
42553 
42554 fail:
42555     string_buffer_free(b);
42556     return JS_EXCEPTION;
42557 }
42558 
lre_check_stack_overflow(void * opaque,size_t alloca_size)42559 BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)
42560 {
42561     JSContext *ctx = opaque;
42562     return js_check_stack_overflow(ctx->rt, alloca_size);
42563 }
42564 
lre_realloc(void * opaque,void * ptr,size_t size)42565 void *lre_realloc(void *opaque, void *ptr, size_t size)
42566 {
42567     JSContext *ctx = opaque;
42568     /* No JS exception is raised here */
42569     return js_realloc_rt(ctx->rt, ptr, size);
42570 }
42571 
js_regexp_exec(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)42572 static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
42573                               int argc, JSValueConst *argv)
42574 {
42575     JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
42576     JSString *str;
42577     JSValue str_val, obj, val, groups = JS_UNDEFINED;
42578     uint8_t *re_bytecode;
42579     int ret;
42580     uint8_t **capture, *str_buf;
42581     int capture_count, shift, i, re_flags;
42582     int64_t last_index;
42583     const char *group_name_ptr;
42584 
42585     if (!re)
42586         return JS_EXCEPTION;
42587     str_val = JS_ToString(ctx, argv[0]);
42588     if (JS_IsException(str_val))
42589         return str_val;
42590     val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
42591     if (JS_IsException(val) ||
42592         JS_ToLengthFree(ctx, &last_index, val)) {
42593         JS_FreeValue(ctx, str_val);
42594         return JS_EXCEPTION;
42595     }
42596     re_bytecode = re->bytecode->u.str8;
42597     re_flags = lre_get_flags(re_bytecode);
42598     if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
42599         last_index = 0;
42600     }
42601     str = JS_VALUE_GET_STRING(str_val);
42602     capture_count = lre_get_capture_count(re_bytecode);
42603     capture = NULL;
42604     if (capture_count > 0) {
42605         capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
42606         if (!capture) {
42607             JS_FreeValue(ctx, str_val);
42608             return JS_EXCEPTION;
42609         }
42610     }
42611     shift = str->is_wide_char;
42612     str_buf = str->u.str8;
42613     if (last_index > str->len) {
42614         ret = 2;
42615     } else {
42616         ret = lre_exec(capture, re_bytecode,
42617                        str_buf, last_index, str->len,
42618                        shift, ctx);
42619     }
42620     obj = JS_NULL;
42621     if (ret != 1) {
42622         if (ret >= 0) {
42623             if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
42624                 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
42625                                    JS_NewInt32(ctx, 0)) < 0)
42626                     goto fail;
42627             }
42628         } else {
42629             JS_ThrowInternalError(ctx, "out of memory in regexp execution");
42630             goto fail;
42631         }
42632         JS_FreeValue(ctx, str_val);
42633     } else {
42634         int prop_flags;
42635         if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {
42636             if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
42637                                JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0)
42638                 goto fail;
42639         }
42640         obj = JS_NewArray(ctx);
42641         if (JS_IsException(obj))
42642             goto fail;
42643         prop_flags = JS_PROP_C_W_E | JS_PROP_THROW;
42644         group_name_ptr = lre_get_groupnames(re_bytecode);
42645         if (group_name_ptr) {
42646             groups = JS_NewObjectProto(ctx, JS_NULL);
42647             if (JS_IsException(groups))
42648                 goto fail;
42649         }
42650 
42651         for(i = 0; i < capture_count; i++) {
42652             int start, end;
42653             JSValue val;
42654             if (capture[2 * i] == NULL ||
42655                 capture[2 * i + 1] == NULL) {
42656                 val = JS_UNDEFINED;
42657             } else {
42658                 start = (capture[2 * i] - str_buf) >> shift;
42659                 end = (capture[2 * i + 1] - str_buf) >> shift;
42660                 val = js_sub_string(ctx, str, start, end);
42661                 if (JS_IsException(val))
42662                     goto fail;
42663             }
42664             if (group_name_ptr && i > 0) {
42665                 if (*group_name_ptr) {
42666                     if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr,
42667                                                   JS_DupValue(ctx, val),
42668                                                   prop_flags) < 0) {
42669                         JS_FreeValue(ctx, val);
42670                         goto fail;
42671                     }
42672                 }
42673                 group_name_ptr += strlen(group_name_ptr) + 1;
42674             }
42675             if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0)
42676                 goto fail;
42677         }
42678         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups,
42679                                    groups, prop_flags) < 0)
42680             goto fail;
42681         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index,
42682                                    JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0)
42683             goto fail;
42684         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0)
42685             goto fail1;
42686     }
42687     js_free(ctx, capture);
42688     return obj;
42689 fail:
42690     JS_FreeValue(ctx, groups);
42691     JS_FreeValue(ctx, str_val);
42692 fail1:
42693     JS_FreeValue(ctx, obj);
42694     js_free(ctx, capture);
42695     return JS_EXCEPTION;
42696 }
42697 
42698 /* delete portions of a string that match a given regex */
JS_RegExpDelete(JSContext * ctx,JSValueConst this_val,JSValueConst arg)42699 static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg)
42700 {
42701     JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
42702     JSString *str;
42703     JSValue str_val, val;
42704     uint8_t *re_bytecode;
42705     int ret;
42706     uint8_t **capture, *str_buf;
42707     int capture_count, shift, re_flags;
42708     int next_src_pos, start, end;
42709     int64_t last_index;
42710     StringBuffer b_s, *b = &b_s;
42711 
42712     if (!re)
42713         return JS_EXCEPTION;
42714 
42715     string_buffer_init(ctx, b, 0);
42716 
42717     capture = NULL;
42718     str_val = JS_ToString(ctx, arg);
42719     if (JS_IsException(str_val))
42720         goto fail;
42721     str = JS_VALUE_GET_STRING(str_val);
42722     re_bytecode = re->bytecode->u.str8;
42723     re_flags = lre_get_flags(re_bytecode);
42724     if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
42725         last_index = 0;
42726     } else {
42727         val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
42728         if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val))
42729             goto fail;
42730     }
42731     capture_count = lre_get_capture_count(re_bytecode);
42732     if (capture_count > 0) {
42733         capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
42734         if (!capture)
42735             goto fail;
42736     }
42737     shift = str->is_wide_char;
42738     str_buf = str->u.str8;
42739     next_src_pos = 0;
42740     for (;;) {
42741         if (last_index > str->len)
42742             break;
42743 
42744         ret = lre_exec(capture, re_bytecode,
42745                        str_buf, last_index, str->len, shift, ctx);
42746         if (ret != 1) {
42747             if (ret >= 0) {
42748                 if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
42749                     if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
42750                                        JS_NewInt32(ctx, 0)) < 0)
42751                         goto fail;
42752                 }
42753             } else {
42754                 JS_ThrowInternalError(ctx, "out of memory in regexp execution");
42755                 goto fail;
42756             }
42757             break;
42758         }
42759         start = (capture[0] - str_buf) >> shift;
42760         end = (capture[1] - str_buf) >> shift;
42761         last_index = end;
42762         if (next_src_pos < start) {
42763             if (string_buffer_concat(b, str, next_src_pos, start))
42764                 goto fail;
42765         }
42766         next_src_pos = end;
42767         if (!(re_flags & LRE_FLAG_GLOBAL)) {
42768             if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
42769                                JS_NewInt32(ctx, end)) < 0)
42770                 goto fail;
42771             break;
42772         }
42773         if (end == start) {
42774             if (!(re_flags & LRE_FLAG_UTF16) || (unsigned)end >= str->len || !str->is_wide_char) {
42775                 end++;
42776             } else {
42777                 string_getc(str, &end);
42778             }
42779         }
42780         last_index = end;
42781     }
42782     if (string_buffer_concat(b, str, next_src_pos, str->len))
42783         goto fail;
42784     JS_FreeValue(ctx, str_val);
42785     js_free(ctx, capture);
42786     return string_buffer_end(b);
42787 fail:
42788     JS_FreeValue(ctx, str_val);
42789     js_free(ctx, capture);
42790     string_buffer_free(b);
42791     return JS_EXCEPTION;
42792 }
42793 
JS_RegExpExec(JSContext * ctx,JSValueConst r,JSValueConst s)42794 static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s)
42795 {
42796     JSValue method, ret;
42797 
42798     method = JS_GetProperty(ctx, r, JS_ATOM_exec);
42799     if (JS_IsException(method))
42800         return method;
42801     if (JS_IsFunction(ctx, method)) {
42802         ret = JS_CallFree(ctx, method, r, 1, &s);
42803         if (JS_IsException(ret))
42804             return ret;
42805         if (!JS_IsObject(ret) && !JS_IsNull(ret)) {
42806             JS_FreeValue(ctx, ret);
42807             return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null");
42808         }
42809         return ret;
42810     }
42811     JS_FreeValue(ctx, method);
42812     return js_regexp_exec(ctx, r, 1, &s);
42813 }
42814 
42815 #if 0
42816 static JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val,
42817                                       int argc, JSValueConst *argv)
42818 {
42819     return JS_RegExpExec(ctx, argv[0], argv[1]);
42820 }
42821 static JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val,
42822                                         int argc, JSValueConst *argv)
42823 {
42824     return JS_RegExpDelete(ctx, argv[0], argv[1]);
42825 }
42826 #endif
42827 
js_regexp_test(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)42828 static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val,
42829                               int argc, JSValueConst *argv)
42830 {
42831     JSValue val;
42832     BOOL ret;
42833 
42834     val = JS_RegExpExec(ctx, this_val, argv[0]);
42835     if (JS_IsException(val))
42836         return JS_EXCEPTION;
42837     ret = !JS_IsNull(val);
42838     JS_FreeValue(ctx, val);
42839     return JS_NewBool(ctx, ret);
42840 }
42841 
js_regexp_Symbol_match(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)42842 static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val,
42843                                       int argc, JSValueConst *argv)
42844 {
42845     // [Symbol.match](str)
42846     JSValueConst rx = this_val;
42847     JSValue A, S, result, matchStr;
42848     int global, n, fullUnicode, isEmpty;
42849     JSString *p;
42850 
42851     if (!JS_IsObject(rx))
42852         return JS_ThrowTypeErrorNotAnObject(ctx);
42853 
42854     A = JS_UNDEFINED;
42855     result = JS_UNDEFINED;
42856     matchStr = JS_UNDEFINED;
42857     S = JS_ToString(ctx, argv[0]);
42858     if (JS_IsException(S))
42859         goto exception;
42860 
42861     global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));
42862     if (global < 0)
42863         goto exception;
42864 
42865     if (!global) {
42866         A = JS_RegExpExec(ctx, rx, S);
42867     } else {
42868         fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
42869         if (fullUnicode < 0)
42870             goto exception;
42871 
42872         if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
42873             goto exception;
42874         A = JS_NewArray(ctx);
42875         if (JS_IsException(A))
42876             goto exception;
42877         n = 0;
42878         for(;;) {
42879             JS_FreeValue(ctx, result);
42880             result = JS_RegExpExec(ctx, rx, S);
42881             if (JS_IsException(result))
42882                 goto exception;
42883             if (JS_IsNull(result))
42884                 break;
42885             matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
42886             if (JS_IsException(matchStr))
42887                 goto exception;
42888             isEmpty = JS_IsEmptyString(matchStr);
42889             if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0)
42890                 goto exception;
42891             if (isEmpty) {
42892                 int64_t thisIndex, nextIndex;
42893                 if (JS_ToLengthFree(ctx, &thisIndex,
42894                                     JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
42895                     goto exception;
42896                 p = JS_VALUE_GET_STRING(S);
42897                 nextIndex = string_advance_index(p, thisIndex, fullUnicode);
42898                 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
42899                     goto exception;
42900             }
42901         }
42902         if (n == 0) {
42903             JS_FreeValue(ctx, A);
42904             A = JS_NULL;
42905         }
42906     }
42907     JS_FreeValue(ctx, result);
42908     JS_FreeValue(ctx, S);
42909     return A;
42910 
42911 exception:
42912     JS_FreeValue(ctx, A);
42913     JS_FreeValue(ctx, result);
42914     JS_FreeValue(ctx, S);
42915     return JS_EXCEPTION;
42916 }
42917 
42918 typedef struct JSRegExpStringIteratorData {
42919     JSValue iterating_regexp;
42920     JSValue iterated_string;
42921     BOOL global;
42922     BOOL unicode;
42923     BOOL done;
42924 } JSRegExpStringIteratorData;
42925 
js_regexp_string_iterator_finalizer(JSRuntime * rt,JSValue val)42926 static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val)
42927 {
42928     JSObject *p = JS_VALUE_GET_OBJ(val);
42929     JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
42930     if (it) {
42931         JS_FreeValueRT(rt, it->iterating_regexp);
42932         JS_FreeValueRT(rt, it->iterated_string);
42933         js_free_rt(rt, it);
42934     }
42935 }
42936 
js_regexp_string_iterator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)42937 static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
42938                                            JS_MarkFunc *mark_func)
42939 {
42940     JSObject *p = JS_VALUE_GET_OBJ(val);
42941     JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
42942     if (it) {
42943         JS_MarkValue(rt, it->iterating_regexp, mark_func);
42944         JS_MarkValue(rt, it->iterated_string, mark_func);
42945     }
42946 }
42947 
js_regexp_string_iterator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,BOOL * pdone,int magic)42948 static JSValue js_regexp_string_iterator_next(JSContext *ctx,
42949                                               JSValueConst this_val,
42950                                               int argc, JSValueConst *argv,
42951                                               BOOL *pdone, int magic)
42952 {
42953     JSRegExpStringIteratorData *it;
42954     JSValueConst R, S;
42955     JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED;
42956     JSString *sp;
42957 
42958     it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR);
42959     if (!it)
42960         goto exception;
42961     if (it->done) {
42962         *pdone = TRUE;
42963         return JS_UNDEFINED;
42964     }
42965     R = it->iterating_regexp;
42966     S = it->iterated_string;
42967     match = JS_RegExpExec(ctx, R, S);
42968     if (JS_IsException(match))
42969         goto exception;
42970     if (JS_IsNull(match)) {
42971         it->done = TRUE;
42972         *pdone = TRUE;
42973         return JS_UNDEFINED;
42974     } else if (it->global) {
42975         matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0));
42976         if (JS_IsException(matchStr))
42977             goto exception;
42978         if (JS_IsEmptyString(matchStr)) {
42979             int64_t thisIndex, nextIndex;
42980             if (JS_ToLengthFree(ctx, &thisIndex,
42981                                 JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0)
42982                 goto exception;
42983             sp = JS_VALUE_GET_STRING(S);
42984             nextIndex = string_advance_index(sp, thisIndex, it->unicode);
42985             if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex,
42986                                JS_NewInt64(ctx, nextIndex)) < 0)
42987                 goto exception;
42988         }
42989         JS_FreeValue(ctx, matchStr);
42990     } else {
42991         it->done = TRUE;
42992     }
42993     *pdone = FALSE;
42994     return match;
42995  exception:
42996     JS_FreeValue(ctx, match);
42997     JS_FreeValue(ctx, matchStr);
42998     *pdone = FALSE;
42999     return JS_EXCEPTION;
43000 }
43001 
js_regexp_Symbol_matchAll(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)43002 static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val,
43003                                          int argc, JSValueConst *argv)
43004 {
43005     // [Symbol.matchAll](str)
43006     JSValueConst R = this_val;
43007     JSValue S, C, flags, matcher, iter;
43008     JSValueConst args[2];
43009     JSString *strp;
43010     int64_t lastIndex;
43011     JSRegExpStringIteratorData *it;
43012 
43013     if (!JS_IsObject(R))
43014         return JS_ThrowTypeErrorNotAnObject(ctx);
43015 
43016     C = JS_UNDEFINED;
43017     flags = JS_UNDEFINED;
43018     matcher = JS_UNDEFINED;
43019     iter = JS_UNDEFINED;
43020 
43021     S = JS_ToString(ctx, argv[0]);
43022     if (JS_IsException(S))
43023         goto exception;
43024     C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor);
43025     if (JS_IsException(C))
43026         goto exception;
43027     flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags));
43028     if (JS_IsException(flags))
43029         goto exception;
43030     args[0] = R;
43031     args[1] = flags;
43032     matcher = JS_CallConstructor(ctx, C, 2, args);
43033     if (JS_IsException(matcher))
43034         goto exception;
43035     if (JS_ToLengthFree(ctx, &lastIndex,
43036                         JS_GetProperty(ctx, R, JS_ATOM_lastIndex)))
43037         goto exception;
43038     if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex,
43039                        JS_NewInt64(ctx, lastIndex)) < 0)
43040         goto exception;
43041 
43042     iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR);
43043     if (JS_IsException(iter))
43044         goto exception;
43045     it = js_malloc(ctx, sizeof(*it));
43046     if (!it)
43047         goto exception;
43048     it->iterating_regexp = matcher;
43049     it->iterated_string = S;
43050     strp = JS_VALUE_GET_STRING(flags);
43051     it->global = string_indexof_char(strp, 'g', 0) >= 0;
43052     it->unicode = string_indexof_char(strp, 'u', 0) >= 0;
43053     it->done = FALSE;
43054     JS_SetOpaque(iter, it);
43055 
43056     JS_FreeValue(ctx, C);
43057     JS_FreeValue(ctx, flags);
43058     return iter;
43059  exception:
43060     JS_FreeValue(ctx, S);
43061     JS_FreeValue(ctx, C);
43062     JS_FreeValue(ctx, flags);
43063     JS_FreeValue(ctx, matcher);
43064     JS_FreeValue(ctx, iter);
43065     return JS_EXCEPTION;
43066 }
43067 
43068 typedef struct ValueBuffer {
43069     JSContext *ctx;
43070     JSValue *arr;
43071     JSValue def[4];
43072     int len;
43073     int size;
43074     int error_status;
43075 } ValueBuffer;
43076 
value_buffer_init(JSContext * ctx,ValueBuffer * b)43077 static int value_buffer_init(JSContext *ctx, ValueBuffer *b)
43078 {
43079     b->ctx = ctx;
43080     b->len = 0;
43081     b->size = 4;
43082     b->error_status = 0;
43083     b->arr = b->def;
43084     return 0;
43085 }
43086 
value_buffer_free(ValueBuffer * b)43087 static void value_buffer_free(ValueBuffer *b)
43088 {
43089     while (b->len > 0)
43090         JS_FreeValue(b->ctx, b->arr[--b->len]);
43091     if (b->arr != b->def)
43092         js_free(b->ctx, b->arr);
43093     b->arr = b->def;
43094     b->size = 4;
43095 }
43096 
value_buffer_append(ValueBuffer * b,JSValue val)43097 static int value_buffer_append(ValueBuffer *b, JSValue val)
43098 {
43099     if (b->error_status)
43100         return -1;
43101 
43102     if (b->len >= b->size) {
43103         int new_size = (b->len + (b->len >> 1) + 31) & ~16;
43104         size_t slack;
43105         JSValue *new_arr;
43106 
43107         if (b->arr == b->def) {
43108             new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack);
43109             if (new_arr)
43110                 memcpy(new_arr, b->def, sizeof b->def);
43111         } else {
43112             new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack);
43113         }
43114         if (!new_arr) {
43115             value_buffer_free(b);
43116             JS_FreeValue(b->ctx, val);
43117             b->error_status = -1;
43118             return -1;
43119         }
43120         new_size += slack / sizeof(*new_arr);
43121         b->arr = new_arr;
43122         b->size = new_size;
43123     }
43124     b->arr[b->len++] = val;
43125     return 0;
43126 }
43127 
js_is_standard_regexp(JSContext * ctx,JSValueConst rx)43128 static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx)
43129 {
43130     JSValue val;
43131     int res;
43132 
43133     val = JS_GetProperty(ctx, rx, JS_ATOM_constructor);
43134     if (JS_IsException(val))
43135         return -1;
43136     // rx.constructor === RegExp
43137     res = js_same_value(ctx, val, ctx->regexp_ctor);
43138     JS_FreeValue(ctx, val);
43139     if (res) {
43140         val = JS_GetProperty(ctx, rx, JS_ATOM_exec);
43141         if (JS_IsException(val))
43142             return -1;
43143         // rx.exec === RE_exec
43144         res = JS_IsCFunction(ctx, val, js_regexp_exec, 0);
43145         JS_FreeValue(ctx, val);
43146     }
43147     return res;
43148 }
43149 
js_regexp_Symbol_replace(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)43150 static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val,
43151                                         int argc, JSValueConst *argv)
43152 {
43153     // [Symbol.replace](str, rep)
43154     JSValueConst rx = this_val, rep = argv[1];
43155     JSValueConst args[6];
43156     JSValue str, rep_val, matched, tab, rep_str, namedCaptures, res;
43157     JSString *sp, *rp;
43158     StringBuffer b_s, *b = &b_s;
43159     ValueBuffer v_b, *results = &v_b;
43160     int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode;
43161     uint32_t nCaptures;
43162     int64_t position;
43163 
43164     if (!JS_IsObject(rx))
43165         return JS_ThrowTypeErrorNotAnObject(ctx);
43166 
43167     string_buffer_init(ctx, b, 0);
43168     value_buffer_init(ctx, results);
43169 
43170     rep_val = JS_UNDEFINED;
43171     matched = JS_UNDEFINED;
43172     tab = JS_UNDEFINED;
43173     rep_str = JS_UNDEFINED;
43174     namedCaptures = JS_UNDEFINED;
43175 
43176     str = JS_ToString(ctx, argv[0]);
43177     if (JS_IsException(str))
43178         goto exception;
43179 
43180     sp = JS_VALUE_GET_STRING(str);
43181     rp = NULL;
43182     functionalReplace = JS_IsFunction(ctx, rep);
43183     if (!functionalReplace) {
43184         rep_val = JS_ToString(ctx, rep);
43185         if (JS_IsException(rep_val))
43186             goto exception;
43187         rp = JS_VALUE_GET_STRING(rep_val);
43188     }
43189     fullUnicode = 0;
43190     is_global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));
43191     if (is_global < 0)
43192         goto exception;
43193     if (is_global) {
43194         fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
43195         if (fullUnicode < 0)
43196             goto exception;
43197         if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
43198             goto exception;
43199     }
43200 
43201     if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) {
43202         /* use faster version for simple cases */
43203         res = JS_RegExpDelete(ctx, rx, str);
43204         goto done;
43205     }
43206     for(;;) {
43207         JSValue result;
43208         result = JS_RegExpExec(ctx, rx, str);
43209         if (JS_IsException(result))
43210             goto exception;
43211         if (JS_IsNull(result))
43212             break;
43213         if (value_buffer_append(results, result) < 0)
43214             goto exception;
43215         if (!is_global)
43216             break;
43217         JS_FreeValue(ctx, matched);
43218         matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
43219         if (JS_IsException(matched))
43220             goto exception;
43221         if (JS_IsEmptyString(matched)) {
43222             /* always advance of at least one char */
43223             int64_t thisIndex, nextIndex;
43224             if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
43225                 goto exception;
43226             nextIndex = string_advance_index(sp, thisIndex, fullUnicode);
43227             if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
43228                 goto exception;
43229         }
43230     }
43231     nextSourcePosition = 0;
43232     for(j = 0; j < results->len; j++) {
43233         JSValueConst result;
43234         result = results->arr[j];
43235         if (js_get_length32(ctx, &nCaptures, result) < 0)
43236             goto exception;
43237         JS_FreeValue(ctx, matched);
43238         matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
43239         if (JS_IsException(matched))
43240             goto exception;
43241         if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index)))
43242             goto exception;
43243         if (position > sp->len)
43244             position = sp->len;
43245         else if (position < 0)
43246             position = 0;
43247         /* ignore substition if going backward (can happen
43248            with custom regexp object) */
43249         JS_FreeValue(ctx, tab);
43250         tab = JS_NewArray(ctx);
43251         if (JS_IsException(tab))
43252             goto exception;
43253         if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched),
43254                                         JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43255             goto exception;
43256         for(n = 1; n < nCaptures; n++) {
43257             JSValue capN;
43258             capN = JS_GetPropertyInt64(ctx, result, n);
43259             if (JS_IsException(capN))
43260                 goto exception;
43261             if (!JS_IsUndefined(capN)) {
43262                 capN = JS_ToStringFree(ctx, capN);
43263                 if (JS_IsException(capN))
43264                     goto exception;
43265             }
43266             if (JS_DefinePropertyValueInt64(ctx, tab, n, capN,
43267                                             JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43268                 goto exception;
43269         }
43270         JS_FreeValue(ctx, namedCaptures);
43271         namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups);
43272         if (JS_IsException(namedCaptures))
43273             goto exception;
43274         if (functionalReplace) {
43275             if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43276                 goto exception;
43277             if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43278                 goto exception;
43279             if (!JS_IsUndefined(namedCaptures)) {
43280                 if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43281                     goto exception;
43282             }
43283             args[0] = JS_UNDEFINED;
43284             args[1] = tab;
43285             JS_FreeValue(ctx, rep_str);
43286             rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0));
43287         } else {
43288             JSValue namedCaptures1;
43289             if (!JS_IsUndefined(namedCaptures)) {
43290                 namedCaptures1 = JS_ToObject(ctx, namedCaptures);
43291                 if (JS_IsException(namedCaptures1))
43292                     goto exception;
43293             } else {
43294                 namedCaptures1 = JS_UNDEFINED;
43295             }
43296             args[0] = matched;
43297             args[1] = str;
43298             args[2] = JS_NewInt32(ctx, position);
43299             args[3] = tab;
43300             args[4] = namedCaptures1;
43301             args[5] = rep_val;
43302             JS_FreeValue(ctx, rep_str);
43303             rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
43304             JS_FreeValue(ctx, namedCaptures1);
43305         }
43306         if (JS_IsException(rep_str))
43307             goto exception;
43308         if (position >= nextSourcePosition) {
43309             string_buffer_concat(b, sp, nextSourcePosition, position);
43310             string_buffer_concat_value(b, rep_str);
43311             nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len;
43312         }
43313     }
43314     string_buffer_concat(b, sp, nextSourcePosition, sp->len);
43315     res = string_buffer_end(b);
43316     goto done1;
43317 
43318 exception:
43319     res = JS_EXCEPTION;
43320 done:
43321     string_buffer_free(b);
43322 done1:
43323     value_buffer_free(results);
43324     JS_FreeValue(ctx, rep_val);
43325     JS_FreeValue(ctx, matched);
43326     JS_FreeValue(ctx, tab);
43327     JS_FreeValue(ctx, rep_str);
43328     JS_FreeValue(ctx, namedCaptures);
43329     JS_FreeValue(ctx, str);
43330     return res;
43331 }
43332 
js_regexp_Symbol_search(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)43333 static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val,
43334                                        int argc, JSValueConst *argv)
43335 {
43336     JSValueConst rx = this_val;
43337     JSValue str, previousLastIndex, currentLastIndex, result, index;
43338 
43339     if (!JS_IsObject(rx))
43340         return JS_ThrowTypeErrorNotAnObject(ctx);
43341 
43342     result = JS_UNDEFINED;
43343     currentLastIndex = JS_UNDEFINED;
43344     previousLastIndex = JS_UNDEFINED;
43345     str = JS_ToString(ctx, argv[0]);
43346     if (JS_IsException(str))
43347         goto exception;
43348 
43349     previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
43350     if (JS_IsException(previousLastIndex))
43351         goto exception;
43352 
43353     if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) {
43354         if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) {
43355             goto exception;
43356         }
43357     }
43358     result = JS_RegExpExec(ctx, rx, str);
43359     if (JS_IsException(result))
43360         goto exception;
43361     currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
43362     if (JS_IsException(currentLastIndex))
43363         goto exception;
43364     if (js_same_value(ctx, currentLastIndex, previousLastIndex)) {
43365         JS_FreeValue(ctx, previousLastIndex);
43366     } else {
43367         if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) {
43368             previousLastIndex = JS_UNDEFINED;
43369             goto exception;
43370         }
43371     }
43372     JS_FreeValue(ctx, str);
43373     JS_FreeValue(ctx, currentLastIndex);
43374 
43375     if (JS_IsNull(result)) {
43376         return JS_NewInt32(ctx, -1);
43377     } else {
43378         index = JS_GetProperty(ctx, result, JS_ATOM_index);
43379         JS_FreeValue(ctx, result);
43380         return index;
43381     }
43382 
43383 exception:
43384     JS_FreeValue(ctx, result);
43385     JS_FreeValue(ctx, str);
43386     JS_FreeValue(ctx, currentLastIndex);
43387     JS_FreeValue(ctx, previousLastIndex);
43388     return JS_EXCEPTION;
43389 }
43390 
js_regexp_Symbol_split(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)43391 static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val,
43392                                        int argc, JSValueConst *argv)
43393 {
43394     // [Symbol.split](str, limit)
43395     JSValueConst rx = this_val;
43396     JSValueConst args[2];
43397     JSValue str, ctor, splitter, A, flags, z, sub;
43398     JSString *strp;
43399     uint32_t lim, size, p, q;
43400     int unicodeMatching;
43401     int64_t lengthA, e, numberOfCaptures, i;
43402 
43403     if (!JS_IsObject(rx))
43404         return JS_ThrowTypeErrorNotAnObject(ctx);
43405 
43406     ctor = JS_UNDEFINED;
43407     splitter = JS_UNDEFINED;
43408     A = JS_UNDEFINED;
43409     flags = JS_UNDEFINED;
43410     z = JS_UNDEFINED;
43411     str = JS_ToString(ctx, argv[0]);
43412     if (JS_IsException(str))
43413         goto exception;
43414     ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor);
43415     if (JS_IsException(ctor))
43416         goto exception;
43417     flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags));
43418     if (JS_IsException(flags))
43419         goto exception;
43420     strp = JS_VALUE_GET_STRING(flags);
43421     unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0;
43422     if (string_indexof_char(strp, 'y', 0) < 0) {
43423         flags = JS_ConcatString3(ctx, "", flags, "y");
43424         if (JS_IsException(flags))
43425             goto exception;
43426     }
43427     args[0] = rx;
43428     args[1] = flags;
43429     splitter = JS_CallConstructor(ctx, ctor, 2, args);
43430     if (JS_IsException(splitter))
43431         goto exception;
43432     A = JS_NewArray(ctx);
43433     if (JS_IsException(A))
43434         goto exception;
43435     lengthA = 0;
43436     if (JS_IsUndefined(argv[1])) {
43437         lim = 0xffffffff;
43438     } else {
43439         if (JS_ToUint32(ctx, &lim, argv[1]) < 0)
43440             goto exception;
43441         if (lim == 0)
43442             goto done;
43443     }
43444     strp = JS_VALUE_GET_STRING(str);
43445     p = q = 0;
43446     size = strp->len;
43447     if (size == 0) {
43448         z = JS_RegExpExec(ctx, splitter, str);
43449         if (JS_IsException(z))
43450             goto exception;
43451         if (JS_IsNull(z))
43452             goto add_tail;
43453         goto done;
43454     }
43455     while (q < size) {
43456         if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0)
43457             goto exception;
43458         JS_FreeValue(ctx, z);
43459         z = JS_RegExpExec(ctx, splitter, str);
43460         if (JS_IsException(z))
43461             goto exception;
43462         if (JS_IsNull(z)) {
43463             q = string_advance_index(strp, q, unicodeMatching);
43464         } else {
43465             if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex)))
43466                 goto exception;
43467             if (e > size)
43468                 e = size;
43469             if (e == p) {
43470                 q = string_advance_index(strp, q, unicodeMatching);
43471             } else {
43472                 sub = js_sub_string(ctx, strp, p, q);
43473                 if (JS_IsException(sub))
43474                     goto exception;
43475                 if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub,
43476                                                 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43477                     goto exception;
43478                 if (lengthA == lim)
43479                     goto done;
43480                 p = e;
43481                 if (js_get_length64(ctx, &numberOfCaptures, z))
43482                     goto exception;
43483                 for(i = 1; i < numberOfCaptures; i++) {
43484                     sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i));
43485                     if (JS_IsException(sub))
43486                         goto exception;
43487                     if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43488                         goto exception;
43489                     if (lengthA == lim)
43490                         goto done;
43491                 }
43492                 q = p;
43493             }
43494         }
43495     }
43496 add_tail:
43497     if (p > size)
43498         p = size;
43499     sub = js_sub_string(ctx, strp, p, size);
43500     if (JS_IsException(sub))
43501         goto exception;
43502     if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43503         goto exception;
43504     goto done;
43505 exception:
43506     JS_FreeValue(ctx, A);
43507     A = JS_EXCEPTION;
43508 done:
43509     JS_FreeValue(ctx, str);
43510     JS_FreeValue(ctx, ctor);
43511     JS_FreeValue(ctx, splitter);
43512     JS_FreeValue(ctx, flags);
43513     JS_FreeValue(ctx, z);
43514     return A;
43515 }
43516 
43517 static const JSCFunctionListEntry js_regexp_funcs[] = {
43518     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
43519     //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ),
43520     //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ),
43521 };
43522 
43523 static const JSCFunctionListEntry js_regexp_proto_funcs[] = {
43524     JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ),
43525     JS_CGETSET_DEF("source", js_regexp_get_source, NULL ),
43526     JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ),
43527     JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ),
43528     JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ),
43529     JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ),
43530     JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ),
43531     JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ),
43532     JS_CFUNC_DEF("exec", 1, js_regexp_exec ),
43533     JS_CFUNC_DEF("compile", 2, js_regexp_compile ),
43534     JS_CFUNC_DEF("test", 1, js_regexp_test ),
43535     JS_CFUNC_DEF("toString", 0, js_regexp_toString ),
43536     JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ),
43537     JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ),
43538     JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ),
43539     JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ),
43540     JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ),
43541     //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ),
43542     //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ),
43543 };
43544 
43545 static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = {
43546     JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ),
43547     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ),
43548 };
43549 
JS_AddIntrinsicRegExpCompiler(JSContext * ctx)43550 void JS_AddIntrinsicRegExpCompiler(JSContext *ctx)
43551 {
43552     ctx->compile_regexp = js_compile_regexp;
43553 }
43554 
JS_AddIntrinsicRegExp(JSContext * ctx)43555 void JS_AddIntrinsicRegExp(JSContext *ctx)
43556 {
43557     JSValueConst obj;
43558 
43559     JS_AddIntrinsicRegExpCompiler(ctx);
43560 
43561     ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx);
43562     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs,
43563                                countof(js_regexp_proto_funcs));
43564     obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2,
43565                                    ctx->class_proto[JS_CLASS_REGEXP]);
43566     ctx->regexp_ctor = JS_DupValue(ctx, obj);
43567     JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs));
43568 
43569     ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] =
43570         JS_NewObjectProto(ctx, ctx->iterator_proto);
43571     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR],
43572                                js_regexp_string_iterator_proto_funcs,
43573                                countof(js_regexp_string_iterator_proto_funcs));
43574 }
43575 
43576 /* JSON */
43577 
json_parse_expect(JSParseState * s,int tok)43578 static int json_parse_expect(JSParseState *s, int tok)
43579 {
43580     if (s->token.val != tok) {
43581         /* XXX: dump token correctly in all cases */
43582         return js_parse_error(s, "expecting '%c'", tok);
43583     }
43584     return json_next_token(s);
43585 }
43586 
json_parse_value(JSParseState * s)43587 static JSValue json_parse_value(JSParseState *s)
43588 {
43589     JSContext *ctx = s->ctx;
43590     JSValue val = JS_NULL;
43591     int ret;
43592 
43593     switch(s->token.val) {
43594     case '{':
43595         {
43596             JSValue prop_val;
43597             JSAtom prop_name;
43598 
43599             if (json_next_token(s))
43600                 goto fail;
43601             val = JS_NewObject(ctx);
43602             if (JS_IsException(val))
43603                 goto fail;
43604             if (s->token.val != '}') {
43605                 for(;;) {
43606                     if (s->token.val == TOK_STRING) {
43607                         prop_name = JS_ValueToAtom(ctx, s->token.u.str.str);
43608                         if (prop_name == JS_ATOM_NULL)
43609                             goto fail;
43610                     } else if (s->ext_json && s->token.val == TOK_IDENT) {
43611                         prop_name = JS_DupAtom(ctx, s->token.u.ident.atom);
43612                     } else {
43613                         js_parse_error(s, "expecting property name");
43614                         goto fail;
43615                     }
43616                     if (json_next_token(s))
43617                         goto fail1;
43618                     if (json_parse_expect(s, ':'))
43619                         goto fail1;
43620                     prop_val = json_parse_value(s);
43621                     if (JS_IsException(prop_val)) {
43622                     fail1:
43623                         JS_FreeAtom(ctx, prop_name);
43624                         goto fail;
43625                     }
43626                     ret = JS_DefinePropertyValue(ctx, val, prop_name,
43627                                                  prop_val, JS_PROP_C_W_E);
43628                     JS_FreeAtom(ctx, prop_name);
43629                     if (ret < 0)
43630                         goto fail;
43631 
43632                     if (s->token.val != ',')
43633                         break;
43634                     if (json_next_token(s))
43635                         goto fail;
43636                     if (s->ext_json && s->token.val == '}')
43637                         break;
43638                 }
43639             }
43640             if (json_parse_expect(s, '}'))
43641                 goto fail;
43642         }
43643         break;
43644     case '[':
43645         {
43646             JSValue el;
43647             uint32_t idx;
43648 
43649             if (json_next_token(s))
43650                 goto fail;
43651             val = JS_NewArray(ctx);
43652             if (JS_IsException(val))
43653                 goto fail;
43654             if (s->token.val != ']') {
43655                 idx = 0;
43656                 for(;;) {
43657                     el = json_parse_value(s);
43658                     if (JS_IsException(el))
43659                         goto fail;
43660                     ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E);
43661                     if (ret < 0)
43662                         goto fail;
43663                     if (s->token.val != ',')
43664                         break;
43665                     if (json_next_token(s))
43666                         goto fail;
43667                     idx++;
43668                     if (s->ext_json && s->token.val == ']')
43669                         break;
43670                 }
43671             }
43672             if (json_parse_expect(s, ']'))
43673                 goto fail;
43674         }
43675         break;
43676     case TOK_STRING:
43677         val = JS_DupValue(ctx, s->token.u.str.str);
43678         if (json_next_token(s))
43679             goto fail;
43680         break;
43681     case TOK_NUMBER:
43682         val = s->token.u.num.val;
43683         if (json_next_token(s))
43684             goto fail;
43685         break;
43686     case TOK_IDENT:
43687         if (s->token.u.ident.atom == JS_ATOM_false ||
43688             s->token.u.ident.atom == JS_ATOM_true) {
43689             val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true));
43690         } else if (s->token.u.ident.atom == JS_ATOM_null) {
43691             val = JS_NULL;
43692         } else {
43693             goto def_token;
43694         }
43695         if (json_next_token(s))
43696             goto fail;
43697         break;
43698     default:
43699     def_token:
43700         if (s->token.val == TOK_EOF) {
43701             js_parse_error(s, "unexpected end of input");
43702         } else {
43703             js_parse_error(s, "unexpected token: '%.*s'",
43704                            (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
43705         }
43706         goto fail;
43707     }
43708     return val;
43709  fail:
43710     JS_FreeValue(ctx, val);
43711     return JS_EXCEPTION;
43712 }
43713 
JS_ParseJSON2(JSContext * ctx,const char * buf,size_t buf_len,const char * filename,int flags)43714 JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
43715                       const char *filename, int flags)
43716 {
43717     JSParseState s1, *s = &s1;
43718     JSValue val = JS_UNDEFINED;
43719 
43720     js_parse_init(ctx, s, buf, buf_len, filename);
43721     s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);
43722     if (json_next_token(s))
43723         goto fail;
43724     val = json_parse_value(s);
43725     if (JS_IsException(val))
43726         goto fail;
43727     if (s->token.val != TOK_EOF) {
43728         if (js_parse_error(s, "unexpected data at the end"))
43729             goto fail;
43730     }
43731     return val;
43732  fail:
43733     JS_FreeValue(ctx, val);
43734     free_token(s, &s->token);
43735     return JS_EXCEPTION;
43736 }
43737 
JS_ParseJSON(JSContext * ctx,const char * buf,size_t buf_len,const char * filename)43738 JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
43739                      const char *filename)
43740 {
43741     return JS_ParseJSON2(ctx, buf, buf_len, filename, 0);
43742 }
43743 
internalize_json_property(JSContext * ctx,JSValueConst holder,JSAtom name,JSValueConst reviver)43744 static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
43745                                          JSAtom name, JSValueConst reviver)
43746 {
43747     JSValue val, new_el, name_val, res;
43748     JSValueConst args[2];
43749     int ret, is_array;
43750     uint32_t i, len = 0;
43751     JSAtom prop;
43752     JSPropertyEnum *atoms = NULL;
43753 
43754     if (js_check_stack_overflow(ctx->rt, 0)) {
43755         return JS_ThrowStackOverflow(ctx);
43756     }
43757 
43758     val = JS_GetProperty(ctx, holder, name);
43759     if (JS_IsException(val))
43760         return val;
43761     if (JS_IsObject(val)) {
43762         is_array = JS_IsArray(ctx, val);
43763         if (is_array < 0)
43764             goto fail;
43765         if (is_array) {
43766             if (js_get_length32(ctx, &len, val))
43767                 goto fail;
43768         } else {
43769             ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);
43770             if (ret < 0)
43771                 goto fail;
43772         }
43773         for(i = 0; i < len; i++) {
43774             if (is_array) {
43775                 prop = JS_NewAtomUInt32(ctx, i);
43776                 if (prop == JS_ATOM_NULL)
43777                     goto fail;
43778             } else {
43779                 prop = JS_DupAtom(ctx, atoms[i].atom);
43780             }
43781             new_el = internalize_json_property(ctx, val, prop, reviver);
43782             if (JS_IsException(new_el)) {
43783                 JS_FreeAtom(ctx, prop);
43784                 goto fail;
43785             }
43786             if (JS_IsUndefined(new_el)) {
43787                 ret = JS_DeleteProperty(ctx, val, prop, 0);
43788             } else {
43789                 ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E);
43790             }
43791             JS_FreeAtom(ctx, prop);
43792             if (ret < 0)
43793                 goto fail;
43794         }
43795     }
43796     js_free_prop_enum(ctx, atoms, len);
43797     atoms = NULL;
43798     name_val = JS_AtomToValue(ctx, name);
43799     if (JS_IsException(name_val))
43800         goto fail;
43801     args[0] = name_val;
43802     args[1] = val;
43803     res = JS_Call(ctx, reviver, holder, 2, args);
43804     JS_FreeValue(ctx, name_val);
43805     JS_FreeValue(ctx, val);
43806     return res;
43807  fail:
43808     js_free_prop_enum(ctx, atoms, len);
43809     JS_FreeValue(ctx, val);
43810     return JS_EXCEPTION;
43811 }
43812 
js_json_parse(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)43813 static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val,
43814                              int argc, JSValueConst *argv)
43815 {
43816     JSValue obj, root;
43817     JSValueConst reviver;
43818     const char *str;
43819     size_t len;
43820 
43821     str = JS_ToCStringLen(ctx, &len, argv[0]);
43822     if (!str)
43823         return JS_EXCEPTION;
43824     obj = JS_ParseJSON(ctx, str, len, "<input>");
43825     JS_FreeCString(ctx, str);
43826     if (JS_IsException(obj))
43827         return obj;
43828     if (argc > 1 && JS_IsFunction(ctx, argv[1])) {
43829         reviver = argv[1];
43830         root = JS_NewObject(ctx);
43831         if (JS_IsException(root)) {
43832             JS_FreeValue(ctx, obj);
43833             return JS_EXCEPTION;
43834         }
43835         if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj,
43836                                    JS_PROP_C_W_E) < 0) {
43837             JS_FreeValue(ctx, root);
43838             return JS_EXCEPTION;
43839         }
43840         obj = internalize_json_property(ctx, root, JS_ATOM_empty_string,
43841                                         reviver);
43842         JS_FreeValue(ctx, root);
43843     }
43844     return obj;
43845 }
43846 
43847 typedef struct JSONStringifyContext {
43848     JSValueConst replacer_func;
43849     JSValue stack;
43850     JSValue property_list;
43851     JSValue gap;
43852     JSValue empty;
43853     StringBuffer *b;
43854 } JSONStringifyContext;
43855 
JS_ToQuotedStringFree(JSContext * ctx,JSValue val)43856 static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) {
43857     JSValue r = JS_ToQuotedString(ctx, val);
43858     JS_FreeValue(ctx, val);
43859     return r;
43860 }
43861 
js_json_check(JSContext * ctx,JSONStringifyContext * jsc,JSValueConst holder,JSValue val,JSValueConst key)43862 static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
43863                              JSValueConst holder, JSValue val, JSValueConst key)
43864 {
43865     JSValue v;
43866     JSValueConst args[2];
43867 
43868     if (JS_IsObject(val)
43869 #ifdef CONFIG_BIGNUM
43870     ||  JS_IsBigInt(ctx, val)   /* XXX: probably useless */
43871 #endif
43872         ) {
43873             JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);
43874             if (JS_IsException(f))
43875                 goto exception;
43876             if (JS_IsFunction(ctx, f)) {
43877                 v = JS_CallFree(ctx, f, val, 1, &key);
43878                 JS_FreeValue(ctx, val);
43879                 val = v;
43880                 if (JS_IsException(val))
43881                     goto exception;
43882             } else {
43883                 JS_FreeValue(ctx, f);
43884             }
43885         }
43886 
43887     if (!JS_IsUndefined(jsc->replacer_func)) {
43888         args[0] = key;
43889         args[1] = val;
43890         v = JS_Call(ctx, jsc->replacer_func, holder, 2, args);
43891         JS_FreeValue(ctx, val);
43892         val = v;
43893         if (JS_IsException(val))
43894             goto exception;
43895     }
43896 
43897     switch (JS_VALUE_GET_NORM_TAG(val)) {
43898     case JS_TAG_OBJECT:
43899         if (JS_IsFunction(ctx, val))
43900             break;
43901     case JS_TAG_STRING:
43902     case JS_TAG_INT:
43903     case JS_TAG_FLOAT64:
43904 #ifdef CONFIG_BIGNUM
43905     case JS_TAG_BIG_FLOAT:
43906 #endif
43907     case JS_TAG_BOOL:
43908     case JS_TAG_NULL:
43909 #ifdef CONFIG_BIGNUM
43910     case JS_TAG_BIG_INT:
43911 #endif
43912     case JS_TAG_EXCEPTION:
43913         return val;
43914     default:
43915         break;
43916     }
43917     JS_FreeValue(ctx, val);
43918     return JS_UNDEFINED;
43919 
43920 exception:
43921     JS_FreeValue(ctx, val);
43922     return JS_EXCEPTION;
43923 }
43924 
js_json_to_str(JSContext * ctx,JSONStringifyContext * jsc,JSValueConst holder,JSValue val,JSValueConst indent)43925 static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
43926                           JSValueConst holder, JSValue val,
43927                           JSValueConst indent)
43928 {
43929     JSValue indent1, sep, sep1, tab, v, prop;
43930     JSObject *p;
43931     int64_t i, len;
43932     int cl, ret;
43933     BOOL has_content;
43934 
43935     indent1 = JS_UNDEFINED;
43936     sep = JS_UNDEFINED;
43937     sep1 = JS_UNDEFINED;
43938     tab = JS_UNDEFINED;
43939     prop = JS_UNDEFINED;
43940 
43941     switch (JS_VALUE_GET_NORM_TAG(val)) {
43942     case JS_TAG_OBJECT:
43943         p = JS_VALUE_GET_OBJ(val);
43944         cl = p->class_id;
43945         if (cl == JS_CLASS_STRING) {
43946             val = JS_ToStringFree(ctx, val);
43947             if (JS_IsException(val))
43948                 goto exception;
43949             val = JS_ToQuotedStringFree(ctx, val);
43950             if (JS_IsException(val))
43951                 goto exception;
43952             return string_buffer_concat_value_free(jsc->b, val);
43953         } else if (cl == JS_CLASS_NUMBER) {
43954             val = JS_ToNumberFree(ctx, val);
43955             if (JS_IsException(val))
43956                 goto exception;
43957             return string_buffer_concat_value_free(jsc->b, val);
43958         } else if (cl == JS_CLASS_BOOLEAN) {
43959             ret = string_buffer_concat_value(jsc->b, p->u.object_data);
43960             JS_FreeValue(ctx, val);
43961             return ret;
43962         }
43963 #ifdef CONFIG_BIGNUM
43964         else if (cl == JS_CLASS_BIG_FLOAT) {
43965             return string_buffer_concat_value_free(jsc->b, val);
43966         } else if (cl == JS_CLASS_BIG_INT) {
43967             JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");
43968             goto exception;
43969         }
43970 #endif
43971         v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);
43972         if (JS_IsException(v))
43973             goto exception;
43974         if (JS_ToBoolFree(ctx, v)) {
43975             JS_ThrowTypeError(ctx, "circular reference");
43976             goto exception;
43977         }
43978         indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap));
43979         if (JS_IsException(indent1))
43980             goto exception;
43981         if (!JS_IsEmptyString(jsc->gap)) {
43982             sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), "");
43983             if (JS_IsException(sep))
43984                 goto exception;
43985             sep1 = JS_NewString(ctx, " ");
43986             if (JS_IsException(sep1))
43987                 goto exception;
43988         } else {
43989             sep = JS_DupValue(ctx, jsc->empty);
43990             sep1 = JS_DupValue(ctx, jsc->empty);
43991         }
43992         v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0);
43993         if (check_exception_free(ctx, v))
43994             goto exception;
43995         ret = JS_IsArray(ctx, val);
43996         if (ret < 0)
43997             goto exception;
43998         if (ret) {
43999             if (js_get_length64(ctx, &len, val))
44000                 goto exception;
44001             string_buffer_putc8(jsc->b, '[');
44002             for(i = 0; i < len; i++) {
44003                 if (i > 0)
44004                     string_buffer_putc8(jsc->b, ',');
44005                 string_buffer_concat_value(jsc->b, sep);
44006                 v = JS_GetPropertyInt64(ctx, val, i);
44007                 if (JS_IsException(v))
44008                     goto exception;
44009                 /* XXX: could do this string conversion only when needed */
44010                 prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i));
44011                 if (JS_IsException(prop))
44012                     goto exception;
44013                 v = js_json_check(ctx, jsc, val, v, prop);
44014                 JS_FreeValue(ctx, prop);
44015                 prop = JS_UNDEFINED;
44016                 if (JS_IsException(v))
44017                     goto exception;
44018                 if (JS_IsUndefined(v))
44019                     v = JS_NULL;
44020                 if (js_json_to_str(ctx, jsc, val, v, indent1))
44021                     goto exception;
44022             }
44023             if (len > 0 && !JS_IsEmptyString(jsc->gap)) {
44024                 string_buffer_putc8(jsc->b, '\n');
44025                 string_buffer_concat_value(jsc->b, indent);
44026             }
44027             string_buffer_putc8(jsc->b, ']');
44028         } else {
44029             if (!JS_IsUndefined(jsc->property_list))
44030                 tab = JS_DupValue(ctx, jsc->property_list);
44031             else
44032                 tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY);
44033             if (JS_IsException(tab))
44034                 goto exception;
44035             if (js_get_length64(ctx, &len, tab))
44036                 goto exception;
44037             string_buffer_putc8(jsc->b, '{');
44038             has_content = FALSE;
44039             for(i = 0; i < len; i++) {
44040                 JS_FreeValue(ctx, prop);
44041                 prop = JS_GetPropertyInt64(ctx, tab, i);
44042                 if (JS_IsException(prop))
44043                     goto exception;
44044                 v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop));
44045                 if (JS_IsException(v))
44046                     goto exception;
44047                 v = js_json_check(ctx, jsc, val, v, prop);
44048                 if (JS_IsException(v))
44049                     goto exception;
44050                 if (!JS_IsUndefined(v)) {
44051                     if (has_content)
44052                         string_buffer_putc8(jsc->b, ',');
44053                     prop = JS_ToQuotedStringFree(ctx, prop);
44054                     if (JS_IsException(prop)) {
44055                         JS_FreeValue(ctx, v);
44056                         goto exception;
44057                     }
44058                     string_buffer_concat_value(jsc->b, sep);
44059                     string_buffer_concat_value(jsc->b, prop);
44060                     string_buffer_putc8(jsc->b, ':');
44061                     string_buffer_concat_value(jsc->b, sep1);
44062                     if (js_json_to_str(ctx, jsc, val, v, indent1))
44063                         goto exception;
44064                     has_content = TRUE;
44065                 }
44066             }
44067             if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) {
44068                 string_buffer_putc8(jsc->b, '\n');
44069                 string_buffer_concat_value(jsc->b, indent);
44070             }
44071             string_buffer_putc8(jsc->b, '}');
44072         }
44073         if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0)))
44074             goto exception;
44075         JS_FreeValue(ctx, val);
44076         JS_FreeValue(ctx, tab);
44077         JS_FreeValue(ctx, sep);
44078         JS_FreeValue(ctx, sep1);
44079         JS_FreeValue(ctx, indent1);
44080         JS_FreeValue(ctx, prop);
44081         return 0;
44082     case JS_TAG_STRING:
44083         val = JS_ToQuotedStringFree(ctx, val);
44084         if (JS_IsException(val))
44085             goto exception;
44086         goto concat_value;
44087     case JS_TAG_FLOAT64:
44088         if (!isfinite(JS_VALUE_GET_FLOAT64(val))) {
44089             val = JS_NULL;
44090         }
44091         goto concat_value;
44092     case JS_TAG_INT:
44093 #ifdef CONFIG_BIGNUM
44094     case JS_TAG_BIG_FLOAT:
44095 #endif
44096     case JS_TAG_BOOL:
44097     case JS_TAG_NULL:
44098     concat_value:
44099         return string_buffer_concat_value_free(jsc->b, val);
44100 #ifdef CONFIG_BIGNUM
44101     case JS_TAG_BIG_INT:
44102         JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");
44103         goto exception;
44104 #endif
44105     default:
44106         JS_FreeValue(ctx, val);
44107         return 0;
44108     }
44109 
44110 exception:
44111     JS_FreeValue(ctx, val);
44112     JS_FreeValue(ctx, tab);
44113     JS_FreeValue(ctx, sep);
44114     JS_FreeValue(ctx, sep1);
44115     JS_FreeValue(ctx, indent1);
44116     JS_FreeValue(ctx, prop);
44117     return -1;
44118 }
44119 
JS_JSONStringify(JSContext * ctx,JSValueConst obj,JSValueConst replacer,JSValueConst space0)44120 JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
44121                          JSValueConst replacer, JSValueConst space0)
44122 {
44123     StringBuffer b_s;
44124     JSONStringifyContext jsc_s, *jsc = &jsc_s;
44125     JSValue val, v, space, ret, wrapper;
44126     int res;
44127     int64_t i, j, n;
44128 
44129     jsc->replacer_func = JS_UNDEFINED;
44130     jsc->stack = JS_UNDEFINED;
44131     jsc->property_list = JS_UNDEFINED;
44132     jsc->gap = JS_UNDEFINED;
44133     jsc->b = &b_s;
44134     jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string);
44135     ret = JS_UNDEFINED;
44136     wrapper = JS_UNDEFINED;
44137 
44138     string_buffer_init(ctx, jsc->b, 0);
44139     jsc->stack = JS_NewArray(ctx);
44140     if (JS_IsException(jsc->stack))
44141         goto exception;
44142     if (JS_IsFunction(ctx, replacer)) {
44143         jsc->replacer_func = replacer;
44144     } else {
44145         res = JS_IsArray(ctx, replacer);
44146         if (res < 0)
44147             goto exception;
44148         if (res) {
44149             /* XXX: enumeration is not fully correct */
44150             jsc->property_list = JS_NewArray(ctx);
44151             if (JS_IsException(jsc->property_list))
44152                 goto exception;
44153             if (js_get_length64(ctx, &n, replacer))
44154                 goto exception;
44155             for (i = j = 0; i < n; i++) {
44156                 JSValue present;
44157                 v = JS_GetPropertyInt64(ctx, replacer, i);
44158                 if (JS_IsException(v))
44159                     goto exception;
44160                 if (JS_IsObject(v)) {
44161                     JSObject *p = JS_VALUE_GET_OBJ(v);
44162                     if (p->class_id == JS_CLASS_STRING ||
44163                         p->class_id == JS_CLASS_NUMBER) {
44164                         v = JS_ToStringFree(ctx, v);
44165                         if (JS_IsException(v))
44166                             goto exception;
44167                     } else {
44168                         JS_FreeValue(ctx, v);
44169                         continue;
44170                     }
44171                 } else if (JS_IsNumber(v)) {
44172                     v = JS_ToStringFree(ctx, v);
44173                     if (JS_IsException(v))
44174                         goto exception;
44175                 } else if (!JS_IsString(v)) {
44176                     JS_FreeValue(ctx, v);
44177                     continue;
44178                 }
44179                 present = js_array_includes(ctx, jsc->property_list,
44180                                             1, (JSValueConst *)&v);
44181                 if (JS_IsException(present)) {
44182                     JS_FreeValue(ctx, v);
44183                     goto exception;
44184                 }
44185                 if (!JS_ToBoolFree(ctx, present)) {
44186                     JS_SetPropertyInt64(ctx, jsc->property_list, j++, v);
44187                 } else {
44188                     JS_FreeValue(ctx, v);
44189                 }
44190             }
44191         }
44192     }
44193     space = JS_DupValue(ctx, space0);
44194     if (JS_IsObject(space)) {
44195         JSObject *p = JS_VALUE_GET_OBJ(space);
44196         if (p->class_id == JS_CLASS_NUMBER) {
44197             space = JS_ToNumberFree(ctx, space);
44198         } else if (p->class_id == JS_CLASS_STRING) {
44199             space = JS_ToStringFree(ctx, space);
44200         }
44201         if (JS_IsException(space)) {
44202             JS_FreeValue(ctx, space);
44203             goto exception;
44204         }
44205     }
44206     if (JS_IsNumber(space)) {
44207         int n;
44208         if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0))
44209             goto exception;
44210         jsc->gap = JS_NewStringLen(ctx, "          ", n);
44211     } else if (JS_IsString(space)) {
44212         JSString *p = JS_VALUE_GET_STRING(space);
44213         jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10));
44214     } else {
44215         jsc->gap = JS_DupValue(ctx, jsc->empty);
44216     }
44217     JS_FreeValue(ctx, space);
44218     if (JS_IsException(jsc->gap))
44219         goto exception;
44220     wrapper = JS_NewObject(ctx);
44221     if (JS_IsException(wrapper))
44222         goto exception;
44223     if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string,
44224                                JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0)
44225         goto exception;
44226     val = JS_DupValue(ctx, obj);
44227 
44228     val = js_json_check(ctx, jsc, wrapper, val, jsc->empty);
44229     if (JS_IsException(val))
44230         goto exception;
44231     if (JS_IsUndefined(val)) {
44232         ret = JS_UNDEFINED;
44233         goto done1;
44234     }
44235     if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty))
44236         goto exception;
44237 
44238     ret = string_buffer_end(jsc->b);
44239     goto done;
44240 
44241 exception:
44242     ret = JS_EXCEPTION;
44243 done1:
44244     string_buffer_free(jsc->b);
44245 done:
44246     JS_FreeValue(ctx, wrapper);
44247     JS_FreeValue(ctx, jsc->empty);
44248     JS_FreeValue(ctx, jsc->gap);
44249     JS_FreeValue(ctx, jsc->property_list);
44250     JS_FreeValue(ctx, jsc->stack);
44251     return ret;
44252 }
44253 
js_json_stringify(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44254 static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val,
44255                                  int argc, JSValueConst *argv)
44256 {
44257     // stringify(val, replacer, space)
44258     return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]);
44259 }
44260 
44261 static const JSCFunctionListEntry js_json_funcs[] = {
44262     JS_CFUNC_DEF("parse", 2, js_json_parse ),
44263     JS_CFUNC_DEF("stringify", 3, js_json_stringify ),
44264     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ),
44265 };
44266 
44267 static const JSCFunctionListEntry js_json_obj[] = {
44268     JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
44269 };
44270 
JS_AddIntrinsicJSON(JSContext * ctx)44271 void JS_AddIntrinsicJSON(JSContext *ctx)
44272 {
44273     /* add JSON as autoinit object */
44274     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj));
44275 }
44276 
44277 /* Reflect */
44278 
js_reflect_apply(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44279 static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val,
44280                                 int argc, JSValueConst *argv)
44281 {
44282     return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2);
44283 }
44284 
js_reflect_construct(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44285 static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val,
44286                                     int argc, JSValueConst *argv)
44287 {
44288     JSValueConst func, array_arg, new_target;
44289     JSValue *tab, ret;
44290     uint32_t len;
44291 
44292     func = argv[0];
44293     array_arg = argv[1];
44294     if (argc > 2) {
44295         new_target = argv[2];
44296         if (!JS_IsConstructor(ctx, new_target))
44297             return JS_ThrowTypeError(ctx, "not a constructor");
44298     } else {
44299         new_target = func;
44300     }
44301     tab = build_arg_list(ctx, &len, array_arg);
44302     if (!tab)
44303         return JS_EXCEPTION;
44304     ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab);
44305     free_arg_list(ctx, tab, len);
44306     return ret;
44307 }
44308 
js_reflect_deleteProperty(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44309 static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val,
44310                                          int argc, JSValueConst *argv)
44311 {
44312     JSValueConst obj;
44313     JSAtom atom;
44314     int ret;
44315 
44316     obj = argv[0];
44317     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
44318         return JS_ThrowTypeErrorNotAnObject(ctx);
44319     atom = JS_ValueToAtom(ctx, argv[1]);
44320     if (unlikely(atom == JS_ATOM_NULL))
44321         return JS_EXCEPTION;
44322     ret = JS_DeleteProperty(ctx, obj, atom, 0);
44323     JS_FreeAtom(ctx, atom);
44324     if (ret < 0)
44325         return JS_EXCEPTION;
44326     else
44327         return JS_NewBool(ctx, ret);
44328 }
44329 
js_reflect_get(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44330 static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val,
44331                               int argc, JSValueConst *argv)
44332 {
44333     JSValueConst obj, prop, receiver;
44334     JSAtom atom;
44335     JSValue ret;
44336 
44337     obj = argv[0];
44338     prop = argv[1];
44339     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
44340         return JS_ThrowTypeErrorNotAnObject(ctx);
44341     if (argc > 2)
44342         receiver = argv[2];
44343     else
44344         receiver = obj;
44345     atom = JS_ValueToAtom(ctx, prop);
44346     if (unlikely(atom == JS_ATOM_NULL))
44347         return JS_EXCEPTION;
44348     ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE);
44349     JS_FreeAtom(ctx, atom);
44350     return ret;
44351 }
44352 
js_reflect_has(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44353 static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val,
44354                               int argc, JSValueConst *argv)
44355 {
44356     JSValueConst obj, prop;
44357     JSAtom atom;
44358     int ret;
44359 
44360     obj = argv[0];
44361     prop = argv[1];
44362     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
44363         return JS_ThrowTypeErrorNotAnObject(ctx);
44364     atom = JS_ValueToAtom(ctx, prop);
44365     if (unlikely(atom == JS_ATOM_NULL))
44366         return JS_EXCEPTION;
44367     ret = JS_HasProperty(ctx, obj, atom);
44368     JS_FreeAtom(ctx, atom);
44369     if (ret < 0)
44370         return JS_EXCEPTION;
44371     else
44372         return JS_NewBool(ctx, ret);
44373 }
44374 
js_reflect_set(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44375 static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val,
44376                               int argc, JSValueConst *argv)
44377 {
44378     JSValueConst obj, prop, val, receiver;
44379     int ret;
44380     JSAtom atom;
44381 
44382     obj = argv[0];
44383     prop = argv[1];
44384     val = argv[2];
44385     if (argc > 3)
44386         receiver = argv[3];
44387     else
44388         receiver = obj;
44389     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
44390         return JS_ThrowTypeErrorNotAnObject(ctx);
44391     atom = JS_ValueToAtom(ctx, prop);
44392     if (unlikely(atom == JS_ATOM_NULL))
44393         return JS_EXCEPTION;
44394     ret = JS_SetPropertyGeneric(ctx, obj, atom,
44395                                 JS_DupValue(ctx, val), receiver, 0);
44396     JS_FreeAtom(ctx, atom);
44397     if (ret < 0)
44398         return JS_EXCEPTION;
44399     else
44400         return JS_NewBool(ctx, ret);
44401 }
44402 
js_reflect_setPrototypeOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44403 static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
44404                                          int argc, JSValueConst *argv)
44405 {
44406     int ret;
44407     ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE);
44408     if (ret < 0)
44409         return JS_EXCEPTION;
44410     else
44411         return JS_NewBool(ctx, ret);
44412 }
44413 
js_reflect_ownKeys(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44414 static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val,
44415                                   int argc, JSValueConst *argv)
44416 {
44417     if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
44418         return JS_ThrowTypeErrorNotAnObject(ctx);
44419     return JS_GetOwnPropertyNames2(ctx, argv[0],
44420                                    JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK,
44421                                    JS_ITERATOR_KIND_KEY);
44422 }
44423 
44424 static const JSCFunctionListEntry js_reflect_funcs[] = {
44425     JS_CFUNC_DEF("apply", 3, js_reflect_apply ),
44426     JS_CFUNC_DEF("construct", 2, js_reflect_construct ),
44427     JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ),
44428     JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ),
44429     JS_CFUNC_DEF("get", 2, js_reflect_get ),
44430     JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ),
44431     JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ),
44432     JS_CFUNC_DEF("has", 2, js_reflect_has ),
44433     JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ),
44434     JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ),
44435     JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ),
44436     JS_CFUNC_DEF("set", 3, js_reflect_set ),
44437     JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ),
44438     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ),
44439 };
44440 
44441 static const JSCFunctionListEntry js_reflect_obj[] = {
44442     JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
44443 };
44444 
44445 /* Proxy */
44446 
js_proxy_finalizer(JSRuntime * rt,JSValue val)44447 static void js_proxy_finalizer(JSRuntime *rt, JSValue val)
44448 {
44449     JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
44450     if (s) {
44451         JS_FreeValueRT(rt, s->target);
44452         JS_FreeValueRT(rt, s->handler);
44453         js_free_rt(rt, s);
44454     }
44455 }
44456 
js_proxy_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)44457 static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
44458                           JS_MarkFunc *mark_func)
44459 {
44460     JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
44461     if (s) {
44462         JS_MarkValue(rt, s->target, mark_func);
44463         JS_MarkValue(rt, s->handler, mark_func);
44464     }
44465 }
44466 
JS_ThrowTypeErrorRevokedProxy(JSContext * ctx)44467 static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx)
44468 {
44469     return JS_ThrowTypeError(ctx, "revoked proxy");
44470 }
44471 
get_proxy_method(JSContext * ctx,JSValue * pmethod,JSValueConst obj,JSAtom name)44472 static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod,
44473                                      JSValueConst obj, JSAtom name)
44474 {
44475     JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);
44476     JSValue method;
44477 
44478     /* safer to test recursion in all proxy methods */
44479     if (js_check_stack_overflow(ctx->rt, 0)) {
44480         JS_ThrowStackOverflow(ctx);
44481         return NULL;
44482     }
44483 
44484     /* 's' should never be NULL */
44485     if (s->is_revoked) {
44486         JS_ThrowTypeErrorRevokedProxy(ctx);
44487         return NULL;
44488     }
44489     method = JS_GetProperty(ctx, s->handler, name);
44490     if (JS_IsException(method))
44491         return NULL;
44492     if (JS_IsNull(method))
44493         method = JS_UNDEFINED;
44494     *pmethod = method;
44495     return s;
44496 }
44497 
js_proxy_getPrototypeOf(JSContext * ctx,JSValueConst obj)44498 static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)
44499 {
44500     JSProxyData *s;
44501     JSValue method, ret, proto1;
44502     int res;
44503 
44504     s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf);
44505     if (!s)
44506         return JS_EXCEPTION;
44507     if (JS_IsUndefined(method))
44508         return JS_GetPrototype(ctx, s->target);
44509     ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
44510     if (JS_IsException(ret))
44511         return ret;
44512     if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL &&
44513         JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
44514         goto fail;
44515     }
44516     res = JS_IsExtensible(ctx, s->target);
44517     if (res < 0) {
44518         JS_FreeValue(ctx, ret);
44519         return JS_EXCEPTION;
44520     }
44521     if (!res) {
44522         /* check invariant */
44523         proto1 = JS_GetPrototype(ctx, s->target);
44524         if (JS_IsException(proto1)) {
44525             JS_FreeValue(ctx, ret);
44526             return JS_EXCEPTION;
44527         }
44528         if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) {
44529             JS_FreeValue(ctx, proto1);
44530         fail:
44531             JS_FreeValue(ctx, ret);
44532             return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
44533         }
44534         JS_FreeValue(ctx, proto1);
44535     }
44536     return ret;
44537 }
44538 
js_proxy_setPrototypeOf(JSContext * ctx,JSValueConst obj,JSValueConst proto_val,BOOL throw_flag)44539 static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
44540                                    JSValueConst proto_val, BOOL throw_flag)
44541 {
44542     JSProxyData *s;
44543     JSValue method, ret, proto1;
44544     JSValueConst args[2];
44545     BOOL res;
44546     int res2;
44547 
44548     s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf);
44549     if (!s)
44550         return -1;
44551     if (JS_IsUndefined(method))
44552         return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag);
44553     args[0] = s->target;
44554     args[1] = proto_val;
44555     ret = JS_CallFree(ctx, method, s->handler, 2, args);
44556     if (JS_IsException(ret))
44557         return -1;
44558     res = JS_ToBoolFree(ctx, ret);
44559     if (!res) {
44560         if (throw_flag) {
44561             JS_ThrowTypeError(ctx, "proxy: bad prototype");
44562             return -1;
44563         } else {
44564             return FALSE;
44565         }
44566     }
44567     res2 = JS_IsExtensible(ctx, s->target);
44568     if (res2 < 0)
44569         return -1;
44570     if (!res2) {
44571         proto1 = JS_GetPrototype(ctx, s->target);
44572         if (JS_IsException(proto1))
44573             return -1;
44574         if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) {
44575             JS_FreeValue(ctx, proto1);
44576             JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
44577             return -1;
44578         }
44579         JS_FreeValue(ctx, proto1);
44580     }
44581     return TRUE;
44582 }
44583 
js_proxy_isExtensible(JSContext * ctx,JSValueConst obj)44584 static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj)
44585 {
44586     JSProxyData *s;
44587     JSValue method, ret;
44588     BOOL res;
44589     int res2;
44590 
44591     s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible);
44592     if (!s)
44593         return -1;
44594     if (JS_IsUndefined(method))
44595         return JS_IsExtensible(ctx, s->target);
44596     ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
44597     if (JS_IsException(ret))
44598         return -1;
44599     res = JS_ToBoolFree(ctx, ret);
44600     res2 = JS_IsExtensible(ctx, s->target);
44601     if (res2 < 0)
44602         return res2;
44603     if (res != res2) {
44604         JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible");
44605         return -1;
44606     }
44607     return res;
44608 }
44609 
js_proxy_preventExtensions(JSContext * ctx,JSValueConst obj)44610 static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj)
44611 {
44612     JSProxyData *s;
44613     JSValue method, ret;
44614     BOOL res;
44615     int res2;
44616 
44617     s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions);
44618     if (!s)
44619         return -1;
44620     if (JS_IsUndefined(method))
44621         return JS_PreventExtensions(ctx, s->target);
44622     ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
44623     if (JS_IsException(ret))
44624         return -1;
44625     res = JS_ToBoolFree(ctx, ret);
44626     if (res) {
44627         res2 = JS_IsExtensible(ctx, s->target);
44628         if (res2 < 0)
44629             return res2;
44630         if (res2) {
44631             JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions");
44632             return -1;
44633         }
44634     }
44635     return res;
44636 }
44637 
js_proxy_has(JSContext * ctx,JSValueConst obj,JSAtom atom)44638 static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
44639 {
44640     JSProxyData *s;
44641     JSValue method, ret1, atom_val;
44642     int ret, res;
44643     JSObject *p;
44644     JSValueConst args[2];
44645     BOOL res2;
44646 
44647     s = get_proxy_method(ctx, &method, obj, JS_ATOM_has);
44648     if (!s)
44649         return -1;
44650     if (JS_IsUndefined(method))
44651         return JS_HasProperty(ctx, s->target, atom);
44652     atom_val = JS_AtomToValue(ctx, atom);
44653     if (JS_IsException(atom_val)) {
44654         JS_FreeValue(ctx, method);
44655         return -1;
44656     }
44657     args[0] = s->target;
44658     args[1] = atom_val;
44659     ret1 = JS_CallFree(ctx, method, s->handler, 2, args);
44660     JS_FreeValue(ctx, atom_val);
44661     if (JS_IsException(ret1))
44662         return -1;
44663     ret = JS_ToBoolFree(ctx, ret1);
44664     if (!ret) {
44665         JSPropertyDescriptor desc;
44666         p = JS_VALUE_GET_OBJ(s->target);
44667         res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
44668         if (res < 0)
44669             return -1;
44670         if (res) {
44671             res2 = !(desc.flags & JS_PROP_CONFIGURABLE);
44672             js_free_desc(ctx, &desc);
44673             if (res2 || !p->extensible) {
44674                 JS_ThrowTypeError(ctx, "proxy: inconsistent has");
44675                 return -1;
44676             }
44677         }
44678     }
44679     return ret;
44680 }
44681 
js_proxy_get(JSContext * ctx,JSValueConst obj,JSAtom atom,JSValueConst receiver)44682 static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom,
44683                             JSValueConst receiver)
44684 {
44685     JSProxyData *s;
44686     JSValue method, ret, atom_val;
44687     int res;
44688     JSValueConst args[3];
44689     JSPropertyDescriptor desc;
44690 
44691     s = get_proxy_method(ctx, &method, obj, JS_ATOM_get);
44692     if (!s)
44693         return JS_EXCEPTION;
44694     /* Note: recursion is possible thru the prototype of s->target */
44695     if (JS_IsUndefined(method))
44696         return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE);
44697     atom_val = JS_AtomToValue(ctx, atom);
44698     if (JS_IsException(atom_val)) {
44699         JS_FreeValue(ctx, method);
44700         return JS_EXCEPTION;
44701     }
44702     args[0] = s->target;
44703     args[1] = atom_val;
44704     args[2] = receiver;
44705     ret = JS_CallFree(ctx, method, s->handler, 3, args);
44706     JS_FreeValue(ctx, atom_val);
44707     if (JS_IsException(ret))
44708         return JS_EXCEPTION;
44709     res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
44710     if (res < 0)
44711         return JS_EXCEPTION;
44712     if (res) {
44713         if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
44714             if (!js_same_value(ctx, desc.value, ret)) {
44715                 goto fail;
44716             }
44717         } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) {
44718             if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) {
44719             fail:
44720                 js_free_desc(ctx, &desc);
44721                 JS_FreeValue(ctx, ret);
44722                 return JS_ThrowTypeError(ctx, "proxy: inconsistent get");
44723             }
44724         }
44725         js_free_desc(ctx, &desc);
44726     }
44727     return ret;
44728 }
44729 
js_proxy_set(JSContext * ctx,JSValueConst obj,JSAtom atom,JSValueConst value,JSValueConst receiver,int flags)44730 static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom,
44731                         JSValueConst value, JSValueConst receiver, int flags)
44732 {
44733     JSProxyData *s;
44734     JSValue method, ret1, atom_val;
44735     int ret, res;
44736     JSValueConst args[4];
44737 
44738     s = get_proxy_method(ctx, &method, obj, JS_ATOM_set);
44739     if (!s)
44740         return -1;
44741     if (JS_IsUndefined(method)) {
44742         return JS_SetPropertyGeneric(ctx, s->target, atom,
44743                                      JS_DupValue(ctx, value), receiver,
44744                                      flags);
44745     }
44746     atom_val = JS_AtomToValue(ctx, atom);
44747     if (JS_IsException(atom_val)) {
44748         JS_FreeValue(ctx, method);
44749         return -1;
44750     }
44751     args[0] = s->target;
44752     args[1] = atom_val;
44753     args[2] = value;
44754     args[3] = receiver;
44755     ret1 = JS_CallFree(ctx, method, s->handler, 4, args);
44756     JS_FreeValue(ctx, atom_val);
44757     if (JS_IsException(ret1))
44758         return -1;
44759     ret = JS_ToBoolFree(ctx, ret1);
44760     if (ret) {
44761         JSPropertyDescriptor desc;
44762         res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
44763         if (res < 0)
44764             return -1;
44765         if (res) {
44766             if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
44767                 if (!js_same_value(ctx, desc.value, value)) {
44768                     goto fail;
44769                 }
44770             } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) {
44771                 fail:
44772                     js_free_desc(ctx, &desc);
44773                     JS_ThrowTypeError(ctx, "proxy: inconsistent set");
44774                     return -1;
44775             }
44776             js_free_desc(ctx, &desc);
44777         }
44778     } else {
44779         if ((flags & JS_PROP_THROW) ||
44780             ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
44781             JS_ThrowTypeError(ctx, "proxy: cannot set property");
44782             return -1;
44783         }
44784     }
44785     return ret;
44786 }
44787 
js_create_desc(JSContext * ctx,JSValueConst val,JSValueConst getter,JSValueConst setter,int flags)44788 static JSValue js_create_desc(JSContext *ctx, JSValueConst val,
44789                               JSValueConst getter, JSValueConst setter,
44790                               int flags)
44791 {
44792     JSValue ret;
44793     ret = JS_NewObject(ctx);
44794     if (JS_IsException(ret))
44795         return ret;
44796     if (flags & JS_PROP_HAS_GET) {
44797         JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter),
44798                                JS_PROP_C_W_E);
44799     }
44800     if (flags & JS_PROP_HAS_SET) {
44801         JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter),
44802                                JS_PROP_C_W_E);
44803     }
44804     if (flags & JS_PROP_HAS_VALUE) {
44805         JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val),
44806                                JS_PROP_C_W_E);
44807     }
44808     if (flags & JS_PROP_HAS_WRITABLE) {
44809         JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
44810                                JS_NewBool(ctx, (flags & JS_PROP_WRITABLE) != 0),
44811                                JS_PROP_C_W_E);
44812     }
44813     if (flags & JS_PROP_HAS_ENUMERABLE) {
44814         JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
44815                                JS_NewBool(ctx, (flags & JS_PROP_ENUMERABLE) != 0),
44816                                JS_PROP_C_W_E);
44817     }
44818     if (flags & JS_PROP_HAS_CONFIGURABLE) {
44819         JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
44820                                JS_NewBool(ctx, (flags & JS_PROP_CONFIGURABLE) != 0),
44821                                JS_PROP_C_W_E);
44822     }
44823     return ret;
44824 }
44825 
js_proxy_get_own_property(JSContext * ctx,JSPropertyDescriptor * pdesc,JSValueConst obj,JSAtom prop)44826 static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc,
44827                                      JSValueConst obj, JSAtom prop)
44828 {
44829     JSProxyData *s;
44830     JSValue method, trap_result_obj, prop_val;
44831     int res, target_desc_ret, ret;
44832     JSObject *p;
44833     JSValueConst args[2];
44834     JSPropertyDescriptor result_desc, target_desc;
44835 
44836     s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor);
44837     if (!s)
44838         return -1;
44839     p = JS_VALUE_GET_OBJ(s->target);
44840     if (JS_IsUndefined(method)) {
44841         return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop);
44842     }
44843     prop_val = JS_AtomToValue(ctx, prop);
44844     if (JS_IsException(prop_val)) {
44845         JS_FreeValue(ctx, method);
44846         return -1;
44847     }
44848     args[0] = s->target;
44849     args[1] = prop_val;
44850     trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args);
44851     JS_FreeValue(ctx, prop_val);
44852     if (JS_IsException(trap_result_obj))
44853         return -1;
44854     if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) {
44855         JS_FreeValue(ctx, trap_result_obj);
44856         goto fail;
44857     }
44858     target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop);
44859     if (target_desc_ret < 0) {
44860         JS_FreeValue(ctx, trap_result_obj);
44861         return -1;
44862     }
44863     if (target_desc_ret)
44864         js_free_desc(ctx, &target_desc);
44865     if (JS_IsUndefined(trap_result_obj)) {
44866         if (target_desc_ret) {
44867             if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible)
44868                 goto fail;
44869         }
44870         ret = FALSE;
44871     } else {
44872         int flags1, extensible_target;
44873         extensible_target = JS_IsExtensible(ctx, s->target);
44874         if (extensible_target < 0) {
44875             JS_FreeValue(ctx, trap_result_obj);
44876             return -1;
44877         }
44878         res = js_obj_to_desc(ctx, &result_desc, trap_result_obj);
44879         JS_FreeValue(ctx, trap_result_obj);
44880         if (res < 0)
44881             return -1;
44882 
44883         if (target_desc_ret) {
44884             /* convert result_desc.flags to defineProperty flags */
44885             flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE;
44886             if (result_desc.flags & JS_PROP_GETSET)
44887                 flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET;
44888             else
44889                 flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE;
44890             /* XXX: not complete check: need to compare value &
44891                getter/setter as in defineproperty */
44892             if (!check_define_prop_flags(target_desc.flags, flags1))
44893                 goto fail1;
44894         } else {
44895             if (!extensible_target)
44896                 goto fail1;
44897         }
44898         if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) {
44899             if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE))
44900                 goto fail1;
44901             if ((result_desc.flags &
44902                  (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 &&
44903                 target_desc_ret &&
44904                 (target_desc.flags & JS_PROP_WRITABLE) != 0) {
44905                 /* proxy-missing-checks */
44906             fail1:
44907                 js_free_desc(ctx, &result_desc);
44908             fail:
44909                 JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor");
44910                 return -1;
44911             }
44912         }
44913         ret = TRUE;
44914         if (pdesc) {
44915             *pdesc = result_desc;
44916         } else {
44917             js_free_desc(ctx, &result_desc);
44918         }
44919     }
44920     return ret;
44921 }
44922 
js_proxy_define_own_property(JSContext * ctx,JSValueConst obj,JSAtom prop,JSValueConst val,JSValueConst getter,JSValueConst setter,int flags)44923 static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj,
44924                                         JSAtom prop, JSValueConst val,
44925                                         JSValueConst getter, JSValueConst setter,
44926                                         int flags)
44927 {
44928     JSProxyData *s;
44929     JSValue method, ret1, prop_val, desc_val;
44930     int res, ret;
44931     JSObject *p;
44932     JSValueConst args[3];
44933     JSPropertyDescriptor desc;
44934     BOOL setting_not_configurable;
44935 
44936     s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty);
44937     if (!s)
44938         return -1;
44939     if (JS_IsUndefined(method)) {
44940         return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags);
44941     }
44942     prop_val = JS_AtomToValue(ctx, prop);
44943     if (JS_IsException(prop_val)) {
44944         JS_FreeValue(ctx, method);
44945         return -1;
44946     }
44947     desc_val = js_create_desc(ctx, val, getter, setter, flags);
44948     if (JS_IsException(desc_val)) {
44949         JS_FreeValue(ctx, prop_val);
44950         JS_FreeValue(ctx, method);
44951         return -1;
44952     }
44953     args[0] = s->target;
44954     args[1] = prop_val;
44955     args[2] = desc_val;
44956     ret1 = JS_CallFree(ctx, method, s->handler, 3, args);
44957     JS_FreeValue(ctx, prop_val);
44958     JS_FreeValue(ctx, desc_val);
44959     if (JS_IsException(ret1))
44960         return -1;
44961     ret = JS_ToBoolFree(ctx, ret1);
44962     if (!ret) {
44963         if (flags & JS_PROP_THROW) {
44964             JS_ThrowTypeError(ctx, "proxy: defineProperty exception");
44965             return -1;
44966         } else {
44967             return 0;
44968         }
44969     }
44970     p = JS_VALUE_GET_OBJ(s->target);
44971     res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
44972     if (res < 0)
44973         return -1;
44974     setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE |
44975                                           JS_PROP_CONFIGURABLE)) ==
44976                                 JS_PROP_HAS_CONFIGURABLE);
44977     if (!res) {
44978         if (!p->extensible || setting_not_configurable)
44979             goto fail;
44980     } else {
44981         if (!check_define_prop_flags(desc.flags, flags) ||
44982             ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) {
44983             goto fail1;
44984         }
44985         if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
44986             if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) ==
44987                 JS_PROP_GETSET) {
44988                 if ((flags & JS_PROP_HAS_GET) &&
44989                     !js_same_value(ctx, getter, desc.getter)) {
44990                     goto fail1;
44991                 }
44992                 if ((flags & JS_PROP_HAS_SET) &&
44993                     !js_same_value(ctx, setter, desc.setter)) {
44994                     goto fail1;
44995                 }
44996             }
44997         } else if (flags & JS_PROP_HAS_VALUE) {
44998             if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) ==
44999                 JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) {
45000                 /* missing-proxy-check feature */
45001                 goto fail1;
45002             } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&
45003                 !js_same_value(ctx, val, desc.value)) {
45004                 goto fail1;
45005             }
45006         }
45007         if (flags & JS_PROP_HAS_WRITABLE) {
45008             if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE |
45009                                JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) {
45010                 /* proxy-missing-checks */
45011             fail1:
45012                 js_free_desc(ctx, &desc);
45013             fail:
45014                 JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty");
45015                 return -1;
45016             }
45017         }
45018         js_free_desc(ctx, &desc);
45019     }
45020     return 1;
45021 }
45022 
js_proxy_delete_property(JSContext * ctx,JSValueConst obj,JSAtom atom)45023 static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj,
45024                                     JSAtom atom)
45025 {
45026     JSProxyData *s;
45027     JSValue method, ret, atom_val;
45028     int res, res2, is_extensible;
45029     JSValueConst args[2];
45030 
45031     s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty);
45032     if (!s)
45033         return -1;
45034     if (JS_IsUndefined(method)) {
45035         return JS_DeleteProperty(ctx, s->target, atom, 0);
45036     }
45037     atom_val = JS_AtomToValue(ctx, atom);;
45038     if (JS_IsException(atom_val)) {
45039         JS_FreeValue(ctx, method);
45040         return -1;
45041     }
45042     args[0] = s->target;
45043     args[1] = atom_val;
45044     ret = JS_CallFree(ctx, method, s->handler, 2, args);
45045     JS_FreeValue(ctx, atom_val);
45046     if (JS_IsException(ret))
45047         return -1;
45048     res = JS_ToBoolFree(ctx, ret);
45049     if (res) {
45050         JSPropertyDescriptor desc;
45051         res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
45052         if (res2 < 0)
45053             return -1;
45054         if (res2) {
45055             if (!(desc.flags & JS_PROP_CONFIGURABLE))
45056                 goto fail;
45057             is_extensible = JS_IsExtensible(ctx, s->target);
45058             if (is_extensible < 0)
45059                 goto fail1;
45060             if (!is_extensible) {
45061                 /* proxy-missing-checks */
45062             fail:
45063                 JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty");
45064             fail1:
45065                 js_free_desc(ctx, &desc);
45066                 return -1;
45067             }
45068             js_free_desc(ctx, &desc);
45069         }
45070     }
45071     return res;
45072 }
45073 
45074 /* return the index of the property or -1 if not found */
find_prop_key(const JSPropertyEnum * tab,int n,JSAtom atom)45075 static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom)
45076 {
45077     int i;
45078     for(i = 0; i < n; i++) {
45079         if (tab[i].atom == atom)
45080             return i;
45081     }
45082     return -1;
45083 }
45084 
js_proxy_get_own_property_names(JSContext * ctx,JSPropertyEnum ** ptab,uint32_t * plen,JSValueConst obj)45085 static int js_proxy_get_own_property_names(JSContext *ctx,
45086                                            JSPropertyEnum **ptab,
45087                                            uint32_t *plen,
45088                                            JSValueConst obj)
45089 {
45090     JSProxyData *s;
45091     JSValue method, prop_array, val;
45092     uint32_t len, i, len2;
45093     JSPropertyEnum *tab, *tab2;
45094     JSAtom atom;
45095     JSPropertyDescriptor desc;
45096     int res, is_extensible, idx;
45097 
45098     s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys);
45099     if (!s)
45100         return -1;
45101     if (JS_IsUndefined(method)) {
45102         return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
45103                                       JS_VALUE_GET_OBJ(s->target),
45104                                       JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK);
45105     }
45106     prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
45107     if (JS_IsException(prop_array))
45108         return -1;
45109     tab = NULL;
45110     len = 0;
45111     tab2 = NULL;
45112     len2 = 0;
45113     if (js_get_length32(ctx, &len, prop_array))
45114         goto fail;
45115     if (len > 0) {
45116         tab = js_mallocz(ctx, sizeof(tab[0]) * len);
45117         if (!tab)
45118             goto fail;
45119     }
45120     for(i = 0; i < len; i++) {
45121         val = JS_GetPropertyUint32(ctx, prop_array, i);
45122         if (JS_IsException(val))
45123             goto fail;
45124         if (!JS_IsString(val) && !JS_IsSymbol(val)) {
45125             JS_FreeValue(ctx, val);
45126             JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols");
45127             goto fail;
45128         }
45129         atom = JS_ValueToAtom(ctx, val);
45130         JS_FreeValue(ctx, val);
45131         if (atom == JS_ATOM_NULL)
45132             goto fail;
45133         tab[i].atom = atom;
45134         tab[i].is_enumerable = FALSE; /* XXX: redundant? */
45135     }
45136 
45137     /* check duplicate properties (XXX: inefficient, could store the
45138      * properties an a temporary object to use the hash) */
45139     for(i = 1; i < len; i++) {
45140         if (find_prop_key(tab, i, tab[i].atom) >= 0) {
45141             JS_ThrowTypeError(ctx, "proxy: duplicate property");
45142             goto fail;
45143         }
45144     }
45145 
45146     is_extensible = JS_IsExtensible(ctx, s->target);
45147     if (is_extensible < 0)
45148         goto fail;
45149 
45150     /* check if there are non configurable properties */
45151     if (s->is_revoked) {
45152         JS_ThrowTypeErrorRevokedProxy(ctx);
45153         goto fail;
45154     }
45155     if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target),
45156                                JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
45157         goto fail;
45158     for(i = 0; i < len2; i++) {
45159         if (s->is_revoked) {
45160             JS_ThrowTypeErrorRevokedProxy(ctx);
45161             goto fail;
45162         }
45163         res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target),
45164                                 tab2[i].atom);
45165         if (res < 0)
45166             goto fail;
45167         if (res) {  /* safety, property should be found */
45168             js_free_desc(ctx, &desc);
45169             if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) {
45170                 idx = find_prop_key(tab, len, tab2[i].atom);
45171                 if (idx < 0) {
45172                     JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys");
45173                     goto fail;
45174                 }
45175                 /* mark the property as found */
45176                 if (!is_extensible)
45177                     tab[idx].is_enumerable = TRUE;
45178             }
45179         }
45180     }
45181     if (!is_extensible) {
45182         /* check that all property in 'tab' were checked */
45183         for(i = 0; i < len; i++) {
45184             if (!tab[i].is_enumerable) {
45185                 JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy");
45186                 goto fail;
45187             }
45188         }
45189     }
45190 
45191     js_free_prop_enum(ctx, tab2, len2);
45192     JS_FreeValue(ctx, prop_array);
45193     *ptab = tab;
45194     *plen = len;
45195     return 0;
45196  fail:
45197     js_free_prop_enum(ctx, tab2, len2);
45198     js_free_prop_enum(ctx, tab, len);
45199     JS_FreeValue(ctx, prop_array);
45200     return -1;
45201 }
45202 
js_proxy_call_constructor(JSContext * ctx,JSValueConst func_obj,JSValueConst new_target,int argc,JSValueConst * argv)45203 static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj,
45204                                          JSValueConst new_target,
45205                                          int argc, JSValueConst *argv)
45206 {
45207     JSProxyData *s;
45208     JSValue method, arg_array, ret;
45209     JSValueConst args[3];
45210 
45211     s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct);
45212     if (!s)
45213         return JS_EXCEPTION;
45214     if (!JS_IsConstructor(ctx, s->target))
45215         return JS_ThrowTypeError(ctx, "not a constructor");
45216     if (JS_IsUndefined(method))
45217         return JS_CallConstructor2(ctx, s->target, new_target, argc, argv);
45218     arg_array = js_create_array(ctx, argc, argv);
45219     if (JS_IsException(arg_array)) {
45220         ret = JS_EXCEPTION;
45221         goto fail;
45222     }
45223     args[0] = s->target;
45224     args[1] = arg_array;
45225     args[2] = new_target;
45226     ret = JS_Call(ctx, method, s->handler, 3, args);
45227     if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
45228         JS_FreeValue(ctx, ret);
45229         ret = JS_ThrowTypeErrorNotAnObject(ctx);
45230     }
45231  fail:
45232     JS_FreeValue(ctx, method);
45233     JS_FreeValue(ctx, arg_array);
45234     return ret;
45235 }
45236 
js_proxy_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)45237 static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj,
45238                              JSValueConst this_obj,
45239                              int argc, JSValueConst *argv, int flags)
45240 {
45241     JSProxyData *s;
45242     JSValue method, arg_array, ret;
45243     JSValueConst args[3];
45244 
45245     if (flags & JS_CALL_FLAG_CONSTRUCTOR)
45246         return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv);
45247 
45248     s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply);
45249     if (!s)
45250         return JS_EXCEPTION;
45251     if (!s->is_func) {
45252         JS_FreeValue(ctx, method);
45253         return JS_ThrowTypeError(ctx, "not a function");
45254     }
45255     if (JS_IsUndefined(method))
45256         return JS_Call(ctx, s->target, this_obj, argc, argv);
45257     arg_array = js_create_array(ctx, argc, argv);
45258     if (JS_IsException(arg_array)) {
45259         ret = JS_EXCEPTION;
45260         goto fail;
45261     }
45262     args[0] = s->target;
45263     args[1] = this_obj;
45264     args[2] = arg_array;
45265     ret = JS_Call(ctx, method, s->handler, 3, args);
45266  fail:
45267     JS_FreeValue(ctx, method);
45268     JS_FreeValue(ctx, arg_array);
45269     return ret;
45270 }
45271 
js_proxy_isArray(JSContext * ctx,JSValueConst obj)45272 static int js_proxy_isArray(JSContext *ctx, JSValueConst obj)
45273 {
45274     JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);
45275     if (!s)
45276         return FALSE;
45277     if (s->is_revoked) {
45278         JS_ThrowTypeErrorRevokedProxy(ctx);
45279         return -1;
45280     }
45281     return JS_IsArray(ctx, s->target);
45282 }
45283 
45284 static const JSClassExoticMethods js_proxy_exotic_methods = {
45285     .get_own_property = js_proxy_get_own_property,
45286     .define_own_property = js_proxy_define_own_property,
45287     .delete_property = js_proxy_delete_property,
45288     .get_own_property_names = js_proxy_get_own_property_names,
45289     .has_property = js_proxy_has,
45290     .get_property = js_proxy_get,
45291     .set_property = js_proxy_set,
45292 };
45293 
js_proxy_constructor(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)45294 static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val,
45295                                     int argc, JSValueConst *argv)
45296 {
45297     JSValueConst target, handler;
45298     JSValue obj;
45299     JSProxyData *s;
45300 
45301     target = argv[0];
45302     handler = argv[1];
45303     if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT ||
45304         JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT)
45305         return JS_ThrowTypeErrorNotAnObject(ctx);
45306 
45307     obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY);
45308     if (JS_IsException(obj))
45309         return obj;
45310     s = js_malloc(ctx, sizeof(JSProxyData));
45311     if (!s) {
45312         JS_FreeValue(ctx, obj);
45313         return JS_EXCEPTION;
45314     }
45315     s->target = JS_DupValue(ctx, target);
45316     s->handler = JS_DupValue(ctx, handler);
45317     s->is_func = JS_IsFunction(ctx, target);
45318     s->is_revoked = FALSE;
45319     JS_SetOpaque(obj, s);
45320     JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target));
45321     return obj;
45322 }
45323 
js_proxy_revoke(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)45324 static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val,
45325                                int argc, JSValueConst *argv, int magic,
45326                                JSValue *func_data)
45327 {
45328     JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY);
45329     if (s) {
45330         /* We do not free the handler and target in case they are
45331            referenced as constants in the C call stack */
45332         s->is_revoked = TRUE;
45333         JS_FreeValue(ctx, func_data[0]);
45334         func_data[0] = JS_NULL;
45335     }
45336     return JS_UNDEFINED;
45337 }
45338 
js_proxy_revoke_constructor(JSContext * ctx,JSValueConst proxy_obj)45339 static JSValue js_proxy_revoke_constructor(JSContext *ctx,
45340                                            JSValueConst proxy_obj)
45341 {
45342     return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj);
45343 }
45344 
js_proxy_revocable(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)45345 static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val,
45346                                  int argc, JSValueConst *argv)
45347 {
45348     JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj;
45349 
45350     proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv);
45351     if (JS_IsException(proxy_obj))
45352         goto fail;
45353     revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj);
45354     if (JS_IsException(revoke_obj))
45355         goto fail;
45356     obj = JS_NewObject(ctx);
45357     if (JS_IsException(obj))
45358         goto fail;
45359     // XXX: exceptions?
45360     JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E);
45361     JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E);
45362     return obj;
45363  fail:
45364     JS_FreeValue(ctx, proxy_obj);
45365     JS_FreeValue(ctx, revoke_obj);
45366     return JS_EXCEPTION;
45367 }
45368 
45369 static const JSCFunctionListEntry js_proxy_funcs[] = {
45370     JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ),
45371 };
45372 
45373 static const JSClassShortDef js_proxy_class_def[] = {
45374     { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */
45375 };
45376 
JS_AddIntrinsicProxy(JSContext * ctx)45377 void JS_AddIntrinsicProxy(JSContext *ctx)
45378 {
45379     JSRuntime *rt = ctx->rt;
45380     JSValue obj1;
45381 
45382     if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) {
45383         init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY,
45384                          countof(js_proxy_class_def));
45385         rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods;
45386         rt->class_array[JS_CLASS_PROXY].call = js_proxy_call;
45387     }
45388 
45389     obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2,
45390                             JS_CFUNC_constructor, 0);
45391     JS_SetConstructorBit(ctx, obj1, TRUE);
45392     JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs,
45393                                countof(js_proxy_funcs));
45394     JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy",
45395                               obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
45396 }
45397 
45398 /* Symbol */
45399 
js_symbol_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)45400 static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target,
45401                                      int argc, JSValueConst *argv)
45402 {
45403     JSValue str;
45404     JSString *p;
45405 
45406     if (!JS_IsUndefined(new_target))
45407         return JS_ThrowTypeError(ctx, "not a constructor");
45408     if (argc == 0 || JS_IsUndefined(argv[0])) {
45409         p = NULL;
45410     } else {
45411         str = JS_ToString(ctx, argv[0]);
45412         if (JS_IsException(str))
45413             return JS_EXCEPTION;
45414         p = JS_VALUE_GET_STRING(str);
45415     }
45416     return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL);
45417 }
45418 
js_thisSymbolValue(JSContext * ctx,JSValueConst this_val)45419 static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val)
45420 {
45421     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL)
45422         return JS_DupValue(ctx, this_val);
45423 
45424     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
45425         JSObject *p = JS_VALUE_GET_OBJ(this_val);
45426         if (p->class_id == JS_CLASS_SYMBOL) {
45427             if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL)
45428                 return JS_DupValue(ctx, p->u.object_data);
45429         }
45430     }
45431     return JS_ThrowTypeError(ctx, "not a symbol");
45432 }
45433 
js_symbol_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)45434 static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val,
45435                                   int argc, JSValueConst *argv)
45436 {
45437     JSValue val, ret;
45438     val = js_thisSymbolValue(ctx, this_val);
45439     if (JS_IsException(val))
45440         return val;
45441     /* XXX: use JS_ToStringInternal() with a flags */
45442     ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val);
45443     JS_FreeValue(ctx, val);
45444     return ret;
45445 }
45446 
js_symbol_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)45447 static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val,
45448                                  int argc, JSValueConst *argv)
45449 {
45450     return js_thisSymbolValue(ctx, this_val);
45451 }
45452 
js_symbol_get_description(JSContext * ctx,JSValueConst this_val)45453 static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val)
45454 {
45455     JSValue val, ret;
45456     JSAtomStruct *p;
45457 
45458     val = js_thisSymbolValue(ctx, this_val);
45459     if (JS_IsException(val))
45460         return val;
45461     p = JS_VALUE_GET_PTR(val);
45462     if (p->len == 0 && p->is_wide_char != 0) {
45463         ret = JS_UNDEFINED;
45464     } else {
45465         ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p));
45466     }
45467     JS_FreeValue(ctx, val);
45468     return ret;
45469 }
45470 
45471 static const JSCFunctionListEntry js_symbol_proto_funcs[] = {
45472     JS_CFUNC_DEF("toString", 0, js_symbol_toString ),
45473     JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ),
45474     // XXX: should have writable: false
45475     JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ),
45476     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ),
45477     JS_CGETSET_DEF("description", js_symbol_get_description, NULL ),
45478 };
45479 
js_symbol_for(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)45480 static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val,
45481                              int argc, JSValueConst *argv)
45482 {
45483     JSValue str;
45484 
45485     str = JS_ToString(ctx, argv[0]);
45486     if (JS_IsException(str))
45487         return JS_EXCEPTION;
45488     return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL);
45489 }
45490 
js_symbol_keyFor(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)45491 static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val,
45492                                 int argc, JSValueConst *argv)
45493 {
45494     JSAtomStruct *p;
45495 
45496     if (!JS_IsSymbol(argv[0]))
45497         return JS_ThrowTypeError(ctx, "not a symbol");
45498     p = JS_VALUE_GET_PTR(argv[0]);
45499     if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL)
45500         return JS_UNDEFINED;
45501     return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
45502 }
45503 
45504 static const JSCFunctionListEntry js_symbol_funcs[] = {
45505     JS_CFUNC_DEF("for", 1, js_symbol_for ),
45506     JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ),
45507 };
45508 
45509 /* Set/Map/WeakSet/WeakMap */
45510 
45511 typedef struct JSMapRecord {
45512     int ref_count; /* used during enumeration to avoid freeing the record */
45513     BOOL empty; /* TRUE if the record is deleted */
45514     struct JSMapState *map;
45515     struct JSMapRecord *next_weak_ref;
45516     struct list_head link;
45517     struct list_head hash_link;
45518     JSValue key;
45519     JSValue value;
45520 } JSMapRecord;
45521 
45522 typedef struct JSMapState {
45523     BOOL is_weak; /* TRUE if WeakSet/WeakMap */
45524     struct list_head records; /* list of JSMapRecord.link */
45525     uint32_t record_count;
45526     struct list_head *hash_table;
45527     uint32_t hash_size; /* must be a power of two */
45528     uint32_t record_count_threshold; /* count at which a hash table
45529                                         resize is needed */
45530 } JSMapState;
45531 
45532 #define MAGIC_SET (1 << 0)
45533 #define MAGIC_WEAK (1 << 1)
45534 
js_map_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv,int magic)45535 static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
45536                                   int argc, JSValueConst *argv, int magic)
45537 {
45538     JSMapState *s;
45539     JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;
45540     JSValueConst arr;
45541     BOOL is_set, is_weak;
45542 
45543     is_set = magic & MAGIC_SET;
45544     is_weak = ((magic & MAGIC_WEAK) != 0);
45545     obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic);
45546     if (JS_IsException(obj))
45547         return JS_EXCEPTION;
45548     s = js_mallocz(ctx, sizeof(*s));
45549     if (!s)
45550         goto fail;
45551     init_list_head(&s->records);
45552     s->is_weak = is_weak;
45553     JS_SetOpaque(obj, s);
45554     s->hash_size = 1;
45555     s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size);
45556     if (!s->hash_table)
45557         goto fail;
45558     init_list_head(&s->hash_table[0]);
45559     s->record_count_threshold = 4;
45560 
45561     arr = JS_UNDEFINED;
45562     if (argc > 0)
45563         arr = argv[0];
45564     if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) {
45565         JSValue item, ret;
45566         BOOL done;
45567 
45568         adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set);
45569         if (JS_IsException(adder))
45570             goto fail;
45571         if (!JS_IsFunction(ctx, adder)) {
45572             JS_ThrowTypeError(ctx, "set/add is not a function");
45573             goto fail;
45574         }
45575 
45576         iter = JS_GetIterator(ctx, arr, FALSE);
45577         if (JS_IsException(iter))
45578             goto fail;
45579         next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
45580         if (JS_IsException(next_method))
45581             goto fail;
45582 
45583         for(;;) {
45584             item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
45585             if (JS_IsException(item))
45586                 goto fail;
45587             if (done) {
45588                 JS_FreeValue(ctx, item);
45589                 break;
45590             }
45591             if (is_set) {
45592                 ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item);
45593                 if (JS_IsException(ret)) {
45594                     JS_FreeValue(ctx, item);
45595                     goto fail;
45596                 }
45597             } else {
45598                 JSValue key, value;
45599                 JSValueConst args[2];
45600                 key = JS_UNDEFINED;
45601                 value = JS_UNDEFINED;
45602                 if (!JS_IsObject(item)) {
45603                     JS_ThrowTypeErrorNotAnObject(ctx);
45604                     goto fail1;
45605                 }
45606                 key = JS_GetPropertyUint32(ctx, item, 0);
45607                 if (JS_IsException(key))
45608                     goto fail1;
45609                 value = JS_GetPropertyUint32(ctx, item, 1);
45610                 if (JS_IsException(value))
45611                     goto fail1;
45612                 args[0] = key;
45613                 args[1] = value;
45614                 ret = JS_Call(ctx, adder, obj, 2, args);
45615                 if (JS_IsException(ret)) {
45616                 fail1:
45617                     JS_FreeValue(ctx, item);
45618                     JS_FreeValue(ctx, key);
45619                     JS_FreeValue(ctx, value);
45620                     goto fail;
45621                 }
45622                 JS_FreeValue(ctx, key);
45623                 JS_FreeValue(ctx, value);
45624             }
45625             JS_FreeValue(ctx, ret);
45626             JS_FreeValue(ctx, item);
45627         }
45628         JS_FreeValue(ctx, next_method);
45629         JS_FreeValue(ctx, iter);
45630         JS_FreeValue(ctx, adder);
45631     }
45632     return obj;
45633  fail:
45634     if (JS_IsObject(iter)) {
45635         /* close the iterator object, preserving pending exception */
45636         JS_IteratorClose(ctx, iter, TRUE);
45637     }
45638     JS_FreeValue(ctx, next_method);
45639     JS_FreeValue(ctx, iter);
45640     JS_FreeValue(ctx, adder);
45641     JS_FreeValue(ctx, obj);
45642     return JS_EXCEPTION;
45643 }
45644 
45645 /* XXX: could normalize strings to speed up comparison */
map_normalize_key(JSContext * ctx,JSValueConst key)45646 static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)
45647 {
45648     uint32_t tag = JS_VALUE_GET_TAG(key);
45649     /* convert -0.0 to +0.0 */
45650     if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) {
45651         key = JS_NewInt32(ctx, 0);
45652     }
45653     return key;
45654 }
45655 
45656 /* XXX: better hash ? */
map_hash_key(JSContext * ctx,JSValueConst key)45657 static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)
45658 {
45659     uint32_t tag = JS_VALUE_GET_NORM_TAG(key);
45660     uint32_t h;
45661     double d;
45662     JSFloat64Union u;
45663 
45664     switch(tag) {
45665     case JS_TAG_BOOL:
45666         h = JS_VALUE_GET_INT(key);
45667         break;
45668     case JS_TAG_STRING:
45669         h = hash_string(JS_VALUE_GET_STRING(key), 0);
45670         break;
45671     case JS_TAG_OBJECT:
45672     case JS_TAG_SYMBOL:
45673         h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163;
45674         break;
45675     case JS_TAG_INT:
45676         d = JS_VALUE_GET_INT(key) * 3163;
45677         goto hash_float64;
45678     case JS_TAG_FLOAT64:
45679         d = JS_VALUE_GET_FLOAT64(key);
45680         /* normalize the NaN */
45681         if (isnan(d))
45682             d = JS_FLOAT64_NAN;
45683     hash_float64:
45684         u.d = d;
45685         h = (u.u32[0] ^ u.u32[1]) * 3163;
45686         break;
45687     default:
45688         h = 0; /* XXX: bignum support */
45689         break;
45690     }
45691     h ^= tag;
45692     return h;
45693 }
45694 
map_find_record(JSContext * ctx,JSMapState * s,JSValueConst key)45695 static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
45696                                     JSValueConst key)
45697 {
45698     struct list_head *el;
45699     JSMapRecord *mr;
45700     uint32_t h;
45701     h = map_hash_key(ctx, key) & (s->hash_size - 1);
45702     list_for_each(el, &s->hash_table[h]) {
45703         mr = list_entry(el, JSMapRecord, hash_link);
45704         if (js_same_value_zero(ctx, mr->key, key))
45705             return mr;
45706     }
45707     return NULL;
45708 }
45709 
map_hash_resize(JSContext * ctx,JSMapState * s)45710 static void map_hash_resize(JSContext *ctx, JSMapState *s)
45711 {
45712     uint32_t new_hash_size, i, h;
45713     size_t slack;
45714     struct list_head *new_hash_table, *el;
45715     JSMapRecord *mr;
45716 
45717     /* XXX: no reporting of memory allocation failure */
45718     if (s->hash_size == 1)
45719         new_hash_size = 4;
45720     else
45721         new_hash_size = s->hash_size * 2;
45722     new_hash_table = js_realloc2(ctx, s->hash_table,
45723                                  sizeof(new_hash_table[0]) * new_hash_size, &slack);
45724     if (!new_hash_table)
45725         return;
45726     new_hash_size += slack / sizeof(*new_hash_table);
45727 
45728     for(i = 0; i < new_hash_size; i++)
45729         init_list_head(&new_hash_table[i]);
45730 
45731     list_for_each(el, &s->records) {
45732         mr = list_entry(el, JSMapRecord, link);
45733         if (!mr->empty) {
45734             h = map_hash_key(ctx, mr->key) & (new_hash_size - 1);
45735             list_add_tail(&mr->hash_link, &new_hash_table[h]);
45736         }
45737     }
45738     s->hash_table = new_hash_table;
45739     s->hash_size = new_hash_size;
45740     s->record_count_threshold = new_hash_size * 2;
45741 }
45742 
map_add_record(JSContext * ctx,JSMapState * s,JSValueConst key)45743 static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
45744                                    JSValueConst key)
45745 {
45746     uint32_t h;
45747     JSMapRecord *mr;
45748 
45749     mr = js_malloc(ctx, sizeof(*mr));
45750     if (!mr)
45751         return NULL;
45752     mr->ref_count = 1;
45753     mr->map = s;
45754     mr->empty = FALSE;
45755     if (s->is_weak) {
45756         JSObject *p = JS_VALUE_GET_OBJ(key);
45757         /* Add the weak reference */
45758         mr->next_weak_ref = p->first_weak_ref;
45759         p->first_weak_ref = mr;
45760     } else {
45761         JS_DupValue(ctx, key);
45762     }
45763     mr->key = JS_VALUE_CONST_CAST(key);
45764     h = map_hash_key(ctx, key) & (s->hash_size - 1);
45765     list_add_tail(&mr->hash_link, &s->hash_table[h]);
45766     list_add_tail(&mr->link, &s->records);
45767     s->record_count++;
45768     if (s->record_count >= s->record_count_threshold) {
45769         map_hash_resize(ctx, s);
45770     }
45771     return mr;
45772 }
45773 
45774 /* Remove the weak reference from the object weak
45775    reference list. we don't use a doubly linked list to
45776    save space, assuming a given object has few weak
45777        references to it */
delete_weak_ref(JSRuntime * rt,JSMapRecord * mr)45778 static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)
45779 {
45780     JSMapRecord **pmr, *mr1;
45781     JSObject *p;
45782 
45783     p = JS_VALUE_GET_OBJ(mr->key);
45784     pmr = &p->first_weak_ref;
45785     for(;;) {
45786         mr1 = *pmr;
45787         assert(mr1 != NULL);
45788         if (mr1 == mr)
45789             break;
45790         pmr = &mr1->next_weak_ref;
45791     }
45792     *pmr = mr1->next_weak_ref;
45793 }
45794 
map_delete_record(JSRuntime * rt,JSMapState * s,JSMapRecord * mr)45795 static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
45796 {
45797     if (mr->empty)
45798         return;
45799     list_del(&mr->hash_link);
45800     if (s->is_weak) {
45801         delete_weak_ref(rt, mr);
45802     } else {
45803         JS_FreeValueRT(rt, mr->key);
45804     }
45805     JS_FreeValueRT(rt, mr->value);
45806     if (--mr->ref_count == 0) {
45807         list_del(&mr->link);
45808         js_free_rt(rt, mr);
45809     } else {
45810         /* keep a zombie record for iterators */
45811         mr->empty = TRUE;
45812         mr->key = JS_UNDEFINED;
45813         mr->value = JS_UNDEFINED;
45814     }
45815     s->record_count--;
45816 }
45817 
map_decref_record(JSRuntime * rt,JSMapRecord * mr)45818 static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
45819 {
45820     if (--mr->ref_count == 0) {
45821         /* the record can be safely removed */
45822         assert(mr->empty);
45823         list_del(&mr->link);
45824         js_free_rt(rt, mr);
45825     }
45826 }
45827 
reset_weak_ref(JSRuntime * rt,JSObject * p)45828 static void reset_weak_ref(JSRuntime *rt, JSObject *p)
45829 {
45830     JSMapRecord *mr, *mr_next;
45831     JSMapState *s;
45832 
45833     /* first pass to remove the records from the WeakMap/WeakSet
45834        lists */
45835     for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) {
45836         s = mr->map;
45837         assert(s->is_weak);
45838         assert(!mr->empty); /* no iterator on WeakMap/WeakSet */
45839         list_del(&mr->hash_link);
45840         list_del(&mr->link);
45841     }
45842 
45843     /* second pass to free the values to avoid modifying the weak
45844        reference list while traversing it. */
45845     for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) {
45846         mr_next = mr->next_weak_ref;
45847         JS_FreeValueRT(rt, mr->value);
45848         js_free_rt(rt, mr);
45849     }
45850 
45851     p->first_weak_ref = NULL; /* fail safe */
45852 }
45853 
js_map_set(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)45854 static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,
45855                           int argc, JSValueConst *argv, int magic)
45856 {
45857     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45858     JSMapRecord *mr;
45859     JSValueConst key, value;
45860 
45861     if (!s)
45862         return JS_EXCEPTION;
45863     key = map_normalize_key(ctx, argv[0]);
45864     if (s->is_weak && !JS_IsObject(key))
45865         return JS_ThrowTypeErrorNotAnObject(ctx);
45866     if (magic & MAGIC_SET)
45867         value = JS_UNDEFINED;
45868     else
45869         value = argv[1];
45870     mr = map_find_record(ctx, s, key);
45871     if (mr) {
45872         JS_FreeValue(ctx, mr->value);
45873     } else {
45874         mr = map_add_record(ctx, s, key);
45875         if (!mr)
45876             return JS_EXCEPTION;
45877     }
45878     mr->value = JS_DupValue(ctx, value);
45879     return JS_DupValue(ctx, this_val);
45880 }
45881 
js_map_get(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)45882 static JSValue js_map_get(JSContext *ctx, JSValueConst this_val,
45883                           int argc, JSValueConst *argv, int magic)
45884 {
45885     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45886     JSMapRecord *mr;
45887     JSValueConst key;
45888 
45889     if (!s)
45890         return JS_EXCEPTION;
45891     key = map_normalize_key(ctx, argv[0]);
45892     mr = map_find_record(ctx, s, key);
45893     if (!mr)
45894         return JS_UNDEFINED;
45895     else
45896         return JS_DupValue(ctx, mr->value);
45897 }
45898 
js_map_has(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)45899 static JSValue js_map_has(JSContext *ctx, JSValueConst this_val,
45900                           int argc, JSValueConst *argv, int magic)
45901 {
45902     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45903     JSMapRecord *mr;
45904     JSValueConst key;
45905 
45906     if (!s)
45907         return JS_EXCEPTION;
45908     key = map_normalize_key(ctx, argv[0]);
45909     mr = map_find_record(ctx, s, key);
45910     return JS_NewBool(ctx, (mr != NULL));
45911 }
45912 
js_map_delete(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)45913 static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,
45914                              int argc, JSValueConst *argv, int magic)
45915 {
45916     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45917     JSMapRecord *mr;
45918     JSValueConst key;
45919 
45920     if (!s)
45921         return JS_EXCEPTION;
45922     key = map_normalize_key(ctx, argv[0]);
45923     mr = map_find_record(ctx, s, key);
45924     if (!mr)
45925         return JS_FALSE;
45926     map_delete_record(ctx->rt, s, mr);
45927     return JS_TRUE;
45928 }
45929 
js_map_clear(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)45930 static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,
45931                             int argc, JSValueConst *argv, int magic)
45932 {
45933     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45934     struct list_head *el, *el1;
45935     JSMapRecord *mr;
45936 
45937     if (!s)
45938         return JS_EXCEPTION;
45939     list_for_each_safe(el, el1, &s->records) {
45940         mr = list_entry(el, JSMapRecord, link);
45941         map_delete_record(ctx->rt, s, mr);
45942     }
45943     return JS_UNDEFINED;
45944 }
45945 
js_map_get_size(JSContext * ctx,JSValueConst this_val,int magic)45946 static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic)
45947 {
45948     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45949     if (!s)
45950         return JS_EXCEPTION;
45951     return JS_NewUint32(ctx, s->record_count);
45952 }
45953 
js_map_forEach(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)45954 static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,
45955                               int argc, JSValueConst *argv, int magic)
45956 {
45957     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45958     JSValueConst func, this_arg;
45959     JSValue ret, args[3];
45960     struct list_head *el;
45961     JSMapRecord *mr;
45962 
45963     if (!s)
45964         return JS_EXCEPTION;
45965     func = argv[0];
45966     if (argc > 1)
45967         this_arg = argv[1];
45968     else
45969         this_arg = JS_UNDEFINED;
45970     if (check_function(ctx, func))
45971         return JS_EXCEPTION;
45972     /* Note: the list can be modified while traversing it, but the
45973        current element is locked */
45974     el = s->records.next;
45975     while (el != &s->records) {
45976         mr = list_entry(el, JSMapRecord, link);
45977         if (!mr->empty) {
45978             mr->ref_count++;
45979             /* must duplicate in case the record is deleted */
45980             args[1] = JS_DupValue(ctx, mr->key);
45981             if (magic)
45982                 args[0] = args[1];
45983             else
45984                 args[0] = JS_DupValue(ctx, mr->value);
45985             args[2] = JS_VALUE_CONST_CAST(this_val);
45986             ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);
45987             JS_FreeValue(ctx, args[0]);
45988             if (!magic)
45989                 JS_FreeValue(ctx, args[1]);
45990             el = el->next;
45991             map_decref_record(ctx->rt, mr);
45992             if (JS_IsException(ret))
45993                 return ret;
45994             JS_FreeValue(ctx, ret);
45995         } else {
45996             el = el->next;
45997         }
45998     }
45999     return JS_UNDEFINED;
46000 }
46001 
js_map_finalizer(JSRuntime * rt,JSValue val)46002 static void js_map_finalizer(JSRuntime *rt, JSValue val)
46003 {
46004     JSObject *p;
46005     JSMapState *s;
46006     struct list_head *el, *el1;
46007     JSMapRecord *mr;
46008 
46009     p = JS_VALUE_GET_OBJ(val);
46010     s = p->u.map_state;
46011     if (s) {
46012         /* if the object is deleted we are sure that no iterator is
46013            using it */
46014         list_for_each_safe(el, el1, &s->records) {
46015             mr = list_entry(el, JSMapRecord, link);
46016             if (!mr->empty) {
46017                 if (s->is_weak)
46018                     delete_weak_ref(rt, mr);
46019                 else
46020                     JS_FreeValueRT(rt, mr->key);
46021                 JS_FreeValueRT(rt, mr->value);
46022             }
46023             js_free_rt(rt, mr);
46024         }
46025         js_free_rt(rt, s->hash_table);
46026         js_free_rt(rt, s);
46027     }
46028 }
46029 
js_map_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)46030 static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
46031 {
46032     JSObject *p = JS_VALUE_GET_OBJ(val);
46033     JSMapState *s;
46034     struct list_head *el;
46035     JSMapRecord *mr;
46036 
46037     s = p->u.map_state;
46038     if (s) {
46039         list_for_each(el, &s->records) {
46040             mr = list_entry(el, JSMapRecord, link);
46041             if (!s->is_weak)
46042                 JS_MarkValue(rt, mr->key, mark_func);
46043             JS_MarkValue(rt, mr->value, mark_func);
46044         }
46045     }
46046 }
46047 
46048 /* Map Iterator */
46049 
46050 typedef struct JSMapIteratorData {
46051     JSValue obj;
46052     JSIteratorKindEnum kind;
46053     JSMapRecord *cur_record;
46054 } JSMapIteratorData;
46055 
js_map_iterator_finalizer(JSRuntime * rt,JSValue val)46056 static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val)
46057 {
46058     JSObject *p;
46059     JSMapIteratorData *it;
46060 
46061     p = JS_VALUE_GET_OBJ(val);
46062     it = p->u.map_iterator_data;
46063     if (it) {
46064         /* During the GC sweep phase the Map finalizer may be
46065            called before the Map iterator finalizer */
46066         if (JS_IsLiveObject(rt, it->obj) && it->cur_record) {
46067             map_decref_record(rt, it->cur_record);
46068         }
46069         JS_FreeValueRT(rt, it->obj);
46070         js_free_rt(rt, it);
46071     }
46072 }
46073 
js_map_iterator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)46074 static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
46075                                  JS_MarkFunc *mark_func)
46076 {
46077     JSObject *p = JS_VALUE_GET_OBJ(val);
46078     JSMapIteratorData *it;
46079     it = p->u.map_iterator_data;
46080     if (it) {
46081         /* the record is already marked by the object */
46082         JS_MarkValue(rt, it->obj, mark_func);
46083     }
46084 }
46085 
js_create_map_iterator(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)46086 static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val,
46087                                       int argc, JSValueConst *argv, int magic)
46088 {
46089     JSIteratorKindEnum kind;
46090     JSMapState *s;
46091     JSMapIteratorData *it;
46092     JSValue enum_obj;
46093 
46094     kind = magic >> 2;
46095     magic &= 3;
46096     s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
46097     if (!s)
46098         return JS_EXCEPTION;
46099     enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic);
46100     if (JS_IsException(enum_obj))
46101         goto fail;
46102     it = js_malloc(ctx, sizeof(*it));
46103     if (!it) {
46104         JS_FreeValue(ctx, enum_obj);
46105         goto fail;
46106     }
46107     it->obj = JS_DupValue(ctx, this_val);
46108     it->kind = kind;
46109     it->cur_record = NULL;
46110     JS_SetOpaque(enum_obj, it);
46111     return enum_obj;
46112  fail:
46113     return JS_EXCEPTION;
46114 }
46115 
js_map_iterator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,BOOL * pdone,int magic)46116 static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,
46117                                     int argc, JSValueConst *argv,
46118                                     BOOL *pdone, int magic)
46119 {
46120     JSMapIteratorData *it;
46121     JSMapState *s;
46122     JSMapRecord *mr;
46123     struct list_head *el;
46124 
46125     it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic);
46126     if (!it) {
46127         *pdone = FALSE;
46128         return JS_EXCEPTION;
46129     }
46130     if (JS_IsUndefined(it->obj))
46131         goto done;
46132     s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic);
46133     assert(s != NULL);
46134     if (!it->cur_record) {
46135         el = s->records.next;
46136     } else {
46137         mr = it->cur_record;
46138         el = mr->link.next;
46139         map_decref_record(ctx->rt, mr); /* the record can be freed here */
46140     }
46141     for(;;) {
46142         if (el == &s->records) {
46143             /* no more record  */
46144             it->cur_record = NULL;
46145             JS_FreeValue(ctx, it->obj);
46146             it->obj = JS_UNDEFINED;
46147         done:
46148             /* end of enumeration */
46149             *pdone = TRUE;
46150             return JS_UNDEFINED;
46151         }
46152         mr = list_entry(el, JSMapRecord, link);
46153         if (!mr->empty)
46154             break;
46155         /* get the next record */
46156         el = mr->link.next;
46157     }
46158 
46159     /* lock the record so that it won't be freed */
46160     mr->ref_count++;
46161     it->cur_record = mr;
46162     *pdone = FALSE;
46163 
46164     if (it->kind == JS_ITERATOR_KIND_KEY) {
46165         return JS_DupValue(ctx, mr->key);
46166     } else {
46167         JSValueConst args[2];
46168         args[0] = mr->key;
46169         if (magic)
46170             args[1] = mr->key;
46171         else
46172             args[1] = mr->value;
46173         if (it->kind == JS_ITERATOR_KIND_VALUE) {
46174             return JS_DupValue(ctx, args[1]);
46175         } else {
46176             return js_create_array(ctx, 2, args);
46177         }
46178     }
46179 }
46180 
46181 static const JSCFunctionListEntry js_map_funcs[] = {
46182     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
46183 };
46184 
46185 static const JSCFunctionListEntry js_map_proto_funcs[] = {
46186     JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ),
46187     JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ),
46188     JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ),
46189     JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ),
46190     JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ),
46191     JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0),
46192     JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ),
46193     JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ),
46194     JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ),
46195     JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ),
46196     JS_ALIAS_DEF("[Symbol.iterator]", "entries" ),
46197     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ),
46198 };
46199 
46200 static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = {
46201     JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ),
46202     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ),
46203 };
46204 
46205 static const JSCFunctionListEntry js_set_proto_funcs[] = {
46206     JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ),
46207     JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ),
46208     JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ),
46209     JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ),
46210     JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ),
46211     JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ),
46212     JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ),
46213     JS_ALIAS_DEF("keys", "values" ),
46214     JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
46215     JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ),
46216     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ),
46217 };
46218 
46219 static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {
46220     JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ),
46221     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ),
46222 };
46223 
46224 static const JSCFunctionListEntry js_weak_map_proto_funcs[] = {
46225     JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ),
46226     JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ),
46227     JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ),
46228     JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ),
46229     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ),
46230 };
46231 
46232 static const JSCFunctionListEntry js_weak_set_proto_funcs[] = {
46233     JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ),
46234     JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ),
46235     JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ),
46236     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ),
46237 };
46238 
46239 static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {
46240     js_map_proto_funcs,
46241     js_set_proto_funcs,
46242     js_weak_map_proto_funcs,
46243     js_weak_set_proto_funcs,
46244     js_map_iterator_proto_funcs,
46245     js_set_iterator_proto_funcs,
46246 };
46247 
46248 static const uint8_t js_map_proto_funcs_count[6] = {
46249     countof(js_map_proto_funcs),
46250     countof(js_set_proto_funcs),
46251     countof(js_weak_map_proto_funcs),
46252     countof(js_weak_set_proto_funcs),
46253     countof(js_map_iterator_proto_funcs),
46254     countof(js_set_iterator_proto_funcs),
46255 };
46256 
JS_AddIntrinsicMapSet(JSContext * ctx)46257 void JS_AddIntrinsicMapSet(JSContext *ctx)
46258 {
46259     int i;
46260     JSValue obj1;
46261     char buf[ATOM_GET_STR_BUF_SIZE];
46262 
46263     for(i = 0; i < 4; i++) {
46264         const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf),
46265                                          JS_ATOM_Map + i);
46266         ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx);
46267         JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i],
46268                                    js_map_proto_funcs_ptr[i],
46269                                    js_map_proto_funcs_count[i]);
46270         obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0,
46271                                     JS_CFUNC_constructor_magic, i);
46272         if (i < 2) {
46273             JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs,
46274                                        countof(js_map_funcs));
46275         }
46276         JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]);
46277     }
46278 
46279     for(i = 0; i < 2; i++) {
46280         ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] =
46281             JS_NewObjectProto(ctx, ctx->iterator_proto);
46282         JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i],
46283                                    js_map_proto_funcs_ptr[i + 4],
46284                                    js_map_proto_funcs_count[i + 4]);
46285     }
46286 }
46287 
46288 /* Generator */
46289 static const JSCFunctionListEntry js_generator_function_proto_funcs[] = {
46290     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE),
46291 };
46292 
46293 static const JSCFunctionListEntry js_generator_proto_funcs[] = {
46294     JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ),
46295     JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ),
46296     JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ),
46297     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE),
46298 };
46299 
46300 /* Promise */
46301 
46302 typedef enum JSPromiseStateEnum {
46303     JS_PROMISE_PENDING,
46304     JS_PROMISE_FULFILLED,
46305     JS_PROMISE_REJECTED,
46306 } JSPromiseStateEnum;
46307 
46308 typedef struct JSPromiseData {
46309     JSPromiseStateEnum promise_state;
46310     /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */
46311     struct list_head promise_reactions[2];
46312     BOOL is_handled; /* Note: only useful to debug */
46313     JSValue promise_result;
46314 } JSPromiseData;
46315 
46316 typedef struct JSPromiseFunctionDataResolved {
46317     int ref_count;
46318     BOOL already_resolved;
46319 } JSPromiseFunctionDataResolved;
46320 
46321 typedef struct JSPromiseFunctionData {
46322     JSValue promise;
46323     JSPromiseFunctionDataResolved *presolved;
46324 } JSPromiseFunctionData;
46325 
46326 typedef struct JSPromiseReactionData {
46327     struct list_head link; /* not used in promise_reaction_job */
46328     JSValue resolving_funcs[2];
46329     JSValue handler;
46330 } JSPromiseReactionData;
46331 
46332 static int js_create_resolving_functions(JSContext *ctx, JSValue *args,
46333                                          JSValueConst promise);
46334 
promise_reaction_data_free(JSRuntime * rt,JSPromiseReactionData * rd)46335 static void promise_reaction_data_free(JSRuntime *rt,
46336                                        JSPromiseReactionData *rd)
46337 {
46338     JS_FreeValueRT(rt, rd->resolving_funcs[0]);
46339     JS_FreeValueRT(rt, rd->resolving_funcs[1]);
46340     JS_FreeValueRT(rt, rd->handler);
46341     js_free_rt(rt, rd);
46342 }
46343 
promise_reaction_job(JSContext * ctx,int argc,JSValueConst * argv)46344 static JSValue promise_reaction_job(JSContext *ctx, int argc,
46345                                     JSValueConst *argv)
46346 {
46347     JSValueConst handler, arg, func;
46348     JSValue res, res2;
46349     BOOL is_reject;
46350 
46351     assert(argc == 5);
46352     handler = argv[2];
46353     is_reject = JS_ToBool(ctx, argv[3]);
46354     arg = argv[4];
46355 #ifdef DUMP_PROMISE
46356     printf("promise_reaction_job: is_reject=%d\n", is_reject);
46357 #endif
46358 
46359     if (JS_IsUndefined(handler)) {
46360         if (is_reject) {
46361             res = JS_Throw(ctx, JS_DupValue(ctx, arg));
46362         } else {
46363             res = JS_DupValue(ctx, arg);
46364         }
46365     } else {
46366         res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg);
46367     }
46368     is_reject = JS_IsException(res);
46369     if (is_reject)
46370         res = JS_GetException(ctx);
46371     func = argv[is_reject];
46372     /* as an extension, we support undefined as value to avoid
46373        creating a dummy promise in the 'await' implementation of async
46374        functions */
46375     if (!JS_IsUndefined(func)) {
46376         res2 = JS_Call(ctx, func, JS_UNDEFINED,
46377                        1, (JSValueConst *)&res);
46378     } else {
46379         res2 = JS_UNDEFINED;
46380     }
46381     JS_FreeValue(ctx, res);
46382 
46383     return res2;
46384 }
46385 
JS_SetHostPromiseRejectionTracker(JSRuntime * rt,JSHostPromiseRejectionTracker * cb,void * opaque)46386 void JS_SetHostPromiseRejectionTracker(JSRuntime *rt,
46387                                        JSHostPromiseRejectionTracker *cb,
46388                                        void *opaque)
46389 {
46390     rt->host_promise_rejection_tracker = cb;
46391     rt->host_promise_rejection_tracker_opaque = opaque;
46392 }
46393 
fulfill_or_reject_promise(JSContext * ctx,JSValueConst promise,JSValueConst value,BOOL is_reject)46394 static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise,
46395                                       JSValueConst value, BOOL is_reject)
46396 {
46397     JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
46398     struct list_head *el, *el1;
46399     JSPromiseReactionData *rd;
46400     JSValueConst args[5];
46401 
46402     if (!s || s->promise_state != JS_PROMISE_PENDING)
46403         return; /* should never happen */
46404     set_value(ctx, &s->promise_result, JS_DupValue(ctx, value));
46405     s->promise_state = JS_PROMISE_FULFILLED + is_reject;
46406 #ifdef DUMP_PROMISE
46407     printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject);
46408 #endif
46409     if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
46410         JSRuntime *rt = ctx->rt;
46411         if (rt->host_promise_rejection_tracker) {
46412             rt->host_promise_rejection_tracker(ctx, promise, value, FALSE,
46413                                                rt->host_promise_rejection_tracker_opaque);
46414         }
46415     }
46416 
46417     list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) {
46418         rd = list_entry(el, JSPromiseReactionData, link);
46419         args[0] = rd->resolving_funcs[0];
46420         args[1] = rd->resolving_funcs[1];
46421         args[2] = rd->handler;
46422         args[3] = JS_NewBool(ctx, is_reject);
46423         args[4] = value;
46424         JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
46425         list_del(&rd->link);
46426         promise_reaction_data_free(ctx->rt, rd);
46427     }
46428 
46429     list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) {
46430         rd = list_entry(el, JSPromiseReactionData, link);
46431         list_del(&rd->link);
46432         promise_reaction_data_free(ctx->rt, rd);
46433     }
46434 }
46435 
reject_promise(JSContext * ctx,JSValueConst promise,JSValueConst value)46436 static void reject_promise(JSContext *ctx, JSValueConst promise,
46437                            JSValueConst value)
46438 {
46439     fulfill_or_reject_promise(ctx, promise, value, TRUE);
46440 }
46441 
js_promise_resolve_thenable_job(JSContext * ctx,int argc,JSValueConst * argv)46442 static JSValue js_promise_resolve_thenable_job(JSContext *ctx,
46443                                                int argc, JSValueConst *argv)
46444 {
46445     JSValueConst promise, thenable, then;
46446     JSValue args[2], res;
46447 
46448 #ifdef DUMP_PROMISE
46449     printf("js_promise_resolve_thenable_job\n");
46450 #endif
46451     assert(argc == 3);
46452     promise = argv[0];
46453     thenable = argv[1];
46454     then = argv[2];
46455     if (js_create_resolving_functions(ctx, args, promise) < 0)
46456         return JS_EXCEPTION;
46457     res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args);
46458     if (JS_IsException(res)) {
46459         JSValue error = JS_GetException(ctx);
46460         res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
46461         JS_FreeValue(ctx, error);
46462     }
46463     JS_FreeValue(ctx, args[0]);
46464     JS_FreeValue(ctx, args[1]);
46465     return res;
46466 }
46467 
js_promise_resolve_function_free_resolved(JSRuntime * rt,JSPromiseFunctionDataResolved * sr)46468 static void js_promise_resolve_function_free_resolved(JSRuntime *rt,
46469                                                       JSPromiseFunctionDataResolved *sr)
46470 {
46471     if (--sr->ref_count == 0) {
46472         js_free_rt(rt, sr);
46473     }
46474 }
46475 
js_create_resolving_functions(JSContext * ctx,JSValue * resolving_funcs,JSValueConst promise)46476 static int js_create_resolving_functions(JSContext *ctx,
46477                                          JSValue *resolving_funcs,
46478                                          JSValueConst promise)
46479 
46480 {
46481     JSValue obj;
46482     JSPromiseFunctionData *s;
46483     JSPromiseFunctionDataResolved *sr;
46484     int i, ret;
46485 
46486     sr = js_malloc(ctx, sizeof(*sr));
46487     if (!sr)
46488         return -1;
46489     sr->ref_count = 1;
46490     sr->already_resolved = FALSE; /* must be shared between the two functions */
46491     ret = 0;
46492     for(i = 0; i < 2; i++) {
46493         obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
46494                                      JS_CLASS_PROMISE_RESOLVE_FUNCTION + i);
46495         if (JS_IsException(obj))
46496             goto fail;
46497         s = js_malloc(ctx, sizeof(*s));
46498         if (!s) {
46499             JS_FreeValue(ctx, obj);
46500         fail:
46501 
46502             if (i != 0)
46503                 JS_FreeValue(ctx, resolving_funcs[0]);
46504             ret = -1;
46505             break;
46506         }
46507         sr->ref_count++;
46508         s->presolved = sr;
46509         s->promise = JS_DupValue(ctx, promise);
46510         JS_SetOpaque(obj, s);
46511         js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1);
46512         resolving_funcs[i] = obj;
46513     }
46514     js_promise_resolve_function_free_resolved(ctx->rt, sr);
46515     return ret;
46516 }
46517 
js_promise_resolve_function_finalizer(JSRuntime * rt,JSValue val)46518 static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val)
46519 {
46520     JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
46521     if (s) {
46522         js_promise_resolve_function_free_resolved(rt, s->presolved);
46523         JS_FreeValueRT(rt, s->promise);
46524         js_free_rt(rt, s);
46525     }
46526 }
46527 
js_promise_resolve_function_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)46528 static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
46529                                              JS_MarkFunc *mark_func)
46530 {
46531     JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
46532     if (s) {
46533         JS_MarkValue(rt, s->promise, mark_func);
46534     }
46535 }
46536 
js_promise_resolve_function_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_val,int argc,JSValueConst * argv,int flags)46537 static JSValue js_promise_resolve_function_call(JSContext *ctx,
46538                                                 JSValueConst func_obj,
46539                                                 JSValueConst this_val,
46540                                                 int argc, JSValueConst *argv,
46541                                                 int flags)
46542 {
46543     JSObject *p = JS_VALUE_GET_OBJ(func_obj);
46544     JSPromiseFunctionData *s;
46545     JSValueConst resolution, args[3];
46546     JSValue then;
46547     BOOL is_reject;
46548 
46549     s = p->u.promise_function_data;
46550     if (!s || s->presolved->already_resolved)
46551         return JS_UNDEFINED;
46552     s->presolved->already_resolved = TRUE;
46553     is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION;
46554     if (argc > 0)
46555         resolution = argv[0];
46556     else
46557         resolution = JS_UNDEFINED;
46558 #ifdef DUMP_PROMISE
46559     printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject);
46560     JS_DumpValue(ctx, resolution);
46561     printf("\n");
46562 #endif
46563     if (is_reject || !JS_IsObject(resolution)) {
46564         goto done;
46565     } else if (js_same_value(ctx, resolution, s->promise)) {
46566         JS_ThrowTypeError(ctx, "promise self resolution");
46567         goto fail_reject;
46568     }
46569     then = JS_GetProperty(ctx, resolution, JS_ATOM_then);
46570     if (JS_IsException(then)) {
46571         JSValue error;
46572     fail_reject:
46573         error = JS_GetException(ctx);
46574         reject_promise(ctx, s->promise, error);
46575         JS_FreeValue(ctx, error);
46576     } else if (!JS_IsFunction(ctx, then)) {
46577         JS_FreeValue(ctx, then);
46578     done:
46579         fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject);
46580     } else {
46581         args[0] = s->promise;
46582         args[1] = resolution;
46583         args[2] = then;
46584         JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args);
46585         JS_FreeValue(ctx, then);
46586     }
46587     return JS_UNDEFINED;
46588 }
46589 
js_promise_finalizer(JSRuntime * rt,JSValue val)46590 static void js_promise_finalizer(JSRuntime *rt, JSValue val)
46591 {
46592     JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
46593     struct list_head *el, *el1;
46594     int i;
46595 
46596     if (!s)
46597         return;
46598     for(i = 0; i < 2; i++) {
46599         list_for_each_safe(el, el1, &s->promise_reactions[i]) {
46600             JSPromiseReactionData *rd =
46601                 list_entry(el, JSPromiseReactionData, link);
46602             promise_reaction_data_free(rt, rd);
46603         }
46604     }
46605     JS_FreeValueRT(rt, s->promise_result);
46606     js_free_rt(rt, s);
46607 }
46608 
js_promise_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)46609 static void js_promise_mark(JSRuntime *rt, JSValueConst val,
46610                             JS_MarkFunc *mark_func)
46611 {
46612     JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
46613     struct list_head *el;
46614     int i;
46615 
46616     if (!s)
46617         return;
46618     for(i = 0; i < 2; i++) {
46619         list_for_each(el, &s->promise_reactions[i]) {
46620             JSPromiseReactionData *rd =
46621                 list_entry(el, JSPromiseReactionData, link);
46622             JS_MarkValue(rt, rd->resolving_funcs[0], mark_func);
46623             JS_MarkValue(rt, rd->resolving_funcs[1], mark_func);
46624             JS_MarkValue(rt, rd->handler, mark_func);
46625         }
46626     }
46627     JS_MarkValue(rt, s->promise_result, mark_func);
46628 }
46629 
js_promise_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)46630 static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target,
46631                                       int argc, JSValueConst *argv)
46632 {
46633     JSValueConst executor;
46634     JSValue obj;
46635     JSPromiseData *s;
46636     JSValue args[2], ret;
46637     int i;
46638 
46639     executor = argv[0];
46640     if (check_function(ctx, executor))
46641         return JS_EXCEPTION;
46642     obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE);
46643     if (JS_IsException(obj))
46644         return JS_EXCEPTION;
46645     s = js_mallocz(ctx, sizeof(*s));
46646     if (!s)
46647         goto fail;
46648     s->promise_state = JS_PROMISE_PENDING;
46649     s->is_handled = FALSE;
46650     for(i = 0; i < 2; i++)
46651         init_list_head(&s->promise_reactions[i]);
46652     s->promise_result = JS_UNDEFINED;
46653     JS_SetOpaque(obj, s);
46654     if (js_create_resolving_functions(ctx, args, obj))
46655         goto fail;
46656     ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args);
46657     if (JS_IsException(ret)) {
46658         JSValue ret2, error;
46659         error = JS_GetException(ctx);
46660         ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
46661         JS_FreeValue(ctx, error);
46662         if (JS_IsException(ret2))
46663             goto fail1;
46664         JS_FreeValue(ctx, ret2);
46665     }
46666     JS_FreeValue(ctx, ret);
46667     JS_FreeValue(ctx, args[0]);
46668     JS_FreeValue(ctx, args[1]);
46669     return obj;
46670  fail1:
46671     JS_FreeValue(ctx, args[0]);
46672     JS_FreeValue(ctx, args[1]);
46673  fail:
46674     JS_FreeValue(ctx, obj);
46675     return JS_EXCEPTION;
46676 }
46677 
js_promise_executor(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)46678 static JSValue js_promise_executor(JSContext *ctx,
46679                                    JSValueConst this_val,
46680                                    int argc, JSValueConst *argv,
46681                                    int magic, JSValue *func_data)
46682 {
46683     int i;
46684 
46685     for(i = 0; i < 2; i++) {
46686         if (!JS_IsUndefined(func_data[i]))
46687             return JS_ThrowTypeError(ctx, "resolving function already set");
46688         func_data[i] = JS_DupValue(ctx, argv[i]);
46689     }
46690     return JS_UNDEFINED;
46691 }
46692 
js_promise_executor_new(JSContext * ctx)46693 static JSValue js_promise_executor_new(JSContext *ctx)
46694 {
46695     JSValueConst func_data[2];
46696 
46697     func_data[0] = JS_UNDEFINED;
46698     func_data[1] = JS_UNDEFINED;
46699     return JS_NewCFunctionData(ctx, js_promise_executor, 2,
46700                                0, 2, func_data);
46701 }
46702 
js_new_promise_capability(JSContext * ctx,JSValue * resolving_funcs,JSValueConst ctor)46703 static JSValue js_new_promise_capability(JSContext *ctx,
46704                                          JSValue *resolving_funcs,
46705                                          JSValueConst ctor)
46706 {
46707     JSValue executor, result_promise;
46708     JSCFunctionDataRecord *s;
46709     int i;
46710 
46711     executor = js_promise_executor_new(ctx);
46712     if (JS_IsException(executor))
46713         return executor;
46714 
46715     if (JS_IsUndefined(ctor)) {
46716         result_promise = js_promise_constructor(ctx, ctor, 1,
46717                                                 (JSValueConst *)&executor);
46718     } else {
46719         result_promise = JS_CallConstructor(ctx, ctor, 1,
46720                                             (JSValueConst *)&executor);
46721     }
46722     if (JS_IsException(result_promise))
46723         goto fail;
46724     s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA);
46725     for(i = 0; i < 2; i++) {
46726         if (check_function(ctx, s->data[i]))
46727             goto fail;
46728     }
46729     for(i = 0; i < 2; i++)
46730         resolving_funcs[i] = JS_DupValue(ctx, s->data[i]);
46731     JS_FreeValue(ctx, executor);
46732     return result_promise;
46733  fail:
46734     JS_FreeValue(ctx, executor);
46735     JS_FreeValue(ctx, result_promise);
46736     return JS_EXCEPTION;
46737 }
46738 
JS_NewPromiseCapability(JSContext * ctx,JSValue * resolving_funcs)46739 JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs)
46740 {
46741     return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED);
46742 }
46743 
js_promise_resolve(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)46744 static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
46745                                   int argc, JSValueConst *argv, int magic)
46746 {
46747     JSValue result_promise, resolving_funcs[2], ret;
46748     BOOL is_reject = magic;
46749 
46750     if (!JS_IsObject(this_val))
46751         return JS_ThrowTypeErrorNotAnObject(ctx);
46752     if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) {
46753         JSValue ctor;
46754         BOOL is_same;
46755         ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor);
46756         if (JS_IsException(ctor))
46757             return ctor;
46758         is_same = js_same_value(ctx, ctor, this_val);
46759         JS_FreeValue(ctx, ctor);
46760         if (is_same)
46761             return JS_DupValue(ctx, argv[0]);
46762     }
46763     result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
46764     if (JS_IsException(result_promise))
46765         return result_promise;
46766     ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv);
46767     JS_FreeValue(ctx, resolving_funcs[0]);
46768     JS_FreeValue(ctx, resolving_funcs[1]);
46769     if (JS_IsException(ret)) {
46770         JS_FreeValue(ctx, result_promise);
46771         return ret;
46772     }
46773     JS_FreeValue(ctx, ret);
46774     return result_promise;
46775 }
46776 
46777 #if 0
46778 static JSValue js_promise___newPromiseCapability(JSContext *ctx,
46779                                                  JSValueConst this_val,
46780                                                  int argc, JSValueConst *argv)
46781 {
46782     JSValue result_promise, resolving_funcs[2], obj;
46783     JSValueConst ctor;
46784     ctor = argv[0];
46785     if (!JS_IsObject(ctor))
46786         return JS_ThrowTypeErrorNotAnObject(ctx);
46787     result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
46788     if (JS_IsException(result_promise))
46789         return result_promise;
46790     obj = JS_NewObject(ctx);
46791     if (JS_IsException(obj)) {
46792         JS_FreeValue(ctx, resolving_funcs[0]);
46793         JS_FreeValue(ctx, resolving_funcs[1]);
46794         JS_FreeValue(ctx, result_promise);
46795         return JS_EXCEPTION;
46796     }
46797     JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E);
46798     JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E);
46799     JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E);
46800     return obj;
46801 }
46802 #endif
46803 
remainingElementsCount_add(JSContext * ctx,JSValueConst resolve_element_env,int addend)46804 static __exception int remainingElementsCount_add(JSContext *ctx,
46805                                                   JSValueConst resolve_element_env,
46806                                                   int addend)
46807 {
46808     JSValue val;
46809     int remainingElementsCount;
46810 
46811     val = JS_GetPropertyUint32(ctx, resolve_element_env, 0);
46812     if (JS_IsException(val))
46813         return -1;
46814     if (JS_ToInt32Free(ctx, &remainingElementsCount, val))
46815         return -1;
46816     remainingElementsCount += addend;
46817     if (JS_SetPropertyUint32(ctx, resolve_element_env, 0,
46818                              JS_NewInt32(ctx, remainingElementsCount)) < 0)
46819         return -1;
46820     return (remainingElementsCount == 0);
46821 }
46822 
46823 #define PROMISE_MAGIC_all        0
46824 #define PROMISE_MAGIC_allSettled 1
46825 #define PROMISE_MAGIC_any        2
46826 
js_promise_all_resolve_element(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)46827 static JSValue js_promise_all_resolve_element(JSContext *ctx,
46828                                               JSValueConst this_val,
46829                                               int argc, JSValueConst *argv,
46830                                               int magic,
46831                                               JSValue *func_data)
46832 {
46833     int resolve_type = magic & 3;
46834     int is_reject = magic & 4;
46835     BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]);
46836     JSValueConst values = func_data[2];
46837     JSValueConst resolve = func_data[3];
46838     JSValueConst resolve_element_env = func_data[4];
46839     JSValue ret, obj;
46840     int is_zero, index;
46841 
46842     if (JS_ToInt32(ctx, &index, func_data[1]))
46843         return JS_EXCEPTION;
46844     if (alreadyCalled)
46845         return JS_UNDEFINED;
46846     func_data[0] = JS_NewBool(ctx, TRUE);
46847 
46848     if (resolve_type == PROMISE_MAGIC_allSettled) {
46849         JSValue str;
46850 
46851         obj = JS_NewObject(ctx);
46852         if (JS_IsException(obj))
46853             return JS_EXCEPTION;
46854         str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled");
46855         if (JS_IsException(str))
46856             goto fail1;
46857         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status,
46858                                    str,
46859                                    JS_PROP_C_W_E) < 0)
46860             goto fail1;
46861         if (JS_DefinePropertyValue(ctx, obj,
46862                                    is_reject ? JS_ATOM_reason : JS_ATOM_value,
46863                                    JS_DupValue(ctx, argv[0]),
46864                                    JS_PROP_C_W_E) < 0) {
46865         fail1:
46866             JS_FreeValue(ctx, obj);
46867             return JS_EXCEPTION;
46868         }
46869     } else {
46870         obj = JS_DupValue(ctx, argv[0]);
46871     }
46872     if (JS_DefinePropertyValueUint32(ctx, values, index,
46873                                      obj, JS_PROP_C_W_E) < 0)
46874         return JS_EXCEPTION;
46875 
46876     is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
46877     if (is_zero < 0)
46878         return JS_EXCEPTION;
46879     if (is_zero) {
46880         if (resolve_type == PROMISE_MAGIC_any) {
46881             JSValue error;
46882             error = js_aggregate_error_constructor(ctx, values);
46883             if (JS_IsException(error))
46884                 return JS_EXCEPTION;
46885             ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error);
46886             JS_FreeValue(ctx, error);
46887         } else {
46888             ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values);
46889         }
46890         if (JS_IsException(ret))
46891             return ret;
46892         JS_FreeValue(ctx, ret);
46893     }
46894     return JS_UNDEFINED;
46895 }
46896 
46897 /* magic = 0: Promise.all 1: Promise.allSettled */
js_promise_all(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)46898 static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
46899                               int argc, JSValueConst *argv, int magic)
46900 {
46901     JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
46902     JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED;
46903     JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element;
46904     JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED;
46905     JSValueConst then_args[2], resolve_element_data[5];
46906     BOOL done;
46907     int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any);
46908 
46909     if (!JS_IsObject(this_val))
46910         return JS_ThrowTypeErrorNotAnObject(ctx);
46911     result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
46912     if (JS_IsException(result_promise))
46913         return result_promise;
46914     promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
46915     if (JS_IsException(promise_resolve) ||
46916         check_function(ctx, promise_resolve))
46917         goto fail_reject;
46918     iter = JS_GetIterator(ctx, argv[0], FALSE);
46919     if (JS_IsException(iter)) {
46920         JSValue error;
46921     fail_reject:
46922         error = JS_GetException(ctx);
46923         ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
46924                        (JSValueConst *)&error);
46925         JS_FreeValue(ctx, error);
46926         if (JS_IsException(ret))
46927             goto fail;
46928         JS_FreeValue(ctx, ret);
46929     } else {
46930         next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
46931         if (JS_IsException(next_method))
46932             goto fail_reject;
46933         values = JS_NewArray(ctx);
46934         if (JS_IsException(values))
46935             goto fail_reject;
46936         resolve_element_env = JS_NewArray(ctx);
46937         if (JS_IsException(resolve_element_env))
46938             goto fail_reject;
46939         /* remainingElementsCount field */
46940         if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0,
46941                                          JS_NewInt32(ctx, 1),
46942                                          JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
46943             goto fail_reject;
46944 
46945         index = 0;
46946         for(;;) {
46947             /* XXX: conformance: should close the iterator if error on 'done'
46948                access, but not on 'value' access */
46949             item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
46950             if (JS_IsException(item))
46951                 goto fail_reject;
46952             if (done)
46953                 break;
46954             next_promise = JS_Call(ctx, promise_resolve,
46955                                    this_val, 1, (JSValueConst *)&item);
46956             JS_FreeValue(ctx, item);
46957             if (JS_IsException(next_promise)) {
46958             fail_reject1:
46959                 JS_IteratorClose(ctx, iter, TRUE);
46960                 goto fail_reject;
46961             }
46962             resolve_element_data[0] = JS_NewBool(ctx, FALSE);
46963             resolve_element_data[1] = JS_VALUE_MAKE_CONST(JS_NewInt32(ctx, index));
46964             resolve_element_data[2] = values;
46965             resolve_element_data[3] = resolving_funcs[is_promise_any];
46966             resolve_element_data[4] = resolve_element_env;
46967             resolve_element =
46968                 JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
46969                                     magic, 5, resolve_element_data);
46970             if (JS_IsException(resolve_element)) {
46971                 JS_FreeValue(ctx, next_promise);
46972                 goto fail_reject1;
46973             }
46974 
46975             if (magic == PROMISE_MAGIC_allSettled) {
46976                 reject_element =
46977                     JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
46978                                         magic | 4, 5, resolve_element_data);
46979                 if (JS_IsException(reject_element)) {
46980                     JS_FreeValue(ctx, next_promise);
46981                     goto fail_reject1;
46982                 }
46983             } else if (magic == PROMISE_MAGIC_any) {
46984                 if (JS_DefinePropertyValueUint32(ctx, values, index,
46985                                                  JS_UNDEFINED, JS_PROP_C_W_E) < 0)
46986                     goto fail_reject1;
46987                 reject_element = resolve_element;
46988                 resolve_element = JS_DupValue(ctx, resolving_funcs[0]);
46989             } else {
46990                 reject_element = JS_DupValue(ctx, resolving_funcs[1]);
46991             }
46992 
46993             if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) {
46994                 JS_FreeValue(ctx, next_promise);
46995                 JS_FreeValue(ctx, resolve_element);
46996                 JS_FreeValue(ctx, reject_element);
46997                 goto fail_reject1;
46998             }
46999 
47000             then_args[0] = resolve_element;
47001             then_args[1] = reject_element;
47002             ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args);
47003             JS_FreeValue(ctx, resolve_element);
47004             JS_FreeValue(ctx, reject_element);
47005             if (check_exception_free(ctx, ret))
47006                 goto fail_reject1;
47007             index++;
47008         }
47009 
47010         is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
47011         if (is_zero < 0)
47012             goto fail_reject;
47013         if (is_zero) {
47014             if (magic == PROMISE_MAGIC_any) {
47015                 JSValue error;
47016                 error = js_aggregate_error_constructor(ctx, values);
47017                 if (JS_IsException(error))
47018                     goto fail_reject;
47019                 JS_FreeValue(ctx, values);
47020                 values = error;
47021             }
47022             ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED,
47023                           1, (JSValueConst *)&values);
47024             if (check_exception_free(ctx, ret))
47025                 goto fail_reject;
47026         }
47027     }
47028  done:
47029     JS_FreeValue(ctx, promise_resolve);
47030     JS_FreeValue(ctx, resolve_element_env);
47031     JS_FreeValue(ctx, values);
47032     JS_FreeValue(ctx, next_method);
47033     JS_FreeValue(ctx, iter);
47034     JS_FreeValue(ctx, resolving_funcs[0]);
47035     JS_FreeValue(ctx, resolving_funcs[1]);
47036     return result_promise;
47037  fail:
47038     JS_FreeValue(ctx, result_promise);
47039     result_promise = JS_EXCEPTION;
47040     goto done;
47041 }
47042 
js_promise_race(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)47043 static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
47044                                int argc, JSValueConst *argv)
47045 {
47046     JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
47047     JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED;
47048     JSValue promise_resolve = JS_UNDEFINED;
47049     BOOL done;
47050 
47051     if (!JS_IsObject(this_val))
47052         return JS_ThrowTypeErrorNotAnObject(ctx);
47053     result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
47054     if (JS_IsException(result_promise))
47055         return result_promise;
47056     promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
47057     if (JS_IsException(promise_resolve) ||
47058         check_function(ctx, promise_resolve))
47059         goto fail_reject;
47060     iter = JS_GetIterator(ctx, argv[0], FALSE);
47061     if (JS_IsException(iter)) {
47062         JSValue error;
47063     fail_reject:
47064         error = JS_GetException(ctx);
47065         ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
47066                        (JSValueConst *)&error);
47067         JS_FreeValue(ctx, error);
47068         if (JS_IsException(ret))
47069             goto fail;
47070         JS_FreeValue(ctx, ret);
47071     } else {
47072         next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
47073         if (JS_IsException(next_method))
47074             goto fail_reject;
47075 
47076         for(;;) {
47077             /* XXX: conformance: should close the iterator if error on 'done'
47078                access, but not on 'value' access */
47079             item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
47080             if (JS_IsException(item))
47081                 goto fail_reject;
47082             if (done)
47083                 break;
47084             next_promise = JS_Call(ctx, promise_resolve,
47085                                    this_val, 1, (JSValueConst *)&item);
47086             JS_FreeValue(ctx, item);
47087             if (JS_IsException(next_promise)) {
47088             fail_reject1:
47089                 JS_IteratorClose(ctx, iter, TRUE);
47090                 goto fail_reject;
47091             }
47092             ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2,
47093                                 (JSValueConst *)resolving_funcs);
47094             if (check_exception_free(ctx, ret))
47095                 goto fail_reject1;
47096         }
47097     }
47098  done:
47099     JS_FreeValue(ctx, promise_resolve);
47100     JS_FreeValue(ctx, next_method);
47101     JS_FreeValue(ctx, iter);
47102     JS_FreeValue(ctx, resolving_funcs[0]);
47103     JS_FreeValue(ctx, resolving_funcs[1]);
47104     return result_promise;
47105  fail:
47106     //JS_FreeValue(ctx, next_method); // why not???
47107     JS_FreeValue(ctx, result_promise);
47108     result_promise = JS_EXCEPTION;
47109     goto done;
47110 }
47111 
perform_promise_then(JSContext * ctx,JSValueConst promise,JSValueConst * resolve_reject,JSValueConst * cap_resolving_funcs)47112 static __exception int perform_promise_then(JSContext *ctx,
47113                                             JSValueConst promise,
47114                                             JSValueConst *resolve_reject,
47115                                             JSValueConst *cap_resolving_funcs)
47116 {
47117     JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
47118     JSPromiseReactionData *rd_array[2], *rd;
47119     int i, j;
47120 
47121     rd_array[0] = NULL;
47122     rd_array[1] = NULL;
47123     for(i = 0; i < 2; i++) {
47124         JSValueConst handler;
47125         rd = js_mallocz(ctx, sizeof(*rd));
47126         if (!rd) {
47127             if (i == 1)
47128                 promise_reaction_data_free(ctx->rt, rd_array[0]);
47129             return -1;
47130         }
47131         for(j = 0; j < 2; j++)
47132             rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]);
47133         handler = resolve_reject[i];
47134         if (!JS_IsFunction(ctx, handler))
47135             handler = JS_UNDEFINED;
47136         rd->handler = JS_DupValue(ctx, handler);
47137         rd_array[i] = rd;
47138     }
47139 
47140     if (s->promise_state == JS_PROMISE_PENDING) {
47141         for(i = 0; i < 2; i++)
47142             list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]);
47143     } else {
47144         JSValueConst args[5];
47145         if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
47146             JSRuntime *rt = ctx->rt;
47147             if (rt->host_promise_rejection_tracker) {
47148                 rt->host_promise_rejection_tracker(ctx, promise, s->promise_result,
47149                                                    TRUE, rt->host_promise_rejection_tracker_opaque);
47150             }
47151         }
47152         i = s->promise_state - JS_PROMISE_FULFILLED;
47153         rd = rd_array[i];
47154         args[0] = rd->resolving_funcs[0];
47155         args[1] = rd->resolving_funcs[1];
47156         args[2] = rd->handler;
47157         args[3] = JS_NewBool(ctx, i);
47158         args[4] = s->promise_result;
47159         JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
47160         for(i = 0; i < 2; i++)
47161             promise_reaction_data_free(ctx->rt, rd_array[i]);
47162     }
47163     s->is_handled = TRUE;
47164     return 0;
47165 }
47166 
js_promise_then(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)47167 static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,
47168                                int argc, JSValueConst *argv)
47169 {
47170     JSValue ctor, result_promise, resolving_funcs[2];
47171     JSPromiseData *s;
47172     int i, ret;
47173 
47174     s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE);
47175     if (!s)
47176         return JS_EXCEPTION;
47177 
47178     ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
47179     if (JS_IsException(ctor))
47180         return ctor;
47181     result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
47182     JS_FreeValue(ctx, ctor);
47183     if (JS_IsException(result_promise))
47184         return result_promise;
47185     ret = perform_promise_then(ctx, this_val, argv,
47186                                (JSValueConst *)resolving_funcs);
47187     for(i = 0; i < 2; i++)
47188         JS_FreeValue(ctx, resolving_funcs[i]);
47189     if (ret) {
47190         JS_FreeValue(ctx, result_promise);
47191         return JS_EXCEPTION;
47192     }
47193     return result_promise;
47194 }
47195 
js_promise_catch(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)47196 static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val,
47197                                 int argc, JSValueConst *argv)
47198 {
47199     JSValueConst args[2];
47200     args[0] = JS_UNDEFINED;
47201     args[1] = argv[0];
47202     return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args);
47203 }
47204 
js_promise_finally_value_thunk(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)47205 static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val,
47206                                               int argc, JSValueConst *argv,
47207                                               int magic, JSValue *func_data)
47208 {
47209     return JS_DupValue(ctx, func_data[0]);
47210 }
47211 
js_promise_finally_thrower(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)47212 static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val,
47213                                           int argc, JSValueConst *argv,
47214                                           int magic, JSValue *func_data)
47215 {
47216     return JS_Throw(ctx, JS_DupValue(ctx, func_data[0]));
47217 }
47218 
js_promise_then_finally_func(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)47219 static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val,
47220                                             int argc, JSValueConst *argv,
47221                                             int magic, JSValue *func_data)
47222 {
47223     JSValueConst ctor = func_data[0];
47224     JSValueConst onFinally = func_data[1];
47225     JSValue res, promise, ret, then_func;
47226 
47227     res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL);
47228     if (JS_IsException(res))
47229         return res;
47230     promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0);
47231     JS_FreeValue(ctx, res);
47232     if (JS_IsException(promise))
47233         return promise;
47234     if (magic == 0) {
47235         then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0,
47236                                         0, 1, argv);
47237     } else {
47238         then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0,
47239                                         0, 1, argv);
47240     }
47241     if (JS_IsException(then_func)) {
47242         JS_FreeValue(ctx, promise);
47243         return then_func;
47244     }
47245     ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func);
47246     JS_FreeValue(ctx, then_func);
47247     return ret;
47248 }
47249 
js_promise_finally(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)47250 static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val,
47251                                   int argc, JSValueConst *argv)
47252 {
47253     JSValueConst onFinally = argv[0];
47254     JSValue ctor, ret;
47255     JSValue then_funcs[2];
47256     JSValueConst func_data[2];
47257     int i;
47258 
47259     ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
47260     if (JS_IsException(ctor))
47261         return ctor;
47262     if (!JS_IsFunction(ctx, onFinally)) {
47263         then_funcs[0] = JS_DupValue(ctx, onFinally);
47264         then_funcs[1] = JS_DupValue(ctx, onFinally);
47265     } else {
47266         func_data[0] = ctor;
47267         func_data[1] = onFinally;
47268         for(i = 0; i < 2; i++) {
47269             then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data);
47270             if (JS_IsException(then_funcs[i])) {
47271                 if (i == 1)
47272                     JS_FreeValue(ctx, then_funcs[0]);
47273                 JS_FreeValue(ctx, ctor);
47274                 return JS_EXCEPTION;
47275             }
47276         }
47277     }
47278     JS_FreeValue(ctx, ctor);
47279     ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs);
47280     JS_FreeValue(ctx, then_funcs[0]);
47281     JS_FreeValue(ctx, then_funcs[1]);
47282     return ret;
47283 }
47284 
47285 static const JSCFunctionListEntry js_promise_funcs[] = {
47286     JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ),
47287     JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ),
47288     JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ),
47289     JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ),
47290     JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ),
47291     JS_CFUNC_DEF("race", 1, js_promise_race ),
47292     //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ),
47293     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL),
47294 };
47295 
47296 static const JSCFunctionListEntry js_promise_proto_funcs[] = {
47297     JS_CFUNC_DEF("then", 2, js_promise_then ),
47298     JS_CFUNC_DEF("catch", 1, js_promise_catch ),
47299     JS_CFUNC_DEF("finally", 1, js_promise_finally ),
47300     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ),
47301 };
47302 
47303 /* AsyncFunction */
47304 static const JSCFunctionListEntry js_async_function_proto_funcs[] = {
47305     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ),
47306 };
47307 
js_async_from_sync_iterator_unwrap(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)47308 static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx,
47309                                                   JSValueConst this_val,
47310                                                   int argc, JSValueConst *argv,
47311                                                   int magic, JSValue *func_data)
47312 {
47313     return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]),
47314                                      JS_ToBool(ctx, func_data[0]));
47315 }
47316 
js_async_from_sync_iterator_unwrap_func_create(JSContext * ctx,BOOL done)47317 static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,
47318                                                               BOOL done)
47319 {
47320     JSValueConst func_data[1];
47321 
47322     func_data[0] = JS_VALUE_MAKE_CONST(JS_NewBool(ctx, done));
47323     return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap,
47324                                1, 0, 1, func_data);
47325 }
47326 
47327 /* AsyncIteratorPrototype */
47328 
47329 static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = {
47330     JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ),
47331 };
47332 
47333 /* AsyncFromSyncIteratorPrototype */
47334 
47335 typedef struct JSAsyncFromSyncIteratorData {
47336     JSValue sync_iter;
47337     JSValue next_method;
47338 } JSAsyncFromSyncIteratorData;
47339 
js_async_from_sync_iterator_finalizer(JSRuntime * rt,JSValue val)47340 static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val)
47341 {
47342     JSAsyncFromSyncIteratorData *s =
47343         JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
47344     if (s) {
47345         JS_FreeValueRT(rt, s->sync_iter);
47346         JS_FreeValueRT(rt, s->next_method);
47347         js_free_rt(rt, s);
47348     }
47349 }
47350 
js_async_from_sync_iterator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)47351 static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val,
47352                                              JS_MarkFunc *mark_func)
47353 {
47354     JSAsyncFromSyncIteratorData *s =
47355         JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
47356     if (s) {
47357         JS_MarkValue(rt, s->sync_iter, mark_func);
47358         JS_MarkValue(rt, s->next_method, mark_func);
47359     }
47360 }
47361 
JS_CreateAsyncFromSyncIterator(JSContext * ctx,JSValueConst sync_iter)47362 static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
47363                                               JSValueConst sync_iter)
47364 {
47365     JSValue async_iter, next_method;
47366     JSAsyncFromSyncIteratorData *s;
47367 
47368     next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next);
47369     if (JS_IsException(next_method))
47370         return JS_EXCEPTION;
47371     async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
47372     if (JS_IsException(async_iter)) {
47373         JS_FreeValue(ctx, next_method);
47374         return async_iter;
47375     }
47376     s = js_mallocz(ctx, sizeof(*s));
47377     if (!s) {
47378         JS_FreeValue(ctx, async_iter);
47379         JS_FreeValue(ctx, next_method);
47380         return JS_EXCEPTION;
47381     }
47382     s->sync_iter = JS_DupValue(ctx, sync_iter);
47383     s->next_method = next_method;
47384     JS_SetOpaque(async_iter, s);
47385     return async_iter;
47386 }
47387 
js_async_from_sync_iterator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)47388 static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val,
47389                                                 int argc, JSValueConst *argv,
47390                                                 int magic)
47391 {
47392     JSValue promise, resolving_funcs[2], value, err, method;
47393     JSAsyncFromSyncIteratorData *s;
47394     int done;
47395     int is_reject;
47396 
47397     promise = JS_NewPromiseCapability(ctx, resolving_funcs);
47398     if (JS_IsException(promise))
47399         return JS_EXCEPTION;
47400     s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
47401     if (!s) {
47402         JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator");
47403         goto reject;
47404     }
47405 
47406     if (magic == GEN_MAGIC_NEXT) {
47407         method = JS_DupValue(ctx, s->next_method);
47408     } else {
47409         method = JS_GetProperty(ctx, s->sync_iter,
47410                                 magic == GEN_MAGIC_RETURN ? JS_ATOM_return :
47411                                 JS_ATOM_throw);
47412         if (JS_IsException(method))
47413             goto reject;
47414         if (JS_IsUndefined(method) || JS_IsNull(method)) {
47415             if (magic == GEN_MAGIC_RETURN) {
47416                 err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE);
47417                 is_reject = 0;
47418             } else {
47419                 err = JS_DupValue(ctx, argv[0]);
47420                 is_reject = 1;
47421             }
47422             goto done_resolve;
47423         }
47424     }
47425     value = JS_IteratorNext2(ctx, s->sync_iter, method,
47426                              argc >= 1 ? 1 : 0, argv, &done);
47427     JS_FreeValue(ctx, method);
47428     if (JS_IsException(value))
47429         goto reject;
47430     if (done == 2) {
47431         JSValue obj = value;
47432         value = JS_IteratorGetCompleteValue(ctx, obj, &done);
47433         JS_FreeValue(ctx, obj);
47434         if (JS_IsException(value))
47435             goto reject;
47436     }
47437 
47438     if (JS_IsException(value)) {
47439         JSValue res2;
47440     reject:
47441         err = JS_GetException(ctx);
47442         is_reject = 1;
47443     done_resolve:
47444         res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED,
47445                        1, (JSValueConst *)&err);
47446         JS_FreeValue(ctx, err);
47447         JS_FreeValue(ctx, res2);
47448         JS_FreeValue(ctx, resolving_funcs[0]);
47449         JS_FreeValue(ctx, resolving_funcs[1]);
47450         return promise;
47451     }
47452     {
47453         JSValue value_wrapper_promise, resolve_reject[2];
47454         int res;
47455 
47456         value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,
47457                                                    1, (JSValueConst *)&value, 0);
47458         if (JS_IsException(value_wrapper_promise)) {
47459             JS_FreeValue(ctx, value);
47460             goto reject;
47461         }
47462 
47463         resolve_reject[0] =
47464             js_async_from_sync_iterator_unwrap_func_create(ctx, done);
47465         if (JS_IsException(resolve_reject[0])) {
47466             JS_FreeValue(ctx, value_wrapper_promise);
47467             goto fail;
47468         }
47469         JS_FreeValue(ctx, value);
47470         resolve_reject[1] = JS_UNDEFINED;
47471 
47472         res = perform_promise_then(ctx, value_wrapper_promise,
47473                                    (JSValueConst *)resolve_reject,
47474                                    (JSValueConst *)resolving_funcs);
47475         JS_FreeValue(ctx, resolve_reject[0]);
47476         JS_FreeValue(ctx, value_wrapper_promise);
47477         JS_FreeValue(ctx, resolving_funcs[0]);
47478         JS_FreeValue(ctx, resolving_funcs[1]);
47479         if (res) {
47480             JS_FreeValue(ctx, promise);
47481             return JS_EXCEPTION;
47482         }
47483     }
47484     return promise;
47485  fail:
47486     JS_FreeValue(ctx, value);
47487     JS_FreeValue(ctx, resolving_funcs[0]);
47488     JS_FreeValue(ctx, resolving_funcs[1]);
47489     JS_FreeValue(ctx, promise);
47490     return JS_EXCEPTION;
47491 }
47492 
47493 static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = {
47494     JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ),
47495     JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ),
47496     JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ),
47497 };
47498 
47499 /* AsyncGeneratorFunction */
47500 
47501 static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = {
47502     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ),
47503 };
47504 
47505 /* AsyncGenerator prototype */
47506 
47507 static const JSCFunctionListEntry js_async_generator_proto_funcs[] = {
47508     JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ),
47509     JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ),
47510     JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ),
47511     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ),
47512 };
47513 
47514 static JSClassShortDef const js_async_class_def[] = {
47515     { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark },                      /* JS_CLASS_PROMISE */
47516     { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */
47517     { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */
47518     { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark },  /* JS_CLASS_ASYNC_FUNCTION */
47519     { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */
47520     { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */
47521     { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
47522     { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark },  /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */
47523     { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark },  /* JS_CLASS_ASYNC_GENERATOR */
47524 };
47525 
JS_AddIntrinsicPromise(JSContext * ctx)47526 void JS_AddIntrinsicPromise(JSContext *ctx)
47527 {
47528     JSRuntime *rt = ctx->rt;
47529     JSValue obj1;
47530 
47531     if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) {
47532         init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE,
47533                          countof(js_async_class_def));
47534         rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call;
47535         rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call;
47536         rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call;
47537         rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call;
47538         rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call;
47539         rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call;
47540     }
47541 
47542     /* Promise */
47543     ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx);
47544     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE],
47545                                js_promise_proto_funcs,
47546                                countof(js_promise_proto_funcs));
47547     obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1,
47548                             JS_CFUNC_constructor, 0);
47549     ctx->promise_ctor = JS_DupValue(ctx, obj1);
47550     JS_SetPropertyFunctionList(ctx, obj1,
47551                                js_promise_funcs,
47552                                countof(js_promise_funcs));
47553     JS_NewGlobalCConstructor2(ctx, obj1, "Promise",
47554                               ctx->class_proto[JS_CLASS_PROMISE]);
47555 
47556     /* AsyncFunction */
47557     ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
47558     obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
47559                             "AsyncFunction", 1,
47560                             JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC,
47561                             ctx->function_ctor);
47562     JS_SetPropertyFunctionList(ctx,
47563                                ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
47564                                js_async_function_proto_funcs,
47565                                countof(js_async_function_proto_funcs));
47566     JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
47567                        0, JS_PROP_CONFIGURABLE);
47568     JS_FreeValue(ctx, obj1);
47569 
47570     /* AsyncIteratorPrototype */
47571     ctx->async_iterator_proto = JS_NewObject(ctx);
47572     JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto,
47573                                js_async_iterator_proto_funcs,
47574                                countof(js_async_iterator_proto_funcs));
47575 
47576     /* AsyncFromSyncIteratorPrototype */
47577     ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] =
47578         JS_NewObjectProto(ctx, ctx->async_iterator_proto);
47579     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR],
47580                                js_async_from_sync_iterator_proto_funcs,
47581                                countof(js_async_from_sync_iterator_proto_funcs));
47582 
47583     /* AsyncGeneratorPrototype */
47584     ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] =
47585         JS_NewObjectProto(ctx, ctx->async_iterator_proto);
47586     JS_SetPropertyFunctionList(ctx,
47587                                ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
47588                                js_async_generator_proto_funcs,
47589                                countof(js_async_generator_proto_funcs));
47590 
47591     /* AsyncGeneratorFunction */
47592     ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] =
47593         JS_NewObjectProto(ctx, ctx->function_proto);
47594     obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
47595                             "AsyncGeneratorFunction", 1,
47596                             JS_CFUNC_constructor_or_func_magic,
47597                             JS_FUNC_ASYNC_GENERATOR,
47598                             ctx->function_ctor);
47599     JS_SetPropertyFunctionList(ctx,
47600                                ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
47601                                js_async_generator_function_proto_funcs,
47602                                countof(js_async_generator_function_proto_funcs));
47603     JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
47604                        ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
47605                        JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
47606     JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
47607                        0, JS_PROP_CONFIGURABLE);
47608     JS_FreeValue(ctx, obj1);
47609 }
47610 
47611 /* URI handling */
47612 
string_get_hex(JSString * p,int k,int n)47613 static int string_get_hex(JSString *p, int k, int n) {
47614     int c = 0, h;
47615     while (n-- > 0) {
47616         if ((h = from_hex(string_get(p, k++))) < 0)
47617             return -1;
47618         c = (c << 4) | h;
47619     }
47620     return c;
47621 }
47622 
isURIReserved(int c)47623 static int isURIReserved(int c) {
47624     return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;
47625 }
47626 
js_throw_URIError(JSContext * ctx,const char * fmt,...)47627 static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...)
47628 {
47629     va_list ap;
47630 
47631     va_start(ap, fmt);
47632     JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap);
47633     va_end(ap);
47634     return -1;
47635 }
47636 
hex_decode(JSContext * ctx,JSString * p,int k)47637 static int hex_decode(JSContext *ctx, JSString *p, int k) {
47638     int c;
47639 
47640     if (k >= p->len || string_get(p, k) != '%')
47641         return js_throw_URIError(ctx, "expecting %%");
47642     if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0)
47643         return js_throw_URIError(ctx, "expecting hex digit");
47644 
47645     return c;
47646 }
47647 
js_global_decodeURI(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int isComponent)47648 static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val,
47649                                    int argc, JSValueConst *argv, int isComponent)
47650 {
47651     JSValue str;
47652     StringBuffer b_s, *b = &b_s;
47653     JSString *p;
47654     int k, c, c1, n, c_min;
47655 
47656     str = JS_ToString(ctx, argv[0]);
47657     if (JS_IsException(str))
47658         return str;
47659 
47660     string_buffer_init(ctx, b, 0);
47661 
47662     p = JS_VALUE_GET_STRING(str);
47663     for (k = 0; k < p->len;) {
47664         c = string_get(p, k);
47665         if (c == '%') {
47666             c = hex_decode(ctx, p, k);
47667             if (c < 0)
47668                 goto fail;
47669             k += 3;
47670             if (c < 0x80) {
47671                 if (!isComponent && isURIReserved(c)) {
47672                     c = '%';
47673                     k -= 2;
47674                 }
47675             } else {
47676                 /* Decode URI-encoded UTF-8 sequence */
47677                 if (c >= 0xc0 && c <= 0xdf) {
47678                     n = 1;
47679                     c_min = 0x80;
47680                     c &= 0x1f;
47681                 } else if (c >= 0xe0 && c <= 0xef) {
47682                     n = 2;
47683                     c_min = 0x800;
47684                     c &= 0xf;
47685                 } else if (c >= 0xf0 && c <= 0xf7) {
47686                     n = 3;
47687                     c_min = 0x10000;
47688                     c &= 0x7;
47689                 } else {
47690                     n = 0;
47691                     c_min = 1;
47692                     c = 0;
47693                 }
47694                 while (n-- > 0) {
47695                     c1 = hex_decode(ctx, p, k);
47696                     if (c1 < 0)
47697                         goto fail;
47698                     k += 3;
47699                     if ((c1 & 0xc0) != 0x80) {
47700                         c = 0;
47701                         break;
47702                     }
47703                     c = (c << 6) | (c1 & 0x3f);
47704                 }
47705                 if (c < c_min || c > 0x10FFFF ||
47706                     (c >= 0xd800 && c < 0xe000)) {
47707                     js_throw_URIError(ctx, "malformed UTF-8");
47708                     goto fail;
47709                 }
47710             }
47711         } else {
47712             k++;
47713         }
47714         string_buffer_putc(b, c);
47715     }
47716     JS_FreeValue(ctx, str);
47717     return string_buffer_end(b);
47718 
47719 fail:
47720     JS_FreeValue(ctx, str);
47721     string_buffer_free(b);
47722     return JS_EXCEPTION;
47723 }
47724 
isUnescaped(int c)47725 static int isUnescaped(int c) {
47726     static char const unescaped_chars[] =
47727         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
47728         "abcdefghijklmnopqrstuvwxyz"
47729         "0123456789"
47730         "@*_+-./";
47731     return c < 0x100 &&
47732         memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1);
47733 }
47734 
isURIUnescaped(int c,int isComponent)47735 static int isURIUnescaped(int c, int isComponent) {
47736     return c < 0x100 &&
47737         ((c >= 0x61 && c <= 0x7a) ||
47738          (c >= 0x41 && c <= 0x5a) ||
47739          (c >= 0x30 && c <= 0x39) ||
47740          memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL ||
47741          (!isComponent && isURIReserved(c)));
47742 }
47743 
encodeURI_hex(StringBuffer * b,int c)47744 static int encodeURI_hex(StringBuffer *b, int c) {
47745     uint8_t buf[6];
47746     int n = 0;
47747     const char *hex = "0123456789ABCDEF";
47748 
47749     buf[n++] = '%';
47750     if (c >= 256) {
47751         buf[n++] = 'u';
47752         buf[n++] = hex[(c >> 12) & 15];
47753         buf[n++] = hex[(c >>  8) & 15];
47754     }
47755     buf[n++] = hex[(c >> 4) & 15];
47756     buf[n++] = hex[(c >> 0) & 15];
47757     return string_buffer_write8(b, buf, n);
47758 }
47759 
js_global_encodeURI(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int isComponent)47760 static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val,
47761                                    int argc, JSValueConst *argv,
47762                                    int isComponent)
47763 {
47764     JSValue str;
47765     StringBuffer b_s, *b = &b_s;
47766     JSString *p;
47767     int k, c, c1;
47768 
47769     str = JS_ToString(ctx, argv[0]);
47770     if (JS_IsException(str))
47771         return str;
47772 
47773     p = JS_VALUE_GET_STRING(str);
47774     string_buffer_init(ctx, b, p->len);
47775     for (k = 0; k < p->len;) {
47776         c = string_get(p, k);
47777         k++;
47778         if (isURIUnescaped(c, isComponent)) {
47779             string_buffer_putc16(b, c);
47780         } else {
47781             if (c >= 0xdc00 && c <= 0xdfff) {
47782                 js_throw_URIError(ctx, "invalid character");
47783                 goto fail;
47784             } else if (c >= 0xd800 && c <= 0xdbff) {
47785                 if (k >= p->len) {
47786                     js_throw_URIError(ctx, "expecting surrogate pair");
47787                     goto fail;
47788                 }
47789                 c1 = string_get(p, k);
47790                 k++;
47791                 if (c1 < 0xdc00 || c1 > 0xdfff) {
47792                     js_throw_URIError(ctx, "expecting surrogate pair");
47793                     goto fail;
47794                 }
47795                 c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
47796             }
47797             if (c < 0x80) {
47798                 encodeURI_hex(b, c);
47799             } else {
47800                 /* XXX: use C UTF-8 conversion ? */
47801                 if (c < 0x800) {
47802                     encodeURI_hex(b, (c >> 6) | 0xc0);
47803                 } else {
47804                     if (c < 0x10000) {
47805                         encodeURI_hex(b, (c >> 12) | 0xe0);
47806                     } else {
47807                         encodeURI_hex(b, (c >> 18) | 0xf0);
47808                         encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80);
47809                     }
47810                     encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80);
47811                 }
47812                 encodeURI_hex(b, (c & 0x3f) | 0x80);
47813             }
47814         }
47815     }
47816     JS_FreeValue(ctx, str);
47817     return string_buffer_end(b);
47818 
47819 fail:
47820     JS_FreeValue(ctx, str);
47821     string_buffer_free(b);
47822     return JS_EXCEPTION;
47823 }
47824 
js_global_escape(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)47825 static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val,
47826                                 int argc, JSValueConst *argv)
47827 {
47828     JSValue str;
47829     StringBuffer b_s, *b = &b_s;
47830     JSString *p;
47831     int i, len, c;
47832 
47833     str = JS_ToString(ctx, argv[0]);
47834     if (JS_IsException(str))
47835         return str;
47836 
47837     p = JS_VALUE_GET_STRING(str);
47838     string_buffer_init(ctx, b, p->len);
47839     for (i = 0, len = p->len; i < len; i++) {
47840         c = string_get(p, i);
47841         if (isUnescaped(c)) {
47842             string_buffer_putc16(b, c);
47843         } else {
47844             encodeURI_hex(b, c);
47845         }
47846     }
47847     JS_FreeValue(ctx, str);
47848     return string_buffer_end(b);
47849 }
47850 
js_global_unescape(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)47851 static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,
47852                                   int argc, JSValueConst *argv)
47853 {
47854     JSValue str;
47855     StringBuffer b_s, *b = &b_s;
47856     JSString *p;
47857     int i, len, c, n;
47858 
47859     str = JS_ToString(ctx, argv[0]);
47860     if (JS_IsException(str))
47861         return str;
47862 
47863     string_buffer_init(ctx, b, 0);
47864     p = JS_VALUE_GET_STRING(str);
47865     for (i = 0, len = p->len; i < len; i++) {
47866         c = string_get(p, i);
47867         if (c == '%') {
47868             if (i + 6 <= len
47869             &&  string_get(p, i + 1) == 'u'
47870             &&  (n = string_get_hex(p, i + 2, 4)) >= 0) {
47871                 c = n;
47872                 i += 6 - 1;
47873             } else
47874             if (i + 3 <= len
47875             &&  (n = string_get_hex(p, i + 1, 2)) >= 0) {
47876                 c = n;
47877                 i += 3 - 1;
47878             }
47879         }
47880         string_buffer_putc16(b, c);
47881     }
47882     JS_FreeValue(ctx, str);
47883     return string_buffer_end(b);
47884 }
47885 
47886 /* global object */
47887 
47888 static const JSCFunctionListEntry js_global_funcs[] = {
47889     JS_CFUNC_DEF("parseInt", 2, js_parseInt ),
47890     JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),
47891     JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
47892     JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
47893 
47894     JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),
47895     JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),
47896     JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),
47897     JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),
47898     JS_CFUNC_DEF("escape", 1, js_global_escape ),
47899     JS_CFUNC_DEF("unescape", 1, js_global_unescape ),
47900 #if defined(HUGE_VAL) || defined(_MSC_VER)
47901     JS_PROP_DOUBLE_DEF("Infinity", HUGE_VAL, 0 ),
47902 #else
47903     JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
47904 #endif
47905     JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
47906     JS_PROP_UNDEFINED_DEF("undefined", 0 ),
47907 
47908     /* for the 'Date' implementation */
47909     JS_CFUNC_DEF("__date_clock", 0, js___date_clock ),
47910     //JS_CFUNC_DEF("__date_now", 0, js___date_now ),
47911     //JS_CFUNC_DEF("__date_getTimezoneOffset", 1, js___date_getTimezoneOffset ),
47912     //JS_CFUNC_DEF("__date_create", 3, js___date_create ),
47913 };
47914 
47915 /* Date */
47916 
math_mod(int64_t a,int64_t b)47917 static int64_t math_mod(int64_t a, int64_t b) {
47918     /* return positive modulo */
47919     int64_t m = a % b;
47920     return m + (m < 0) * b;
47921 }
47922 
floor_div(int64_t a,int64_t b)47923 static int64_t floor_div(int64_t a, int64_t b) {
47924     /* integer division rounding toward -Infinity */
47925     int64_t m = a % b;
47926     return (a - (m + (m < 0) * b)) / b;
47927 }
47928 
47929 static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
47930                              int argc, JSValueConst *argv);
47931 
JS_ThisTimeValue(JSContext * ctx,double * valp,JSValueConst this_val)47932 static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
47933 {
47934     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
47935         JSObject *p = JS_VALUE_GET_OBJ(this_val);
47936         if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))
47937             return JS_ToFloat64(ctx, valp, p->u.object_data);
47938     }
47939     JS_ThrowTypeError(ctx, "not a Date object");
47940     return -1;
47941 }
47942 
JS_SetThisTimeValue(JSContext * ctx,JSValueConst this_val,double v)47943 static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)
47944 {
47945     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
47946         JSObject *p = JS_VALUE_GET_OBJ(this_val);
47947         if (p->class_id == JS_CLASS_DATE) {
47948             JS_FreeValue(ctx, p->u.object_data);
47949             p->u.object_data = JS_NewFloat64(ctx, v);
47950             return JS_DupValue(ctx, p->u.object_data);
47951         }
47952     }
47953     return JS_ThrowTypeError(ctx, "not a Date object");
47954 }
47955 
days_from_year(int64_t y)47956 static int64_t days_from_year(int64_t y) {
47957     return 365 * (y - 1970) + floor_div(y - 1969, 4) -
47958         floor_div(y - 1901, 100) + floor_div(y - 1601, 400);
47959 }
47960 
days_in_year(int64_t y)47961 static int64_t days_in_year(int64_t y) {
47962     return 365 + !(y % 4) - !(y % 100) + !(y % 400);
47963 }
47964 
47965 /* return the year, update days */
year_from_days(int64_t * days)47966 static int64_t year_from_days(int64_t *days) {
47967     int64_t y, d1, nd, d = *days;
47968     y = floor_div(d * 10000, 3652425) + 1970;
47969     /* the initial approximation is very good, so only a few
47970        iterations are necessary */
47971     for(;;) {
47972         d1 = d - days_from_year(y);
47973         if (d1 < 0) {
47974             y--;
47975             d1 += days_in_year(y);
47976         } else {
47977             nd = days_in_year(y);
47978             if (d1 < nd)
47979                 break;
47980             d1 -= nd;
47981             y++;
47982         }
47983     }
47984     *days = d1;
47985     return y;
47986 }
47987 
47988 static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
47989 static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
47990 static char const day_names[] = "SunMonTueWedThuFriSat";
47991 
get_date_fields(JSContext * ctx,JSValueConst obj,double fields[9],int is_local,int force)47992 static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
47993                                        double fields[9], int is_local, int force)
47994 {
47995     double dval;
47996     int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
47997 
47998     if (JS_ThisTimeValue(ctx, &dval, obj))
47999         return -1;
48000 
48001     if (isnan(dval)) {
48002         if (!force)
48003             return FALSE; /* NaN */
48004         d = 0;        /* initialize all fields to 0 */
48005     } else {
48006         d = dval;
48007         if (is_local) {
48008             tz = -getTimezoneOffset(d);
48009             d += tz * 60000;
48010         }
48011     }
48012 
48013     /* result is >= 0, we can use % */
48014     h = math_mod(d, 86400000);
48015     days = (d - h) / 86400000;
48016     ms = h % 1000;
48017     h = (h - ms) / 1000;
48018     s = h % 60;
48019     h = (h - s) / 60;
48020     m = h % 60;
48021     h = (h - m) / 60;
48022     wd = math_mod(days + 4, 7); /* week day */
48023     y = year_from_days(&days);
48024 
48025     for(i = 0; i < 11; i++) {
48026         md = month_days[i];
48027         if (i == 1)
48028             md += days_in_year(y) - 365;
48029         if (days < md)
48030             break;
48031         days -= md;
48032     }
48033     fields[0] = y;
48034     fields[1] = i;
48035     fields[2] = days + 1;
48036     fields[3] = h;
48037     fields[4] = m;
48038     fields[5] = s;
48039     fields[6] = ms;
48040     fields[7] = wd;
48041     fields[8] = tz;
48042     return TRUE;
48043 }
48044 
time_clip(double t)48045 static double time_clip(double t) {
48046     if (t >= -8.64e15 && t <= 8.64e15)
48047         return trunc(t) + 0.0;  /* convert -0 to +0 */
48048     else
48049         return NAN;
48050 }
48051 
48052 /* The spec mandates the use of 'double' and it fixes the order
48053    of the operations */
set_date_fields(double fields[],int is_local)48054 static double set_date_fields(double fields[], int is_local) {
48055     int64_t y;
48056     double days, d, h, m1;
48057     int i, m, md;
48058 
48059     m1 = fields[1];
48060     m = fmod(m1, 12);
48061     if (m < 0)
48062         m += 12;
48063     y = (int64_t)(fields[0] + floor(m1 / 12));
48064     days = days_from_year(y);
48065 
48066     for(i = 0; i < m; i++) {
48067         md = month_days[i];
48068         if (i == 1)
48069             md += days_in_year(y) - 365;
48070         days += md;
48071     }
48072     days += fields[2] - 1;
48073     h = fields[3] * 3600000 + fields[4] * 60000 +
48074         fields[5] * 1000 + fields[6];
48075     d = days * 86400000 + h;
48076     if (is_local)
48077         d += getTimezoneOffset(d) * 60000;
48078     return time_clip(d);
48079 }
48080 
get_date_field(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)48081 static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,
48082                               int argc, JSValueConst *argv, int magic)
48083 {
48084     // get_date_field(obj, n, is_local)
48085     double fields[9];
48086     int res, n, is_local;
48087 
48088     is_local = magic & 0x0F;
48089     n = (magic >> 4) & 0x0F;
48090     res = get_date_fields(ctx, this_val, fields, is_local, 0);
48091     if (res < 0)
48092         return JS_EXCEPTION;
48093     if (!res)
48094         return JS_NAN;
48095 
48096     if (magic & 0x100) {    // getYear
48097         fields[0] -= 1900;
48098     }
48099     return JS_NewFloat64(ctx, fields[n]);
48100 }
48101 
set_date_field(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)48102 static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,
48103                               int argc, JSValueConst *argv, int magic)
48104 {
48105     // _field(obj, first_field, end_field, args, is_local)
48106     double fields[9];
48107     int res, first_field, end_field, is_local, i, n;
48108     double d, a;
48109 
48110     d = NAN;
48111     first_field = (magic >> 8) & 0x0F;
48112     end_field = (magic >> 4) & 0x0F;
48113     is_local = magic & 0x0F;
48114 
48115     res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);
48116     if (res < 0)
48117         return JS_EXCEPTION;
48118     if (res && argc > 0) {
48119         n = end_field - first_field;
48120         if (argc < n)
48121             n = argc;
48122         for(i = 0; i < n; i++) {
48123             if (JS_ToFloat64(ctx, &a, argv[i]))
48124                 return JS_EXCEPTION;
48125             if (!isfinite(a))
48126                 goto done;
48127             fields[first_field + i] = trunc(a);
48128         }
48129         d = set_date_fields(fields, is_local);
48130     }
48131 done:
48132     return JS_SetThisTimeValue(ctx, this_val, d);
48133 }
48134 
48135 /* fmt:
48136    0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"
48137    1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"
48138    2: toISOString: "2018-01-02T23:02:56.927Z"
48139    3: toLocaleString: "1/2/2018, 11:40:40 PM"
48140    part: 1=date, 2=time 3=all
48141    XXX: should use a variant of strftime().
48142  */
get_date_string(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)48143 static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
48144                                int argc, JSValueConst *argv, int magic)
48145 {
48146     // _string(obj, fmt, part)
48147     char buf[64];
48148     double fields[9];
48149     int res, fmt, part, pos;
48150     int y, mon, d, h, m, s, ms, wd, tz;
48151 
48152     fmt = (magic >> 4) & 0x0F;
48153     part = magic & 0x0F;
48154 
48155     res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);
48156     if (res < 0)
48157         return JS_EXCEPTION;
48158     if (!res) {
48159         if (fmt == 2)
48160             return JS_ThrowRangeError(ctx, "Date value is NaN");
48161         else
48162             return JS_NewString(ctx, "Invalid Date");
48163     }
48164 
48165     y = fields[0];
48166     mon = fields[1];
48167     d = fields[2];
48168     h = fields[3];
48169     m = fields[4];
48170     s = fields[5];
48171     ms = fields[6];
48172     wd = fields[7];
48173     tz = fields[8];
48174 
48175     pos = 0;
48176 
48177     if (part & 1) { /* date part */
48178         switch(fmt) {
48179         case 0:
48180             pos += snprintf(buf + pos, sizeof(buf) - pos,
48181                             "%.3s, %02d %.3s %0*d ",
48182                             day_names + wd * 3, d,
48183                             month_names + mon * 3, 4 + (y < 0), y);
48184             break;
48185         case 1:
48186             pos += snprintf(buf + pos, sizeof(buf) - pos,
48187                             "%.3s %.3s %02d %0*d",
48188                             day_names + wd * 3,
48189                             month_names + mon * 3, d, 4 + (y < 0), y);
48190             if (part == 3) {
48191                 buf[pos++] = ' ';
48192             }
48193             break;
48194         case 2:
48195             if (y >= 0 && y <= 9999) {
48196                 pos += snprintf(buf + pos, sizeof(buf) - pos,
48197                                 "%04d", y);
48198             } else {
48199                 pos += snprintf(buf + pos, sizeof(buf) - pos,
48200                                 "%+07d", y);
48201             }
48202             pos += snprintf(buf + pos, sizeof(buf) - pos,
48203                             "-%02d-%02dT", mon + 1, d);
48204             break;
48205         case 3:
48206             pos += snprintf(buf + pos, sizeof(buf) - pos,
48207                             "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);
48208             if (part == 3) {
48209                 buf[pos++] = ',';
48210                 buf[pos++] = ' ';
48211             }
48212             break;
48213         }
48214     }
48215     if (part & 2) { /* time part */
48216         switch(fmt) {
48217         case 0:
48218             pos += snprintf(buf + pos, sizeof(buf) - pos,
48219                             "%02d:%02d:%02d GMT", h, m, s);
48220             break;
48221         case 1:
48222             pos += snprintf(buf + pos, sizeof(buf) - pos,
48223                             "%02d:%02d:%02d GMT", h, m, s);
48224             if (tz < 0) {
48225                 buf[pos++] = '-';
48226                 tz = -tz;
48227             } else {
48228                 buf[pos++] = '+';
48229             }
48230             /* tz is >= 0, can use % */
48231             pos += snprintf(buf + pos, sizeof(buf) - pos,
48232                             "%02d%02d", tz / 60, tz % 60);
48233             /* XXX: tack the time zone code? */
48234             break;
48235         case 2:
48236             pos += snprintf(buf + pos, sizeof(buf) - pos,
48237                             "%02d:%02d:%02d.%03dZ", h, m, s, ms);
48238             break;
48239         case 3:
48240             pos += snprintf(buf + pos, sizeof(buf) - pos,
48241                             "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s,
48242                             (h < 12) ? 'A' : 'P');
48243             break;
48244         }
48245     }
48246     return JS_NewStringLen(ctx, buf, pos);
48247 }
48248 
48249 /* OS dependent: return the UTC time in ms since 1970. */
date_now(void)48250 static int64_t date_now(void) {
48251     struct timeval tv;
48252     gettimeofday(&tv, NULL);
48253     return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
48254 }
48255 
js_date_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)48256 static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
48257                                    int argc, JSValueConst *argv)
48258 {
48259     // Date(y, mon, d, h, m, s, ms)
48260     JSValue rv;
48261     int i, n;
48262     double a, val;
48263 
48264     if (JS_IsUndefined(new_target)) {
48265         /* invoked as function */
48266         argc = 0;
48267     }
48268     n = argc;
48269     if (n == 0) {
48270         val = date_now();
48271     } else if (n == 1) {
48272         JSValue v, dv;
48273         if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
48274             JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
48275             if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {
48276                 if (JS_ToFloat64(ctx, &val, p->u.object_data))
48277                     return JS_EXCEPTION;
48278                 val = time_clip(val);
48279                 goto has_val;
48280             }
48281         }
48282         v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);
48283         if (JS_IsString(v)) {
48284             dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);
48285             JS_FreeValue(ctx, v);
48286             if (JS_IsException(dv))
48287                 return JS_EXCEPTION;
48288             if (JS_ToFloat64Free(ctx, &val, dv))
48289                 return JS_EXCEPTION;
48290         } else {
48291             if (JS_ToFloat64Free(ctx, &val, v))
48292                 return JS_EXCEPTION;
48293         }
48294         val = time_clip(val);
48295     } else {
48296         double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
48297         if (n > 7)
48298             n = 7;
48299         for(i = 0; i < n; i++) {
48300             if (JS_ToFloat64(ctx, &a, argv[i]))
48301                 return JS_EXCEPTION;
48302             if (!isfinite(a))
48303                 break;
48304             fields[i] = trunc(a);
48305             if (i == 0 && fields[0] >= 0 && fields[0] < 100)
48306                 fields[0] += 1900;
48307         }
48308         val = (i == n) ? set_date_fields(fields, 1) : NAN;
48309     }
48310 has_val:
48311 #if 0
48312     JSValueConst args[3];
48313     args[0] = new_target;
48314     args[1] = ctx->class_proto[JS_CLASS_DATE];
48315     args[2] = JS_NewFloat64(ctx, val);
48316     rv = js___date_create(ctx, JS_UNDEFINED, 3, args);
48317 #else
48318     rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);
48319     if (!JS_IsException(rv))
48320         JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val));
48321 #endif
48322     if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {
48323         /* invoked as a function, return (new Date()).toString(); */
48324         JSValue s;
48325         s = get_date_string(ctx, rv, 0, NULL, 0x13);
48326         JS_FreeValue(ctx, rv);
48327         rv = s;
48328     }
48329     return rv;
48330 }
48331 
js_Date_UTC(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48332 static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
48333                            int argc, JSValueConst *argv)
48334 {
48335     // UTC(y, mon, d, h, m, s, ms)
48336     double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
48337     int i, n;
48338     double a;
48339 
48340     n = argc;
48341     if (n == 0)
48342         return JS_NAN;
48343     if (n > 7)
48344         n = 7;
48345     for(i = 0; i < n; i++) {
48346         if (JS_ToFloat64(ctx, &a, argv[i]))
48347             return JS_EXCEPTION;
48348         if (!isfinite(a))
48349             return JS_NAN;
48350         fields[i] = trunc(a);
48351         if (i == 0 && fields[0] >= 0 && fields[0] < 100)
48352             fields[0] += 1900;
48353     }
48354     return JS_NewFloat64(ctx, set_date_fields(fields, 0));
48355 }
48356 
string_skip_spaces(JSString * sp,int * pp)48357 static void string_skip_spaces(JSString *sp, int *pp) {
48358     while (*pp < sp->len && string_get(sp, *pp) == ' ')
48359         *pp += 1;
48360 }
48361 
string_skip_non_spaces(JSString * sp,int * pp)48362 static void string_skip_non_spaces(JSString *sp, int *pp) {
48363     while (*pp < sp->len && string_get(sp, *pp) != ' ')
48364         *pp += 1;
48365 }
48366 
48367 /* parse a numeric field with an optional sign if accept_sign is TRUE */
string_get_digits(JSString * sp,int * pp,int64_t * pval)48368 static int string_get_digits(JSString *sp, int *pp, int64_t *pval) {
48369     int64_t v = 0;
48370     int c, p = *pp, p_start;
48371 
48372     if (p >= sp->len)
48373         return -1;
48374     p_start = p;
48375     while (p < sp->len) {
48376         c = string_get(sp, p);
48377         if (!(c >= '0' && c <= '9')) {
48378             if (p == p_start)
48379                 return -1;
48380             else
48381                 break;
48382         }
48383         v = v * 10 + c - '0';
48384         p++;
48385     }
48386     *pval = v;
48387     *pp = p;
48388     return 0;
48389 }
48390 
string_get_signed_digits(JSString * sp,int * pp,int64_t * pval)48391 static int string_get_signed_digits(JSString *sp, int *pp, int64_t *pval) {
48392     int res, sgn, p = *pp;
48393 
48394     if (p >= sp->len)
48395         return -1;
48396 
48397     sgn = string_get(sp, p);
48398     if (sgn == '-' || sgn == '+')
48399         p++;
48400 
48401     res = string_get_digits(sp, &p, pval);
48402     if (res == 0 && sgn == '-')
48403         *pval = -*pval;
48404     *pp = p;
48405     return res;
48406 }
48407 
48408 /* parse a fixed width numeric field */
string_get_fixed_width_digits(JSString * sp,int * pp,int n,int64_t * pval)48409 static int string_get_fixed_width_digits(JSString *sp, int *pp, int n, int64_t *pval) {
48410     int64_t v = 0;
48411     int i, c, p = *pp;
48412 
48413     for(i = 0; i < n; i++) {
48414         if (p >= sp->len)
48415             return -1;
48416         c = string_get(sp, p);
48417         if (!(c >= '0' && c <= '9'))
48418             return -1;
48419         v = v * 10 + c - '0';
48420         p++;
48421     }
48422     *pval = v;
48423     *pp = p;
48424     return 0;
48425 }
48426 
string_get_milliseconds(JSString * sp,int * pp,int64_t * pval)48427 static int string_get_milliseconds(JSString *sp, int *pp, int64_t *pval) {
48428     /* parse milliseconds as a fractional part, round to nearest */
48429     /* XXX: the spec does not indicate which rounding should be used */
48430     int mul = 1000, ms = 0, p = *pp, c, p_start;
48431     if (p >= sp->len)
48432         return -1;
48433     p_start = p;
48434     while (p < sp->len) {
48435         c = string_get(sp, p);
48436         if (!(c >= '0' && c <= '9')) {
48437             if (p == p_start)
48438                 return -1;
48439             else
48440                 break;
48441         }
48442         if (mul == 1 && c >= '5')
48443             ms += 1;
48444         ms += (c - '0') * (mul /= 10);
48445         p++;
48446     }
48447     *pval = ms;
48448     *pp = p;
48449     return 0;
48450 }
48451 
48452 
find_abbrev(JSString * sp,int p,const char * list,int count)48453 static int find_abbrev(JSString *sp, int p, const char *list, int count) {
48454     int n, i;
48455 
48456     if (p + 3 <= sp->len) {
48457         for (n = 0; n < count; n++) {
48458             for (i = 0; i < 3; i++) {
48459                 if (string_get(sp, p + i) != month_names[n * 3 + i])
48460                     goto next;
48461             }
48462             return n;
48463         next:;
48464         }
48465     }
48466     return -1;
48467 }
48468 
string_get_month(JSString * sp,int * pp,int64_t * pval)48469 static int string_get_month(JSString *sp, int *pp, int64_t *pval) {
48470     int n;
48471 
48472     string_skip_spaces(sp, pp);
48473     n = find_abbrev(sp, *pp, month_names, 12);
48474     if (n < 0)
48475         return -1;
48476 
48477     *pval = n;
48478     *pp += 3;
48479     return 0;
48480 }
48481 
js_Date_parse(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48482 static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
48483                              int argc, JSValueConst *argv)
48484 {
48485     // parse(s)
48486     JSValue s, rv;
48487     int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 };
48488     double fields1[7];
48489     int64_t tz, hh, mm;
48490     double d;
48491     int p, i, c, sgn, l;
48492     JSString *sp;
48493     BOOL is_local;
48494 
48495     rv = JS_NAN;
48496 
48497     s = JS_ToString(ctx, argv[0]);
48498     if (JS_IsException(s))
48499         return JS_EXCEPTION;
48500 
48501     sp = JS_VALUE_GET_STRING(s);
48502     p = 0;
48503     if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) {
48504         /* ISO format */
48505         /* year field can be negative */
48506         if (string_get_signed_digits(sp, &p, &fields[0]))
48507             goto done;
48508 
48509         for (i = 1; i < 7; i++) {
48510             if (p >= sp->len)
48511                 break;
48512             switch(i) {
48513             case 1:
48514             case 2:
48515                 c = '-';
48516                 break;
48517             case 3:
48518                 c = 'T';
48519                 break;
48520             case 4:
48521             case 5:
48522                 c = ':';
48523                 break;
48524             case 6:
48525                 c = '.';
48526                 break;
48527             }
48528             if (string_get(sp, p) != c)
48529                 break;
48530             p++;
48531             if (i == 6) {
48532                 if (string_get_milliseconds(sp, &p, &fields[i]))
48533                     goto done;
48534             } else {
48535                 if (string_get_digits(sp, &p, &fields[i]))
48536                     goto done;
48537             }
48538         }
48539         /* no time: UTC by default */
48540         is_local = (i > 3);
48541         fields[1] -= 1;
48542 
48543         /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */
48544         tz = 0;
48545         if (p < sp->len) {
48546             sgn = string_get(sp, p);
48547             if (sgn == '+' || sgn == '-') {
48548                 p++;
48549                 l = sp->len - p;
48550                 if (l != 4 && l != 5)
48551                     goto done;
48552                 if (string_get_fixed_width_digits(sp, &p, 2, &hh))
48553                     goto done;
48554                 if (l == 5) {
48555                     if (string_get(sp, p) != ':')
48556                         goto done;
48557                     p++;
48558                 }
48559                 if (string_get_fixed_width_digits(sp, &p, 2, &mm))
48560                     goto done;
48561                 tz = hh * 60 + mm;
48562                 if (sgn == '-')
48563                     tz = -tz;
48564                 is_local = FALSE;
48565             } else if (sgn == 'Z') {
48566                 p++;
48567                 is_local = FALSE;
48568             } else {
48569                 goto done;
48570             }
48571             /* error if extraneous characters */
48572             if (p != sp->len)
48573                 goto done;
48574         }
48575     } else {
48576         /* toString or toUTCString format */
48577         /* skip the day of the week */
48578         string_skip_non_spaces(sp, &p);
48579         string_skip_spaces(sp, &p);
48580         if (p >= sp->len)
48581             goto done;
48582         c = string_get(sp, p);
48583         if (c >= '0' && c <= '9') {
48584             /* day of month first */
48585             if (string_get_digits(sp, &p, &fields[2]))
48586                 goto done;
48587             if (string_get_month(sp, &p, &fields[1]))
48588                 goto done;
48589         } else {
48590             /* month first */
48591             if (string_get_month(sp, &p, &fields[1]))
48592                 goto done;
48593             string_skip_spaces(sp, &p);
48594             if (string_get_digits(sp, &p, &fields[2]))
48595                 goto done;
48596         }
48597         /* year */
48598         string_skip_spaces(sp, &p);
48599         if (string_get_signed_digits(sp, &p, &fields[0]))
48600             goto done;
48601 
48602         /* hour, min, seconds */
48603         string_skip_spaces(sp, &p);
48604         for(i = 0; i < 3; i++) {
48605             if (i == 1 || i == 2) {
48606                 if (p >= sp->len)
48607                     goto done;
48608                 if (string_get(sp, p) != ':')
48609                     goto done;
48610                 p++;
48611             }
48612             if (string_get_digits(sp, &p, &fields[3 + i]))
48613                 goto done;
48614         }
48615         // XXX: parse optional milliseconds?
48616 
48617         /* parse the time zone offset if present: [+-]HHmm */
48618         is_local = FALSE;
48619         tz = 0;
48620         for (tz = 0; p < sp->len; p++) {
48621             sgn = string_get(sp, p);
48622             if (sgn == '+' || sgn == '-') {
48623                 p++;
48624                 if (string_get_fixed_width_digits(sp, &p, 2, &hh))
48625                     goto done;
48626                 if (string_get_fixed_width_digits(sp, &p, 2, &mm))
48627                     goto done;
48628                 tz = hh * 60 + mm;
48629                 if (sgn == '-')
48630                     tz = -tz;
48631                 break;
48632             }
48633         }
48634     }
48635     for(i = 0; i < 7; i++)
48636         fields1[i] = fields[i];
48637     d = set_date_fields(fields1, is_local) - tz * 60000;
48638     rv = JS_NewFloat64(ctx, d);
48639 
48640 done:
48641     JS_FreeValue(ctx, s);
48642     return rv;
48643 }
48644 
js_Date_now(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48645 static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,
48646                            int argc, JSValueConst *argv)
48647 {
48648     // now()
48649     return JS_NewInt64(ctx, date_now());
48650 }
48651 
js_date_Symbol_toPrimitive(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48652 static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,
48653                                           int argc, JSValueConst *argv)
48654 {
48655     // Symbol_toPrimitive(hint)
48656     JSValueConst obj = this_val;
48657     JSAtom hint = JS_ATOM_NULL;
48658     int hint_num;
48659 
48660     if (!JS_IsObject(obj))
48661         return JS_ThrowTypeErrorNotAnObject(ctx);
48662 
48663     if (JS_IsString(argv[0])) {
48664         hint = JS_ValueToAtom(ctx, argv[0]);
48665         if (hint == JS_ATOM_NULL)
48666             return JS_EXCEPTION;
48667         JS_FreeAtom(ctx, hint);
48668     }
48669     switch (hint) {
48670     case JS_ATOM_number:
48671 #ifdef CONFIG_BIGNUM
48672     case JS_ATOM_integer:
48673 #endif
48674         hint_num = HINT_NUMBER;
48675         break;
48676     case JS_ATOM_string:
48677     case JS_ATOM_default:
48678         hint_num = HINT_STRING;
48679         break;
48680     default:
48681         return JS_ThrowTypeError(ctx, "invalid hint");
48682     }
48683     return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);
48684 }
48685 
js_date_getTimezoneOffset(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48686 static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
48687                                          int argc, JSValueConst *argv)
48688 {
48689     // getTimezoneOffset()
48690     double v;
48691 
48692     if (JS_ThisTimeValue(ctx, &v, this_val))
48693         return JS_EXCEPTION;
48694     if (isnan(v))
48695         return JS_NAN;
48696     else
48697         return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));
48698 }
48699 
js_date_getTime(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48700 static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,
48701                                int argc, JSValueConst *argv)
48702 {
48703     // getTime()
48704     double v;
48705 
48706     if (JS_ThisTimeValue(ctx, &v, this_val))
48707         return JS_EXCEPTION;
48708     return JS_NewFloat64(ctx, v);
48709 }
48710 
js_date_setTime(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48711 static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,
48712                                int argc, JSValueConst *argv)
48713 {
48714     // setTime(v)
48715     double v;
48716 
48717     if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))
48718         return JS_EXCEPTION;
48719     return JS_SetThisTimeValue(ctx, this_val, time_clip(v));
48720 }
48721 
js_date_setYear(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48722 static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,
48723                                int argc, JSValueConst *argv)
48724 {
48725     // setYear(y)
48726     double y;
48727     JSValueConst args[1];
48728 
48729     if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))
48730         return JS_EXCEPTION;
48731     y = +y;
48732     if (isfinite(y)) {
48733         y = trunc(y);
48734         if (y >= 0 && y < 100)
48735             y += 1900;
48736     }
48737     args[0] = JS_NewFloat64(ctx, y);
48738     return set_date_field(ctx, this_val, 1, args, 0x011);
48739 }
48740 
js_date_toJSON(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48741 static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,
48742                               int argc, JSValueConst *argv)
48743 {
48744     // toJSON(key)
48745     JSValue obj, tv, method, rv;
48746     double d;
48747 
48748     rv = JS_EXCEPTION;
48749     tv = JS_UNDEFINED;
48750 
48751     obj = JS_ToObject(ctx, this_val);
48752     tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);
48753     if (JS_IsException(tv))
48754         goto exception;
48755     if (JS_IsNumber(tv)) {
48756         if (JS_ToFloat64(ctx, &d, tv) < 0)
48757             goto exception;
48758         if (!isfinite(d)) {
48759             rv = JS_NULL;
48760             goto done;
48761         }
48762     }
48763     method = JS_GetPropertyStr(ctx, obj, "toISOString");
48764     if (JS_IsException(method))
48765         goto exception;
48766     if (!JS_IsFunction(ctx, method)) {
48767         JS_ThrowTypeError(ctx, "object needs toISOString method");
48768         JS_FreeValue(ctx, method);
48769         goto exception;
48770     }
48771     rv = JS_CallFree(ctx, method, obj, 0, NULL);
48772 exception:
48773 done:
48774     JS_FreeValue(ctx, obj);
48775     JS_FreeValue(ctx, tv);
48776     return rv;
48777 }
48778 
48779 static const JSCFunctionListEntry js_date_funcs[] = {
48780     JS_CFUNC_DEF("now", 0, js_Date_now ),
48781     JS_CFUNC_DEF("parse", 1, js_Date_parse ),
48782     JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),
48783 };
48784 
48785 static const JSCFunctionListEntry js_date_proto_funcs[] = {
48786     JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),
48787     JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),
48788     JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),
48789     JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),
48790     JS_ALIAS_DEF("toGMTString", "toUTCString" ),
48791     JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),
48792     JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),
48793     JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),
48794     JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),
48795     JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),
48796     JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),
48797     JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),
48798     JS_CFUNC_DEF("getTime", 0, js_date_getTime ),
48799     JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),
48800     JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),
48801     JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),
48802     JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),
48803     JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),
48804     JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),
48805     JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),
48806     JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),
48807     JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),
48808     JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),
48809     JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),
48810     JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),
48811     JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),
48812     JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),
48813     JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),
48814     JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),
48815     JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),
48816     JS_CFUNC_DEF("setTime", 1, js_date_setTime ),
48817     JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),
48818     JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),
48819     JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),
48820     JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),
48821     JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),
48822     JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),
48823     JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),
48824     JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),
48825     JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),
48826     JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),
48827     JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),
48828     JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),
48829     JS_CFUNC_DEF("setYear", 1, js_date_setYear ),
48830     JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),
48831     JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),
48832     JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),
48833 };
48834 
JS_AddIntrinsicDate(JSContext * ctx)48835 void JS_AddIntrinsicDate(JSContext *ctx)
48836 {
48837     JSValueConst obj;
48838 
48839     /* Date */
48840     ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx);
48841     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs,
48842                                countof(js_date_proto_funcs));
48843     obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7,
48844                                    ctx->class_proto[JS_CLASS_DATE]);
48845     JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs));
48846 }
48847 
48848 /* eval */
48849 
JS_AddIntrinsicEval(JSContext * ctx)48850 void JS_AddIntrinsicEval(JSContext *ctx)
48851 {
48852     ctx->eval_internal = __JS_EvalInternal;
48853 }
48854 
48855 #ifdef CONFIG_BIGNUM
48856 
48857 /* Operators */
48858 
js_operator_set_finalizer(JSRuntime * rt,JSValue val)48859 static void js_operator_set_finalizer(JSRuntime *rt, JSValue val)
48860 {
48861     JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
48862     int i, j;
48863     JSBinaryOperatorDefEntry *ent;
48864 
48865     if (opset) {
48866         for(i = 0; i < JS_OVOP_COUNT; i++) {
48867             if (opset->self_ops[i])
48868                 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]));
48869         }
48870         for(j = 0; j < opset->left.count; j++) {
48871             ent = &opset->left.tab[j];
48872             for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
48873                 if (ent->ops[i])
48874                     JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
48875             }
48876         }
48877         js_free_rt(rt, opset->left.tab);
48878         for(j = 0; j < opset->right.count; j++) {
48879             ent = &opset->right.tab[j];
48880             for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
48881                 if (ent->ops[i])
48882                     JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
48883             }
48884         }
48885         js_free_rt(rt, opset->right.tab);
48886         js_free_rt(rt, opset);
48887     }
48888 }
48889 
js_operator_set_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)48890 static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
48891                                  JS_MarkFunc *mark_func)
48892 {
48893     JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
48894     int i, j;
48895     JSBinaryOperatorDefEntry *ent;
48896 
48897     if (opset) {
48898         for(i = 0; i < JS_OVOP_COUNT; i++) {
48899             if (opset->self_ops[i])
48900                 JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]),
48901                              mark_func);
48902         }
48903         for(j = 0; j < opset->left.count; j++) {
48904             ent = &opset->left.tab[j];
48905             for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
48906                 if (ent->ops[i])
48907                     JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
48908                                  mark_func);
48909             }
48910         }
48911         for(j = 0; j < opset->right.count; j++) {
48912             ent = &opset->right.tab[j];
48913             for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
48914                 if (ent->ops[i])
48915                     JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
48916                                  mark_func);
48917             }
48918         }
48919     }
48920 }
48921 
48922 
48923 /* create an OperatorSet object */
js_operators_create_internal(JSContext * ctx,int argc,JSValueConst * argv,BOOL is_primitive)48924 static JSValue js_operators_create_internal(JSContext *ctx,
48925                                             int argc, JSValueConst *argv,
48926                                             BOOL is_primitive)
48927 {
48928     JSValue opset_obj, prop, obj;
48929     JSOperatorSetData *opset, *opset1;
48930     JSBinaryOperatorDef *def;
48931     JSValueConst arg;
48932     int i, j;
48933     JSBinaryOperatorDefEntry *new_tab;
48934     JSBinaryOperatorDefEntry *ent;
48935     uint32_t op_count;
48936 
48937     if (ctx->rt->operator_count == UINT32_MAX) {
48938         return JS_ThrowTypeError(ctx, "too many operators");
48939     }
48940     opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET);
48941     if (JS_IsException(opset_obj))
48942         goto fail;
48943     opset = js_mallocz(ctx, sizeof(*opset));
48944     if (!opset)
48945         goto fail;
48946     JS_SetOpaque(opset_obj, opset);
48947     if (argc >= 1) {
48948         arg = argv[0];
48949         /* self operators */
48950         for(i = 0; i < JS_OVOP_COUNT; i++) {
48951             prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]);
48952             if (JS_IsException(prop))
48953                 goto fail;
48954             if (!JS_IsUndefined(prop)) {
48955                 if (check_function(ctx, prop)) {
48956                     JS_FreeValue(ctx, prop);
48957                     goto fail;
48958                 }
48959                 opset->self_ops[i] = JS_VALUE_GET_OBJ(prop);
48960             }
48961         }
48962     }
48963     /* left & right operators */
48964     for(j = 1; j < argc; j++) {
48965         arg = argv[j];
48966         prop = JS_GetPropertyStr(ctx, arg, "left");
48967         if (JS_IsException(prop))
48968             goto fail;
48969         def = &opset->right;
48970         if (JS_IsUndefined(prop)) {
48971             prop = JS_GetPropertyStr(ctx, arg, "right");
48972             if (JS_IsException(prop))
48973                 goto fail;
48974             if (JS_IsUndefined(prop)) {
48975                 JS_ThrowTypeError(ctx, "left or right property must be present");
48976                 goto fail;
48977             }
48978             def = &opset->left;
48979         }
48980         /* get the operator set */
48981         obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype);
48982         JS_FreeValue(ctx, prop);
48983         if (JS_IsException(obj))
48984             goto fail;
48985         prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
48986         JS_FreeValue(ctx, obj);
48987         if (JS_IsException(prop))
48988             goto fail;
48989         opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET);
48990         if (!opset1) {
48991             JS_FreeValue(ctx, prop);
48992             goto fail;
48993         }
48994         op_count = opset1->operator_counter;
48995         JS_FreeValue(ctx, prop);
48996 
48997         /* we assume there are few entries */
48998         new_tab = js_realloc(ctx, def->tab,
48999                              (def->count + 1) * sizeof(def->tab[0]));
49000         if (!new_tab)
49001             goto fail;
49002         def->tab = new_tab;
49003         def->count++;
49004         ent = def->tab + def->count - 1;
49005         memset(ent, 0, sizeof(def->tab[0]));
49006         ent->operator_index = op_count;
49007 
49008         for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
49009             prop = JS_GetPropertyStr(ctx, arg,
49010                                      js_overloadable_operator_names[i]);
49011             if (JS_IsException(prop))
49012                 goto fail;
49013             if (!JS_IsUndefined(prop)) {
49014                 if (check_function(ctx, prop)) {
49015                     JS_FreeValue(ctx, prop);
49016                     goto fail;
49017                 }
49018                 ent->ops[i] = JS_VALUE_GET_OBJ(prop);
49019             }
49020         }
49021     }
49022     opset->is_primitive = is_primitive;
49023     opset->operator_counter = ctx->rt->operator_count++;
49024     return opset_obj;
49025  fail:
49026     JS_FreeValue(ctx, opset_obj);
49027     return JS_EXCEPTION;
49028 }
49029 
js_operators_create(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49030 static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val,
49031                                 int argc, JSValueConst *argv)
49032 {
49033     return js_operators_create_internal(ctx, argc, argv, FALSE);
49034 }
49035 
js_operators_updateBigIntOperators(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49036 static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val,
49037                                                   int argc, JSValueConst *argv)
49038 {
49039     JSValue opset_obj, prop;
49040     JSOperatorSetData *opset;
49041     const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW };
49042     JSOverloadableOperatorEnum op;
49043     int i;
49044 
49045     opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
49046                                JS_ATOM_Symbol_operatorSet);
49047     if (JS_IsException(opset_obj))
49048         goto fail;
49049     opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET);
49050     if (!opset)
49051         goto fail;
49052     for(i = 0; i < countof(ops); i++) {
49053         op = ops[i];
49054         prop = JS_GetPropertyStr(ctx, argv[0],
49055                                  js_overloadable_operator_names[op]);
49056         if (JS_IsException(prop))
49057             goto fail;
49058         if (!JS_IsUndefined(prop)) {
49059             if (!JS_IsNull(prop) && check_function(ctx, prop)) {
49060                 JS_FreeValue(ctx, prop);
49061                 goto fail;
49062             }
49063             if (opset->self_ops[op])
49064                 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op]));
49065             if (JS_IsNull(prop)) {
49066                 opset->self_ops[op] = NULL;
49067             } else {
49068                 opset->self_ops[op] = JS_VALUE_GET_PTR(prop);
49069             }
49070         }
49071     }
49072     JS_FreeValue(ctx, opset_obj);
49073     return JS_UNDEFINED;
49074  fail:
49075     JS_FreeValue(ctx, opset_obj);
49076     return JS_EXCEPTION;
49077 }
49078 
js_operators_set_default(JSContext * ctx,JSValueConst obj)49079 static int js_operators_set_default(JSContext *ctx, JSValueConst obj)
49080 {
49081     JSValue opset_obj;
49082 
49083     if (!JS_IsObject(obj)) /* in case the prototype is not defined */
49084         return 0;
49085     opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE);
49086     if (JS_IsException(opset_obj))
49087         return -1;
49088     /* cannot be modified by the user */
49089     JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet,
49090                            opset_obj, 0);
49091     return 0;
49092 }
49093 
js_dummy_operators_ctor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)49094 static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target,
49095                                        int argc, JSValueConst *argv)
49096 {
49097     return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
49098 }
49099 
js_global_operators(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49100 static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val,
49101                                    int argc, JSValueConst *argv)
49102 {
49103     JSValue func_obj, proto, opset_obj;
49104 
49105     func_obj = JS_UNDEFINED;
49106     proto = JS_NewObject(ctx);
49107     if (JS_IsException(proto))
49108         return JS_EXCEPTION;
49109     opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE);
49110     if (JS_IsException(opset_obj))
49111         goto fail;
49112     JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet,
49113                            opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
49114     func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators",
49115                                 0, JS_CFUNC_constructor, 0);
49116     if (JS_IsException(func_obj))
49117         goto fail;
49118     JS_SetConstructor2(ctx, func_obj, proto,
49119                        0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
49120     JS_FreeValue(ctx, proto);
49121     return func_obj;
49122  fail:
49123     JS_FreeValue(ctx, proto);
49124     JS_FreeValue(ctx, func_obj);
49125     return JS_EXCEPTION;
49126 }
49127 
49128 static const JSCFunctionListEntry js_operators_funcs[] = {
49129     JS_CFUNC_DEF("create", 1, js_operators_create ),
49130     JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ),
49131 };
49132 
49133 /* must be called after all overloadable base types are initialized */
JS_AddIntrinsicOperators(JSContext * ctx)49134 void JS_AddIntrinsicOperators(JSContext *ctx)
49135 {
49136     JSValue obj;
49137 
49138     ctx->allow_operator_overloading = TRUE;
49139     obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1);
49140     JS_SetPropertyFunctionList(ctx, obj,
49141                                js_operators_funcs,
49142                                countof(js_operators_funcs));
49143     JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators,
49144                            obj,
49145                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
49146     /* add default operatorSets */
49147     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]);
49148     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]);
49149     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]);
49150     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]);
49151     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]);
49152     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
49153 }
49154 
49155 /* BigInt */
49156 
JS_ToBigIntCtorFree(JSContext * ctx,JSValue val)49157 static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
49158 {
49159     uint32_t tag;
49160 
49161  redo:
49162     tag = JS_VALUE_GET_NORM_TAG(val);
49163     switch(tag) {
49164     case JS_TAG_INT:
49165     case JS_TAG_BOOL:
49166         val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
49167         break;
49168     case JS_TAG_BIG_INT:
49169         break;
49170     case JS_TAG_FLOAT64:
49171     case JS_TAG_BIG_FLOAT:
49172         {
49173             bf_t *a, a_s;
49174 
49175             a = JS_ToBigFloat(ctx, &a_s, val);
49176             if (!bf_is_finite(a)) {
49177                 JS_FreeValue(ctx, val);
49178                 val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint");
49179             } else {
49180                 JSValue val1 = JS_NewBigInt(ctx);
49181                 bf_t *r;
49182                 int ret;
49183                 if (JS_IsException(val1)) {
49184                     JS_FreeValue(ctx, val);
49185                     return JS_EXCEPTION;
49186                 }
49187                 r = JS_GetBigInt(val1);
49188                 ret = bf_set(r, a);
49189                 ret |= bf_rint(r, BF_RNDZ);
49190                 JS_FreeValue(ctx, val);
49191                 if (ret & BF_ST_MEM_ERROR) {
49192                     JS_FreeValue(ctx, val1);
49193                     val = JS_ThrowOutOfMemory(ctx);
49194                 } else if (ret & BF_ST_INEXACT) {
49195                     JS_FreeValue(ctx, val1);
49196                     val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer");
49197                 } else {
49198                     val = JS_CompactBigInt(ctx, val1);
49199                 }
49200             }
49201             if (a == &a_s)
49202                 bf_delete(a);
49203         }
49204         break;
49205     case JS_TAG_BIG_DECIMAL:
49206         val = JS_ToStringFree(ctx, val);
49207          if (JS_IsException(val))
49208             break;
49209         goto redo;
49210     case JS_TAG_STRING:
49211         val = JS_StringToBigIntErr(ctx, val);
49212         break;
49213     case JS_TAG_OBJECT:
49214         val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
49215         if (JS_IsException(val))
49216             break;
49217         goto redo;
49218     case JS_TAG_NULL:
49219     case JS_TAG_UNDEFINED:
49220     default:
49221         JS_FreeValue(ctx, val);
49222         return JS_ThrowTypeError(ctx, "cannot convert to bigint");
49223     }
49224     return val;
49225 }
49226 
js_bigint_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)49227 static JSValue js_bigint_constructor(JSContext *ctx,
49228                                      JSValueConst new_target,
49229                                      int argc, JSValueConst *argv)
49230 {
49231     if (!JS_IsUndefined(new_target))
49232         return JS_ThrowTypeError(ctx, "not a constructor");
49233     return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
49234 }
49235 
js_thisBigIntValue(JSContext * ctx,JSValueConst this_val)49236 static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
49237 {
49238     if (JS_IsBigInt(ctx, this_val))
49239         return JS_DupValue(ctx, this_val);
49240 
49241     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
49242         JSObject *p = JS_VALUE_GET_OBJ(this_val);
49243         if (p->class_id == JS_CLASS_BIG_INT) {
49244             if (JS_IsBigInt(ctx, p->u.object_data))
49245                 return JS_DupValue(ctx, p->u.object_data);
49246         }
49247     }
49248     return JS_ThrowTypeError(ctx, "not a bigint");
49249 }
49250 
js_bigint_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49251 static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
49252                                   int argc, JSValueConst *argv)
49253 {
49254     JSValue val;
49255     int base;
49256     JSValue ret;
49257 
49258     val = js_thisBigIntValue(ctx, this_val);
49259     if (JS_IsException(val))
49260         return val;
49261     if (argc == 0 || JS_IsUndefined(argv[0])) {
49262         base = 10;
49263     } else {
49264         base = js_get_radix(ctx, argv[0]);
49265         if (base < 0)
49266             goto fail;
49267     }
49268     ret = js_bigint_to_string1(ctx, val, base);
49269     JS_FreeValue(ctx, val);
49270     return ret;
49271  fail:
49272     JS_FreeValue(ctx, val);
49273     return JS_EXCEPTION;
49274 }
49275 
js_bigint_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49276 static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,
49277                                  int argc, JSValueConst *argv)
49278 {
49279     return js_thisBigIntValue(ctx, this_val);
49280 }
49281 
js_bigint_div(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)49282 static JSValue js_bigint_div(JSContext *ctx,
49283                               JSValueConst this_val,
49284                               int argc, JSValueConst *argv, int magic)
49285 {
49286     bf_t a_s, b_s, *a, *b, *r, *q;
49287     int status;
49288     JSValue q_val, r_val;
49289 
49290     q_val = JS_NewBigInt(ctx);
49291     if (JS_IsException(q_val))
49292         return JS_EXCEPTION;
49293     r_val = JS_NewBigInt(ctx);
49294     if (JS_IsException(r_val))
49295         goto fail;
49296     b = NULL;
49297     a = JS_ToBigInt(ctx, &a_s, argv[0]);
49298     if (!a)
49299         goto fail;
49300     b = JS_ToBigInt(ctx, &b_s, argv[1]);
49301     if (!b) {
49302         JS_FreeBigInt(ctx, a, &a_s);
49303         goto fail;
49304     }
49305     q = JS_GetBigInt(q_val);
49306     r = JS_GetBigInt(r_val);
49307     status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf);
49308     JS_FreeBigInt(ctx, a, &a_s);
49309     JS_FreeBigInt(ctx, b, &b_s);
49310     if (unlikely(status)) {
49311         throw_bf_exception(ctx, status);
49312         goto fail;
49313     }
49314     q_val = JS_CompactBigInt(ctx, q_val);
49315     if (magic & 0x10) {
49316         JSValue ret;
49317         ret = JS_NewArray(ctx);
49318         if (JS_IsException(ret))
49319             goto fail;
49320         JS_SetPropertyUint32(ctx, ret, 0, q_val);
49321         JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val));
49322         return ret;
49323     } else {
49324         JS_FreeValue(ctx, r_val);
49325         return q_val;
49326     }
49327  fail:
49328     JS_FreeValue(ctx, q_val);
49329     JS_FreeValue(ctx, r_val);
49330     return JS_EXCEPTION;
49331 }
49332 
js_bigint_sqrt(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)49333 static JSValue js_bigint_sqrt(JSContext *ctx,
49334                                JSValueConst this_val,
49335                                int argc, JSValueConst *argv, int magic)
49336 {
49337     bf_t a_s, *a, *r, *rem;
49338     int status;
49339     JSValue r_val, rem_val;
49340 
49341     r_val = JS_NewBigInt(ctx);
49342     if (JS_IsException(r_val))
49343         return JS_EXCEPTION;
49344     rem_val = JS_NewBigInt(ctx);
49345     if (JS_IsException(rem_val))
49346         return JS_EXCEPTION;
49347     r = JS_GetBigInt(r_val);
49348     rem = JS_GetBigInt(rem_val);
49349 
49350     a = JS_ToBigInt(ctx, &a_s, argv[0]);
49351     if (!a)
49352         goto fail;
49353     status = bf_sqrtrem(r, rem, a);
49354     JS_FreeBigInt(ctx, a, &a_s);
49355     if (unlikely(status & ~BF_ST_INEXACT)) {
49356         throw_bf_exception(ctx, status);
49357         goto fail;
49358     }
49359     r_val = JS_CompactBigInt(ctx, r_val);
49360     if (magic) {
49361         JSValue ret;
49362         ret = JS_NewArray(ctx);
49363         if (JS_IsException(ret))
49364             goto fail;
49365         JS_SetPropertyUint32(ctx, ret, 0, r_val);
49366         JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val));
49367         return ret;
49368     } else {
49369         JS_FreeValue(ctx, rem_val);
49370         return r_val;
49371     }
49372  fail:
49373     JS_FreeValue(ctx, r_val);
49374     JS_FreeValue(ctx, rem_val);
49375     return JS_EXCEPTION;
49376 }
49377 
js_bigint_op1(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)49378 static JSValue js_bigint_op1(JSContext *ctx,
49379                               JSValueConst this_val,
49380                               int argc, JSValueConst *argv,
49381                               int magic)
49382 {
49383     bf_t a_s, *a;
49384     int64_t res;
49385 
49386     a = JS_ToBigInt(ctx, &a_s, argv[0]);
49387     if (!a)
49388         return JS_EXCEPTION;
49389     switch(magic) {
49390     case 0: /* floorLog2 */
49391         if (a->sign || a->expn <= 0) {
49392             res = -1;
49393         } else {
49394             res = a->expn - 1;
49395         }
49396         break;
49397     case 1: /* ctz */
49398         if (bf_is_zero(a)) {
49399             res = -1;
49400         } else {
49401             res = bf_get_exp_min(a);
49402         }
49403         break;
49404     default:
49405         abort();
49406     }
49407     JS_FreeBigInt(ctx, a, &a_s);
49408     return JS_NewBigInt64(ctx, res);
49409 }
49410 
js_bigint_asUintN(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int asIntN)49411 static JSValue js_bigint_asUintN(JSContext *ctx,
49412                                   JSValueConst this_val,
49413                                   int argc, JSValueConst *argv, int asIntN)
49414 {
49415     uint64_t bits;
49416     bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s;
49417     JSValue res;
49418 
49419     if (JS_ToIndex(ctx, &bits, argv[0]))
49420         return JS_EXCEPTION;
49421     res = JS_NewBigInt(ctx);
49422     if (JS_IsException(res))
49423         return JS_EXCEPTION;
49424     r = JS_GetBigInt(res);
49425     a = JS_ToBigInt(ctx, &a_s, argv[1]);
49426     if (!a) {
49427         JS_FreeValue(ctx, res);
49428         return JS_EXCEPTION;
49429     }
49430     /* XXX: optimize */
49431     r = JS_GetBigInt(res);
49432     bf_init(ctx->bf_ctx, mask);
49433     bf_set_ui(mask, 1);
49434     bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
49435     bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ);
49436     bf_logic_and(r, a, mask);
49437     if (asIntN && bits != 0) {
49438         bf_set_ui(mask, 1);
49439         bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ);
49440         if (bf_cmpu(r, mask) >= 0) {
49441             bf_set_ui(mask, 1);
49442             bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
49443             bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ);
49444         }
49445     }
49446     bf_delete(mask);
49447     JS_FreeBigInt(ctx, a, &a_s);
49448     return JS_CompactBigInt(ctx, res);
49449 }
49450 
49451 static const JSCFunctionListEntry js_bigint_funcs[] = {
49452     JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),
49453     JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),
49454     /* QuickJS extensions */
49455     JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ),
49456     JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ),
49457     JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ),
49458     JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ),
49459     JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ),
49460     JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ),
49461     JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ),
49462     JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ),
49463     JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ),
49464     JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ),
49465     JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ),
49466     JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ),
49467 };
49468 
49469 static const JSCFunctionListEntry js_bigint_proto_funcs[] = {
49470     JS_CFUNC_DEF("toString", 0, js_bigint_toString ),
49471     JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),
49472     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),
49473 };
49474 
JS_AddIntrinsicBigInt(JSContext * ctx)49475 void JS_AddIntrinsicBigInt(JSContext *ctx)
49476 {
49477     JSRuntime *rt = ctx->rt;
49478     JSValueConst obj1;
49479 
49480     rt->bigint_ops.to_string = js_bigint_to_string;
49481     rt->bigint_ops.from_string = js_string_to_bigint;
49482     rt->bigint_ops.unary_arith = js_unary_arith_bigint;
49483     rt->bigint_ops.binary_arith = js_binary_arith_bigint;
49484     rt->bigint_ops.compare = js_compare_bigfloat;
49485 
49486     ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);
49487     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
49488                                js_bigint_proto_funcs,
49489                                countof(js_bigint_proto_funcs));
49490     obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1,
49491                                     ctx->class_proto[JS_CLASS_BIG_INT]);
49492     JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,
49493                                countof(js_bigint_funcs));
49494 }
49495 
49496 /* BigFloat */
49497 
js_thisBigFloatValue(JSContext * ctx,JSValueConst this_val)49498 static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val)
49499 {
49500     if (JS_IsBigFloat(this_val))
49501         return JS_DupValue(ctx, this_val);
49502 
49503     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
49504         JSObject *p = JS_VALUE_GET_OBJ(this_val);
49505         if (p->class_id == JS_CLASS_BIG_FLOAT) {
49506             if (JS_IsBigFloat(p->u.object_data))
49507                 return JS_DupValue(ctx, p->u.object_data);
49508         }
49509     }
49510     return JS_ThrowTypeError(ctx, "not a bigfloat");
49511 }
49512 
js_bigfloat_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49513 static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val,
49514                                     int argc, JSValueConst *argv)
49515 {
49516     JSValue val;
49517     int base;
49518     JSValue ret;
49519 
49520     val = js_thisBigFloatValue(ctx, this_val);
49521     if (JS_IsException(val))
49522         return val;
49523     if (argc == 0 || JS_IsUndefined(argv[0])) {
49524         base = 10;
49525     } else {
49526         base = js_get_radix(ctx, argv[0]);
49527         if (base < 0)
49528             goto fail;
49529     }
49530     ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
49531     JS_FreeValue(ctx, val);
49532     return ret;
49533  fail:
49534     JS_FreeValue(ctx, val);
49535     return JS_EXCEPTION;
49536 }
49537 
js_bigfloat_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49538 static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val,
49539                                    int argc, JSValueConst *argv)
49540 {
49541     return js_thisBigFloatValue(ctx, this_val);
49542 }
49543 
bigfloat_get_rnd_mode(JSContext * ctx,JSValueConst val)49544 static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val)
49545 {
49546     int rnd_mode;
49547     if (JS_ToInt32Sat(ctx, &rnd_mode, val))
49548         return -1;
49549     if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) {
49550         JS_ThrowRangeError(ctx, "invalid rounding mode");
49551         return -1;
49552     }
49553     return rnd_mode;
49554 }
49555 
js_bigfloat_toFixed(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49556 static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val,
49557                                  int argc, JSValueConst *argv)
49558 {
49559     JSValue val, ret;
49560     int64_t f;
49561     int rnd_mode, radix;
49562 
49563     val = js_thisBigFloatValue(ctx, this_val);
49564     if (JS_IsException(val))
49565         return val;
49566     if (JS_ToInt64Sat(ctx, &f, argv[0]))
49567         goto fail;
49568     if (f < 0 || f > BF_PREC_MAX) {
49569         JS_ThrowRangeError(ctx, "invalid number of digits");
49570         goto fail;
49571     }
49572     rnd_mode = BF_RNDNA;
49573     radix = 10;
49574     /* XXX: swap parameter order for rounding mode and radix */
49575     if (argc > 1) {
49576         rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
49577         if (rnd_mode < 0)
49578             goto fail;
49579     }
49580     if (argc > 2) {
49581         radix = js_get_radix(ctx, argv[2]);
49582         if (radix < 0)
49583             goto fail;
49584     }
49585     ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
49586     JS_FreeValue(ctx, val);
49587     return ret;
49588  fail:
49589     JS_FreeValue(ctx, val);
49590     return JS_EXCEPTION;
49591 }
49592 
js_bigfloat_is_finite(JSContext * ctx,JSValueConst val)49593 static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val)
49594 {
49595     BOOL res;
49596     uint32_t tag;
49597 
49598     tag = JS_VALUE_GET_NORM_TAG(val);
49599     switch(tag) {
49600     case JS_TAG_BIG_FLOAT:
49601         {
49602             JSBigFloat *p = JS_VALUE_GET_PTR(val);
49603             res = bf_is_finite(&p->num);
49604         }
49605         break;
49606     default:
49607         res = FALSE;
49608         break;
49609     }
49610     return res;
49611 }
49612 
js_bigfloat_toExponential(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49613 static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val,
49614                                        int argc, JSValueConst *argv)
49615 {
49616     JSValue val, ret;
49617     int64_t f;
49618     int rnd_mode, radix;
49619 
49620     val = js_thisBigFloatValue(ctx, this_val);
49621     if (JS_IsException(val))
49622         return val;
49623     if (JS_ToInt64Sat(ctx, &f, argv[0]))
49624         goto fail;
49625     if (!js_bigfloat_is_finite(ctx, val)) {
49626         ret = JS_ToString(ctx, val);
49627     } else if (JS_IsUndefined(argv[0])) {
49628         ret = js_ftoa(ctx, val, 10, 0,
49629                       BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
49630     } else {
49631         if (f < 0 || f > BF_PREC_MAX) {
49632             JS_ThrowRangeError(ctx, "invalid number of digits");
49633             goto fail;
49634         }
49635         rnd_mode = BF_RNDNA;
49636         radix = 10;
49637         if (argc > 1) {
49638             rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
49639             if (rnd_mode < 0)
49640                 goto fail;
49641         }
49642         if (argc > 2) {
49643             radix = js_get_radix(ctx, argv[2]);
49644             if (radix < 0)
49645                 goto fail;
49646         }
49647         ret = js_ftoa(ctx, val, radix, f + 1,
49648                       rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
49649     }
49650     JS_FreeValue(ctx, val);
49651     return ret;
49652  fail:
49653     JS_FreeValue(ctx, val);
49654     return JS_EXCEPTION;
49655 }
49656 
js_bigfloat_toPrecision(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49657 static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val,
49658                                      int argc, JSValueConst *argv)
49659 {
49660     JSValue val, ret;
49661     int64_t p;
49662     int rnd_mode, radix;
49663 
49664     val = js_thisBigFloatValue(ctx, this_val);
49665     if (JS_IsException(val))
49666         return val;
49667     if (JS_IsUndefined(argv[0]))
49668         goto to_string;
49669     if (JS_ToInt64Sat(ctx, &p, argv[0]))
49670         goto fail;
49671     if (!js_bigfloat_is_finite(ctx, val)) {
49672     to_string:
49673         ret = JS_ToString(ctx, this_val);
49674     } else {
49675         if (p < 1 || p > BF_PREC_MAX) {
49676             JS_ThrowRangeError(ctx, "invalid number of digits");
49677             goto fail;
49678         }
49679         rnd_mode = BF_RNDNA;
49680         radix = 10;
49681         if (argc > 1) {
49682             rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
49683             if (rnd_mode < 0)
49684                 goto fail;
49685         }
49686         if (argc > 2) {
49687             radix = js_get_radix(ctx, argv[2]);
49688             if (radix < 0)
49689                 goto fail;
49690         }
49691         ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED);
49692     }
49693     JS_FreeValue(ctx, val);
49694     return ret;
49695  fail:
49696     JS_FreeValue(ctx, val);
49697     return JS_EXCEPTION;
49698 }
49699 
49700 static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = {
49701     JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ),
49702     JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ),
49703     JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ),
49704     JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ),
49705     JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ),
49706 };
49707 
js_bigfloat_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)49708 static JSValue js_bigfloat_constructor(JSContext *ctx,
49709                                        JSValueConst new_target,
49710                                        int argc, JSValueConst *argv)
49711 {
49712     JSValue val;
49713     if (!JS_IsUndefined(new_target))
49714         return JS_ThrowTypeError(ctx, "not a constructor");
49715     if (argc == 0) {
49716         bf_t *r;
49717         val = JS_NewBigFloat(ctx);
49718         if (JS_IsException(val))
49719             return val;
49720         r = JS_GetBigFloat(val);
49721         bf_set_zero(r, 0);
49722     } else {
49723         val = JS_DupValue(ctx, argv[0]);
49724     redo:
49725         switch(JS_VALUE_GET_NORM_TAG(val)) {
49726         case JS_TAG_BIG_FLOAT:
49727             break;
49728         case JS_TAG_FLOAT64:
49729             {
49730                 bf_t *r;
49731                 double d = JS_VALUE_GET_FLOAT64(val);
49732                 val = JS_NewBigFloat(ctx);
49733                 if (JS_IsException(val))
49734                     break;
49735                 r = JS_GetBigFloat(val);
49736                 if (bf_set_float64(r, d))
49737                     goto fail;
49738             }
49739             break;
49740         case JS_TAG_INT:
49741             {
49742                 bf_t *r;
49743                 int32_t v = JS_VALUE_GET_INT(val);
49744                 val = JS_NewBigFloat(ctx);
49745                 if (JS_IsException(val))
49746                     break;
49747                 r = JS_GetBigFloat(val);
49748                 if (bf_set_si(r, v))
49749                     goto fail;
49750             }
49751             break;
49752         case JS_TAG_BIG_INT:
49753             /* We keep the full precision of the integer */
49754             {
49755                 JSBigFloat *p = JS_VALUE_GET_PTR(val);
49756                 val = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
49757             }
49758             break;
49759         case JS_TAG_BIG_DECIMAL:
49760             val = JS_ToStringFree(ctx, val);
49761             if (JS_IsException(val))
49762                 break;
49763             goto redo;
49764         case JS_TAG_STRING:
49765             {
49766                 const char *str, *p;
49767                 size_t len;
49768                 int err;
49769 
49770                 str = JS_ToCStringLen(ctx, &len, val);
49771                 JS_FreeValue(ctx, val);
49772                 if (!str)
49773                     return JS_EXCEPTION;
49774                 p = str;
49775                 p += skip_spaces(p);
49776                 if ((p - str) == len) {
49777                     bf_t *r;
49778                     val = JS_NewBigFloat(ctx);
49779                     if (JS_IsException(val))
49780                         break;
49781                     r = JS_GetBigFloat(val);
49782                     bf_set_zero(r, 0);
49783                     err = 0;
49784                 } else {
49785                     val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT |
49786                                   ATOD_TYPE_BIG_FLOAT |
49787                                   ATOD_ACCEPT_PREFIX_AFTER_SIGN);
49788                     if (JS_IsException(val)) {
49789                         JS_FreeCString(ctx, str);
49790                         return JS_EXCEPTION;
49791                     }
49792                     p += skip_spaces(p);
49793                     err = ((p - str) != len);
49794                 }
49795                 JS_FreeCString(ctx, str);
49796                 if (err) {
49797                     JS_FreeValue(ctx, val);
49798                     return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal");
49799                 }
49800             }
49801             break;
49802         case JS_TAG_OBJECT:
49803             val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
49804             if (JS_IsException(val))
49805                 break;
49806             goto redo;
49807         case JS_TAG_NULL:
49808         case JS_TAG_UNDEFINED:
49809         default:
49810             JS_FreeValue(ctx, val);
49811             return JS_ThrowTypeError(ctx, "cannot convert to bigfloat");
49812         }
49813     }
49814     return val;
49815  fail:
49816     JS_FreeValue(ctx, val);
49817     return JS_EXCEPTION;
49818 }
49819 
js_bigfloat_get_const(JSContext * ctx,JSValueConst this_val,int magic)49820 static JSValue js_bigfloat_get_const(JSContext *ctx,
49821                                      JSValueConst this_val, int magic)
49822 {
49823     bf_t *r;
49824     JSValue val;
49825     val = JS_NewBigFloat(ctx);
49826     if (JS_IsException(val))
49827         return val;
49828     r = JS_GetBigFloat(val);
49829     switch(magic) {
49830     case 0: /* PI */
49831         bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags);
49832         break;
49833     case 1: /* LN2 */
49834         bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags);
49835         break;
49836     case 2: /* MIN_VALUE */
49837     case 3: /* MAX_VALUE */
49838         {
49839             slimb_t e_range, e;
49840             e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1);
49841             bf_set_ui(r, 1);
49842             if (magic == 2) {
49843                 e = -e_range + 2;
49844                 if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL)
49845                     e -= ctx->fp_env.prec - 1;
49846                 bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags);
49847             } else {
49848                 bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec,
49849                             ctx->fp_env.flags);
49850                 bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags);
49851                 bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec,
49852                             ctx->fp_env.flags);
49853             }
49854         }
49855         break;
49856     case 4: /* EPSILON */
49857         bf_set_ui(r, 1);
49858         bf_mul_2exp(r, 1 - ctx->fp_env.prec,
49859                     ctx->fp_env.prec, ctx->fp_env.flags);
49860         break;
49861     default:
49862         abort();
49863     }
49864     return val;
49865 }
49866 
js_bigfloat_parseFloat(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49867 static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val,
49868                                       int argc, JSValueConst *argv)
49869 {
49870     bf_t *a;
49871     const char *str;
49872     JSValue ret;
49873     int radix;
49874     JSFloatEnv *fe;
49875 
49876     str = JS_ToCString(ctx, argv[0]);
49877     if (!str)
49878         return JS_EXCEPTION;
49879     if (JS_ToInt32(ctx, &radix, argv[1])) {
49880     fail:
49881         JS_FreeCString(ctx, str);
49882         return JS_EXCEPTION;
49883     }
49884     if (radix != 0 && (radix < 2 || radix > 36)) {
49885         JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
49886         goto fail;
49887     }
49888     fe = &ctx->fp_env;
49889     if (argc > 2) {
49890         fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
49891         if (!fe)
49892             goto fail;
49893     }
49894     ret = JS_NewBigFloat(ctx);
49895     if (JS_IsException(ret))
49896         goto done;
49897     a = JS_GetBigFloat(ret);
49898     /* XXX: use js_atof() */
49899     bf_atof(a, str, NULL, radix, fe->prec, fe->flags);
49900  done:
49901     JS_FreeCString(ctx, str);
49902     return ret;
49903 }
49904 
js_bigfloat_isFinite(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49905 static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val,
49906                                     int argc, JSValueConst *argv)
49907 {
49908     JSValueConst val = argv[0];
49909     JSBigFloat *p;
49910 
49911     if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
49912         return JS_FALSE;
49913     p = JS_VALUE_GET_PTR(val);
49914     return JS_NewBool(ctx, bf_is_finite(&p->num));
49915 }
49916 
js_bigfloat_isNaN(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49917 static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val,
49918                                  int argc, JSValueConst *argv)
49919 {
49920     JSValueConst val = argv[0];
49921     JSBigFloat *p;
49922 
49923     if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
49924         return JS_FALSE;
49925     p = JS_VALUE_GET_PTR(val);
49926     return JS_NewBool(ctx, bf_is_nan(&p->num));
49927 }
49928 
49929 enum {
49930     MATH_OP_ABS,
49931     MATH_OP_FLOOR,
49932     MATH_OP_CEIL,
49933     MATH_OP_ROUND,
49934     MATH_OP_TRUNC,
49935     MATH_OP_SQRT,
49936     MATH_OP_FPROUND,
49937     MATH_OP_ACOS,
49938     MATH_OP_ASIN,
49939     MATH_OP_ATAN,
49940     MATH_OP_ATAN2,
49941     MATH_OP_COS,
49942     MATH_OP_EXP,
49943     MATH_OP_LOG,
49944     MATH_OP_POW,
49945     MATH_OP_SIN,
49946     MATH_OP_TAN,
49947     MATH_OP_FMOD,
49948     MATH_OP_REM,
49949     MATH_OP_SIGN,
49950 
49951     MATH_OP_ADD,
49952     MATH_OP_SUB,
49953     MATH_OP_MUL,
49954     MATH_OP_DIV,
49955 };
49956 
js_bigfloat_fop(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)49957 static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val,
49958                            int argc, JSValueConst *argv, int magic)
49959 {
49960     bf_t a_s, *a, *r;
49961     JSFloatEnv *fe;
49962     int rnd_mode;
49963     JSValue op1, res;
49964 
49965     op1 = JS_ToNumeric(ctx, argv[0]);
49966     if (JS_IsException(op1))
49967         return op1;
49968     a = JS_ToBigFloat(ctx, &a_s, op1);
49969     fe = &ctx->fp_env;
49970     if (argc > 1) {
49971         fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV);
49972         if (!fe)
49973             goto fail;
49974     }
49975     res = JS_NewBigFloat(ctx);
49976     if (JS_IsException(res)) {
49977     fail:
49978         if (a == &a_s)
49979             bf_delete(a);
49980         JS_FreeValue(ctx, op1);
49981         return JS_EXCEPTION;
49982     }
49983     r = JS_GetBigFloat(res);
49984     switch (magic) {
49985     case MATH_OP_ABS:
49986         bf_set(r, a);
49987         r->sign = 0;
49988         break;
49989     case MATH_OP_FLOOR:
49990         rnd_mode = BF_RNDD;
49991         goto rint;
49992     case MATH_OP_CEIL:
49993         rnd_mode = BF_RNDU;
49994         goto rint;
49995     case MATH_OP_ROUND:
49996         rnd_mode = BF_RNDNA;
49997         goto rint;
49998     case MATH_OP_TRUNC:
49999         rnd_mode = BF_RNDZ;
50000     rint:
50001         bf_set(r, a);
50002         fe->status |= bf_rint(r, rnd_mode);
50003         break;
50004     case MATH_OP_SQRT:
50005         fe->status |= bf_sqrt(r, a, fe->prec, fe->flags);
50006         break;
50007     case MATH_OP_FPROUND:
50008         bf_set(r, a);
50009         fe->status |= bf_round(r, fe->prec, fe->flags);
50010         break;
50011     case MATH_OP_ACOS:
50012         fe->status |= bf_acos(r, a, fe->prec, fe->flags);
50013         break;
50014     case MATH_OP_ASIN:
50015         fe->status |= bf_asin(r, a, fe->prec, fe->flags);
50016         break;
50017     case MATH_OP_ATAN:
50018         fe->status |= bf_atan(r, a, fe->prec, fe->flags);
50019         break;
50020     case MATH_OP_COS:
50021         fe->status |= bf_cos(r, a, fe->prec, fe->flags);
50022         break;
50023     case MATH_OP_EXP:
50024         fe->status |= bf_exp(r, a, fe->prec, fe->flags);
50025         break;
50026     case MATH_OP_LOG:
50027         fe->status |= bf_log(r, a, fe->prec, fe->flags);
50028         break;
50029     case MATH_OP_SIN:
50030         fe->status |= bf_sin(r, a, fe->prec, fe->flags);
50031         break;
50032     case MATH_OP_TAN:
50033         fe->status |= bf_tan(r, a, fe->prec, fe->flags);
50034         break;
50035     case MATH_OP_SIGN:
50036         if (bf_is_nan(a) || bf_is_zero(a)) {
50037             bf_set(r, a);
50038         } else {
50039             bf_set_si(r, 1 - 2 * a->sign);
50040         }
50041         break;
50042     default:
50043         abort();
50044     }
50045     if (a == &a_s)
50046         bf_delete(a);
50047     JS_FreeValue(ctx, op1);
50048     return res;
50049 }
50050 
js_bigfloat_fop2(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)50051 static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val,
50052                             int argc, JSValueConst *argv, int magic)
50053 {
50054     bf_t a_s, *a, b_s, *b, r_s, *r = &r_s;
50055     JSFloatEnv *fe;
50056     JSValue op1, op2, res;
50057 
50058     op1 = JS_ToNumeric(ctx, argv[0]);
50059     if (JS_IsException(op1))
50060         return op1;
50061     op2 = JS_ToNumeric(ctx, argv[1]);
50062     if (JS_IsException(op2)) {
50063         JS_FreeValue(ctx, op1);
50064         return op2;
50065     }
50066     a = JS_ToBigFloat(ctx, &a_s, op1);
50067     b = JS_ToBigFloat(ctx, &b_s, op2);
50068     fe = &ctx->fp_env;
50069     if (argc > 2) {
50070         fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
50071         if (!fe)
50072             goto fail;
50073     }
50074     res = JS_NewBigFloat(ctx);
50075     if (JS_IsException(res)) {
50076     fail:
50077         if (a == &a_s)
50078             bf_delete(a);
50079         if (b == &b_s)
50080             bf_delete(b);
50081         JS_FreeValue(ctx, op1);
50082         JS_FreeValue(ctx, op2);
50083         return JS_EXCEPTION;
50084     }
50085     r = JS_GetBigFloat(res);
50086     switch (magic) {
50087     case MATH_OP_ATAN2:
50088         fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags);
50089         break;
50090     case MATH_OP_POW:
50091         fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS);
50092         break;
50093     case MATH_OP_FMOD:
50094         fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
50095         break;
50096     case MATH_OP_REM:
50097         fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN);
50098         break;
50099     case MATH_OP_ADD:
50100         fe->status |= bf_add(r, a, b, fe->prec, fe->flags);
50101         break;
50102     case MATH_OP_SUB:
50103         fe->status |= bf_sub(r, a, b, fe->prec, fe->flags);
50104         break;
50105     case MATH_OP_MUL:
50106         fe->status |= bf_mul(r, a, b, fe->prec, fe->flags);
50107         break;
50108     case MATH_OP_DIV:
50109         fe->status |= bf_div(r, a, b, fe->prec, fe->flags);
50110         break;
50111     default:
50112         abort();
50113     }
50114     if (a == &a_s)
50115         bf_delete(a);
50116     if (b == &b_s)
50117         bf_delete(b);
50118     JS_FreeValue(ctx, op1);
50119     JS_FreeValue(ctx, op2);
50120     return res;
50121 }
50122 
50123 static const JSCFunctionListEntry js_bigfloat_funcs[] = {
50124     JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ),
50125     JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ),
50126     JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ),
50127     JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ),
50128     JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ),
50129     JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ),
50130     JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ),
50131     JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ),
50132     JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ),
50133     JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ),
50134     JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ),
50135     JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ),
50136     JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ),
50137     JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ),
50138     JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ),
50139     JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ),
50140     JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ),
50141     JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ),
50142     JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ),
50143     JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ),
50144     JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ),
50145     JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ),
50146     JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ),
50147     JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ),
50148     JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ),
50149     JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ),
50150     JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ),
50151     JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ),
50152     JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ),
50153     JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ),
50154     JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ),
50155     JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ),
50156 };
50157 
50158 /* FloatEnv */
50159 
js_float_env_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)50160 static JSValue js_float_env_constructor(JSContext *ctx,
50161                                         JSValueConst new_target,
50162                                         int argc, JSValueConst *argv)
50163 {
50164     JSValue obj;
50165     JSFloatEnv *fe;
50166     int64_t prec;
50167     int flags, rndmode;
50168 
50169     prec = ctx->fp_env.prec;
50170     flags = ctx->fp_env.flags;
50171     if (!JS_IsUndefined(argv[0])) {
50172         if (JS_ToInt64Sat(ctx, &prec, argv[0]))
50173             return JS_EXCEPTION;
50174         if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
50175             return JS_ThrowRangeError(ctx, "invalid precision");
50176         flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */
50177         if (argc > 1 && !JS_IsUndefined(argv[1])) {
50178             if (JS_ToInt32Sat(ctx, &rndmode, argv[1]))
50179                 return JS_EXCEPTION;
50180             if (rndmode < BF_RNDN || rndmode > BF_RNDF)
50181                 return JS_ThrowRangeError(ctx, "invalid rounding mode");
50182             flags = rndmode;
50183         }
50184     }
50185 
50186     obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV);
50187     if (JS_IsException(obj))
50188         return JS_EXCEPTION;
50189     fe = js_malloc(ctx, sizeof(*fe));
50190     if (!fe)
50191         return JS_EXCEPTION;
50192     fe->prec = prec;
50193     fe->flags = flags;
50194     fe->status = 0;
50195     JS_SetOpaque(obj, fe);
50196     return obj;
50197 }
50198 
js_float_env_finalizer(JSRuntime * rt,JSValue val)50199 static void js_float_env_finalizer(JSRuntime *rt, JSValue val)
50200 {
50201     JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV);
50202     js_free_rt(rt, fe);
50203 }
50204 
js_float_env_get_prec(JSContext * ctx,JSValueConst this_val)50205 static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val)
50206 {
50207     return JS_NewInt64(ctx, ctx->fp_env.prec);
50208 }
50209 
js_float_env_get_expBits(JSContext * ctx,JSValueConst this_val)50210 static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val)
50211 {
50212     return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags));
50213 }
50214 
js_float_env_setPrec(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50215 static JSValue js_float_env_setPrec(JSContext *ctx,
50216                                     JSValueConst this_val,
50217                                     int argc, JSValueConst *argv)
50218 {
50219     JSValueConst func;
50220     int exp_bits, flags, saved_flags;
50221     JSValue ret;
50222     limb_t saved_prec;
50223     int64_t prec;
50224 
50225     func = argv[0];
50226     if (JS_ToInt64Sat(ctx, &prec, argv[1]))
50227         return JS_EXCEPTION;
50228     if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
50229         return JS_ThrowRangeError(ctx, "invalid precision");
50230     exp_bits = BF_EXP_BITS_MAX;
50231 
50232     if (argc > 2 && !JS_IsUndefined(argv[2])) {
50233         if (JS_ToInt32Sat(ctx, &exp_bits, argv[2]))
50234             return JS_EXCEPTION;
50235         if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX)
50236             return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
50237     }
50238 
50239     flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits);
50240 
50241     saved_prec = ctx->fp_env.prec;
50242     saved_flags = ctx->fp_env.flags;
50243 
50244     ctx->fp_env.prec = prec;
50245     ctx->fp_env.flags = flags;
50246 
50247     ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL);
50248     /* always restore the floating point precision */
50249     ctx->fp_env.prec = saved_prec;
50250     ctx->fp_env.flags = saved_flags;
50251     return ret;
50252 }
50253 
50254 #define FE_PREC      (-1)
50255 #define FE_EXP       (-2)
50256 #define FE_RNDMODE   (-3)
50257 #define FE_SUBNORMAL (-4)
50258 
js_float_env_proto_get_status(JSContext * ctx,JSValueConst this_val,int magic)50259 static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic)
50260 {
50261     JSFloatEnv *fe;
50262     fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
50263     if (!fe)
50264         return JS_EXCEPTION;
50265     switch(magic) {
50266     case FE_PREC:
50267         return JS_NewInt64(ctx, fe->prec);
50268     case FE_EXP:
50269         return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags));
50270     case FE_RNDMODE:
50271         return JS_NewInt32(ctx, fe->flags & BF_RND_MASK);
50272     case FE_SUBNORMAL:
50273         return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0);
50274     default:
50275         return JS_NewBool(ctx, (fe->status & magic) != 0);
50276     }
50277 }
50278 
js_float_env_proto_set_status(JSContext * ctx,JSValueConst this_val,JSValueConst val,int magic)50279 static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic)
50280 {
50281     JSFloatEnv *fe;
50282     int b;
50283     int64_t prec;
50284 
50285     fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
50286     if (!fe)
50287         return JS_EXCEPTION;
50288     switch(magic) {
50289     case FE_PREC:
50290         if (JS_ToInt64Sat(ctx, &prec, val))
50291             return JS_EXCEPTION;
50292         if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
50293             return JS_ThrowRangeError(ctx, "invalid precision");
50294         fe->prec = prec;
50295         break;
50296     case FE_EXP:
50297         if (JS_ToInt32Sat(ctx, &b, val))
50298             return JS_EXCEPTION;
50299         if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX)
50300             return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
50301         fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) |
50302             bf_set_exp_bits(b);
50303         break;
50304     case FE_RNDMODE:
50305         b = bigfloat_get_rnd_mode(ctx, val);
50306         if (b < 0)
50307             return JS_EXCEPTION;
50308         fe->flags = (fe->flags & ~BF_RND_MASK) | b;
50309         break;
50310     case FE_SUBNORMAL:
50311         b = JS_ToBool(ctx, val);
50312         fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0);
50313         break;
50314     default:
50315         b = JS_ToBool(ctx, val);
50316         fe->status = (fe->status & ~magic) & ((-b) & magic);
50317         break;
50318     }
50319     return JS_UNDEFINED;
50320 }
50321 
js_float_env_clearStatus(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50322 static JSValue js_float_env_clearStatus(JSContext *ctx,
50323                                         JSValueConst this_val,
50324                                         int argc, JSValueConst *argv)
50325 {
50326     JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
50327     if (!fe)
50328         return JS_EXCEPTION;
50329     fe->status = 0;
50330     return JS_UNDEFINED;
50331 }
50332 
50333 static const JSCFunctionListEntry js_float_env_funcs[] = {
50334     JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ),
50335     JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ),
50336     JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ),
50337     JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ),
50338     JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ),
50339     JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ),
50340     JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ),
50341     JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ),
50342     JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ),
50343     JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ),
50344     JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ),
50345     JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ),
50346     JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ),
50347     JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ),
50348 };
50349 
50350 static const JSCFunctionListEntry js_float_env_proto_funcs[] = {
50351     JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status,
50352                          js_float_env_proto_set_status, FE_PREC ),
50353     JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status,
50354                          js_float_env_proto_set_status, FE_EXP ),
50355     JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status,
50356                          js_float_env_proto_set_status, FE_RNDMODE ),
50357     JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status,
50358                          js_float_env_proto_set_status, FE_SUBNORMAL ),
50359     JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status,
50360                          js_float_env_proto_set_status, BF_ST_INVALID_OP ),
50361     JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status,
50362                          js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ),
50363     JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status,
50364                          js_float_env_proto_set_status, BF_ST_OVERFLOW ),
50365     JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status,
50366                          js_float_env_proto_set_status, BF_ST_UNDERFLOW ),
50367     JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status,
50368                          js_float_env_proto_set_status, BF_ST_INEXACT ),
50369     JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ),
50370 };
50371 
JS_AddIntrinsicBigFloat(JSContext * ctx)50372 void JS_AddIntrinsicBigFloat(JSContext *ctx)
50373 {
50374     JSRuntime *rt = ctx->rt;
50375     JSValueConst obj1;
50376 
50377     rt->bigfloat_ops.to_string = js_bigfloat_to_string;
50378     rt->bigfloat_ops.from_string = js_string_to_bigfloat;
50379     rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat;
50380     rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat;
50381     rt->bigfloat_ops.compare = js_compare_bigfloat;
50382     rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64;
50383     rt->bigfloat_ops.mul_pow10 = js_mul_pow10;
50384 
50385     ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx);
50386     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT],
50387                                js_bigfloat_proto_funcs,
50388                                countof(js_bigfloat_proto_funcs));
50389     obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1,
50390                                     ctx->class_proto[JS_CLASS_BIG_FLOAT]);
50391     JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs,
50392                                countof(js_bigfloat_funcs));
50393 
50394     ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx);
50395     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV],
50396                                js_float_env_proto_funcs,
50397                                countof(js_float_env_proto_funcs));
50398     obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv",
50399                                         js_float_env_constructor, 1,
50400                                         ctx->class_proto[JS_CLASS_FLOAT_ENV]);
50401     JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs,
50402                                countof(js_float_env_funcs));
50403 }
50404 
50405 /* BigDecimal */
50406 
JS_ToBigDecimalFree(JSContext * ctx,JSValue val,BOOL allow_null_or_undefined)50407 static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
50408                                    BOOL allow_null_or_undefined)
50409 {
50410  redo:
50411     switch(JS_VALUE_GET_NORM_TAG(val)) {
50412     case JS_TAG_BIG_DECIMAL:
50413         break;
50414     case JS_TAG_NULL:
50415         if (!allow_null_or_undefined)
50416             goto fail;
50417         /* fall thru */
50418     case JS_TAG_BOOL:
50419     case JS_TAG_INT:
50420         {
50421             bfdec_t *r;
50422             int32_t v = JS_VALUE_GET_INT(val);
50423 
50424             val = JS_NewBigDecimal(ctx);
50425             if (JS_IsException(val))
50426                 break;
50427             r = JS_GetBigDecimal(val);
50428             if (bfdec_set_si(r, v)) {
50429                 JS_FreeValue(ctx, val);
50430                 val = JS_EXCEPTION;
50431                 break;
50432             }
50433         }
50434         break;
50435     case JS_TAG_FLOAT64:
50436     case JS_TAG_BIG_INT:
50437     case JS_TAG_BIG_FLOAT:
50438         val = JS_ToStringFree(ctx, val);
50439         if (JS_IsException(val))
50440             break;
50441         goto redo;
50442     case JS_TAG_STRING:
50443         {
50444             const char *str, *p;
50445             size_t len;
50446             int err;
50447 
50448             str = JS_ToCStringLen(ctx, &len, val);
50449             JS_FreeValue(ctx, val);
50450             if (!str)
50451                 return JS_EXCEPTION;
50452             p = str;
50453             p += skip_spaces(p);
50454             if ((p - str) == len) {
50455                 bfdec_t *r;
50456                 val = JS_NewBigDecimal(ctx);
50457                 if (JS_IsException(val))
50458                     break;
50459                 r = JS_GetBigDecimal(val);
50460                 bfdec_set_zero(r, 0);
50461                 err = 0;
50462             } else {
50463                 val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL);
50464                 if (JS_IsException(val)) {
50465                     JS_FreeCString(ctx, str);
50466                     return JS_EXCEPTION;
50467                 }
50468                 p += skip_spaces(p);
50469                 err = ((p - str) != len);
50470             }
50471             JS_FreeCString(ctx, str);
50472             if (err) {
50473                 JS_FreeValue(ctx, val);
50474                 return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal");
50475             }
50476         }
50477         break;
50478     case JS_TAG_OBJECT:
50479         val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
50480         if (JS_IsException(val))
50481             break;
50482         goto redo;
50483     case JS_TAG_UNDEFINED:
50484         {
50485             bfdec_t *r;
50486             if (!allow_null_or_undefined)
50487                 goto fail;
50488             val = JS_NewBigDecimal(ctx);
50489             if (JS_IsException(val))
50490                 break;
50491             r = JS_GetBigDecimal(val);
50492             bfdec_set_nan(r);
50493         }
50494         break;
50495     default:
50496     fail:
50497         JS_FreeValue(ctx, val);
50498         return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal");
50499     }
50500     return val;
50501 }
50502 
js_bigdecimal_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)50503 static JSValue js_bigdecimal_constructor(JSContext *ctx,
50504                                          JSValueConst new_target,
50505                                          int argc, JSValueConst *argv)
50506 {
50507     JSValue val;
50508     if (!JS_IsUndefined(new_target))
50509         return JS_ThrowTypeError(ctx, "not a constructor");
50510     if (argc == 0) {
50511         bfdec_t *r;
50512         val = JS_NewBigDecimal(ctx);
50513         if (JS_IsException(val))
50514             return val;
50515         r = JS_GetBigDecimal(val);
50516         bfdec_set_zero(r, 0);
50517     } else {
50518         val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE);
50519     }
50520     return val;
50521 }
50522 
js_thisBigDecimalValue(JSContext * ctx,JSValueConst this_val)50523 static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val)
50524 {
50525     if (JS_IsBigDecimal(this_val))
50526         return JS_DupValue(ctx, this_val);
50527 
50528     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
50529         JSObject *p = JS_VALUE_GET_OBJ(this_val);
50530         if (p->class_id == JS_CLASS_BIG_DECIMAL) {
50531             if (JS_IsBigDecimal(p->u.object_data))
50532                 return JS_DupValue(ctx, p->u.object_data);
50533         }
50534     }
50535     return JS_ThrowTypeError(ctx, "not a bigdecimal");
50536 }
50537 
js_bigdecimal_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50538 static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val,
50539                                       int argc, JSValueConst *argv)
50540 {
50541     JSValue val;
50542 
50543     val = js_thisBigDecimalValue(ctx, this_val);
50544     if (JS_IsException(val))
50545         return val;
50546     return JS_ToStringFree(ctx, val);
50547 }
50548 
js_bigdecimal_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50549 static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val,
50550                                    int argc, JSValueConst *argv)
50551 {
50552     return js_thisBigDecimalValue(ctx, this_val);
50553 }
50554 
js_bigdecimal_get_rnd_mode(JSContext * ctx,JSValueConst obj)50555 static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj)
50556 {
50557     const char *str;
50558     size_t size;
50559     int rnd_mode;
50560 
50561     str = JS_ToCStringLen(ctx, &size, obj);
50562     if (!str)
50563         return -1;
50564     if (strlen(str) != size)
50565         goto invalid_rounding_mode;
50566     if (!strcmp(str, "floor")) {
50567         rnd_mode = BF_RNDD;
50568     } else if (!strcmp(str, "ceiling")) {
50569         rnd_mode = BF_RNDU;
50570     } else if (!strcmp(str, "down")) {
50571         rnd_mode = BF_RNDZ;
50572     } else if (!strcmp(str, "up")) {
50573         rnd_mode = BF_RNDA;
50574     } else if (!strcmp(str, "half-even")) {
50575         rnd_mode = BF_RNDN;
50576     } else if (!strcmp(str, "half-up")) {
50577         rnd_mode = BF_RNDNA;
50578     } else {
50579     invalid_rounding_mode:
50580         JS_FreeCString(ctx, str);
50581         JS_ThrowTypeError(ctx, "invalid rounding mode");
50582         return -1;
50583     }
50584     JS_FreeCString(ctx, str);
50585     return rnd_mode;
50586 }
50587 
50588 typedef struct {
50589     int64_t prec;
50590     bf_flags_t flags;
50591 } BigDecimalEnv;
50592 
js_bigdecimal_get_env(JSContext * ctx,BigDecimalEnv * fe,JSValueConst obj)50593 static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,
50594                                  JSValueConst obj)
50595 {
50596     JSValue prop;
50597     int64_t val;
50598     BOOL has_prec;
50599     int rnd_mode;
50600 
50601     if (!JS_IsObject(obj)) {
50602         JS_ThrowTypeErrorNotAnObject(ctx);
50603         return -1;
50604     }
50605     prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode);
50606     if (JS_IsException(prop))
50607         return -1;
50608     rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop);
50609     JS_FreeValue(ctx, prop);
50610     if (rnd_mode < 0)
50611         return -1;
50612     fe->flags = rnd_mode;
50613 
50614     prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits);
50615     if (JS_IsException(prop))
50616         return -1;
50617     has_prec = FALSE;
50618     if (!JS_IsUndefined(prop)) {
50619         if (JS_ToInt64SatFree(ctx, &val, prop))
50620             return -1;
50621         if (val < 1 || val > BF_PREC_MAX)
50622             goto invalid_precision;
50623         fe->prec = val;
50624         has_prec = TRUE;
50625     }
50626 
50627     prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits);
50628     if (JS_IsException(prop))
50629         return -1;
50630     if (!JS_IsUndefined(prop)) {
50631         if (has_prec) {
50632             JS_FreeValue(ctx, prop);
50633             JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits");
50634             return -1;
50635         }
50636         if (JS_ToInt64SatFree(ctx, &val, prop))
50637             return -1;
50638         if (val < 0 || val > BF_PREC_MAX) {
50639         invalid_precision:
50640             JS_ThrowTypeError(ctx, "invalid precision");
50641             return -1;
50642         }
50643         fe->prec = val;
50644         fe->flags |= BF_FLAG_RADPNT_PREC;
50645         has_prec = TRUE;
50646     }
50647     if (!has_prec) {
50648         JS_ThrowTypeError(ctx, "precision must be present");
50649         return -1;
50650     }
50651     return 0;
50652 }
50653 
50654 
js_bigdecimal_fop(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)50655 static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,
50656                                  int argc, JSValueConst *argv, int magic)
50657 {
50658     bfdec_t *a, *b, r_s, *r = &r_s;
50659     JSValue op1, op2, res;
50660     BigDecimalEnv fe_s, *fe = &fe_s;
50661     int op_count, ret;
50662 
50663     if (magic == MATH_OP_SQRT ||
50664         magic == MATH_OP_ROUND)
50665         op_count = 1;
50666     else
50667         op_count = 2;
50668 
50669     op1 = JS_ToNumeric(ctx, argv[0]);
50670     if (JS_IsException(op1))
50671         return op1;
50672     a = JS_ToBigDecimal(ctx, op1);
50673     if (!a) {
50674         JS_FreeValue(ctx, op1);
50675         return JS_EXCEPTION;
50676     }
50677     if (op_count >= 2) {
50678         op2 = JS_ToNumeric(ctx, argv[1]);
50679         if (JS_IsException(op2)) {
50680             JS_FreeValue(ctx, op1);
50681             return op2;
50682         }
50683         b = JS_ToBigDecimal(ctx, op2);
50684         if (!b)
50685             goto fail;
50686     } else {
50687         op2 = JS_UNDEFINED;
50688         b = NULL;
50689     }
50690     fe->flags = BF_RNDZ;
50691     fe->prec = BF_PREC_INF;
50692     if (op_count < argc) {
50693         if (js_bigdecimal_get_env(ctx, fe, argv[op_count]))
50694             goto fail;
50695     }
50696 
50697     res = JS_NewBigDecimal(ctx);
50698     if (JS_IsException(res)) {
50699     fail:
50700         JS_FreeValue(ctx, op1);
50701         JS_FreeValue(ctx, op2);
50702         return JS_EXCEPTION;
50703     }
50704     r = JS_GetBigDecimal(res);
50705     switch (magic) {
50706     case MATH_OP_ADD:
50707         ret = bfdec_add(r, a, b, fe->prec, fe->flags);
50708         break;
50709     case MATH_OP_SUB:
50710         ret = bfdec_sub(r, a, b, fe->prec, fe->flags);
50711         break;
50712     case MATH_OP_MUL:
50713         ret = bfdec_mul(r, a, b, fe->prec, fe->flags);
50714         break;
50715     case MATH_OP_DIV:
50716         ret = bfdec_div(r, a, b, fe->prec, fe->flags);
50717         break;
50718     case MATH_OP_FMOD:
50719         ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
50720         break;
50721     case MATH_OP_SQRT:
50722         ret = bfdec_sqrt(r, a, fe->prec, fe->flags);
50723         break;
50724     case MATH_OP_ROUND:
50725         ret = bfdec_set(r, a);
50726         if (!(ret & BF_ST_MEM_ERROR))
50727             ret = bfdec_round(r, fe->prec, fe->flags);
50728         break;
50729     default:
50730         abort();
50731     }
50732     JS_FreeValue(ctx, op1);
50733     JS_FreeValue(ctx, op2);
50734     ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP |
50735         BF_ST_OVERFLOW;
50736     if (ret != 0) {
50737         JS_FreeValue(ctx, res);
50738         return throw_bf_exception(ctx, ret);
50739     } else {
50740         return res;
50741     }
50742 }
50743 
js_bigdecimal_toFixed(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50744 static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val,
50745                                  int argc, JSValueConst *argv)
50746 {
50747     JSValue val, ret;
50748     int64_t f;
50749     int rnd_mode;
50750 
50751     val = js_thisBigDecimalValue(ctx, this_val);
50752     if (JS_IsException(val))
50753         return val;
50754     if (JS_ToInt64Sat(ctx, &f, argv[0]))
50755         goto fail;
50756     if (f < 0 || f > BF_PREC_MAX) {
50757         JS_ThrowRangeError(ctx, "invalid number of digits");
50758         goto fail;
50759     }
50760     rnd_mode = BF_RNDNA;
50761     if (argc > 1) {
50762         rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
50763         if (rnd_mode < 0)
50764             goto fail;
50765     }
50766     ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
50767     JS_FreeValue(ctx, val);
50768     return ret;
50769  fail:
50770     JS_FreeValue(ctx, val);
50771     return JS_EXCEPTION;
50772 }
50773 
js_bigdecimal_toExponential(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50774 static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val,
50775                                        int argc, JSValueConst *argv)
50776 {
50777     JSValue val, ret;
50778     int64_t f;
50779     int rnd_mode;
50780 
50781     val = js_thisBigDecimalValue(ctx, this_val);
50782     if (JS_IsException(val))
50783         return val;
50784     if (JS_ToInt64Sat(ctx, &f, argv[0]))
50785         goto fail;
50786     if (JS_IsUndefined(argv[0])) {
50787         ret = js_bigdecimal_to_string1(ctx, val, 0,
50788                   BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
50789     } else {
50790         if (f < 0 || f > BF_PREC_MAX) {
50791             JS_ThrowRangeError(ctx, "invalid number of digits");
50792             goto fail;
50793         }
50794         rnd_mode = BF_RNDNA;
50795         if (argc > 1) {
50796             rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
50797             if (rnd_mode < 0)
50798                 goto fail;
50799         }
50800         ret = js_bigdecimal_to_string1(ctx, val, f + 1,
50801                       rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
50802     }
50803     JS_FreeValue(ctx, val);
50804     return ret;
50805  fail:
50806     JS_FreeValue(ctx, val);
50807     return JS_EXCEPTION;
50808 }
50809 
js_bigdecimal_toPrecision(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50810 static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val,
50811                                      int argc, JSValueConst *argv)
50812 {
50813     JSValue val, ret;
50814     int64_t p;
50815     int rnd_mode;
50816 
50817     val = js_thisBigDecimalValue(ctx, this_val);
50818     if (JS_IsException(val))
50819         return val;
50820     if (JS_IsUndefined(argv[0])) {
50821         return JS_ToStringFree(ctx, val);
50822     }
50823     if (JS_ToInt64Sat(ctx, &p, argv[0]))
50824         goto fail;
50825     if (p < 1 || p > BF_PREC_MAX) {
50826         JS_ThrowRangeError(ctx, "invalid number of digits");
50827         goto fail;
50828     }
50829     rnd_mode = BF_RNDNA;
50830     if (argc > 1) {
50831         rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
50832         if (rnd_mode < 0)
50833             goto fail;
50834     }
50835     ret = js_bigdecimal_to_string1(ctx, val, p,
50836                                    rnd_mode | BF_FTOA_FORMAT_FIXED);
50837     JS_FreeValue(ctx, val);
50838     return ret;
50839  fail:
50840     JS_FreeValue(ctx, val);
50841     return JS_EXCEPTION;
50842 }
50843 
50844 static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = {
50845     JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ),
50846     JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ),
50847     JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ),
50848     JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ),
50849     JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ),
50850 };
50851 
50852 static const JSCFunctionListEntry js_bigdecimal_funcs[] = {
50853     JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ),
50854     JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ),
50855     JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ),
50856     JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ),
50857     JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ),
50858     JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ),
50859     JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ),
50860 };
50861 
JS_AddIntrinsicBigDecimal(JSContext * ctx)50862 void JS_AddIntrinsicBigDecimal(JSContext *ctx)
50863 {
50864     JSRuntime *rt = ctx->rt;
50865     JSValueConst obj1;
50866 
50867     rt->bigdecimal_ops.to_string = js_bigdecimal_to_string;
50868     rt->bigdecimal_ops.from_string = js_string_to_bigdecimal;
50869     rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal;
50870     rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal;
50871     rt->bigdecimal_ops.compare = js_compare_bigdecimal;
50872 
50873     ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx);
50874     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL],
50875                                js_bigdecimal_proto_funcs,
50876                                countof(js_bigdecimal_proto_funcs));
50877     obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal",
50878                                     js_bigdecimal_constructor, 1,
50879                                     ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
50880     JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs,
50881                                countof(js_bigdecimal_funcs));
50882 }
50883 
JS_EnableBignumExt(JSContext * ctx,BOOL enable)50884 void JS_EnableBignumExt(JSContext *ctx, BOOL enable)
50885 {
50886     ctx->bignum_ext = enable;
50887 }
50888 
50889 #endif /* CONFIG_BIGNUM */
50890 
50891 static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = {
50892     "EvalError", "RangeError", "ReferenceError",
50893     "SyntaxError", "TypeError", "URIError",
50894     "InternalError", "AggregateError",
50895 };
50896 
50897 /* Minimum amount of objects to be able to compile code and display
50898    error messages. No JSAtom should be allocated by this function. */
JS_AddIntrinsicBasicObjects(JSContext * ctx)50899 static void JS_AddIntrinsicBasicObjects(JSContext *ctx)
50900 {
50901     JSValue proto;
50902     int i;
50903 
50904     ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL);
50905     ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0,
50906                                            JS_CFUNC_generic, 0,
50907                                            ctx->class_proto[JS_CLASS_OBJECT]);
50908     ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto);
50909     ctx->class_proto[JS_CLASS_ERROR] = JS_NewObject(ctx);
50910 #if 0
50911     /* these are auto-initialized from js_error_proto_funcs,
50912        but delaying might be a problem */
50913     JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_name,
50914                            JS_AtomToString(ctx, JS_ATOM_Error),
50915                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
50916     JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_message,
50917                            JS_AtomToString(ctx, JS_ATOM_empty_string),
50918                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
50919 #endif
50920     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ERROR],
50921                                js_error_proto_funcs,
50922                                countof(js_error_proto_funcs));
50923 
50924     for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
50925         proto = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ERROR]);
50926         JS_DefinePropertyValue(ctx, proto, JS_ATOM_name,
50927                                JS_NewAtomString(ctx, native_error_name[i]),
50928                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
50929         JS_DefinePropertyValue(ctx, proto, JS_ATOM_message,
50930                                JS_AtomToString(ctx, JS_ATOM_empty_string),
50931                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
50932         ctx->native_error_proto[i] = proto;
50933     }
50934 
50935     /* the array prototype is an array */
50936     ctx->class_proto[JS_CLASS_ARRAY] =
50937         JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
50938                                JS_CLASS_ARRAY);
50939 
50940     ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]),
50941                                      JS_PROP_INITIAL_HASH_SIZE, 1);
50942     add_shape_property(ctx, &ctx->array_shape, NULL,
50943                        JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH);
50944 
50945     /* XXX: could test it on first context creation to ensure that no
50946        new atoms are created in JS_AddIntrinsicBasicObjects(). It is
50947        necessary to avoid useless renumbering of atoms after
50948        JS_EvalBinary() if it is done just after
50949        JS_AddIntrinsicBasicObjects(). */
50950     //    assert(ctx->rt->atom_count == JS_ATOM_END);
50951 }
50952 
JS_AddIntrinsicBaseObjects(JSContext * ctx)50953 void JS_AddIntrinsicBaseObjects(JSContext *ctx)
50954 {
50955     int i;
50956     JSValueConst obj, number_obj;
50957     JSValue obj1;
50958 
50959     ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0);
50960 
50961     /* add caller and arguments properties to throw a TypeError */
50962     obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0);
50963     JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED,
50964                       obj1, ctx->throw_type_error,
50965                       JS_PROP_HAS_GET | JS_PROP_HAS_SET |
50966                       JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
50967     JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED,
50968                       obj1, ctx->throw_type_error,
50969                       JS_PROP_HAS_GET | JS_PROP_HAS_SET |
50970                       JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
50971     JS_FreeValue(ctx, obj1);
50972     JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1));
50973 
50974     ctx->global_obj = JS_NewObject(ctx);
50975     ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL);
50976 
50977     /* Object */
50978     obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1,
50979                                    ctx->class_proto[JS_CLASS_OBJECT]);
50980     JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs));
50981     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT],
50982                                js_object_proto_funcs, countof(js_object_proto_funcs));
50983 
50984     /* Function */
50985     JS_SetPropertyFunctionList(ctx, ctx->function_proto, js_function_proto_funcs, countof(js_function_proto_funcs));
50986     ctx->function_ctor = JS_NewCFunctionMagic(ctx, js_function_constructor,
50987                                               "Function", 1, JS_CFUNC_constructor_or_func_magic,
50988                                               JS_FUNC_NORMAL);
50989     JS_NewGlobalCConstructor2(ctx, JS_DupValue(ctx, ctx->function_ctor), "Function",
50990                               ctx->function_proto);
50991 
50992     /* Error */
50993     obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor,
50994                                 "Error", 1, JS_CFUNC_constructor_or_func_magic, -1);
50995     JS_NewGlobalCConstructor2(ctx, obj1,
50996                               "Error", ctx->class_proto[JS_CLASS_ERROR]);
50997 
50998     for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
50999         JSValue func_obj;
51000         int n_args;
51001         n_args = 1 + (i == JS_AGGREGATE_ERROR);
51002         func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_error_constructor,
51003                                     native_error_name[i], n_args,
51004                                     JS_CFUNC_constructor_or_func_magic, i, obj1);
51005         JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i],
51006                                   ctx->native_error_proto[i]);
51007     }
51008 
51009     /* Iterator prototype */
51010     ctx->iterator_proto = JS_NewObject(ctx);
51011     JS_SetPropertyFunctionList(ctx, ctx->iterator_proto,
51012                                js_iterator_proto_funcs,
51013                                countof(js_iterator_proto_funcs));
51014 
51015     /* Array */
51016     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY],
51017                                js_array_proto_funcs,
51018                                countof(js_array_proto_funcs));
51019 
51020     obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1,
51021                                    ctx->class_proto[JS_CLASS_ARRAY]);
51022     ctx->array_ctor = JS_DupValue(ctx, obj);
51023     JS_SetPropertyFunctionList(ctx, obj, js_array_funcs,
51024                                countof(js_array_funcs));
51025 
51026     /* XXX: create auto_initializer */
51027     {
51028         /* initialize Array.prototype[Symbol.unscopables] */
51029         char const unscopables[] = "copyWithin" "\0" "entries" "\0" "fill" "\0" "find" "\0"
51030             "findIndex" "\0" "flat" "\0" "flatMap" "\0" "includes" "\0" "keys" "\0" "values" "\0";
51031         const char *p = unscopables;
51032         obj1 = JS_NewObjectProto(ctx, JS_NULL);
51033         for(p = unscopables; *p; p += strlen(p) + 1) {
51034             JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E);
51035         }
51036         JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY],
51037                                JS_ATOM_Symbol_unscopables, obj1,
51038                                JS_PROP_CONFIGURABLE);
51039     }
51040 
51041     /* needed to initialize arguments[Symbol.iterator] */
51042     ctx->array_proto_values =
51043         JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values);
51044 
51045     ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
51046     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR],
51047                                js_array_iterator_proto_funcs,
51048                                countof(js_array_iterator_proto_funcs));
51049 
51050     /* parseFloat and parseInteger must be defined before Number
51051        because of the Number.parseFloat and Number.parseInteger
51052        aliases */
51053     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs,
51054                                countof(js_global_funcs));
51055 
51056     /* Number */
51057     ctx->class_proto[JS_CLASS_NUMBER] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
51058                                                                JS_CLASS_NUMBER);
51059     JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], JS_NewInt32(ctx, 0));
51060     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_NUMBER],
51061                                js_number_proto_funcs,
51062                                countof(js_number_proto_funcs));
51063     number_obj = JS_NewGlobalCConstructor(ctx, "Number", js_number_constructor, 1,
51064                                           ctx->class_proto[JS_CLASS_NUMBER]);
51065     JS_SetPropertyFunctionList(ctx, number_obj, js_number_funcs, countof(js_number_funcs));
51066 
51067     /* Boolean */
51068     ctx->class_proto[JS_CLASS_BOOLEAN] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
51069                                                                 JS_CLASS_BOOLEAN);
51070     JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], JS_NewBool(ctx, FALSE));
51071     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], js_boolean_proto_funcs,
51072                                countof(js_boolean_proto_funcs));
51073     JS_NewGlobalCConstructor(ctx, "Boolean", js_boolean_constructor, 1,
51074                              ctx->class_proto[JS_CLASS_BOOLEAN]);
51075 
51076     /* String */
51077     ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
51078                                                                JS_CLASS_STRING);
51079     JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string));
51080     obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1,
51081                                    ctx->class_proto[JS_CLASS_STRING]);
51082     JS_SetPropertyFunctionList(ctx, obj, js_string_funcs,
51083                                countof(js_string_funcs));
51084     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_funcs,
51085                                countof(js_string_proto_funcs));
51086 
51087     ctx->class_proto[JS_CLASS_STRING_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
51088     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING_ITERATOR],
51089                                js_string_iterator_proto_funcs,
51090                                countof(js_string_iterator_proto_funcs));
51091 
51092     /* Math: create as autoinit object */
51093     js_random_init(ctx);
51094     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj));
51095 
51096     /* ES6 Reflect: create as autoinit object */
51097     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj));
51098 
51099     /* ES6 Symbol */
51100     ctx->class_proto[JS_CLASS_SYMBOL] = JS_NewObject(ctx);
51101     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SYMBOL], js_symbol_proto_funcs,
51102                                countof(js_symbol_proto_funcs));
51103     obj = JS_NewGlobalCConstructor(ctx, "Symbol", js_symbol_constructor, 0,
51104                                    ctx->class_proto[JS_CLASS_SYMBOL]);
51105     JS_SetPropertyFunctionList(ctx, obj, js_symbol_funcs,
51106                                countof(js_symbol_funcs));
51107     for(i = JS_ATOM_Symbol_toPrimitive; i < JS_ATOM_END; i++) {
51108         char buf[ATOM_GET_STR_BUF_SIZE];
51109         const char *str, *p;
51110         str = JS_AtomGetStr(ctx, buf, sizeof(buf), i);
51111         /* skip "Symbol." */
51112         p = strchr(str, '.');
51113         if (p)
51114             str = p + 1;
51115         JS_DefinePropertyValueStr(ctx, obj, str, JS_AtomToValue(ctx, i), 0);
51116     }
51117 
51118     /* ES6 Generator */
51119     ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
51120     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR],
51121                                js_generator_proto_funcs,
51122                                countof(js_generator_proto_funcs));
51123 
51124     ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
51125     obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor,
51126                                 "GeneratorFunction", 1,
51127                                 JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR);
51128     JS_SetPropertyFunctionList(ctx,
51129                                ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
51130                                js_generator_function_proto_funcs,
51131                                countof(js_generator_function_proto_funcs));
51132     JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
51133                        ctx->class_proto[JS_CLASS_GENERATOR],
51134                        JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
51135     JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
51136                        0, JS_PROP_CONFIGURABLE);
51137     JS_FreeValue(ctx, obj1);
51138 
51139     /* global properties */
51140     ctx->eval_obj = JS_NewCFunction(ctx, js_global_eval, "eval", 1);
51141     JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_eval,
51142                            JS_DupValue(ctx, ctx->eval_obj),
51143                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
51144 
51145     JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis,
51146                            JS_DupValue(ctx, ctx->global_obj),
51147                            JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
51148 }
51149 
51150 /* Typed Arrays */
51151 
51152 static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {
51153     0, 0, 0, 1, 1, 2, 2,
51154 #ifdef CONFIG_BIGNUM
51155     3, 3, /* BigInt64Array, BigUint64Array */
51156 #endif
51157     2, 3
51158 };
51159 
js_array_buffer_constructor3(JSContext * ctx,JSValueConst new_target,uint64_t len,JSClassID class_id,uint8_t * buf,JSFreeArrayBufferDataFunc * free_func,void * opaque,BOOL alloc_flag)51160 static JSValue js_array_buffer_constructor3(JSContext *ctx,
51161                                             JSValueConst new_target,
51162                                             uint64_t len, JSClassID class_id,
51163                                             uint8_t *buf,
51164                                             JSFreeArrayBufferDataFunc *free_func,
51165                                             void *opaque, BOOL alloc_flag)
51166 {
51167     JSRuntime *rt = ctx->rt;
51168     JSValue obj;
51169     JSArrayBuffer *abuf = NULL;
51170 
51171     obj = js_create_from_ctor(ctx, new_target, class_id);
51172     if (JS_IsException(obj))
51173         return obj;
51174     /* XXX: we are currently limited to 2 GB */
51175     if (len > INT32_MAX) {
51176         JS_ThrowRangeError(ctx, "invalid array buffer length");
51177         goto fail;
51178     }
51179     abuf = js_malloc(ctx, sizeof(*abuf));
51180     if (!abuf)
51181         goto fail;
51182     abuf->byte_length = len;
51183     if (alloc_flag) {
51184         if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
51185             rt->sab_funcs.sab_alloc) {
51186             abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque,
51187                                                  max_int(len, 1));
51188             if (!abuf->data)
51189                 goto fail;
51190             memset(abuf->data, 0, len);
51191         } else {
51192             /* the allocation must be done after the object creation */
51193             abuf->data = js_mallocz(ctx, max_int(len, 1));
51194             if (!abuf->data)
51195                 goto fail;
51196         }
51197     } else {
51198         if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
51199             rt->sab_funcs.sab_dup) {
51200             rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf);
51201         }
51202         abuf->data = buf;
51203     }
51204     init_list_head(&abuf->array_list);
51205     abuf->detached = FALSE;
51206     abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER);
51207     abuf->opaque = opaque;
51208     abuf->free_func = free_func;
51209     if (alloc_flag && buf)
51210         memcpy(abuf->data, buf, len);
51211     JS_SetOpaque(obj, abuf);
51212     return obj;
51213  fail:
51214     JS_FreeValue(ctx, obj);
51215     js_free(ctx, abuf);
51216     return JS_EXCEPTION;
51217 }
51218 
js_array_buffer_free(JSRuntime * rt,void * opaque,void * ptr)51219 static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr)
51220 {
51221     js_free_rt(rt, ptr);
51222 }
51223 
js_array_buffer_constructor2(JSContext * ctx,JSValueConst new_target,uint64_t len,JSClassID class_id)51224 static JSValue js_array_buffer_constructor2(JSContext *ctx,
51225                                             JSValueConst new_target,
51226                                             uint64_t len, JSClassID class_id)
51227 {
51228     return js_array_buffer_constructor3(ctx, new_target, len, class_id,
51229                                         NULL, js_array_buffer_free, NULL,
51230                                         TRUE);
51231 }
51232 
js_array_buffer_constructor1(JSContext * ctx,JSValueConst new_target,uint64_t len)51233 static JSValue js_array_buffer_constructor1(JSContext *ctx,
51234                                             JSValueConst new_target,
51235                                             uint64_t len)
51236 {
51237     return js_array_buffer_constructor2(ctx, new_target, len,
51238                                         JS_CLASS_ARRAY_BUFFER);
51239 }
51240 
JS_NewArrayBuffer(JSContext * ctx,uint8_t * buf,size_t len,JSFreeArrayBufferDataFunc * free_func,void * opaque,BOOL is_shared)51241 JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
51242                           JSFreeArrayBufferDataFunc *free_func, void *opaque,
51243                           BOOL is_shared)
51244 {
51245     return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,
51246                                         is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER,
51247                                         buf, free_func, opaque, FALSE);
51248 }
51249 
51250 /* create a new ArrayBuffer of length 'len' and copy 'buf' to it */
JS_NewArrayBufferCopy(JSContext * ctx,const uint8_t * buf,size_t len)51251 JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len)
51252 {
51253     return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,
51254                                         JS_CLASS_ARRAY_BUFFER,
51255                                         (uint8_t *)buf,
51256                                         js_array_buffer_free, NULL,
51257                                         TRUE);
51258 }
51259 
js_array_buffer_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)51260 static JSValue js_array_buffer_constructor(JSContext *ctx,
51261                                            JSValueConst new_target,
51262                                            int argc, JSValueConst *argv)
51263 {
51264     uint64_t len;
51265     if (JS_ToIndex(ctx, &len, argv[0]))
51266         return JS_EXCEPTION;
51267     return js_array_buffer_constructor1(ctx, new_target, len);
51268 }
51269 
js_shared_array_buffer_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)51270 static JSValue js_shared_array_buffer_constructor(JSContext *ctx,
51271                                                   JSValueConst new_target,
51272                                                   int argc, JSValueConst *argv)
51273 {
51274     uint64_t len;
51275     if (JS_ToIndex(ctx, &len, argv[0]))
51276         return JS_EXCEPTION;
51277     return js_array_buffer_constructor2(ctx, new_target, len,
51278                                         JS_CLASS_SHARED_ARRAY_BUFFER);
51279 }
51280 
51281 /* also used for SharedArrayBuffer */
js_array_buffer_finalizer(JSRuntime * rt,JSValue val)51282 static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val)
51283 {
51284     JSObject *p = JS_VALUE_GET_OBJ(val);
51285     JSArrayBuffer *abuf = p->u.array_buffer;
51286     if (abuf) {
51287         /* The ArrayBuffer finalizer may be called before the typed
51288            array finalizers using it, so abuf->array_list is not
51289            necessarily empty. */
51290         // assert(list_empty(&abuf->array_list));
51291         if (abuf->shared && rt->sab_funcs.sab_free) {
51292             rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data);
51293         } else {
51294             if (abuf->free_func)
51295                 abuf->free_func(rt, abuf->opaque, abuf->data);
51296         }
51297         js_free_rt(rt, abuf);
51298     }
51299 }
51300 
js_array_buffer_isView(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51301 static JSValue js_array_buffer_isView(JSContext *ctx,
51302                                       JSValueConst this_val,
51303                                       int argc, JSValueConst *argv)
51304 {
51305     JSObject *p;
51306     BOOL res;
51307     res = FALSE;
51308     if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
51309         p = JS_VALUE_GET_OBJ(argv[0]);
51310         if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
51311             p->class_id <= JS_CLASS_DATAVIEW) {
51312             res = TRUE;
51313         }
51314     }
51315     return JS_NewBool(ctx, res);
51316 }
51317 
51318 static const JSCFunctionListEntry js_array_buffer_funcs[] = {
51319     JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ),
51320     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
51321 };
51322 
JS_ThrowTypeErrorDetachedArrayBuffer(JSContext * ctx)51323 static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx)
51324 {
51325     return JS_ThrowTypeError(ctx, "ArrayBuffer is detached");
51326 }
51327 
js_array_buffer_get_byteLength(JSContext * ctx,JSValueConst this_val,int class_id)51328 static JSValue js_array_buffer_get_byteLength(JSContext *ctx,
51329                                               JSValueConst this_val,
51330                                               int class_id)
51331 {
51332     JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id);
51333     if (!abuf)
51334         return JS_EXCEPTION;
51335     /* return 0 if detached */
51336     return JS_NewUint32(ctx, abuf->byte_length);
51337 }
51338 
JS_DetachArrayBuffer(JSContext * ctx,JSValueConst obj)51339 void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj)
51340 {
51341     JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER);
51342     struct list_head *el;
51343 
51344     if (!abuf || abuf->detached)
51345         return;
51346     if (abuf->free_func)
51347         abuf->free_func(ctx->rt, abuf->opaque, abuf->data);
51348     abuf->data = NULL;
51349     abuf->byte_length = 0;
51350     abuf->detached = TRUE;
51351 
51352     list_for_each(el, &abuf->array_list) {
51353         JSTypedArray *ta;
51354         JSObject *p;
51355 
51356         ta = list_entry(el, JSTypedArray, link);
51357         p = ta->obj;
51358         /* Note: the typed array length and offset fields are not modified */
51359         if (p->class_id != JS_CLASS_DATAVIEW) {
51360             p->u.array.count = 0;
51361             p->u.array.u.ptr = NULL;
51362         }
51363     }
51364 }
51365 
51366 /* get an ArrayBuffer or SharedArrayBuffer */
js_get_array_buffer(JSContext * ctx,JSValueConst obj)51367 static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj)
51368 {
51369     JSObject *p;
51370     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
51371         goto fail;
51372     p = JS_VALUE_GET_OBJ(obj);
51373     if (p->class_id != JS_CLASS_ARRAY_BUFFER &&
51374         p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) {
51375     fail:
51376         JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER);
51377         return NULL;
51378     }
51379     return p->u.array_buffer;
51380 }
51381 
51382 /* return NULL if exception. WARNING: any JS call can detach the
51383    buffer and render the returned pointer invalid */
JS_GetArrayBuffer(JSContext * ctx,size_t * psize,JSValueConst obj)51384 uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj)
51385 {
51386     JSArrayBuffer *abuf = js_get_array_buffer(ctx, obj);
51387     if (!abuf)
51388         goto fail;
51389     if (abuf->detached) {
51390         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51391         goto fail;
51392     }
51393     *psize = abuf->byte_length;
51394     return abuf->data;
51395  fail:
51396     *psize = 0;
51397     return NULL;
51398 }
51399 
js_array_buffer_slice(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int class_id)51400 static JSValue js_array_buffer_slice(JSContext *ctx,
51401                                      JSValueConst this_val,
51402                                      int argc, JSValueConst *argv, int class_id)
51403 {
51404     JSArrayBuffer *abuf, *new_abuf;
51405     int64_t len, start, end, new_len;
51406     JSValue ctor, new_obj;
51407 
51408     abuf = JS_GetOpaque2(ctx, this_val, class_id);
51409     if (!abuf)
51410         return JS_EXCEPTION;
51411     if (abuf->detached)
51412         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51413     len = abuf->byte_length;
51414 
51415     if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
51416         return JS_EXCEPTION;
51417 
51418     end = len;
51419     if (!JS_IsUndefined(argv[1])) {
51420         if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len))
51421             return JS_EXCEPTION;
51422     }
51423     new_len = max_int64(end - start, 0);
51424     ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
51425     if (JS_IsException(ctor))
51426         return ctor;
51427     if (JS_IsUndefined(ctor)) {
51428         new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len,
51429                                                class_id);
51430     } else {
51431         JSValue args[1];
51432         args[0] = JS_NewInt64(ctx, new_len);
51433         new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)args);
51434         JS_FreeValue(ctx, ctor);
51435         JS_FreeValue(ctx, args[0]);
51436     }
51437     if (JS_IsException(new_obj))
51438         return new_obj;
51439     new_abuf = JS_GetOpaque2(ctx, new_obj, class_id);
51440     if (!new_abuf)
51441         goto fail;
51442     if (js_same_value(ctx, new_obj, this_val)) {
51443         JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer");
51444         goto fail;
51445     }
51446     if (new_abuf->detached) {
51447         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51448         goto fail;
51449     }
51450     if (new_abuf->byte_length < new_len) {
51451         JS_ThrowTypeError(ctx, "new ArrayBuffer is too small");
51452         goto fail;
51453     }
51454     /* must test again because of side effects */
51455     if (abuf->detached) {
51456         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51457         goto fail;
51458     }
51459     memcpy(new_abuf->data, abuf->data + start, new_len);
51460     return new_obj;
51461  fail:
51462     JS_FreeValue(ctx, new_obj);
51463     return JS_EXCEPTION;
51464 }
51465 
51466 static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = {
51467     JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ),
51468     JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ),
51469     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ),
51470 };
51471 
51472 /* SharedArrayBuffer */
51473 
51474 static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = {
51475     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
51476 };
51477 
51478 static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = {
51479     JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ),
51480     JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ),
51481     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ),
51482 };
51483 
get_typed_array(JSContext * ctx,JSValueConst this_val,int is_dataview)51484 static JSObject *get_typed_array(JSContext *ctx,
51485                                  JSValueConst this_val,
51486                                  int is_dataview)
51487 {
51488     JSObject *p;
51489     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
51490         goto fail;
51491     p = JS_VALUE_GET_OBJ(this_val);
51492     if (is_dataview) {
51493         if (p->class_id != JS_CLASS_DATAVIEW)
51494             goto fail;
51495     } else {
51496         if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
51497               p->class_id <= JS_CLASS_FLOAT64_ARRAY)) {
51498         fail:
51499             JS_ThrowTypeError(ctx, "not a %s", is_dataview ? "DataView" : "TypedArray");
51500             return NULL;
51501         }
51502     }
51503     return p;
51504 }
51505 
51506 /* WARNING: 'p' must be a typed array */
typed_array_is_detached(JSContext * ctx,JSObject * p)51507 static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p)
51508 {
51509     JSTypedArray *ta = p->u.typed_array;
51510     JSArrayBuffer *abuf = ta->buffer->u.array_buffer;
51511     /* XXX: could simplify test by ensuring that
51512        p->u.array.u.ptr is NULL iff it is detached */
51513     return abuf->detached;
51514 }
51515 
51516 /* WARNING: 'p' must be a typed array. Works even if the array buffer
51517    is detached */
typed_array_get_length(JSContext * ctx,JSObject * p)51518 static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p)
51519 {
51520     JSTypedArray *ta = p->u.typed_array;
51521     int size_log2 = typed_array_size_log2(p->class_id);
51522     return ta->length >> size_log2;
51523 }
51524 
validate_typed_array(JSContext * ctx,JSValueConst this_val)51525 static int validate_typed_array(JSContext *ctx, JSValueConst this_val)
51526 {
51527     JSObject *p;
51528     p = get_typed_array(ctx, this_val, 0);
51529     if (!p)
51530         return -1;
51531     if (typed_array_is_detached(ctx, p)) {
51532         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51533         return -1;
51534     }
51535     return 0;
51536 }
51537 
js_typed_array_get_length(JSContext * ctx,JSValueConst this_val)51538 static JSValue js_typed_array_get_length(JSContext *ctx,
51539                                          JSValueConst this_val)
51540 {
51541     JSObject *p;
51542     p = get_typed_array(ctx, this_val, 0);
51543     if (!p)
51544         return JS_EXCEPTION;
51545     return JS_NewInt32(ctx, p->u.array.count);
51546 }
51547 
js_typed_array_get_buffer(JSContext * ctx,JSValueConst this_val,int is_dataview)51548 static JSValue js_typed_array_get_buffer(JSContext *ctx,
51549                                          JSValueConst this_val, int is_dataview)
51550 {
51551     JSObject *p;
51552     JSTypedArray *ta;
51553     p = get_typed_array(ctx, this_val, is_dataview);
51554     if (!p)
51555         return JS_EXCEPTION;
51556     ta = p->u.typed_array;
51557     return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
51558 }
51559 
js_typed_array_get_byteLength(JSContext * ctx,JSValueConst this_val,int is_dataview)51560 static JSValue js_typed_array_get_byteLength(JSContext *ctx,
51561                                              JSValueConst this_val,
51562                                              int is_dataview)
51563 {
51564     JSObject *p;
51565     JSTypedArray *ta;
51566     p = get_typed_array(ctx, this_val, is_dataview);
51567     if (!p)
51568         return JS_EXCEPTION;
51569     if (typed_array_is_detached(ctx, p)) {
51570         if (is_dataview) {
51571             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51572         } else {
51573             return JS_NewInt32(ctx, 0);
51574         }
51575     }
51576     ta = p->u.typed_array;
51577     return JS_NewInt32(ctx, ta->length);
51578 }
51579 
js_typed_array_get_byteOffset(JSContext * ctx,JSValueConst this_val,int is_dataview)51580 static JSValue js_typed_array_get_byteOffset(JSContext *ctx,
51581                                              JSValueConst this_val,
51582                                              int is_dataview)
51583 {
51584     JSObject *p;
51585     JSTypedArray *ta;
51586     p = get_typed_array(ctx, this_val, is_dataview);
51587     if (!p)
51588         return JS_EXCEPTION;
51589     if (typed_array_is_detached(ctx, p)) {
51590         if (is_dataview) {
51591             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51592         } else {
51593             return JS_NewInt32(ctx, 0);
51594         }
51595     }
51596     ta = p->u.typed_array;
51597     return JS_NewInt32(ctx, ta->offset);
51598 }
51599 
51600 /* Return the buffer associated to the typed array or an exception if
51601    it is not a typed array or if the buffer is detached. pbyte_offset,
51602    pbyte_length or pbytes_per_element can be NULL. */
JS_GetTypedArrayBuffer(JSContext * ctx,JSValueConst obj,size_t * pbyte_offset,size_t * pbyte_length,size_t * pbytes_per_element)51603 JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
51604                                size_t *pbyte_offset,
51605                                size_t *pbyte_length,
51606                                size_t *pbytes_per_element)
51607 {
51608     JSObject *p;
51609     JSTypedArray *ta;
51610     p = get_typed_array(ctx, obj, FALSE);
51611     if (!p)
51612         return JS_EXCEPTION;
51613     if (typed_array_is_detached(ctx, p))
51614         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51615     ta = p->u.typed_array;
51616     if (pbyte_offset)
51617         *pbyte_offset = ta->offset;
51618     if (pbyte_length)
51619         *pbyte_length = ta->length;
51620     if (pbytes_per_element) {
51621         *pbytes_per_element = 1 << typed_array_size_log2(p->class_id);
51622     }
51623     return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
51624 }
51625 
js_typed_array_get_toStringTag(JSContext * ctx,JSValueConst this_val)51626 static JSValue js_typed_array_get_toStringTag(JSContext *ctx,
51627                                               JSValueConst this_val)
51628 {
51629     JSObject *p;
51630     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
51631         return JS_UNDEFINED;
51632     p = JS_VALUE_GET_OBJ(this_val);
51633     if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
51634           p->class_id <= JS_CLASS_FLOAT64_ARRAY))
51635         return JS_UNDEFINED;
51636     return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name);
51637 }
51638 
js_typed_array_set_internal(JSContext * ctx,JSValueConst dst,JSValueConst src,JSValueConst off)51639 static JSValue js_typed_array_set_internal(JSContext *ctx,
51640                                            JSValueConst dst,
51641                                            JSValueConst src,
51642                                            JSValueConst off)
51643 {
51644     JSObject *p;
51645     JSObject *src_p;
51646     uint32_t i;
51647     int64_t src_len, offset;
51648     JSValue val, src_obj = JS_UNDEFINED;
51649 
51650     p = get_typed_array(ctx, dst, 0);
51651     if (!p)
51652         goto fail;
51653     if (JS_ToInt64Sat(ctx, &offset, off))
51654         goto fail;
51655     if (offset < 0)
51656         goto range_error;
51657     if (typed_array_is_detached(ctx, p)) {
51658     detached:
51659         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51660         goto fail;
51661     }
51662     src_obj = JS_ToObject(ctx, src);
51663     if (JS_IsException(src_obj))
51664         goto fail;
51665     src_p = JS_VALUE_GET_OBJ(src_obj);
51666     if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY &&
51667         src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
51668         JSTypedArray *dest_ta = p->u.typed_array;
51669         JSArrayBuffer *dest_abuf = dest_ta->buffer->u.array_buffer;
51670         JSTypedArray *src_ta = src_p->u.typed_array;
51671         JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer;
51672         int shift = typed_array_size_log2(p->class_id);
51673 
51674         if (src_abuf->detached)
51675             goto detached;
51676 
51677         src_len = src_p->u.array.count;
51678         if (offset > (int64_t)(p->u.array.count - src_len))
51679             goto range_error;
51680 
51681         /* copying between typed objects */
51682         if (src_p->class_id == p->class_id) {
51683             /* same type, use memmove */
51684             memmove(dest_abuf->data + dest_ta->offset + (offset << shift),
51685                     src_abuf->data + src_ta->offset, src_len << shift);
51686             goto done;
51687         }
51688         if (dest_abuf->data == src_abuf->data) {
51689             /* copying between the same buffer using different types of mappings
51690                would require a temporary buffer */
51691         }
51692         /* otherwise, default behavior is slow but correct */
51693     } else {
51694         if (js_get_length64(ctx, &src_len, src_obj))
51695             goto fail;
51696         if (offset > (int64_t)(p->u.array.count - src_len)) {
51697         range_error:
51698             JS_ThrowRangeError(ctx, "invalid array length");
51699             goto fail;
51700         }
51701     }
51702     for(i = 0; i < src_len; i++) {
51703         val = JS_GetPropertyUint32(ctx, src_obj, i);
51704         if (JS_IsException(val))
51705             goto fail;
51706         if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0)
51707             goto fail;
51708     }
51709 done:
51710     JS_FreeValue(ctx, src_obj);
51711     return JS_UNDEFINED;
51712 fail:
51713     JS_FreeValue(ctx, src_obj);
51714     return JS_EXCEPTION;
51715 }
51716 
js_typed_array_set(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51717 static JSValue js_typed_array_set(JSContext *ctx,
51718                                   JSValueConst this_val,
51719                                   int argc, JSValueConst *argv)
51720 {
51721     JSValueConst offset = JS_UNDEFINED;
51722     if (argc > 1) {
51723         offset = argv[1];
51724     }
51725     return js_typed_array_set_internal(ctx, this_val, argv[0], offset);
51726 }
51727 
js_create_typed_array_iterator(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)51728 static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValueConst this_val,
51729                                               int argc, JSValueConst *argv, int magic)
51730 {
51731     if (validate_typed_array(ctx, this_val))
51732         return JS_EXCEPTION;
51733     return js_create_array_iterator(ctx, this_val, argc, argv, magic);
51734 }
51735 
51736 /* return < 0 if exception */
js_typed_array_get_length_internal(JSContext * ctx,JSValueConst obj)51737 static int js_typed_array_get_length_internal(JSContext *ctx,
51738                                               JSValueConst obj)
51739 {
51740     JSObject *p;
51741     p = get_typed_array(ctx, obj, 0);
51742     if (!p)
51743         return -1;
51744     if (typed_array_is_detached(ctx, p)) {
51745         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51746         return -1;
51747     }
51748     return p->u.array.count;
51749 }
51750 
51751 #if 0
51752 /* validate a typed array and return its length */
51753 static JSValue js_typed_array___getLength(JSContext *ctx,
51754                                           JSValueConst this_val,
51755                                           int argc, JSValueConst *argv)
51756 {
51757     BOOL ignore_detached = JS_ToBool(ctx, argv[1]);
51758 
51759     if (ignore_detached) {
51760         return js_typed_array_get_length(ctx, argv[0]);
51761     } else {
51762         int len;
51763         len = js_typed_array_get_length_internal(ctx, argv[0]);
51764         if (len < 0)
51765             return JS_EXCEPTION;
51766         return JS_NewInt32(ctx, len);
51767     }
51768 }
51769 #endif
51770 
js_typed_array_create(JSContext * ctx,JSValueConst ctor,int argc,JSValueConst * argv)51771 static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor,
51772                                      int argc, JSValueConst *argv)
51773 {
51774     JSValue ret;
51775     int new_len;
51776     int64_t len;
51777 
51778     ret = JS_CallConstructor(ctx, ctor, argc, argv);
51779     if (JS_IsException(ret))
51780         return ret;
51781     /* validate the typed array */
51782     new_len = js_typed_array_get_length_internal(ctx, ret);
51783     if (new_len < 0)
51784         goto fail;
51785     if (argc == 1) {
51786         /* ensure that it is large enough */
51787         if (JS_ToLengthFree(ctx, &len, JS_DupValue(ctx, argv[0])))
51788             goto fail;
51789         if (new_len < len) {
51790             JS_ThrowTypeError(ctx, "TypedArray length is too small");
51791         fail:
51792             JS_FreeValue(ctx, ret);
51793             return JS_EXCEPTION;
51794         }
51795     }
51796     return ret;
51797 }
51798 
51799 #if 0
51800 static JSValue js_typed_array___create(JSContext *ctx,
51801                                        JSValueConst this_val,
51802                                        int argc, JSValueConst *argv)
51803 {
51804     return js_typed_array_create(ctx, argv[0], max_int(argc - 1, 0), argv + 1);
51805 }
51806 #endif
51807 
js_typed_array___speciesCreate(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51808 static JSValue js_typed_array___speciesCreate(JSContext *ctx,
51809                                               JSValueConst this_val,
51810                                               int argc, JSValueConst *argv)
51811 {
51812     JSValueConst obj;
51813     JSObject *p;
51814     JSValue ctor, ret;
51815     int argc1;
51816 
51817     obj = argv[0];
51818     p = get_typed_array(ctx, obj, 0);
51819     if (!p)
51820         return JS_EXCEPTION;
51821     ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED);
51822     if (JS_IsException(ctor))
51823         return ctor;
51824     argc1 = max_int(argc - 1, 0);
51825     if (JS_IsUndefined(ctor)) {
51826         ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1,
51827                                          p->class_id);
51828     } else {
51829         ret = js_typed_array_create(ctx, ctor, argc1, argv + 1);
51830         JS_FreeValue(ctx, ctor);
51831     }
51832     return ret;
51833 }
51834 
js_typed_array_from(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51835 static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val,
51836                                    int argc, JSValueConst *argv)
51837 {
51838     // from(items, mapfn = void 0, this_arg = void 0)
51839     JSValueConst items = argv[0], mapfn, this_arg;
51840     JSValueConst args[2];
51841     JSValue stack[2];
51842     JSValue iter, arr, r, v, v2;
51843     int64_t k, len;
51844     int done, mapping;
51845 
51846     mapping = FALSE;
51847     mapfn = JS_UNDEFINED;
51848     this_arg = JS_UNDEFINED;
51849     r = JS_UNDEFINED;
51850     arr = JS_UNDEFINED;
51851     stack[0] = JS_UNDEFINED;
51852     stack[1] = JS_UNDEFINED;
51853 
51854     if (argc > 1) {
51855         mapfn = argv[1];
51856         if (!JS_IsUndefined(mapfn)) {
51857             if (check_function(ctx, mapfn))
51858                 goto exception;
51859             mapping = 1;
51860             if (argc > 2)
51861                 this_arg = argv[2];
51862         }
51863     }
51864     iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
51865     if (JS_IsException(iter))
51866         goto exception;
51867     if (!JS_IsUndefined(iter)) {
51868         JS_FreeValue(ctx, iter);
51869         arr = JS_NewArray(ctx);
51870         if (JS_IsException(arr))
51871             goto exception;
51872         stack[0] = JS_DupValue(ctx, items);
51873         if (js_for_of_start(ctx, &stack[1], FALSE))
51874             goto exception;
51875         for (k = 0;; k++) {
51876             v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);
51877             if (JS_IsException(v))
51878                 goto exception_close;
51879             if (done)
51880                 break;
51881             if (JS_DefinePropertyValueInt64(ctx, arr, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
51882                 goto exception_close;
51883         }
51884     } else {
51885         arr = JS_ToObject(ctx, items);
51886         if (JS_IsException(arr))
51887             goto exception;
51888     }
51889     if (js_get_length64(ctx, &len, arr) < 0)
51890         goto exception;
51891     v = JS_NewInt64(ctx, len);
51892     args[0] = v;
51893     r = js_typed_array_create(ctx, this_val, 1, args);
51894     JS_FreeValue(ctx, v);
51895     if (JS_IsException(r))
51896         goto exception;
51897     for(k = 0; k < len; k++) {
51898         v = JS_GetPropertyInt64(ctx, arr, k);
51899         if (JS_IsException(v))
51900             goto exception;
51901         if (mapping) {
51902             args[0] = v;
51903             args[1] = JS_NewInt32(ctx, k);
51904             v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
51905             JS_FreeValue(ctx, v);
51906             v = v2;
51907             if (JS_IsException(v))
51908                 goto exception;
51909         }
51910         if (JS_SetPropertyInt64(ctx, r, k, v) < 0)
51911             goto exception;
51912     }
51913     goto done;
51914 
51915  exception_close:
51916     if (!JS_IsUndefined(stack[0]))
51917         JS_IteratorClose(ctx, stack[0], TRUE);
51918  exception:
51919     JS_FreeValue(ctx, r);
51920     r = JS_EXCEPTION;
51921  done:
51922     JS_FreeValue(ctx, arr);
51923     JS_FreeValue(ctx, stack[0]);
51924     JS_FreeValue(ctx, stack[1]);
51925     return r;
51926 }
51927 
js_typed_array_of(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51928 static JSValue js_typed_array_of(JSContext *ctx, JSValueConst this_val,
51929                                  int argc, JSValueConst *argv)
51930 {
51931     JSValue obj;
51932     JSValueConst args[1];
51933     int i;
51934 
51935     args[0] = JS_NewInt32(ctx, argc);
51936     obj = js_typed_array_create(ctx, this_val, 1, args);
51937     if (JS_IsException(obj))
51938         return obj;
51939 
51940     for(i = 0; i < argc; i++) {
51941         if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) {
51942             JS_FreeValue(ctx, obj);
51943             return JS_EXCEPTION;
51944         }
51945     }
51946     return obj;
51947 }
51948 
js_typed_array_copyWithin(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51949 static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val,
51950                                          int argc, JSValueConst *argv)
51951 {
51952     JSObject *p;
51953     int len, to, from, final, count, shift;
51954 
51955     len = js_typed_array_get_length_internal(ctx, this_val);
51956     if (len < 0)
51957         return JS_EXCEPTION;
51958 
51959     if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len))
51960         return JS_EXCEPTION;
51961 
51962     if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len))
51963         return JS_EXCEPTION;
51964 
51965     final = len;
51966     if (argc > 2 && !JS_IsUndefined(argv[2])) {
51967         if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
51968             return JS_EXCEPTION;
51969     }
51970 
51971     count = min_int(final - from, len - to);
51972     if (count > 0) {
51973         p = JS_VALUE_GET_OBJ(this_val);
51974         if (typed_array_is_detached(ctx, p))
51975             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51976         shift = typed_array_size_log2(p->class_id);
51977         memmove(p->u.array.u.uint8_ptr + (to << shift),
51978                 p->u.array.u.uint8_ptr + (from << shift),
51979                 count << shift);
51980     }
51981     return JS_DupValue(ctx, this_val);
51982 }
51983 
js_typed_array_fill(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51984 static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val,
51985                                    int argc, JSValueConst *argv)
51986 {
51987     JSObject *p;
51988     int len, k, final, shift;
51989     uint64_t v64;
51990 
51991     len = js_typed_array_get_length_internal(ctx, this_val);
51992     if (len < 0)
51993         return JS_EXCEPTION;
51994     p = JS_VALUE_GET_OBJ(this_val);
51995 
51996     if (p->class_id == JS_CLASS_UINT8C_ARRAY) {
51997         int32_t v;
51998         if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0])))
51999             return JS_EXCEPTION;
52000         v64 = v;
52001     } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) {
52002         uint32_t v;
52003         if (JS_ToUint32(ctx, &v, argv[0]))
52004             return JS_EXCEPTION;
52005         v64 = v;
52006     } else
52007 #ifdef CONFIG_BIGNUM
52008     if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
52009         if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0]))
52010             return JS_EXCEPTION;
52011     } else
52012 #endif
52013     {
52014         double d;
52015         if (JS_ToFloat64(ctx, &d, argv[0]))
52016             return JS_EXCEPTION;
52017         if (p->class_id == JS_CLASS_FLOAT32_ARRAY) {
52018             union {
52019                 float f;
52020                 uint32_t u32;
52021             } u;
52022             u.f = d;
52023             v64 = u.u32;
52024         } else {
52025             JSFloat64Union u;
52026             u.d = d;
52027             v64 = u.u64;
52028         }
52029     }
52030 
52031     k = 0;
52032     if (argc > 1) {
52033         if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
52034             return JS_EXCEPTION;
52035     }
52036 
52037     final = len;
52038     if (argc > 2 && !JS_IsUndefined(argv[2])) {
52039         if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
52040             return JS_EXCEPTION;
52041     }
52042 
52043     if (typed_array_is_detached(ctx, p))
52044         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
52045 
52046     shift = typed_array_size_log2(p->class_id);
52047     switch(shift) {
52048     case 0:
52049         if (k < final) {
52050             memset(p->u.array.u.uint8_ptr + k, v64, final - k);
52051         }
52052         break;
52053     case 1:
52054         for(; k < final; k++) {
52055             p->u.array.u.uint16_ptr[k] = v64;
52056         }
52057         break;
52058     case 2:
52059         for(; k < final; k++) {
52060             p->u.array.u.uint32_ptr[k] = v64;
52061         }
52062         break;
52063     case 3:
52064         for(; k < final; k++) {
52065             p->u.array.u.uint64_ptr[k] = v64;
52066         }
52067         break;
52068     default:
52069         abort();
52070     }
52071     return JS_DupValue(ctx, this_val);
52072 }
52073 
js_typed_array_find(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int findIndex)52074 static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val,
52075                                    int argc, JSValueConst *argv, int findIndex)
52076 {
52077     JSValueConst func, this_arg;
52078     JSValueConst args[3];
52079     JSValue val, index_val, res;
52080     int len, k;
52081 
52082     val = JS_UNDEFINED;
52083     len = js_typed_array_get_length_internal(ctx, this_val);
52084     if (len < 0)
52085         goto exception;
52086 
52087     func = argv[0];
52088     if (check_function(ctx, func))
52089         goto exception;
52090 
52091     this_arg = JS_UNDEFINED;
52092     if (argc > 1)
52093         this_arg = argv[1];
52094 
52095     for(k = 0; k < len; k++) {
52096         index_val = JS_NewInt32(ctx, k);
52097         val = JS_GetPropertyValue(ctx, this_val, index_val);
52098         if (JS_IsException(val))
52099             goto exception;
52100         args[0] = val;
52101         args[1] = index_val;
52102         args[2] = this_val;
52103         res = JS_Call(ctx, func, this_arg, 3, args);
52104         if (JS_IsException(res))
52105             goto exception;
52106         if (JS_ToBoolFree(ctx, res)) {
52107             if (findIndex) {
52108                 JS_FreeValue(ctx, val);
52109                 return index_val;
52110             } else {
52111                 return val;
52112             }
52113         }
52114         JS_FreeValue(ctx, val);
52115     }
52116     if (findIndex)
52117         return JS_NewInt32(ctx, -1);
52118     else
52119         return JS_UNDEFINED;
52120 
52121 exception:
52122     JS_FreeValue(ctx, val);
52123     return JS_EXCEPTION;
52124 }
52125 
52126 #define special_indexOf 0
52127 #define special_lastIndexOf 1
52128 #define special_includes -1
52129 
js_typed_array_indexOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int special)52130 static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
52131                                       int argc, JSValueConst *argv, int special)
52132 {
52133     JSObject *p;
52134     int len, tag, is_int, is_bigint, k, stop, inc, res = -1;
52135     int64_t v64;
52136     double d;
52137     float f;
52138 
52139     len = js_typed_array_get_length_internal(ctx, this_val);
52140     if (len < 0)
52141         goto exception;
52142     if (len == 0)
52143         goto done;
52144 
52145     if (special == special_lastIndexOf) {
52146         k = len - 1;
52147         if (argc > 1) {
52148             if (JS_ToFloat64(ctx, &d, argv[1]))
52149                 goto exception;
52150             if (isnan(d)) {
52151                 k = 0;
52152             } else {
52153                 if (d >= 0) {
52154                     if (d < k) {
52155                         k = d;
52156                     }
52157                 } else {
52158                     d += len;
52159                     if (d < 0)
52160                         goto done;
52161                     k = d;
52162                 }
52163             }
52164         }
52165         stop = -1;
52166         inc = -1;
52167     } else {
52168         k = 0;
52169         if (argc > 1) {
52170             if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
52171                 goto exception;
52172         }
52173         stop = len;
52174         inc = 1;
52175     }
52176 
52177     p = JS_VALUE_GET_OBJ(this_val);
52178     /* if the array was detached, no need to go further (but no
52179        exception is raised) */
52180     if (typed_array_is_detached(ctx, p)) {
52181         /* "includes" scans all the properties, so "undefined" can match */
52182         if (special == special_includes && JS_IsUndefined(argv[0]) && len > 0)
52183             res = 0;
52184         goto done;
52185     }
52186 
52187     is_bigint = 0;
52188     is_int = 0; /* avoid warning */
52189     v64 = 0; /* avoid warning */
52190     tag = JS_VALUE_GET_NORM_TAG(argv[0]);
52191     if (tag == JS_TAG_INT) {
52192         is_int = 1;
52193         v64 = JS_VALUE_GET_INT(argv[0]);
52194         d = v64;
52195     } else
52196     if (tag == JS_TAG_FLOAT64) {
52197         d = JS_VALUE_GET_FLOAT64(argv[0]);
52198         v64 = d;
52199         is_int = (v64 == d);
52200     } else
52201 #ifdef CONFIG_BIGNUM
52202     if (tag == JS_TAG_BIG_INT) {
52203         JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]);
52204 
52205         if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) {
52206             if (bf_get_int64(&v64, &p1->num, 0) != 0)
52207                 goto done;
52208         } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) {
52209             if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0)
52210                 goto done;
52211         } else {
52212             goto done;
52213         }
52214         d = 0;
52215         is_bigint = 1;
52216     } else
52217 #endif
52218     {
52219         goto done;
52220     }
52221 
52222     switch (p->class_id) {
52223     case JS_CLASS_INT8_ARRAY:
52224         if (is_int && (int8_t)v64 == v64)
52225             goto scan8;
52226         break;
52227     case JS_CLASS_UINT8C_ARRAY:
52228     case JS_CLASS_UINT8_ARRAY:
52229         if (is_int && (uint8_t)v64 == v64) {
52230             const uint8_t *pv, *pp;
52231             uint16_t v;
52232         scan8:
52233             pv = p->u.array.u.uint8_ptr;
52234             v = v64;
52235             if (inc > 0) {
52236                 pp = memchr(pv + k, v, len - k);
52237                 if (pp)
52238                     res = pp - pv;
52239             } else {
52240                 for (; k != stop; k += inc) {
52241                     if (pv[k] == v) {
52242                         res = k;
52243                         break;
52244                     }
52245                 }
52246             }
52247         }
52248         break;
52249     case JS_CLASS_INT16_ARRAY:
52250         if (is_int && (int16_t)v64 == v64)
52251             goto scan16;
52252         break;
52253     case JS_CLASS_UINT16_ARRAY:
52254         if (is_int && (uint16_t)v64 == v64) {
52255             const uint16_t *pv;
52256             uint16_t v;
52257         scan16:
52258             pv = p->u.array.u.uint16_ptr;
52259             v = v64;
52260             for (; k != stop; k += inc) {
52261                 if (pv[k] == v) {
52262                     res = k;
52263                     break;
52264                 }
52265             }
52266         }
52267         break;
52268     case JS_CLASS_INT32_ARRAY:
52269         if (is_int && (int32_t)v64 == v64)
52270             goto scan32;
52271         break;
52272     case JS_CLASS_UINT32_ARRAY:
52273         if (is_int && (uint32_t)v64 == v64) {
52274             const uint32_t *pv;
52275             uint32_t v;
52276         scan32:
52277             pv = p->u.array.u.uint32_ptr;
52278             v = v64;
52279             for (; k != stop; k += inc) {
52280                 if (pv[k] == v) {
52281                     res = k;
52282                     break;
52283                 }
52284             }
52285         }
52286         break;
52287     case JS_CLASS_FLOAT32_ARRAY:
52288         if (is_bigint)
52289             break;
52290         if (isnan(d)) {
52291             const float *pv = p->u.array.u.float_ptr;
52292             /* special case: indexOf returns -1, includes finds NaN */
52293             if (special != special_includes)
52294                 goto done;
52295             for (; k != stop; k += inc) {
52296                 if (isnan(pv[k])) {
52297                     res = k;
52298                     break;
52299                 }
52300             }
52301         } else if ((f = (float)d) == d) {
52302             const float *pv = p->u.array.u.float_ptr;
52303             for (; k != stop; k += inc) {
52304                 if (pv[k] == f) {
52305                     res = k;
52306                     break;
52307                 }
52308             }
52309         }
52310         break;
52311     case JS_CLASS_FLOAT64_ARRAY:
52312         if (is_bigint)
52313             break;
52314         if (isnan(d)) {
52315             const double *pv = p->u.array.u.double_ptr;
52316             /* special case: indexOf returns -1, includes finds NaN */
52317             if (special != special_includes)
52318                 goto done;
52319             for (; k != stop; k += inc) {
52320                 if (isnan(pv[k])) {
52321                     res = k;
52322                     break;
52323                 }
52324             }
52325         } else {
52326             const double *pv = p->u.array.u.double_ptr;
52327             for (; k != stop; k += inc) {
52328                 if (pv[k] == d) {
52329                     res = k;
52330                     break;
52331                 }
52332             }
52333         }
52334         break;
52335 #ifdef CONFIG_BIGNUM
52336     case JS_CLASS_BIG_INT64_ARRAY:
52337         if (is_bigint || (is_math_mode(ctx) && is_int &&
52338                           v64 >= -MAX_SAFE_INTEGER &&
52339                           v64 <= MAX_SAFE_INTEGER)) {
52340             goto scan64;
52341         }
52342         break;
52343     case JS_CLASS_BIG_UINT64_ARRAY:
52344         if (is_bigint || (is_math_mode(ctx) && is_int &&
52345                           v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) {
52346             const uint64_t *pv;
52347             uint64_t v;
52348         scan64:
52349             pv = p->u.array.u.uint64_ptr;
52350             v = v64;
52351             for (; k != stop; k += inc) {
52352                 if (pv[k] == v) {
52353                     res = k;
52354                     break;
52355                 }
52356             }
52357         }
52358         break;
52359 #endif
52360     }
52361 
52362 done:
52363     if (special == special_includes)
52364         return JS_NewBool(ctx, res >= 0);
52365     else
52366         return JS_NewInt32(ctx, res);
52367 
52368 exception:
52369     return JS_EXCEPTION;
52370 }
52371 
js_typed_array_join(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int toLocaleString)52372 static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val,
52373                                    int argc, JSValueConst *argv, int toLocaleString)
52374 {
52375     JSValue sep = JS_UNDEFINED, el;
52376     StringBuffer b_s, *b = &b_s;
52377     JSString *p = NULL;
52378     int i, n;
52379     int c;
52380 
52381     n = js_typed_array_get_length_internal(ctx, this_val);
52382     if (n < 0)
52383         goto exception;
52384 
52385     c = ',';    /* default separator */
52386     if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
52387         sep = JS_ToString(ctx, argv[0]);
52388         if (JS_IsException(sep))
52389             goto exception;
52390         p = JS_VALUE_GET_STRING(sep);
52391         if (p->len == 1 && !p->is_wide_char)
52392             c = p->u.str8[0];
52393         else
52394             c = -1;
52395     }
52396     string_buffer_init(ctx, b, 0);
52397 
52398     /* XXX: optimize with direct access */
52399     for(i = 0; i < n; i++) {
52400         if (i > 0) {
52401             if (c >= 0) {
52402                 if (string_buffer_putc8(b, c))
52403                     goto fail;
52404             } else {
52405                 if (string_buffer_concat(b, p, 0, p->len))
52406                     goto fail;
52407             }
52408         }
52409         el = JS_GetPropertyUint32(ctx, this_val, i);
52410         /* Can return undefined for example if the typed array is detached */
52411         if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
52412             if (JS_IsException(el))
52413                 goto fail;
52414             if (toLocaleString) {
52415                 el = JS_ToLocaleStringFree(ctx, el);
52416             }
52417             if (string_buffer_concat_value_free(b, el))
52418                 goto fail;
52419         }
52420     }
52421     JS_FreeValue(ctx, sep);
52422     return string_buffer_end(b);
52423 
52424 fail:
52425     string_buffer_free(b);
52426     JS_FreeValue(ctx, sep);
52427 exception:
52428     return JS_EXCEPTION;
52429 }
52430 
js_typed_array_reverse(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)52431 static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val,
52432                                       int argc, JSValueConst *argv)
52433 {
52434     JSObject *p;
52435     int len;
52436 
52437     len = js_typed_array_get_length_internal(ctx, this_val);
52438     if (len < 0)
52439         return JS_EXCEPTION;
52440     if (len > 0) {
52441         p = JS_VALUE_GET_OBJ(this_val);
52442         switch (typed_array_size_log2(p->class_id)) {
52443         case 0:
52444             {
52445                 uint8_t *p1 = p->u.array.u.uint8_ptr;
52446                 uint8_t *p2 = p1 + len - 1;
52447                 while (p1 < p2) {
52448                     uint8_t v = *p1;
52449                     *p1++ = *p2;
52450                     *p2-- = v;
52451                 }
52452             }
52453             break;
52454         case 1:
52455             {
52456                 uint16_t *p1 = p->u.array.u.uint16_ptr;
52457                 uint16_t *p2 = p1 + len - 1;
52458                 while (p1 < p2) {
52459                     uint16_t v = *p1;
52460                     *p1++ = *p2;
52461                     *p2-- = v;
52462                 }
52463             }
52464             break;
52465         case 2:
52466             {
52467                 uint32_t *p1 = p->u.array.u.uint32_ptr;
52468                 uint32_t *p2 = p1 + len - 1;
52469                 while (p1 < p2) {
52470                     uint32_t v = *p1;
52471                     *p1++ = *p2;
52472                     *p2-- = v;
52473                 }
52474             }
52475             break;
52476         case 3:
52477             {
52478                 uint64_t *p1 = p->u.array.u.uint64_ptr;
52479                 uint64_t *p2 = p1 + len - 1;
52480                 while (p1 < p2) {
52481                     uint64_t v = *p1;
52482                     *p1++ = *p2;
52483                     *p2-- = v;
52484                 }
52485             }
52486             break;
52487         default:
52488             abort();
52489         }
52490     }
52491     return JS_DupValue(ctx, this_val);
52492 }
52493 
js_typed_array_slice(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)52494 static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val,
52495                                     int argc, JSValueConst *argv)
52496 {
52497     JSValueConst args[2];
52498     JSValue arr, val;
52499     JSObject *p, *p1;
52500     int n, len, start, final, count, shift;
52501 
52502     arr = JS_UNDEFINED;
52503     len = js_typed_array_get_length_internal(ctx, this_val);
52504     if (len < 0)
52505         goto exception;
52506 
52507     if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
52508         goto exception;
52509     final = len;
52510     if (!JS_IsUndefined(argv[1])) {
52511         if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
52512             goto exception;
52513     }
52514     count = max_int(final - start, 0);
52515 
52516     p = get_typed_array(ctx, this_val, 0);
52517     if (p == NULL)
52518         goto exception;
52519     shift = typed_array_size_log2(p->class_id);
52520 
52521     args[0] = this_val;
52522     args[1] = JS_NewInt32(ctx, count);
52523     arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
52524     if (JS_IsException(arr))
52525         goto exception;
52526 
52527     if (count > 0) {
52528         if (validate_typed_array(ctx, this_val)
52529         ||  validate_typed_array(ctx, arr))
52530             goto exception;
52531 
52532         p1 = get_typed_array(ctx, arr, 0);
52533         if (p1 != NULL && p->class_id == p1->class_id &&
52534             typed_array_get_length(ctx, p1) >= count &&
52535             typed_array_get_length(ctx, p) >= start + count) {
52536             memcpy(p1->u.array.u.uint8_ptr,
52537                    p->u.array.u.uint8_ptr + (start << shift),
52538                    count << shift);
52539         } else {
52540             for (n = 0; n < count; n++) {
52541                 val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n));
52542                 if (JS_IsException(val))
52543                     goto exception;
52544                 if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val,
52545                                         JS_PROP_THROW) < 0)
52546                     goto exception;
52547             }
52548         }
52549     }
52550     return arr;
52551 
52552  exception:
52553     JS_FreeValue(ctx, arr);
52554     return JS_EXCEPTION;
52555 }
52556 
js_typed_array_subarray(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)52557 static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val,
52558                                        int argc, JSValueConst *argv)
52559 {
52560     JSValueConst args[4];
52561     JSValue arr, byteOffset, ta_buffer;
52562     JSObject *p;
52563     int len, start, final, count, shift, offset;
52564 
52565     p = get_typed_array(ctx, this_val, 0);
52566     if (!p)
52567         goto exception;
52568     len = p->u.array.count;
52569     if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
52570         goto exception;
52571 
52572     final = len;
52573     if (!JS_IsUndefined(argv[1])) {
52574         if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
52575             goto exception;
52576     }
52577     count = max_int(final - start, 0);
52578     byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0);
52579     if (JS_IsException(byteOffset))
52580         goto exception;
52581     shift = typed_array_size_log2(p->class_id);
52582     offset = JS_VALUE_GET_INT(byteOffset) + (start << shift);
52583     JS_FreeValue(ctx, byteOffset);
52584     ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0);
52585     if (JS_IsException(ta_buffer))
52586         goto exception;
52587     args[0] = this_val;
52588     args[1] = ta_buffer;
52589     args[2] = JS_NewInt32(ctx, offset);
52590     args[3] = JS_NewInt32(ctx, count);
52591     arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args);
52592     JS_FreeValue(ctx, ta_buffer);
52593     return arr;
52594 
52595  exception:
52596     return JS_EXCEPTION;
52597 }
52598 
52599 /* TypedArray.prototype.sort */
52600 
js_cmp_doubles(double x,double y)52601 static int js_cmp_doubles(double x, double y)
52602 {
52603     if (isnan(x))    return isnan(y) ? 0 : +1;
52604     if (isnan(y))    return -1;
52605     if (x < y)       return -1;
52606     if (x > y)       return 1;
52607     if (x != 0)      return 0;
52608     if (signbit(x))  return signbit(y) ? 0 : -1;
52609     else             return signbit(y) ? 1 : 0;
52610 }
52611 
js_TA_cmp_int8(const void * a,const void * b,void * opaque)52612 static int js_TA_cmp_int8(const void *a, const void *b, void *opaque) {
52613     return *(const int8_t *)a - *(const int8_t *)b;
52614 }
52615 
js_TA_cmp_uint8(const void * a,const void * b,void * opaque)52616 static int js_TA_cmp_uint8(const void *a, const void *b, void *opaque) {
52617     return *(const uint8_t *)a - *(const uint8_t *)b;
52618 }
52619 
js_TA_cmp_int16(const void * a,const void * b,void * opaque)52620 static int js_TA_cmp_int16(const void *a, const void *b, void *opaque) {
52621     return *(const int16_t *)a - *(const int16_t *)b;
52622 }
52623 
js_TA_cmp_uint16(const void * a,const void * b,void * opaque)52624 static int js_TA_cmp_uint16(const void *a, const void *b, void *opaque) {
52625     return *(const uint16_t *)a - *(const uint16_t *)b;
52626 }
52627 
js_TA_cmp_int32(const void * a,const void * b,void * opaque)52628 static int js_TA_cmp_int32(const void *a, const void *b, void *opaque) {
52629     int32_t x = *(const int32_t *)a;
52630     int32_t y = *(const int32_t *)b;
52631     return (y < x) - (y > x);
52632 }
52633 
js_TA_cmp_uint32(const void * a,const void * b,void * opaque)52634 static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) {
52635     uint32_t x = *(const uint32_t *)a;
52636     uint32_t y = *(const uint32_t *)b;
52637     return (y < x) - (y > x);
52638 }
52639 
52640 #ifdef CONFIG_BIGNUM
js_TA_cmp_int64(const void * a,const void * b,void * opaque)52641 static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) {
52642     int64_t x = *(const int64_t *)a;
52643     int64_t y = *(const int64_t *)b;
52644     return (y < x) - (y > x);
52645 }
52646 
js_TA_cmp_uint64(const void * a,const void * b,void * opaque)52647 static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) {
52648     uint64_t x = *(const uint64_t *)a;
52649     uint64_t y = *(const uint64_t *)b;
52650     return (y < x) - (y > x);
52651 }
52652 #endif
52653 
js_TA_cmp_float32(const void * a,const void * b,void * opaque)52654 static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) {
52655     return js_cmp_doubles(*(const float *)a, *(const float *)b);
52656 }
52657 
js_TA_cmp_float64(const void * a,const void * b,void * opaque)52658 static int js_TA_cmp_float64(const void *a, const void *b, void *opaque) {
52659     return js_cmp_doubles(*(const double *)a, *(const double *)b);
52660 }
52661 
js_TA_get_int8(JSContext * ctx,const void * a)52662 static JSValue js_TA_get_int8(JSContext *ctx, const void *a) {
52663     return JS_NewInt32(ctx, *(const int8_t *)a);
52664 }
52665 
js_TA_get_uint8(JSContext * ctx,const void * a)52666 static JSValue js_TA_get_uint8(JSContext *ctx, const void *a) {
52667     return JS_NewInt32(ctx, *(const uint8_t *)a);
52668 }
52669 
js_TA_get_int16(JSContext * ctx,const void * a)52670 static JSValue js_TA_get_int16(JSContext *ctx, const void *a) {
52671     return JS_NewInt32(ctx, *(const int16_t *)a);
52672 }
52673 
js_TA_get_uint16(JSContext * ctx,const void * a)52674 static JSValue js_TA_get_uint16(JSContext *ctx, const void *a) {
52675     return JS_NewInt32(ctx, *(const uint16_t *)a);
52676 }
52677 
js_TA_get_int32(JSContext * ctx,const void * a)52678 static JSValue js_TA_get_int32(JSContext *ctx, const void *a) {
52679     return JS_NewInt32(ctx, *(const int32_t *)a);
52680 }
52681 
js_TA_get_uint32(JSContext * ctx,const void * a)52682 static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) {
52683     return JS_NewUint32(ctx, *(const uint32_t *)a);
52684 }
52685 
52686 #ifdef CONFIG_BIGNUM
js_TA_get_int64(JSContext * ctx,const void * a)52687 static JSValue js_TA_get_int64(JSContext *ctx, const void *a) {
52688     return JS_NewBigInt64(ctx, *(int64_t *)a);
52689 }
52690 
js_TA_get_uint64(JSContext * ctx,const void * a)52691 static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) {
52692     return JS_NewBigUint64(ctx, *(uint64_t *)a);
52693 }
52694 #endif
52695 
js_TA_get_float32(JSContext * ctx,const void * a)52696 static JSValue js_TA_get_float32(JSContext *ctx, const void *a) {
52697     return __JS_NewFloat64(ctx, *(const float *)a);
52698 }
52699 
js_TA_get_float64(JSContext * ctx,const void * a)52700 static JSValue js_TA_get_float64(JSContext *ctx, const void *a) {
52701     return __JS_NewFloat64(ctx, *(const double *)a);
52702 }
52703 
52704 struct TA_sort_context {
52705     JSContext *ctx;
52706     int exception;
52707     JSValueConst arr;
52708     JSValueConst cmp;
52709     JSValue (*getfun)(JSContext *ctx, const void *a);
52710     uint8_t *array_ptr; /* cannot change unless the array is detached */
52711     int elt_size;
52712 };
52713 
js_TA_cmp_generic(const void * a,const void * b,void * opaque)52714 static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
52715     struct TA_sort_context *psc = opaque;
52716     JSContext *ctx = psc->ctx;
52717     uint32_t a_idx, b_idx;
52718     JSValueConst argv[2];
52719     JSValue res;
52720     int cmp;
52721 
52722     cmp = 0;
52723     if (!psc->exception) {
52724         a_idx = *(uint32_t *)a;
52725         b_idx = *(uint32_t *)b;
52726         argv[0] = psc->getfun(ctx, psc->array_ptr +
52727                               a_idx * (size_t)psc->elt_size);
52728         argv[1] = psc->getfun(ctx, psc->array_ptr +
52729                               b_idx * (size_t)(psc->elt_size));
52730         res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv);
52731         if (JS_IsException(res)) {
52732             psc->exception = 1;
52733             goto done;
52734         }
52735         if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
52736             int val = JS_VALUE_GET_INT(res);
52737             cmp = (val > 0) - (val < 0);
52738         } else {
52739             double val;
52740             if (JS_ToFloat64Free(ctx, &val, res) < 0) {
52741                 psc->exception = 1;
52742                 goto done;
52743             } else {
52744                 cmp = (val > 0) - (val < 0);
52745             }
52746         }
52747         if (cmp == 0) {
52748             /* make sort stable: compare array offsets */
52749             cmp = (a_idx > b_idx) - (a_idx < b_idx);
52750         }
52751         if (validate_typed_array(ctx, psc->arr) < 0) {
52752             psc->exception = 1;
52753         }
52754     done:
52755         JS_FreeValue(ctx, JS_VALUE_CONST_CAST(argv[0]));
52756         JS_FreeValue(ctx, JS_VALUE_CONST_CAST(argv[1]));
52757     }
52758     return cmp;
52759 }
52760 
js_typed_array_sort(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)52761 static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
52762                                    int argc, JSValueConst *argv)
52763 {
52764     JSObject *p;
52765     int len;
52766     size_t elt_size;
52767     struct TA_sort_context tsc;
52768     void *array_ptr;
52769     int (*cmpfun)(const void *a, const void *b, void *opaque);
52770 
52771     tsc.ctx = ctx;
52772     tsc.exception = 0;
52773     tsc.arr = this_val;
52774     tsc.cmp = argv[0];
52775 
52776     len = js_typed_array_get_length_internal(ctx, this_val);
52777     if (len < 0)
52778         return JS_EXCEPTION;
52779     if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp))
52780         return JS_EXCEPTION;
52781 
52782     if (len > 1) {
52783         p = JS_VALUE_GET_OBJ(this_val);
52784         switch (p->class_id) {
52785         case JS_CLASS_INT8_ARRAY:
52786             tsc.getfun = js_TA_get_int8;
52787             cmpfun = js_TA_cmp_int8;
52788             break;
52789         case JS_CLASS_UINT8C_ARRAY:
52790         case JS_CLASS_UINT8_ARRAY:
52791             tsc.getfun = js_TA_get_uint8;
52792             cmpfun = js_TA_cmp_uint8;
52793             break;
52794         case JS_CLASS_INT16_ARRAY:
52795             tsc.getfun = js_TA_get_int16;
52796             cmpfun = js_TA_cmp_int16;
52797             break;
52798         case JS_CLASS_UINT16_ARRAY:
52799             tsc.getfun = js_TA_get_uint16;
52800             cmpfun = js_TA_cmp_uint16;
52801             break;
52802         case JS_CLASS_INT32_ARRAY:
52803             tsc.getfun = js_TA_get_int32;
52804             cmpfun = js_TA_cmp_int32;
52805             break;
52806         case JS_CLASS_UINT32_ARRAY:
52807             tsc.getfun = js_TA_get_uint32;
52808             cmpfun = js_TA_cmp_uint32;
52809             break;
52810 #ifdef CONFIG_BIGNUM
52811         case JS_CLASS_BIG_INT64_ARRAY:
52812             tsc.getfun = js_TA_get_int64;
52813             cmpfun = js_TA_cmp_int64;
52814             break;
52815         case JS_CLASS_BIG_UINT64_ARRAY:
52816             tsc.getfun = js_TA_get_uint64;
52817             cmpfun = js_TA_cmp_uint64;
52818             break;
52819 #endif
52820         case JS_CLASS_FLOAT32_ARRAY:
52821             tsc.getfun = js_TA_get_float32;
52822             cmpfun = js_TA_cmp_float32;
52823             break;
52824         case JS_CLASS_FLOAT64_ARRAY:
52825             tsc.getfun = js_TA_get_float64;
52826             cmpfun = js_TA_cmp_float64;
52827             break;
52828         default:
52829             abort();
52830         }
52831         array_ptr = p->u.array.u.ptr;
52832         elt_size = 1 << typed_array_size_log2(p->class_id);
52833         if (!JS_IsUndefined(tsc.cmp)) {
52834             uint32_t *array_idx;
52835             void *array_tmp;
52836             size_t i, j;
52837 
52838             /* XXX: a stable sort would use less memory */
52839             array_idx = js_malloc(ctx, len * sizeof(array_idx[0]));
52840             if (!array_idx)
52841                 return JS_EXCEPTION;
52842             for(i = 0; i < len; i++)
52843                 array_idx[i] = i;
52844             tsc.array_ptr = array_ptr;
52845             tsc.elt_size = elt_size;
52846             rqsort(array_idx, len, sizeof(array_idx[0]),
52847                    js_TA_cmp_generic, &tsc);
52848             if (tsc.exception)
52849                 goto fail;
52850             array_tmp = js_malloc(ctx, len * elt_size);
52851             if (!array_tmp) {
52852             fail:
52853                 js_free(ctx, array_idx);
52854                 return JS_EXCEPTION;
52855             }
52856             memcpy(array_tmp, array_ptr, len * elt_size);
52857             switch(elt_size) {
52858             case 1:
52859                 for(i = 0; i < len; i++) {
52860                     j = array_idx[i];
52861                     ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j];
52862                 }
52863                 break;
52864             case 2:
52865                 for(i = 0; i < len; i++) {
52866                     j = array_idx[i];
52867                     ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j];
52868                 }
52869                 break;
52870             case 4:
52871                 for(i = 0; i < len; i++) {
52872                     j = array_idx[i];
52873                     ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j];
52874                 }
52875                 break;
52876             case 8:
52877                 for(i = 0; i < len; i++) {
52878                     j = array_idx[i];
52879                     ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j];
52880                 }
52881                 break;
52882             default:
52883                 abort();
52884             }
52885             js_free(ctx, array_tmp);
52886             js_free(ctx, array_idx);
52887         } else {
52888             rqsort(array_ptr, len, elt_size, cmpfun, &tsc);
52889             if (tsc.exception)
52890                 return JS_EXCEPTION;
52891         }
52892     }
52893     return JS_DupValue(ctx, this_val);
52894 }
52895 
52896 static const JSCFunctionListEntry js_typed_array_base_funcs[] = {
52897     JS_CFUNC_DEF("from", 1, js_typed_array_from ),
52898     JS_CFUNC_DEF("of", 0, js_typed_array_of ),
52899     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
52900     //JS_CFUNC_DEF("__getLength", 2, js_typed_array___getLength ),
52901     //JS_CFUNC_DEF("__create", 2, js_typed_array___create ),
52902     //JS_CFUNC_DEF("__speciesCreate", 2, js_typed_array___speciesCreate ),
52903 };
52904 
52905 static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = {
52906     JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ),
52907     JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0 ),
52908     JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0 ),
52909     JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0 ),
52910     JS_CFUNC_DEF("set", 1, js_typed_array_set ),
52911     JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ),
52912     JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
52913     JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY ),
52914     JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
52915     JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL ),
52916     JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin ),
52917     JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA ),
52918     JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA ),
52919     JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA ),
52920     JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA ),
52921     JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA ),
52922     JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ),
52923     JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ),
52924     JS_CFUNC_DEF("fill", 1, js_typed_array_fill ),
52925     JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, 0 ),
52926     JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, 1 ),
52927     JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ),
52928     JS_CFUNC_DEF("slice", 2, js_typed_array_slice ),
52929     JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ),
52930     JS_CFUNC_DEF("sort", 1, js_typed_array_sort ),
52931     JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ),
52932     JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ),
52933     JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ),
52934     JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf ),
52935     JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes ),
52936     //JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@
52937 };
52938 
js_typed_array_base_constructor(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)52939 static JSValue js_typed_array_base_constructor(JSContext *ctx,
52940                                                JSValueConst this_val,
52941                                                int argc, JSValueConst *argv)
52942 {
52943     return JS_ThrowTypeError(ctx, "cannot be called");
52944 }
52945 
52946 /* 'obj' must be an allocated typed array object */
typed_array_init(JSContext * ctx,JSValueConst obj,JSValue buffer,uint64_t offset,uint64_t len)52947 static int typed_array_init(JSContext *ctx, JSValueConst obj,
52948                             JSValue buffer, uint64_t offset, uint64_t len)
52949 {
52950     JSTypedArray *ta;
52951     JSObject *p, *pbuffer;
52952     JSArrayBuffer *abuf;
52953     int size_log2;
52954 
52955     p = JS_VALUE_GET_OBJ(obj);
52956     size_log2 = typed_array_size_log2(p->class_id);
52957     ta = js_malloc(ctx, sizeof(*ta));
52958     if (!ta) {
52959         JS_FreeValue(ctx, buffer);
52960         return -1;
52961     }
52962     pbuffer = JS_VALUE_GET_OBJ(buffer);
52963     abuf = pbuffer->u.array_buffer;
52964     ta->obj = p;
52965     ta->buffer = pbuffer;
52966     ta->offset = offset;
52967     ta->length = len << size_log2;
52968     list_add_tail(&ta->link, &abuf->array_list);
52969     p->u.typed_array = ta;
52970     p->u.array.count = len;
52971     p->u.array.u.ptr = abuf->data + offset;
52972     return 0;
52973 }
52974 
52975 
js_array_from_iterator(JSContext * ctx,uint32_t * plen,JSValueConst obj,JSValueConst method)52976 static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen,
52977                                       JSValueConst obj, JSValueConst method)
52978 {
52979     JSValue arr, iter, next_method = JS_UNDEFINED, val;
52980     BOOL done;
52981     uint32_t k;
52982 
52983     *plen = 0;
52984     arr = JS_NewArray(ctx);
52985     if (JS_IsException(arr))
52986         return arr;
52987     iter = JS_GetIterator2(ctx, obj, method);
52988     if (JS_IsException(iter))
52989         goto fail;
52990     next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
52991     if (JS_IsException(next_method))
52992         goto fail;
52993     k = 0;
52994     for(;;) {
52995         val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
52996         if (JS_IsException(val))
52997             goto fail;
52998         if (done) {
52999             JS_FreeValue(ctx, val);
53000             break;
53001         }
53002         if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0)
53003             goto fail;
53004         k++;
53005     }
53006     JS_FreeValue(ctx, next_method);
53007     JS_FreeValue(ctx, iter);
53008     *plen = k;
53009     return arr;
53010  fail:
53011     JS_FreeValue(ctx, next_method);
53012     JS_FreeValue(ctx, iter);
53013     JS_FreeValue(ctx, arr);
53014     return JS_EXCEPTION;
53015 }
53016 
js_typed_array_constructor_obj(JSContext * ctx,JSValueConst new_target,JSValueConst obj,int classid)53017 static JSValue js_typed_array_constructor_obj(JSContext *ctx,
53018                                               JSValueConst new_target,
53019                                               JSValueConst obj,
53020                                               int classid)
53021 {
53022     JSValue iter, ret, arr = JS_UNDEFINED, val, buffer;
53023     uint32_t i;
53024     int size_log2;
53025     int64_t len;
53026 
53027     size_log2 = typed_array_size_log2(classid);
53028     ret = js_create_from_ctor(ctx, new_target, classid);
53029     if (JS_IsException(ret))
53030         return JS_EXCEPTION;
53031 
53032     iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
53033     if (JS_IsException(iter))
53034         goto fail;
53035     if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) {
53036         uint32_t len1;
53037         arr = js_array_from_iterator(ctx, &len1, obj, iter);
53038         JS_FreeValue(ctx, iter);
53039         if (JS_IsException(arr))
53040             goto fail;
53041         len = len1;
53042     } else {
53043         if (js_get_length64(ctx, &len, obj))
53044             goto fail;
53045         arr = JS_DupValue(ctx, obj);
53046     }
53047 
53048     buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
53049                                           len << size_log2);
53050     if (JS_IsException(buffer))
53051         goto fail;
53052     if (typed_array_init(ctx, ret, buffer, 0, len))
53053         goto fail;
53054 
53055     for(i = 0; i < len; i++) {
53056         val = JS_GetPropertyUint32(ctx, arr, i);
53057         if (JS_IsException(val))
53058             goto fail;
53059         if (JS_SetPropertyUint32(ctx, ret, i, val) < 0)
53060             goto fail;
53061     }
53062     JS_FreeValue(ctx, arr);
53063     return ret;
53064  fail:
53065     JS_FreeValue(ctx, arr);
53066     JS_FreeValue(ctx, ret);
53067     return JS_EXCEPTION;
53068 }
53069 
js_typed_array_constructor_ta(JSContext * ctx,JSValueConst new_target,JSValueConst src_obj,int classid)53070 static JSValue js_typed_array_constructor_ta(JSContext *ctx,
53071                                              JSValueConst new_target,
53072                                              JSValueConst src_obj,
53073                                              int classid)
53074 {
53075     JSObject *p, *src_buffer;
53076     JSTypedArray *ta;
53077     JSValue ctor, obj, buffer;
53078     uint32_t len, i;
53079     int size_log2;
53080     JSArrayBuffer *src_abuf, *abuf;
53081 
53082     obj = js_create_from_ctor(ctx, new_target, classid);
53083     if (JS_IsException(obj))
53084         return obj;
53085     p = JS_VALUE_GET_OBJ(src_obj);
53086     if (typed_array_is_detached(ctx, p)) {
53087         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53088         goto fail;
53089     }
53090     ta = p->u.typed_array;
53091     len = p->u.array.count;
53092     src_buffer = ta->buffer;
53093     src_abuf = src_buffer->u.array_buffer;
53094     if (!src_abuf->shared) {
53095         ctor = JS_SpeciesConstructor(ctx, JS_MKPTR(JS_TAG_OBJECT, src_buffer),
53096                                      JS_UNDEFINED);
53097         if (JS_IsException(ctor))
53098             goto fail;
53099     } else {
53100         /* force ArrayBuffer default constructor */
53101         ctor = JS_UNDEFINED;
53102     }
53103     size_log2 = typed_array_size_log2(classid);
53104     buffer = js_array_buffer_constructor1(ctx, ctor,
53105                                           (uint64_t)len << size_log2);
53106     JS_FreeValue(ctx, ctor);
53107     if (JS_IsException(buffer))
53108         goto fail;
53109     /* necessary because it could have been detached */
53110     if (typed_array_is_detached(ctx, p)) {
53111         JS_FreeValue(ctx, buffer);
53112         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53113         goto fail;
53114     }
53115     abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER);
53116     if (typed_array_init(ctx, obj, buffer, 0, len))
53117         goto fail;
53118     if (p->class_id == classid) {
53119         /* same type: copy the content */
53120         memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length);
53121     } else {
53122         for(i = 0; i < len; i++) {
53123             JSValue val;
53124             val = JS_GetPropertyUint32(ctx, src_obj, i);
53125             if (JS_IsException(val))
53126                 goto fail;
53127             if (JS_SetPropertyUint32(ctx, obj, i, val) < 0)
53128                 goto fail;
53129         }
53130     }
53131     return obj;
53132  fail:
53133     JS_FreeValue(ctx, obj);
53134     return JS_EXCEPTION;
53135 }
53136 
js_typed_array_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv,int classid)53137 static JSValue js_typed_array_constructor(JSContext *ctx,
53138                                           JSValueConst new_target,
53139                                           int argc, JSValueConst *argv,
53140                                           int classid)
53141 {
53142     JSValue buffer, obj;
53143     JSArrayBuffer *abuf;
53144     int size_log2;
53145     uint64_t len, offset;
53146 
53147     size_log2 = typed_array_size_log2(classid);
53148     if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) {
53149         if (JS_ToIndex(ctx, &len, argv[0]))
53150             return JS_EXCEPTION;
53151         buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
53152                                               len << size_log2);
53153         if (JS_IsException(buffer))
53154             return JS_EXCEPTION;
53155         offset = 0;
53156     } else {
53157         JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
53158         if (p->class_id == JS_CLASS_ARRAY_BUFFER ||
53159             p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) {
53160             abuf = p->u.array_buffer;
53161             if (JS_ToIndex(ctx, &offset, argv[1]))
53162                 return JS_EXCEPTION;
53163             if (abuf->detached)
53164                 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53165             if ((offset & ((1 << size_log2) - 1)) != 0 ||
53166                 offset > abuf->byte_length)
53167                 return JS_ThrowRangeError(ctx, "invalid offset");
53168             if (JS_IsUndefined(argv[2])) {
53169                 if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0)
53170                     goto invalid_length;
53171                 len = (abuf->byte_length - offset) >> size_log2;
53172             } else {
53173                 if (JS_ToIndex(ctx, &len, argv[2]))
53174                     return JS_EXCEPTION;
53175                 if (abuf->detached)
53176                     return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53177                 if ((offset + (len << size_log2)) > abuf->byte_length) {
53178                 invalid_length:
53179                     return JS_ThrowRangeError(ctx, "invalid length");
53180                 }
53181             }
53182             buffer = JS_DupValue(ctx, argv[0]);
53183         } else {
53184             if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
53185                 p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
53186                 return js_typed_array_constructor_ta(ctx, new_target, argv[0], classid);
53187             } else {
53188                 return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid);
53189             }
53190         }
53191     }
53192 
53193     obj = js_create_from_ctor(ctx, new_target, classid);
53194     if (JS_IsException(obj)) {
53195         JS_FreeValue(ctx, buffer);
53196         return JS_EXCEPTION;
53197     }
53198     if (typed_array_init(ctx, obj, buffer, offset, len)) {
53199         JS_FreeValue(ctx, obj);
53200         return JS_EXCEPTION;
53201     }
53202     return obj;
53203 }
53204 
js_typed_array_finalizer(JSRuntime * rt,JSValue val)53205 static void js_typed_array_finalizer(JSRuntime *rt, JSValue val)
53206 {
53207     JSObject *p = JS_VALUE_GET_OBJ(val);
53208     JSTypedArray *ta = p->u.typed_array;
53209     if (ta) {
53210         /* during the GC the finalizers are called in an arbitrary
53211            order so the ArrayBuffer finalizer may have been called */
53212         if (JS_IsLiveObject(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) {
53213             list_del(&ta->link);
53214         }
53215         JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
53216         js_free_rt(rt, ta);
53217     }
53218 }
53219 
js_typed_array_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)53220 static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
53221                                 JS_MarkFunc *mark_func)
53222 {
53223     JSObject *p = JS_VALUE_GET_OBJ(val);
53224     JSTypedArray *ta = p->u.typed_array;
53225     if (ta) {
53226         JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func);
53227     }
53228 }
53229 
js_dataview_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)53230 static JSValue js_dataview_constructor(JSContext *ctx,
53231                                        JSValueConst new_target,
53232                                        int argc, JSValueConst *argv)
53233 {
53234     JSArrayBuffer *abuf;
53235     uint64_t offset;
53236     uint32_t len;
53237     JSValueConst buffer;
53238     JSValue obj;
53239     JSTypedArray *ta;
53240     JSObject *p;
53241 
53242     buffer = argv[0];
53243     abuf = js_get_array_buffer(ctx, buffer);
53244     if (!abuf)
53245         return JS_EXCEPTION;
53246     offset = 0;
53247     if (argc > 1) {
53248         if (JS_ToIndex(ctx, &offset, argv[1]))
53249             return JS_EXCEPTION;
53250     }
53251     if (abuf->detached)
53252         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53253     if (offset > abuf->byte_length)
53254         return JS_ThrowRangeError(ctx, "invalid byteOffset");
53255     len = abuf->byte_length - offset;
53256     if (argc > 2 && !JS_IsUndefined(argv[2])) {
53257         uint64_t l;
53258         if (JS_ToIndex(ctx, &l, argv[2]))
53259             return JS_EXCEPTION;
53260         if (l > len)
53261             return JS_ThrowRangeError(ctx, "invalid byteLength");
53262         len = l;
53263     }
53264 
53265     obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW);
53266     if (JS_IsException(obj))
53267         return JS_EXCEPTION;
53268     if (abuf->detached) {
53269         /* could have been detached in js_create_from_ctor() */
53270         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53271         goto fail;
53272     }
53273     ta = js_malloc(ctx, sizeof(*ta));
53274     if (!ta) {
53275     fail:
53276         JS_FreeValue(ctx, obj);
53277         return JS_EXCEPTION;
53278     }
53279     p = JS_VALUE_GET_OBJ(obj);
53280     ta->obj = p;
53281     ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer));
53282     ta->offset = offset;
53283     ta->length = len;
53284     list_add_tail(&ta->link, &abuf->array_list);
53285     p->u.typed_array = ta;
53286     return obj;
53287 }
53288 
js_dataview_getValue(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv,int class_id)53289 static JSValue js_dataview_getValue(JSContext *ctx,
53290                                     JSValueConst this_obj,
53291                                     int argc, JSValueConst *argv, int class_id)
53292 {
53293     JSTypedArray *ta;
53294     JSArrayBuffer *abuf;
53295     int is_swap, size;
53296     uint8_t *ptr;
53297     uint32_t v;
53298     uint64_t pos;
53299 
53300     ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
53301     if (!ta)
53302         return JS_EXCEPTION;
53303     size = 1 << typed_array_size_log2(class_id);
53304     if (JS_ToIndex(ctx, &pos, argv[0]))
53305         return JS_EXCEPTION;
53306     is_swap = FALSE;
53307     if (argc > 1)
53308         is_swap = JS_ToBool(ctx, argv[1]);
53309 #ifndef WORDS_BIGENDIAN
53310     is_swap ^= 1;
53311 #endif
53312     abuf = ta->buffer->u.array_buffer;
53313     if (abuf->detached)
53314         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53315     if ((pos + size) > ta->length)
53316         return JS_ThrowRangeError(ctx, "out of bound");
53317     ptr = abuf->data + ta->offset + pos;
53318 
53319     switch(class_id) {
53320     case JS_CLASS_INT8_ARRAY:
53321         return JS_NewInt32(ctx, *(int8_t *)ptr);
53322     case JS_CLASS_UINT8_ARRAY:
53323         return JS_NewInt32(ctx, *(uint8_t *)ptr);
53324     case JS_CLASS_INT16_ARRAY:
53325         v = get_u16(ptr);
53326         if (is_swap)
53327             v = bswap16(v);
53328         return JS_NewInt32(ctx, (int16_t)v);
53329     case JS_CLASS_UINT16_ARRAY:
53330         v = get_u16(ptr);
53331         if (is_swap)
53332             v = bswap16(v);
53333         return JS_NewInt32(ctx, v);
53334     case JS_CLASS_INT32_ARRAY:
53335         v = get_u32(ptr);
53336         if (is_swap)
53337             v = bswap32(v);
53338         return JS_NewInt32(ctx, v);
53339     case JS_CLASS_UINT32_ARRAY:
53340         v = get_u32(ptr);
53341         if (is_swap)
53342             v = bswap32(v);
53343         return JS_NewUint32(ctx, v);
53344 #ifdef CONFIG_BIGNUM
53345     case JS_CLASS_BIG_INT64_ARRAY:
53346         {
53347             uint64_t v;
53348             v = get_u64(ptr);
53349             if (is_swap)
53350                 v = bswap64(v);
53351             return JS_NewBigInt64(ctx, v);
53352         }
53353         break;
53354     case JS_CLASS_BIG_UINT64_ARRAY:
53355         {
53356             uint64_t v;
53357             v = get_u64(ptr);
53358             if (is_swap)
53359                 v = bswap64(v);
53360             return JS_NewBigUint64(ctx, v);
53361         }
53362         break;
53363 #endif
53364     case JS_CLASS_FLOAT32_ARRAY:
53365         {
53366             union {
53367                 float f;
53368                 uint32_t i;
53369             } u;
53370             v = get_u32(ptr);
53371             if (is_swap)
53372                 v = bswap32(v);
53373             u.i = v;
53374             return __JS_NewFloat64(ctx, u.f);
53375         }
53376     case JS_CLASS_FLOAT64_ARRAY:
53377         {
53378             union {
53379                 double f;
53380                 uint64_t i;
53381             } u;
53382             u.i = get_u64(ptr);
53383             if (is_swap)
53384                 u.i = bswap64(u.i);
53385             return __JS_NewFloat64(ctx, u.f);
53386         }
53387     default:
53388         abort();
53389     }
53390 }
53391 
js_dataview_setValue(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv,int class_id)53392 static JSValue js_dataview_setValue(JSContext *ctx,
53393                                     JSValueConst this_obj,
53394                                     int argc, JSValueConst *argv, int class_id)
53395 {
53396     JSTypedArray *ta;
53397     JSArrayBuffer *abuf;
53398     int is_swap, size;
53399     uint8_t *ptr;
53400     uint64_t v64;
53401     uint32_t v;
53402     uint64_t pos;
53403     JSValueConst val;
53404 
53405     ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
53406     if (!ta)
53407         return JS_EXCEPTION;
53408     size = 1 << typed_array_size_log2(class_id);
53409     if (JS_ToIndex(ctx, &pos, argv[0]))
53410         return JS_EXCEPTION;
53411     val = argv[1];
53412     v = 0; /* avoid warning */
53413     v64 = 0; /* avoid warning */
53414     if (class_id <= JS_CLASS_UINT32_ARRAY) {
53415         if (JS_ToUint32(ctx, &v, val))
53416             return JS_EXCEPTION;
53417     } else
53418 #ifdef CONFIG_BIGNUM
53419     if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
53420         if (JS_ToBigInt64(ctx, (int64_t *)&v64, val))
53421             return JS_EXCEPTION;
53422     } else
53423 #endif
53424     {
53425         double d;
53426         if (JS_ToFloat64(ctx, &d, val))
53427             return JS_EXCEPTION;
53428         if (class_id == JS_CLASS_FLOAT32_ARRAY) {
53429             union {
53430                 float f;
53431                 uint32_t i;
53432             } u;
53433             u.f = d;
53434             v = u.i;
53435         } else {
53436             JSFloat64Union u;
53437             u.d = d;
53438             v64 = u.u64;
53439         }
53440     }
53441     is_swap = FALSE;
53442     if (argc > 2)
53443         is_swap = JS_ToBool(ctx, argv[2]);
53444 #ifndef WORDS_BIGENDIAN
53445     is_swap ^= 1;
53446 #endif
53447     abuf = ta->buffer->u.array_buffer;
53448     if (abuf->detached)
53449         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53450     if ((pos + size) > ta->length)
53451         return JS_ThrowRangeError(ctx, "out of bound");
53452     ptr = abuf->data + ta->offset + pos;
53453 
53454     switch(class_id) {
53455     case JS_CLASS_INT8_ARRAY:
53456     case JS_CLASS_UINT8_ARRAY:
53457         *ptr = v;
53458         break;
53459     case JS_CLASS_INT16_ARRAY:
53460     case JS_CLASS_UINT16_ARRAY:
53461         if (is_swap)
53462             v = bswap16(v);
53463         put_u16(ptr, v);
53464         break;
53465     case JS_CLASS_INT32_ARRAY:
53466     case JS_CLASS_UINT32_ARRAY:
53467     case JS_CLASS_FLOAT32_ARRAY:
53468         if (is_swap)
53469             v = bswap32(v);
53470         put_u32(ptr, v);
53471         break;
53472 #ifdef CONFIG_BIGNUM
53473     case JS_CLASS_BIG_INT64_ARRAY:
53474     case JS_CLASS_BIG_UINT64_ARRAY:
53475 #endif
53476     case JS_CLASS_FLOAT64_ARRAY:
53477         if (is_swap)
53478             v64 = bswap64(v64);
53479         put_u64(ptr, v64);
53480         break;
53481     default:
53482         abort();
53483     }
53484     return JS_UNDEFINED;
53485 }
53486 
53487 static const JSCFunctionListEntry js_dataview_proto_funcs[] = {
53488     JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1 ),
53489     JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 1 ),
53490     JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 1 ),
53491     JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ),
53492     JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ),
53493     JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ),
53494     JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ),
53495     JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ),
53496     JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ),
53497 #ifdef CONFIG_BIGNUM
53498     JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ),
53499     JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ),
53500 #endif
53501     JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ),
53502     JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ),
53503     JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ),
53504     JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY ),
53505     JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY ),
53506     JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ),
53507     JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ),
53508     JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ),
53509 #ifdef CONFIG_BIGNUM
53510     JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ),
53511     JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ),
53512 #endif
53513     JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ),
53514     JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ),
53515     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ),
53516 };
53517 
53518 /* Atomics */
53519 #ifdef CONFIG_ATOMICS
53520 
53521 typedef enum AtomicsOpEnum {
53522     ATOMICS_OP_ADD,
53523     ATOMICS_OP_AND,
53524     ATOMICS_OP_OR,
53525     ATOMICS_OP_SUB,
53526     ATOMICS_OP_XOR,
53527     ATOMICS_OP_EXCHANGE,
53528     ATOMICS_OP_COMPARE_EXCHANGE,
53529     ATOMICS_OP_LOAD,
53530 } AtomicsOpEnum;
53531 
js_atomics_get_ptr(JSContext * ctx,JSArrayBuffer ** pabuf,int * psize_log2,JSClassID * pclass_id,JSValueConst obj,JSValueConst idx_val,int is_waitable)53532 static void *js_atomics_get_ptr(JSContext *ctx,
53533                                 JSArrayBuffer **pabuf,
53534                                 int *psize_log2, JSClassID *pclass_id,
53535                                 JSValueConst obj, JSValueConst idx_val,
53536                                 int is_waitable)
53537 {
53538     JSObject *p;
53539     JSTypedArray *ta;
53540     JSArrayBuffer *abuf;
53541     void *ptr;
53542     uint64_t idx;
53543     BOOL err;
53544     int size_log2;
53545 
53546     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
53547         goto fail;
53548     p = JS_VALUE_GET_OBJ(obj);
53549 #ifdef CONFIG_BIGNUM
53550     if (is_waitable)
53551         err = (p->class_id != JS_CLASS_INT32_ARRAY &&
53552                p->class_id != JS_CLASS_BIG_INT64_ARRAY);
53553     else
53554         err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&
53555                 p->class_id <= JS_CLASS_BIG_UINT64_ARRAY);
53556 #else
53557     if (is_waitable)
53558         err = (p->class_id != JS_CLASS_INT32_ARRAY);
53559     else
53560         err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&
53561                 p->class_id <= JS_CLASS_UINT32_ARRAY);
53562 #endif
53563     if (err) {
53564     fail:
53565         JS_ThrowTypeError(ctx, "integer TypedArray expected");
53566         return NULL;
53567     }
53568     ta = p->u.typed_array;
53569     abuf = ta->buffer->u.array_buffer;
53570     if (!abuf->shared) {
53571         if (is_waitable == 2) {
53572             JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray");
53573             return NULL;
53574         }
53575         if (abuf->detached) {
53576             JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53577             return NULL;
53578         }
53579     }
53580     if (JS_ToIndex(ctx, &idx, idx_val)) {
53581         return NULL;
53582     }
53583     /* if the array buffer is detached, p->u.array.count = 0 */
53584     if (idx >= p->u.array.count) {
53585         JS_ThrowRangeError(ctx, "out-of-bound access");
53586         return NULL;
53587     }
53588     size_log2 = typed_array_size_log2(p->class_id);
53589     ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
53590     if (pabuf)
53591         *pabuf = abuf;
53592     if (psize_log2)
53593         *psize_log2 = size_log2;
53594     if (pclass_id)
53595         *pclass_id = p->class_id;
53596     return ptr;
53597 }
53598 
js_atomics_op(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv,int op)53599 static JSValue js_atomics_op(JSContext *ctx,
53600                              JSValueConst this_obj,
53601                              int argc, JSValueConst *argv, int op)
53602 {
53603     int size_log2;
53604 #ifdef CONFIG_BIGNUM
53605     uint64_t v, a, rep_val;
53606 #else
53607     uint32_t v, a, rep_val;
53608 #endif
53609     void *ptr;
53610     JSValue ret;
53611     JSClassID class_id;
53612     JSArrayBuffer *abuf;
53613 
53614     ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id,
53615                              argv[0], argv[1], 0);
53616     if (!ptr)
53617         return JS_EXCEPTION;
53618     rep_val = 0;
53619     if (op == ATOMICS_OP_LOAD) {
53620         v = 0;
53621     } else {
53622 #ifdef CONFIG_BIGNUM
53623         if (size_log2 == 3) {
53624             int64_t v64;
53625             if (JS_ToBigInt64(ctx, &v64, argv[2]))
53626                 return JS_EXCEPTION;
53627             v = v64;
53628             if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
53629                 if (JS_ToBigInt64(ctx, &v64, argv[3]))
53630                     return JS_EXCEPTION;
53631                 rep_val = v64;
53632             }
53633         } else
53634 #endif
53635         {
53636                 uint32_t v32;
53637                 if (JS_ToUint32(ctx, &v32, argv[2]))
53638                     return JS_EXCEPTION;
53639                 v = v32;
53640                 if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
53641                     if (JS_ToUint32(ctx, &v32, argv[3]))
53642                         return JS_EXCEPTION;
53643                     rep_val = v32;
53644                 }
53645         }
53646         if (abuf->detached)
53647             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53648    }
53649 
53650    switch(op | (size_log2 << 3)) {
53651 
53652 #ifdef CONFIG_BIGNUM
53653 #define OP(op_name, func_name)                          \
53654     case ATOMICS_OP_ ## op_name | (0 << 3):             \
53655        a = func_name((_Atomic(uint8_t) *)ptr, v);       \
53656        break;                                           \
53657     case ATOMICS_OP_ ## op_name | (1 << 3):             \
53658         a = func_name((_Atomic(uint16_t) *)ptr, v);     \
53659         break;                                          \
53660     case ATOMICS_OP_ ## op_name | (2 << 3):             \
53661         a = func_name((_Atomic(uint32_t) *)ptr, v);     \
53662         break;                                          \
53663     case ATOMICS_OP_ ## op_name | (3 << 3):             \
53664         a = func_name((_Atomic(uint64_t) *)ptr, v);     \
53665         break;
53666 #else
53667 #define OP(op_name, func_name)                          \
53668     case ATOMICS_OP_ ## op_name | (0 << 3):             \
53669        a = func_name((_Atomic(uint8_t) *)ptr, v);       \
53670        break;                                           \
53671     case ATOMICS_OP_ ## op_name | (1 << 3):             \
53672         a = func_name((_Atomic(uint16_t) *)ptr, v);     \
53673         break;                                          \
53674     case ATOMICS_OP_ ## op_name | (2 << 3):             \
53675         a = func_name((_Atomic(uint32_t) *)ptr, v);     \
53676         break;
53677 #endif
53678         OP(ADD, atomic_fetch_add)
53679         OP(AND, atomic_fetch_and)
53680         OP(OR, atomic_fetch_or)
53681         OP(SUB, atomic_fetch_sub)
53682         OP(XOR, atomic_fetch_xor)
53683         OP(EXCHANGE, atomic_exchange)
53684 #undef OP
53685 
53686     case ATOMICS_OP_LOAD | (0 << 3):
53687         a = atomic_load((_Atomic(uint8_t) *)ptr);
53688         break;
53689     case ATOMICS_OP_LOAD | (1 << 3):
53690         a = atomic_load((_Atomic(uint16_t) *)ptr);
53691         break;
53692     case ATOMICS_OP_LOAD | (2 << 3):
53693         a = atomic_load((_Atomic(uint32_t) *)ptr);
53694         break;
53695 #ifdef CONFIG_BIGNUM
53696     case ATOMICS_OP_LOAD | (3 << 3):
53697         a = atomic_load((_Atomic(uint64_t) *)ptr);
53698         break;
53699 #endif
53700 
53701     case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3):
53702         {
53703             uint8_t v1 = v;
53704             atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val);
53705             a = v1;
53706         }
53707         break;
53708     case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3):
53709         {
53710             uint16_t v1 = v;
53711             atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val);
53712             a = v1;
53713         }
53714         break;
53715     case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3):
53716         {
53717             uint32_t v1 = v;
53718             atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val);
53719             a = v1;
53720         }
53721         break;
53722 #ifdef CONFIG_BIGNUM
53723     case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3):
53724         {
53725             uint64_t v1 = v;
53726             atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val);
53727             a = v1;
53728         }
53729         break;
53730 #endif
53731     default:
53732         abort();
53733     }
53734 
53735     switch(class_id) {
53736     case JS_CLASS_INT8_ARRAY:
53737         a = (int8_t)a;
53738         goto done;
53739     case JS_CLASS_UINT8_ARRAY:
53740         a = (uint8_t)a;
53741         goto done;
53742     case JS_CLASS_INT16_ARRAY:
53743         a = (int16_t)a;
53744         goto done;
53745     case JS_CLASS_UINT16_ARRAY:
53746         a = (uint16_t)a;
53747         goto done;
53748     case JS_CLASS_INT32_ARRAY:
53749     done:
53750         ret = JS_NewInt32(ctx, a);
53751         break;
53752     case JS_CLASS_UINT32_ARRAY:
53753         ret = JS_NewUint32(ctx, a);
53754         break;
53755 #ifdef CONFIG_BIGNUM
53756     case JS_CLASS_BIG_INT64_ARRAY:
53757         ret = JS_NewBigInt64(ctx, a);
53758         break;
53759     case JS_CLASS_BIG_UINT64_ARRAY:
53760         ret = JS_NewBigUint64(ctx, a);
53761         break;
53762 #endif
53763     default:
53764         abort();
53765     }
53766     return ret;
53767 }
53768 
js_atomics_store(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv)53769 static JSValue js_atomics_store(JSContext *ctx,
53770                                 JSValueConst this_obj,
53771                                 int argc, JSValueConst *argv)
53772 {
53773     int size_log2;
53774     void *ptr;
53775     JSValue ret;
53776     JSArrayBuffer *abuf;
53777 
53778     ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL,
53779                              argv[0], argv[1], 0);
53780     if (!ptr)
53781         return JS_EXCEPTION;
53782 #ifdef CONFIG_BIGNUM
53783     if (size_log2 == 3) {
53784         int64_t v64;
53785         ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2]));
53786         if (JS_IsException(ret))
53787             return ret;
53788         if (JS_ToBigInt64(ctx, &v64, ret)) {
53789             JS_FreeValue(ctx, ret);
53790             return JS_EXCEPTION;
53791         }
53792         if (abuf->detached)
53793             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53794         atomic_store((_Atomic(uint64_t) *)ptr, v64);
53795     } else
53796 #endif
53797     {
53798         uint32_t v;
53799         /* XXX: spec, would be simpler to return the written value */
53800         ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2]));
53801         if (JS_IsException(ret))
53802             return ret;
53803         if (JS_ToUint32(ctx, &v, ret)) {
53804             JS_FreeValue(ctx, ret);
53805             return JS_EXCEPTION;
53806         }
53807         if (abuf->detached)
53808             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53809         switch(size_log2) {
53810         case 0:
53811             atomic_store((_Atomic(uint8_t) *)ptr, v);
53812             break;
53813         case 1:
53814             atomic_store((_Atomic(uint16_t) *)ptr, v);
53815             break;
53816         case 2:
53817             atomic_store((_Atomic(uint32_t) *)ptr, v);
53818             break;
53819         default:
53820             abort();
53821         }
53822     }
53823     return ret;
53824 }
53825 
js_atomics_isLockFree(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv)53826 static JSValue js_atomics_isLockFree(JSContext *ctx,
53827                                      JSValueConst this_obj,
53828                                      int argc, JSValueConst *argv)
53829 {
53830     int v, ret;
53831     if (JS_ToInt32Sat(ctx, &v, argv[0]))
53832         return JS_EXCEPTION;
53833     ret = (v == 1 || v == 2 || v == 4
53834 #ifdef CONFIG_BIGNUM
53835            || v == 8
53836 #endif
53837            );
53838     return JS_NewBool(ctx, ret);
53839 }
53840 
53841 typedef struct JSAtomicsWaiter {
53842     struct list_head link;
53843     BOOL linked;
53844     pthread_cond_t cond;
53845     int32_t *ptr;
53846 } JSAtomicsWaiter;
53847 
53848 static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER;
53849 static struct list_head js_atomics_waiter_list =
53850     LIST_HEAD_INIT(js_atomics_waiter_list);
53851 
js_atomics_wait(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv)53852 static JSValue js_atomics_wait(JSContext *ctx,
53853                                JSValueConst this_obj,
53854                                int argc, JSValueConst *argv)
53855 {
53856     int64_t v;
53857     int32_t v32;
53858     void *ptr;
53859     int64_t timeout;
53860     struct timespec ts;
53861     JSAtomicsWaiter waiter_s, *waiter;
53862     int ret, size_log2, res;
53863     double d;
53864 
53865     ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL,
53866                              argv[0], argv[1], 2);
53867     if (!ptr)
53868         return JS_EXCEPTION;
53869 #ifdef CONFIG_BIGNUM
53870     if (size_log2 == 3) {
53871         if (JS_ToBigInt64(ctx, &v, argv[2]))
53872             return JS_EXCEPTION;
53873     } else
53874 #endif
53875     {
53876         if (JS_ToInt32(ctx, &v32, argv[2]))
53877             return JS_EXCEPTION;
53878         v = v32;
53879     }
53880     if (JS_ToFloat64(ctx, &d, argv[3]))
53881         return JS_EXCEPTION;
53882     if (isnan(d) || d > INT64_MAX)
53883         timeout = INT64_MAX;
53884     else if (d < 0)
53885         timeout = 0;
53886     else
53887         timeout = (int64_t)d;
53888     if (!ctx->rt->can_block)
53889         return JS_ThrowTypeError(ctx, "cannot block in this thread");
53890 
53891     /* XXX: inefficient if large number of waiters, should hash on
53892        'ptr' value */
53893     /* XXX: use Linux futexes when available ? */
53894     pthread_mutex_lock(&js_atomics_mutex);
53895     if (size_log2 == 3) {
53896         res = *(int64_t *)ptr != v;
53897     } else {
53898         res = *(int32_t *)ptr != v;
53899     }
53900     if (res) {
53901         pthread_mutex_unlock(&js_atomics_mutex);
53902         return JS_AtomToString(ctx, JS_ATOM_not_equal);
53903     }
53904 
53905     waiter = &waiter_s;
53906     waiter->ptr = ptr;
53907     pthread_cond_init(&waiter->cond, NULL);
53908     waiter->linked = TRUE;
53909     list_add_tail(&waiter->link, &js_atomics_waiter_list);
53910 
53911     if (timeout == INT64_MAX) {
53912         pthread_cond_wait(&waiter->cond, &js_atomics_mutex);
53913         ret = 0;
53914     } else {
53915         /* XXX: use clock monotonic */
53916         clock_gettime(CLOCK_REALTIME, &ts);
53917         ts.tv_sec += timeout / 1000;
53918         ts.tv_nsec += (timeout % 1000) * 1000000;
53919         if (ts.tv_nsec >= 1000000000) {
53920             ts.tv_nsec -= 1000000000;
53921             ts.tv_sec++;
53922         }
53923         ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex,
53924                                      &ts);
53925     }
53926     if (waiter->linked)
53927         list_del(&waiter->link);
53928     pthread_mutex_unlock(&js_atomics_mutex);
53929     pthread_cond_destroy(&waiter->cond);
53930     if (ret == ETIMEDOUT) {
53931         return JS_AtomToString(ctx, JS_ATOM_timed_out);
53932     } else {
53933         return JS_AtomToString(ctx, JS_ATOM_ok);
53934     }
53935 }
53936 
js_atomics_notify(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv)53937 static JSValue js_atomics_notify(JSContext *ctx,
53938                                  JSValueConst this_obj,
53939                                  int argc, JSValueConst *argv)
53940 {
53941     struct list_head *el, *el1, waiter_list;
53942     int32_t count, n;
53943     void *ptr;
53944     JSAtomicsWaiter *waiter;
53945     JSArrayBuffer *abuf;
53946 
53947     ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1);
53948     if (!ptr)
53949         return JS_EXCEPTION;
53950 
53951     if (JS_IsUndefined(argv[2])) {
53952         count = INT32_MAX;
53953     } else {
53954         if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0))
53955             return JS_EXCEPTION;
53956     }
53957     if (abuf->detached)
53958         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53959 
53960     n = 0;
53961     if (abuf->shared && count > 0) {
53962         pthread_mutex_lock(&js_atomics_mutex);
53963         init_list_head(&waiter_list);
53964         list_for_each_safe(el, el1, &js_atomics_waiter_list) {
53965             waiter = list_entry(el, JSAtomicsWaiter, link);
53966             if (waiter->ptr == ptr) {
53967                 list_del(&waiter->link);
53968                 waiter->linked = FALSE;
53969                 list_add_tail(&waiter->link, &waiter_list);
53970                 n++;
53971                 if (n >= count)
53972                     break;
53973             }
53974         }
53975         list_for_each(el, &waiter_list) {
53976             waiter = list_entry(el, JSAtomicsWaiter, link);
53977             pthread_cond_signal(&waiter->cond);
53978         }
53979         pthread_mutex_unlock(&js_atomics_mutex);
53980     }
53981     return JS_NewInt32(ctx, n);
53982 }
53983 
53984 static const JSCFunctionListEntry js_atomics_funcs[] = {
53985     JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ),
53986     JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ),
53987     JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ),
53988     JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ),
53989     JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ),
53990     JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ),
53991     JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ),
53992     JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ),
53993     JS_CFUNC_DEF("store", 3, js_atomics_store ),
53994     JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ),
53995     JS_CFUNC_DEF("wait", 4, js_atomics_wait ),
53996     JS_CFUNC_DEF("notify", 3, js_atomics_notify ),
53997     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ),
53998 };
53999 
54000 static const JSCFunctionListEntry js_atomics_obj[] = {
54001     JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
54002 };
54003 
JS_AddIntrinsicAtomics(JSContext * ctx)54004 void JS_AddIntrinsicAtomics(JSContext *ctx)
54005 {
54006     /* add Atomics as autoinit object */
54007     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj));
54008 }
54009 
54010 #endif /* CONFIG_ATOMICS */
54011 
JS_AddIntrinsicTypedArrays(JSContext * ctx)54012 void JS_AddIntrinsicTypedArrays(JSContext *ctx)
54013 {
54014     JSValue typed_array_base_proto, typed_array_base_func;
54015     JSValueConst array_buffer_func, shared_array_buffer_func;
54016     int i;
54017 
54018     ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx);
54019     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER],
54020                                js_array_buffer_proto_funcs,
54021                                countof(js_array_buffer_proto_funcs));
54022 
54023     array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer",
54024                                                  js_array_buffer_constructor, 1,
54025                                                  ctx->class_proto[JS_CLASS_ARRAY_BUFFER]);
54026     JS_SetPropertyFunctionList(ctx, array_buffer_func,
54027                                js_array_buffer_funcs,
54028                                countof(js_array_buffer_funcs));
54029 
54030     ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx);
54031     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER],
54032                                js_shared_array_buffer_proto_funcs,
54033                                countof(js_shared_array_buffer_proto_funcs));
54034 
54035     shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer",
54036                                                  js_shared_array_buffer_constructor, 1,
54037                                                  ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]);
54038     JS_SetPropertyFunctionList(ctx, shared_array_buffer_func,
54039                                js_shared_array_buffer_funcs,
54040                                countof(js_shared_array_buffer_funcs));
54041 
54042     typed_array_base_proto = JS_NewObject(ctx);
54043     JS_SetPropertyFunctionList(ctx, typed_array_base_proto,
54044                                js_typed_array_base_proto_funcs,
54045                                countof(js_typed_array_base_proto_funcs));
54046 
54047     /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */
54048     JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString);
54049     /* XXX: should use alias method in JSCFunctionListEntry */ //@@@
54050     JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj,
54051                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
54052 
54053     typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor,
54054                                             "TypedArray", 0);
54055     JS_SetPropertyFunctionList(ctx, typed_array_base_func,
54056                                js_typed_array_base_funcs,
54057                                countof(js_typed_array_base_funcs));
54058     JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto);
54059 
54060     for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) {
54061         JSValue func_obj;
54062         char buf[ATOM_GET_STR_BUF_SIZE];
54063         const char *name;
54064 
54065         ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto);
54066         JS_DefinePropertyValueStr(ctx, ctx->class_proto[i],
54067                                   "BYTES_PER_ELEMENT",
54068                                   JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),
54069                                   0);
54070         name = JS_AtomGetStr(ctx, buf, sizeof(buf),
54071                              JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY);
54072         func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_typed_array_constructor,
54073                                     name, 3, JS_CFUNC_constructor_magic, i,
54074                                     typed_array_base_func);
54075         JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]);
54076         JS_DefinePropertyValueStr(ctx, func_obj,
54077                                   "BYTES_PER_ELEMENT",
54078                                   JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),
54079                                   0);
54080     }
54081     JS_FreeValue(ctx, typed_array_base_proto);
54082     JS_FreeValue(ctx, typed_array_base_func);
54083 
54084     /* DataView */
54085     ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx);
54086     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW],
54087                                js_dataview_proto_funcs,
54088                                countof(js_dataview_proto_funcs));
54089     JS_NewGlobalCConstructorOnly(ctx, "DataView",
54090                                  js_dataview_constructor, 1,
54091                                  ctx->class_proto[JS_CLASS_DATAVIEW]);
54092     /* Atomics */
54093 #ifdef CONFIG_ATOMICS
54094     JS_AddIntrinsicAtomics(ctx);
54095 #endif
54096 }
54097 
54098 #include "quickjs-debugger.c"
54099 #include "quickjs-limitedcontext.c"
54100