• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * QuickJS Javascript Engine
3  *
4  * Copyright (c) 2017-2020 Fabrice Bellard
5  * Copyright (c) 2017-2020 Charlie Gordon
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include <inttypes.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <sys/time.h>
32 #include <time.h>
33 #include <fenv.h>
34 #include <math.h>
35 #if defined(__APPLE__)
36 #include <malloc/malloc.h>
37 #elif defined(__linux__)
38 #include <malloc.h>
39 #endif
40 
41 #include "config.h"
42 #include "cutils.h"
43 #include "list.h"
44 #include "quickjs.h"
45 #include "libregexp.h"
46 #ifdef ENABLE_JS_DEBUG
47 #include "debugger.h"
48 #endif
49 #ifdef CONFIG_BIGNUM
50 #include "libbf.h"
51 #endif
52 
53 #define OPTIMIZE         1
54 #define SHORT_OPCODES    1
55 #if defined(EMSCRIPTEN)
56 #define DIRECT_DISPATCH  0
57 #else
58 #define DIRECT_DISPATCH  1
59 #endif
60 
61 #if defined(__APPLE__)
62 #define MALLOC_OVERHEAD  0
63 #else
64 #define MALLOC_OVERHEAD  8
65 #endif
66 
67 #if !defined(_WIN32)
68 /* define it if printf uses the RNDN rounding mode instead of RNDNA */
69 #define CONFIG_PRINTF_RNDN
70 #endif
71 
72 /* define to include Atomics.* operations which depend on the OS
73    threads */
74 #if !defined(EMSCRIPTEN)
75 #define CONFIG_ATOMICS
76 #endif
77 
78 #if !defined(EMSCRIPTEN)
79 /* enable stack limitation */
80 #define CONFIG_STACK_CHECK
81 #endif
82 
83 
84 /* dump object free */
85 //#define DUMP_FREE
86 //#define DUMP_CLOSURE
87 /* dump the bytecode of the compiled functions: combination of bits
88    1: dump pass 3 final byte code
89    2: dump pass 2 code
90    4: dump pass 1 code
91    8: dump stdlib functions
92   16: dump bytecode in hex
93   32: dump line number table
94  */
95 //#define DUMP_BYTECODE  (1)
96 /* dump the occurence of the automatic GC */
97 //#define DUMP_GC
98 /* dump objects freed by the garbage collector */
99 //#define DUMP_GC_FREE
100 /* dump objects leaking when freeing the runtime */
101 //#define DUMP_LEAKS  1
102 /* dump memory usage before running the garbage collector */
103 //#define DUMP_MEM
104 //#define DUMP_OBJECTS    /* dump objects in JS_FreeContext */
105 //#define DUMP_ATOMS      /* dump atoms in JS_FreeContext */
106 //#define DUMP_SHAPES     /* dump shapes in JS_FreeContext */
107 //#define DUMP_MODULE_RESOLVE
108 //#define DUMP_PROMISE
109 //#define DUMP_READ_OBJECT
110 
111 /* test the GC by forcing it before each object allocation */
112 //#define FORCE_GC_AT_MALLOC
113 
114 #ifdef CONFIG_ATOMICS
115 #include <pthread.h>
116 #include <stdatomic.h>
117 #include <errno.h>
118 #endif
119 
120 enum {
121     /* classid tag        */    /* union usage   | properties */
122     JS_CLASS_OBJECT = 1,        /* must be first */
123     JS_CLASS_ARRAY,             /* u.array       | length */
124     JS_CLASS_ERROR,
125     JS_CLASS_NUMBER,            /* u.object_data */
126     JS_CLASS_STRING,            /* u.object_data */
127     JS_CLASS_BOOLEAN,           /* u.object_data */
128     JS_CLASS_SYMBOL,            /* u.object_data */
129     JS_CLASS_ARGUMENTS,         /* u.array       | length */
130     JS_CLASS_MAPPED_ARGUMENTS,  /*               | length */
131     JS_CLASS_DATE,              /* u.object_data */
132     JS_CLASS_MODULE_NS,
133     JS_CLASS_C_FUNCTION,        /* u.cfunc */
134     JS_CLASS_BYTECODE_FUNCTION, /* u.func */
135     JS_CLASS_BOUND_FUNCTION,    /* u.bound_function */
136     JS_CLASS_C_FUNCTION_DATA,   /* u.c_function_data_record */
137     JS_CLASS_GENERATOR_FUNCTION, /* u.func */
138     JS_CLASS_FOR_IN_ITERATOR,   /* u.for_in_iterator */
139     JS_CLASS_REGEXP,            /* u.regexp */
140     JS_CLASS_ARRAY_BUFFER,      /* u.array_buffer */
141     JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */
142     JS_CLASS_UINT8C_ARRAY,      /* u.array (typed_array) */
143     JS_CLASS_INT8_ARRAY,        /* u.array (typed_array) */
144     JS_CLASS_UINT8_ARRAY,       /* u.array (typed_array) */
145     JS_CLASS_INT16_ARRAY,       /* u.array (typed_array) */
146     JS_CLASS_UINT16_ARRAY,      /* u.array (typed_array) */
147     JS_CLASS_INT32_ARRAY,       /* u.array (typed_array) */
148     JS_CLASS_UINT32_ARRAY,      /* u.array (typed_array) */
149 #ifdef CONFIG_BIGNUM
150     JS_CLASS_BIG_INT64_ARRAY,   /* u.array (typed_array) */
151     JS_CLASS_BIG_UINT64_ARRAY,  /* u.array (typed_array) */
152 #endif
153     JS_CLASS_FLOAT32_ARRAY,     /* u.array (typed_array) */
154     JS_CLASS_FLOAT64_ARRAY,     /* u.array (typed_array) */
155     JS_CLASS_DATAVIEW,          /* u.typed_array */
156 #ifdef CONFIG_BIGNUM
157     JS_CLASS_BIG_INT,           /* u.object_data */
158     JS_CLASS_BIG_FLOAT,         /* u.object_data */
159     JS_CLASS_FLOAT_ENV,         /* u.float_env */
160     JS_CLASS_BIG_DECIMAL,       /* u.object_data */
161     JS_CLASS_OPERATOR_SET,      /* u.operator_set */
162 #endif
163     JS_CLASS_MAP,               /* u.map_state */
164     JS_CLASS_SET,               /* u.map_state */
165     JS_CLASS_WEAKMAP,           /* u.map_state */
166     JS_CLASS_WEAKSET,           /* u.map_state */
167     JS_CLASS_MAP_ITERATOR,      /* u.map_iterator_data */
168     JS_CLASS_SET_ITERATOR,      /* u.map_iterator_data */
169     JS_CLASS_ARRAY_ITERATOR,    /* u.array_iterator_data */
170     JS_CLASS_STRING_ITERATOR,   /* u.array_iterator_data */
171     JS_CLASS_REGEXP_STRING_ITERATOR,   /* u.regexp_string_iterator_data */
172     JS_CLASS_GENERATOR,         /* u.generator_data */
173     JS_CLASS_PROXY,             /* u.proxy_data */
174     JS_CLASS_PROMISE,           /* u.promise_data */
175     JS_CLASS_PROMISE_RESOLVE_FUNCTION,  /* u.promise_function_data */
176     JS_CLASS_PROMISE_REJECT_FUNCTION,   /* u.promise_function_data */
177     JS_CLASS_ASYNC_FUNCTION,            /* u.func */
178     JS_CLASS_ASYNC_FUNCTION_RESOLVE,    /* u.async_function_data */
179     JS_CLASS_ASYNC_FUNCTION_REJECT,     /* u.async_function_data */
180     JS_CLASS_ASYNC_FROM_SYNC_ITERATOR,  /* u.async_from_sync_iterator_data */
181     JS_CLASS_ASYNC_GENERATOR_FUNCTION,  /* u.func */
182     JS_CLASS_ASYNC_GENERATOR,   /* u.async_generator_data */
183 
184     JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
185 };
186 
187 // define the deviation of line number
188 #define DEBUG_MODE_LINE_DEVIATION 4
189 #define RELEASE_MODE_LINE_DEVIATION 13
190 
191 /* number of typed array types */
192 #define JS_TYPED_ARRAY_COUNT  (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1)
193 static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT];
194 #define typed_array_size_log2(classid)  (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY])
195 
196 typedef enum JSErrorEnum {
197     JS_EVAL_ERROR,
198     JS_RANGE_ERROR,
199     JS_REFERENCE_ERROR,
200     JS_SYNTAX_ERROR,
201     JS_TYPE_ERROR,
202     JS_URI_ERROR,
203     JS_INTERNAL_ERROR,
204     JS_AGGREGATE_ERROR,
205 
206     JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */
207 } JSErrorEnum;
208 
209 #define JS_MAX_LOCAL_VARS 65536
210 #define JS_STACK_SIZE_MAX 65534
211 #define JS_STRING_LEN_MAX ((1 << 30) - 1)
212 
213 #define __exception __attribute__((warn_unused_result))
214 
215 typedef struct JSShape JSShape;
216 typedef struct JSString JSString;
217 typedef struct JSString JSAtomStruct;
218 
219 typedef enum {
220     JS_GC_PHASE_NONE,
221     JS_GC_PHASE_DECREF,
222     JS_GC_PHASE_REMOVE_CYCLES,
223 } JSGCPhaseEnum;
224 
225 typedef enum OPCodeEnum OPCodeEnum;
226 
227 #ifdef CONFIG_BIGNUM
228 /* function pointers are used for numeric operations so that it is
229    possible to remove some numeric types */
230 typedef struct {
231     JSValue (*to_string)(JSContext *ctx, JSValueConst val);
232     JSValue (*from_string)(JSContext *ctx, const char *buf,
233                            int radix, int flags, slimb_t *pexponent);
234     int (*unary_arith)(JSContext *ctx,
235                        JSValue *pres, OPCodeEnum op, JSValue op1);
236     int (*binary_arith)(JSContext *ctx, OPCodeEnum op,
237                         JSValue *pres, JSValue op1, JSValue op2);
238     int (*compare)(JSContext *ctx, OPCodeEnum op,
239                    JSValue op1, JSValue op2);
240     /* only for bigfloat: */
241     JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a,
242                                     int64_t exponent);
243     int (*mul_pow10)(JSContext *ctx, JSValue *sp);
244 } JSNumericOperations;
245 #endif
246 
247 struct JSRuntime {
248     JSMallocFunctions mf;
249     JSMallocState malloc_state;
250     const char *rt_info;
251 
252     int atom_hash_size; /* power of two */
253     int atom_count;
254     int atom_size;
255     int atom_count_resize; /* resize hash table at this count */
256     uint32_t *atom_hash;
257     JSAtomStruct **atom_array;
258     int atom_free_index; /* 0 = none */
259 
260     int class_count;    /* size of class_array */
261     JSClass *class_array;
262 
263     struct list_head context_list; /* list of JSContext.link */
264     /* list of JSGCObjectHeader.link. List of allocated GC objects (used
265        by the garbage collector) */
266     struct list_head gc_obj_list;
267     /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */
268     struct list_head gc_zero_ref_count_list;
269     struct list_head tmp_obj_list; /* used during GC */
270     JSGCPhaseEnum gc_phase : 8;
271     size_t malloc_gc_threshold;
272 #ifdef DUMP_LEAKS
273     struct list_head string_list; /* list of JSString.link */
274 #endif
275     /* stack limitation */
276     const uint8_t *stack_top;
277     size_t stack_size; /* in bytes */
278 
279     JSValue current_exception;
280     /* true if inside an out of memory error, to avoid recursing */
281     BOOL in_out_of_memory : 8;
282 
283     struct JSStackFrame *current_stack_frame;
284 
285     JSInterruptHandler *interrupt_handler;
286     void *interrupt_opaque;
287 
288     JSHostPromiseRejectionTracker *host_promise_rejection_tracker;
289     void *host_promise_rejection_tracker_opaque;
290 
291     struct list_head job_list; /* list of JSJobEntry.link */
292 
293     JSModuleNormalizeFunc *module_normalize_func;
294     JSModuleLoaderFunc *module_loader_func;
295     void *module_loader_opaque;
296 
297     BOOL can_block : 8; /* TRUE if Atomics.wait can block */
298     /* used to allocate, free and clone SharedArrayBuffers */
299     JSSharedArrayBufferFunctions sab_funcs;
300 
301     /* Shape hash table */
302     int shape_hash_bits;
303     int shape_hash_size;
304     int shape_hash_count; /* number of hashed shapes */
305     JSShape **shape_hash;
306 #ifdef CONFIG_BIGNUM
307     bf_context_t bf_ctx;
308     JSNumericOperations bigint_ops;
309     JSNumericOperations bigfloat_ops;
310     JSNumericOperations bigdecimal_ops;
311     uint32_t operator_count;
312 #endif
313     void *user_opaque;
314 };
315 
316 struct JSClass {
317     uint32_t class_id; /* 0 means free entry */
318     JSAtom class_name;
319     JSClassFinalizer *finalizer;
320     JSClassGCMark *gc_mark;
321     JSClassCall *call;
322     /* pointers for exotic behavior, can be NULL if none are present */
323     const JSClassExoticMethods *exotic;
324 };
325 
326 #define JS_MODE_STRICT (1 << 0)
327 #define JS_MODE_STRIP  (1 << 1)
328 #define JS_MODE_MATH   (1 << 2)
329 
330 typedef struct JSStackFrame {
331     struct JSStackFrame *prev_frame; /* NULL if first stack frame */
332     JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
333     JSValue *arg_buf; /* arguments */
334     JSValue *var_buf; /* variables */
335     struct list_head var_ref_list; /* list of JSVarRef.link */
336     const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
337                         instruction after the call */
338     int arg_count;
339     int js_mode; /* 0 or JS_MODE_MATH for C functions */
340     /* only used in generators. Current stack pointer value. NULL if
341        the function is running. */
342     JSValue *cur_sp;
343 } JSStackFrame;
344 
345 typedef enum {
346     JS_GC_OBJ_TYPE_JS_OBJECT,
347     JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,
348     JS_GC_OBJ_TYPE_SHAPE,
349     JS_GC_OBJ_TYPE_VAR_REF,
350     JS_GC_OBJ_TYPE_ASYNC_FUNCTION,
351     JS_GC_OBJ_TYPE_JS_CONTEXT,
352 } JSGCObjectTypeEnum;
353 
354 /* header for GC objects. GC objects are C data structures with a
355    reference count that can reference other GC objects. JS Objects are
356    a particular type of GC object. */
357 struct JSGCObjectHeader {
358     int ref_count; /* must come first, 32-bit */
359     JSGCObjectTypeEnum gc_obj_type : 4;
360     uint8_t mark : 4; /* used by the GC */
361     uint8_t dummy1; /* not used by the GC */
362     uint16_t dummy2; /* not used by the GC */
363     struct list_head link;
364 };
365 
366 typedef struct JSVarRef {
367     union {
368         JSGCObjectHeader header; /* must come first */
369         struct {
370             int __gc_ref_count; /* corresponds to header.ref_count */
371             uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
372 
373             /* 0 : the JSVarRef is on the stack. header.link is an element
374                of JSStackFrame.var_ref_list.
375                1 : the JSVarRef is detached. header.link has the normal meanning
376             */
377             uint8_t is_detached : 1;
378             uint8_t is_arg : 1;
379             uint16_t var_idx; /* index of the corresponding function variable on
380                                  the stack */
381         };
382     };
383     JSValue *pvalue; /* pointer to the value, either on the stack or
384                         to 'value' */
385     JSValue value; /* used when the variable is no longer on the stack */
386 } JSVarRef;
387 
388 #ifdef CONFIG_BIGNUM
389 typedef struct JSFloatEnv {
390     limb_t prec;
391     bf_flags_t flags;
392     unsigned int status;
393 } JSFloatEnv;
394 
395 /* the same structure is used for big integers and big floats. Big
396    integers are never infinite or NaNs */
397 typedef struct JSBigFloat {
398     JSRefCountHeader header; /* must come first, 32-bit */
399     bf_t num;
400 } JSBigFloat;
401 
402 typedef struct JSBigDecimal {
403     JSRefCountHeader header; /* must come first, 32-bit */
404     bfdec_t num;
405 } JSBigDecimal;
406 #endif
407 
408 typedef enum {
409     JS_AUTOINIT_ID_PROTOTYPE,
410     JS_AUTOINIT_ID_MODULE_NS,
411     JS_AUTOINIT_ID_PROP,
412 } JSAutoInitIDEnum;
413 
414 /* must be large enough to have a negligible runtime cost and small
415    enough to call the interrupt callback often. */
416 #define JS_INTERRUPT_COUNTER_INIT 10000
417 
418 struct JSContext {
419     JSGCObjectHeader header; /* must come first */
420     JSRuntime *rt;
421     struct list_head link;
422 
423     uint16_t binary_object_count;
424     int binary_object_size;
425 
426     JSShape *array_shape;   /* initial shape for Array objects */
427 
428 #ifdef ENABLE_JS_DEBUG
429     DebuggerInfo *debugger_info;
430 #endif
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 #ifdef ENABLE_JS_DEBUG
634     struct JSDebuggerFunctionInfo debugger;
635 #endif
636 } JSFunctionBytecode;
637 
638 typedef struct JSBoundFunction {
639     JSValue func_obj;
640     JSValue this_val;
641     int argc;
642     JSValue argv[0];
643 } JSBoundFunction;
644 
645 typedef enum JSIteratorKindEnum {
646     JS_ITERATOR_KIND_KEY,
647     JS_ITERATOR_KIND_VALUE,
648     JS_ITERATOR_KIND_KEY_AND_VALUE,
649 } JSIteratorKindEnum;
650 
651 typedef struct JSForInIterator {
652     JSValue obj;
653     BOOL is_array;
654     uint32_t array_length;
655     uint32_t idx;
656 } JSForInIterator;
657 
658 typedef struct JSRegExp {
659     JSString *pattern;
660     JSString *bytecode; /* also contains the flags */
661 } JSRegExp;
662 
663 typedef struct JSProxyData {
664     JSValue target;
665     JSValue handler;
666     uint8_t is_func;
667     uint8_t is_revoked;
668 } JSProxyData;
669 
670 typedef struct JSArrayBuffer {
671     int byte_length; /* 0 if detached */
672     uint8_t detached;
673     uint8_t shared; /* if shared, the array buffer cannot be detached */
674     uint8_t *data; /* NULL if detached */
675     struct list_head array_list;
676     void *opaque;
677     JSFreeArrayBufferDataFunc *free_func;
678 } JSArrayBuffer;
679 
680 typedef struct JSTypedArray {
681     struct list_head link; /* link to arraybuffer */
682     JSObject *obj; /* back pointer to the TypedArray/DataView object */
683     JSObject *buffer; /* based array buffer */
684     uint32_t offset; /* offset in the array buffer */
685     uint32_t length; /* length in the array buffer */
686 } JSTypedArray;
687 
688 typedef struct JSAsyncFunctionState {
689     JSValue this_val; /* 'this' generator argument */
690     int argc; /* number of function arguments */
691     BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */
692     JSStackFrame frame;
693 } JSAsyncFunctionState;
694 
695 /* XXX: could use an object instead to avoid the
696    JS_TAG_ASYNC_FUNCTION tag for the GC */
697 typedef struct JSAsyncFunctionData {
698     JSGCObjectHeader header; /* must come first */
699     JSValue resolving_funcs[2];
700     BOOL is_active; /* true if the async function state is valid */
701     JSAsyncFunctionState func_state;
702 } JSAsyncFunctionData;
703 
704 typedef enum {
705    /* binary operators */
706    JS_OVOP_ADD,
707    JS_OVOP_SUB,
708    JS_OVOP_MUL,
709    JS_OVOP_DIV,
710    JS_OVOP_MOD,
711    JS_OVOP_POW,
712    JS_OVOP_OR,
713    JS_OVOP_AND,
714    JS_OVOP_XOR,
715    JS_OVOP_SHL,
716    JS_OVOP_SAR,
717    JS_OVOP_SHR,
718    JS_OVOP_EQ,
719    JS_OVOP_LESS,
720 
721    JS_OVOP_BINARY_COUNT,
722    /* unary operators */
723    JS_OVOP_POS = JS_OVOP_BINARY_COUNT,
724    JS_OVOP_NEG,
725    JS_OVOP_INC,
726    JS_OVOP_DEC,
727    JS_OVOP_NOT,
728 
729    JS_OVOP_COUNT,
730 } JSOverloadableOperatorEnum;
731 
732 typedef struct {
733     uint32_t operator_index;
734     JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */
735 } JSBinaryOperatorDefEntry;
736 
737 typedef struct {
738     int count;
739     JSBinaryOperatorDefEntry *tab;
740 } JSBinaryOperatorDef;
741 
742 typedef struct {
743     uint32_t operator_counter;
744     BOOL is_primitive; /* OperatorSet for a primitive type */
745     /* NULL if no operator is defined */
746     JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */
747     JSBinaryOperatorDef left;
748     JSBinaryOperatorDef right;
749 } JSOperatorSetData;
750 
751 typedef struct JSReqModuleEntry {
752     JSAtom module_name;
753     JSModuleDef *module; /* used using resolution */
754 } JSReqModuleEntry;
755 
756 typedef enum JSExportTypeEnum {
757     JS_EXPORT_TYPE_LOCAL,
758     JS_EXPORT_TYPE_INDIRECT,
759 } JSExportTypeEnum;
760 
761 typedef struct JSExportEntry {
762     union {
763         struct {
764             int var_idx; /* closure variable index */
765             JSVarRef *var_ref; /* if != NULL, reference to the variable */
766         } local; /* for local export */
767         int req_module_idx; /* module for indirect export */
768     } u;
769     JSExportTypeEnum export_type;
770     JSAtom local_name; /* '*' if export ns from. not used for local
771                           export after compilation */
772     JSAtom export_name; /* exported variable name */
773 } JSExportEntry;
774 
775 typedef struct JSStarExportEntry {
776     int req_module_idx; /* in req_module_entries */
777 } JSStarExportEntry;
778 
779 typedef struct JSImportEntry {
780     int var_idx; /* closure variable index */
781     JSAtom import_name;
782     int req_module_idx; /* in req_module_entries */
783 } JSImportEntry;
784 
785 struct JSModuleDef {
786     JSRefCountHeader header; /* must come first, 32-bit */
787     JSAtom module_name;
788     struct list_head link;
789 
790     JSReqModuleEntry *req_module_entries;
791     int req_module_entries_count;
792     int req_module_entries_size;
793 
794     JSExportEntry *export_entries;
795     int export_entries_count;
796     int export_entries_size;
797 
798     JSStarExportEntry *star_export_entries;
799     int star_export_entries_count;
800     int star_export_entries_size;
801 
802     JSImportEntry *import_entries;
803     int import_entries_count;
804     int import_entries_size;
805 
806     JSValue module_ns;
807     JSValue func_obj; /* only used for JS modules */
808     JSModuleInitFunc *init_func; /* only used for C modules */
809     BOOL resolved : 8;
810     BOOL func_created : 8;
811     BOOL instantiated : 8;
812     BOOL evaluated : 8;
813     BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */
814     /* true if evaluation yielded an exception. It is saved in
815        eval_exception */
816     BOOL eval_has_exception : 8;
817     JSValue eval_exception;
818     JSValue meta_obj; /* for import.meta */
819 };
820 
821 typedef struct JSJobEntry {
822     struct list_head link;
823     JSContext *ctx;
824     JSJobFunc *job_func;
825     int argc;
826     JSValue argv[0];
827 } JSJobEntry;
828 
829 typedef struct JSProperty {
830     union {
831         JSValue value;      /* JS_PROP_NORMAL */
832         struct {            /* JS_PROP_GETSET */
833             JSObject *getter; /* NULL if undefined */
834             JSObject *setter; /* NULL if undefined */
835         } getset;
836         JSVarRef *var_ref;  /* JS_PROP_VARREF */
837         struct {            /* JS_PROP_AUTOINIT */
838             /* in order to use only 2 pointers, we compress the realm
839                and the init function pointer */
840             uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)
841                                        in the 2 low bits */
842             void *opaque;
843         } init;
844     } u;
845 } JSProperty;
846 
847 #define JS_PROP_INITIAL_SIZE 2
848 #define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */
849 #define JS_ARRAY_INITIAL_SIZE 2
850 
851 typedef struct JSShapeProperty {
852     uint32_t hash_next : 26; /* 0 if last in list */
853     uint32_t flags : 6;   /* JS_PROP_XXX */
854     JSAtom atom; /* JS_ATOM_NULL = free property entry */
855 } JSShapeProperty;
856 
857 struct JSShape {
858     /* hash table of size hash_mask + 1 before the start of the
859        structure (see prop_hash_end()). */
860     JSGCObjectHeader header;
861     /* true if the shape is inserted in the shape hash table. If not,
862        JSShape.hash is not valid */
863     uint8_t is_hashed;
864     /* If true, the shape may have small array index properties 'n' with 0
865        <= n <= 2^31-1. If false, the shape is guaranteed not to have
866        small array index properties */
867     uint8_t has_small_array_index;
868     uint32_t hash; /* current hash value */
869     uint32_t prop_hash_mask;
870     int prop_size; /* allocated properties */
871     int prop_count; /* include deleted properties */
872     int deleted_prop_count;
873     JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */
874     JSObject *proto;
875     JSShapeProperty prop[0]; /* prop_size elements */
876 };
877 
878 struct JSObject {
879     union {
880         JSGCObjectHeader header;
881         struct {
882             int __gc_ref_count; /* corresponds to header.ref_count */
883             uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
884 
885             uint8_t extensible : 1;
886             uint8_t free_mark : 1; /* only used when freeing objects with cycles */
887             uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */
888             uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */
889             uint8_t is_constructor : 1; /* TRUE if object is a constructor function */
890             uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */
891             uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */
892             uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */
893             uint16_t class_id; /* see JS_CLASS_x */
894         };
895     };
896     /* byte offsets: 16/24 */
897     JSShape *shape; /* prototype and property names + flag */
898     JSProperty *prop; /* array of properties */
899     /* byte offsets: 24/40 */
900     struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */
901     /* byte offsets: 28/48 */
902     union {
903         void *opaque;
904         struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */
905         struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */
906         struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */
907         struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */
908         struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */
909 #ifdef CONFIG_BIGNUM
910         struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */
911         struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */
912 #endif
913         struct JSMapState *map_state;   /* JS_CLASS_MAP..JS_CLASS_WEAKSET */
914         struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */
915         struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */
916         struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */
917         struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */
918         struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */
919         struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */
920         struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */
921         struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
922         struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
923         struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */
924         struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */
925             /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */
926             struct JSFunctionBytecode *function_bytecode;
927             JSVarRef **var_refs;
928             JSObject *home_object; /* for 'super' access */
929         } func;
930         struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */
931             JSContext *realm;
932             JSCFunctionType c_function;
933             uint8_t length;
934             uint8_t cproto;
935             int16_t magic;
936         } cfunc;
937         /* array part for fast arrays and typed arrays */
938         struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
939             union {
940                 uint32_t size;          /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
941                 struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
942             } u1;
943             union {
944                 JSValue *values;        /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
945                 void *ptr;              /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
946                 int8_t *int8_ptr;       /* JS_CLASS_INT8_ARRAY */
947                 uint8_t *uint8_ptr;     /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */
948                 int16_t *int16_ptr;     /* JS_CLASS_INT16_ARRAY */
949                 uint16_t *uint16_ptr;   /* JS_CLASS_UINT16_ARRAY */
950                 int32_t *int32_ptr;     /* JS_CLASS_INT32_ARRAY */
951                 uint32_t *uint32_ptr;   /* JS_CLASS_UINT32_ARRAY */
952                 int64_t *int64_ptr;     /* JS_CLASS_INT64_ARRAY */
953                 uint64_t *uint64_ptr;   /* JS_CLASS_UINT64_ARRAY */
954                 float *float_ptr;       /* JS_CLASS_FLOAT32_ARRAY */
955                 double *double_ptr;     /* JS_CLASS_FLOAT64_ARRAY */
956             } u;
957             uint32_t count; /* <= 2^31-1. 0 for a detached typed array */
958         } array;    /* 12/20 bytes */
959         JSRegExp regexp;    /* JS_CLASS_REGEXP: 8/16 bytes */
960         JSValue object_data;    /* for JS_SetObjectData(): 8/16/16 bytes */
961     } u;
962     /* byte sizes: 40/48/72 */
963 };
964 enum {
965     __JS_ATOM_NULL = JS_ATOM_NULL,
966 #define DEF(name, str) JS_ATOM_ ## name,
967 #include "quickjs-atom.h"
968 #undef DEF
969     JS_ATOM_END,
970 };
971 #define JS_ATOM_LAST_KEYWORD JS_ATOM_super
972 #define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield
973 
974 static const char js_atom_init[] =
975 #define DEF(name, str) str "\0"
976 #include "quickjs-atom.h"
977 #undef DEF
978 ;
979 
980 typedef enum OPCodeFormat {
981 #define FMT(f) OP_FMT_ ## f,
982 #define DEF(id, size, n_pop, n_push, f)
983 #include "quickjs-opcode.h"
984 #undef DEF
985 #undef FMT
986 } OPCodeFormat;
987 
988 enum OPCodeEnum {
989 #define FMT(f)
990 #define DEF(id, size, n_pop, n_push, f) OP_ ## id,
991 #define def(id, size, n_pop, n_push, f)
992 #include "quickjs-opcode.h"
993 #undef def
994 #undef DEF
995 #undef FMT
996     OP_COUNT, /* excluding temporary opcodes */
997     /* temporary opcodes : overlap with the short opcodes */
998     OP_TEMP_START = OP_nop + 1,
999     OP___dummy = OP_TEMP_START - 1,
1000 #define FMT(f)
1001 #define DEF(id, size, n_pop, n_push, f)
1002 #define def(id, size, n_pop, n_push, f) OP_ ## id,
1003 #include "quickjs-opcode.h"
1004 #undef def
1005 #undef DEF
1006 #undef FMT
1007     OP_TEMP_END,
1008 };
1009 
1010 static int JS_InitAtoms(JSRuntime *rt);
1011 static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
1012                                int atom_type);
1013 static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p);
1014 static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);
1015 static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
1016                                   JSValueConst this_obj,
1017                                   int argc, JSValueConst *argv, int flags);
1018 static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
1019                                       JSValueConst this_obj,
1020                                       int argc, JSValueConst *argv, int flags);
1021 static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
1022                                JSValueConst this_obj, JSValueConst new_target,
1023                                int argc, JSValue *argv, int flags);
1024 static JSValue JS_CallConstructorInternal(JSContext *ctx,
1025                                           JSValueConst func_obj,
1026                                           JSValueConst new_target,
1027                                           int argc, JSValue *argv, int flags);
1028 static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
1029                            int argc, JSValueConst *argv);
1030 static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
1031                              int argc, JSValueConst *argv);
1032 static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
1033                                             JSValue val, BOOL is_array_ctor);
1034 static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
1035                              JSValueConst val, int flags, int scope_idx);
1036 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
1037 static __maybe_unused void JS_DumpAtoms(JSRuntime *rt);
1038 static __maybe_unused void JS_DumpString(JSRuntime *rt,
1039                                                   const JSString *p);
1040 static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
1041 static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
1042 static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
1043 static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
1044                                                       JSValueConst val);
1045 static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);
1046 static __maybe_unused void JS_PrintValue(JSContext *ctx,
1047                                                   const char *str,
1048                                                   JSValueConst val);
1049 static __maybe_unused void JS_DumpShapes(JSRuntime *rt);
1050 static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
1051                                  int argc, JSValueConst *argv, int magic);
1052 static void js_array_finalizer(JSRuntime *rt, JSValue val);
1053 static void js_array_mark(JSRuntime *rt, JSValueConst val,
1054                           JS_MarkFunc *mark_func);
1055 static void js_object_data_finalizer(JSRuntime *rt, JSValue val);
1056 static void js_object_data_mark(JSRuntime *rt, JSValueConst val,
1057                                 JS_MarkFunc *mark_func);
1058 static void js_c_function_finalizer(JSRuntime *rt, JSValue val);
1059 static void js_c_function_mark(JSRuntime *rt, JSValueConst val,
1060                                JS_MarkFunc *mark_func);
1061 static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val);
1062 static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
1063                                 JS_MarkFunc *mark_func);
1064 static void js_bound_function_finalizer(JSRuntime *rt, JSValue val);
1065 static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
1066                                 JS_MarkFunc *mark_func);
1067 static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val);
1068 static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
1069                                 JS_MarkFunc *mark_func);
1070 static void js_regexp_finalizer(JSRuntime *rt, JSValue val);
1071 static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val);
1072 static void js_typed_array_finalizer(JSRuntime *rt, JSValue val);
1073 static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
1074                                 JS_MarkFunc *mark_func);
1075 static void js_proxy_finalizer(JSRuntime *rt, JSValue val);
1076 static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
1077                                 JS_MarkFunc *mark_func);
1078 static void js_map_finalizer(JSRuntime *rt, JSValue val);
1079 static void js_map_mark(JSRuntime *rt, JSValueConst val,
1080                                 JS_MarkFunc *mark_func);
1081 static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val);
1082 static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
1083                                 JS_MarkFunc *mark_func);
1084 static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val);
1085 static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
1086                                 JS_MarkFunc *mark_func);
1087 static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val);
1088 static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
1089                                 JS_MarkFunc *mark_func);
1090 static void js_generator_finalizer(JSRuntime *rt, JSValue obj);
1091 static void js_generator_mark(JSRuntime *rt, JSValueConst val,
1092                                 JS_MarkFunc *mark_func);
1093 static void js_promise_finalizer(JSRuntime *rt, JSValue val);
1094 static void js_promise_mark(JSRuntime *rt, JSValueConst val,
1095                                 JS_MarkFunc *mark_func);
1096 static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);
1097 static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
1098                                 JS_MarkFunc *mark_func);
1099 #ifdef CONFIG_BIGNUM
1100 static void js_operator_set_finalizer(JSRuntime *rt, JSValue val);
1101 static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
1102                                  JS_MarkFunc *mark_func);
1103 #endif
1104 static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
1105 static int JS_ToBoolFree(JSContext *ctx, JSValue val);
1106 static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
1107 static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val);
1108 static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val);
1109 static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
1110                                  JSValueConst flags);
1111 static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
1112                                               JSValue pattern, JSValue bc);
1113 static void gc_decref(JSRuntime *rt);
1114 static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
1115                         const JSClassDef *class_def, JSAtom name);
1116 
1117 typedef enum JSStrictEqModeEnum {
1118     JS_EQ_STRICT,
1119     JS_EQ_SAME_VALUE,
1120     JS_EQ_SAME_VALUE_ZERO,
1121 } JSStrictEqModeEnum;
1122 
1123 static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
1124                           JSStrictEqModeEnum eq_mode);
1125 static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2);
1126 static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2);
1127 static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2);
1128 static JSValue JS_ToObject(JSContext *ctx, JSValueConst val);
1129 static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);
1130 static JSProperty *add_property(JSContext *ctx,
1131                                 JSObject *p, JSAtom prop, int prop_flags);
1132 #ifdef CONFIG_BIGNUM
1133 static void js_float_env_finalizer(JSRuntime *rt, JSValue val);
1134 static JSValue JS_NewBigFloat(JSContext *ctx);
JS_GetBigFloat(JSValueConst val)1135 static inline bf_t *JS_GetBigFloat(JSValueConst val)
1136 {
1137     JSBigFloat *p = JS_VALUE_GET_PTR(val);
1138     return &p->num;
1139 }
1140 static JSValue JS_NewBigDecimal(JSContext *ctx);
JS_GetBigDecimal(JSValueConst val)1141 static inline bfdec_t *JS_GetBigDecimal(JSValueConst val)
1142 {
1143     JSBigDecimal *p = JS_VALUE_GET_PTR(val);
1144     return &p->num;
1145 }
1146 static JSValue JS_NewBigInt(JSContext *ctx);
JS_GetBigInt(JSValueConst val)1147 static inline bf_t *JS_GetBigInt(JSValueConst val)
1148 {
1149     JSBigFloat *p = JS_VALUE_GET_PTR(val);
1150     return &p->num;
1151 }
1152 static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
1153                                  BOOL convert_to_safe_integer);
1154 static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val);
1155 static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
1156 static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val);
1157 static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf);
1158 static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val);
1159 static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
1160                                    BOOL allow_null_or_undefined);
1161 static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);
1162 #endif
1163 JSValue JS_ThrowOutOfMemory(JSContext *ctx);
1164 static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
1165 static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
1166 static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
1167                                    JSValueConst proto_val, BOOL throw_flag);
1168 static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj);
1169 static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj);
1170 static int js_proxy_isArray(JSContext *ctx, JSValueConst obj);
1171 static int JS_CreateProperty(JSContext *ctx, JSObject *p,
1172                              JSAtom prop, JSValueConst val,
1173                              JSValueConst getter, JSValueConst setter,
1174                              int flags);
1175 static int js_string_memcmp(const JSString *p1, const JSString *p2, int len);
1176 static void reset_weak_ref(JSRuntime *rt, JSObject *p);
1177 static JSValue js_array_buffer_constructor3(JSContext *ctx,
1178                                             JSValueConst new_target,
1179                                             uint64_t len, JSClassID class_id,
1180                                             uint8_t *buf,
1181                                             JSFreeArrayBufferDataFunc *free_func,
1182                                             void *opaque, BOOL alloc_flag);
1183 static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj);
1184 static JSValue js_typed_array_constructor(JSContext *ctx,
1185                                           JSValueConst this_val,
1186                                           int argc, JSValueConst *argv,
1187                                           int classid);
1188 static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p);
1189 static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p);
1190 static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
1191 static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
1192                              BOOL is_arg);
1193 static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
1194                                           JSValueConst this_obj,
1195                                           int argc, JSValueConst *argv,
1196                                           int flags);
1197 static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val);
1198 static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
1199                                            JS_MarkFunc *mark_func);
1200 static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
1201                                const char *input, size_t input_len,
1202                                const char *filename, int flags, int scope_idx);
1203 static void js_free_module_def(JSContext *ctx, JSModuleDef *m);
1204 static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
1205                                JS_MarkFunc *mark_func);
1206 static JSValue js_import_meta(JSContext *ctx);
1207 static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier);
1208 static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);
1209 static JSValue js_new_promise_capability(JSContext *ctx,
1210                                          JSValue *resolving_funcs,
1211                                          JSValueConst ctor);
1212 static __exception int perform_promise_then(JSContext *ctx,
1213                                             JSValueConst promise,
1214                                             JSValueConst *resolve_reject,
1215                                             JSValueConst *cap_resolving_funcs);
1216 static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
1217                                   int argc, JSValueConst *argv, int magic);
1218 static int js_string_compare(JSContext *ctx,
1219                              const JSString *p1, const JSString *p2);
1220 static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val);
1221 static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
1222                                JSValue prop, JSValue val, int flags);
1223 static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val);
1224 static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val);
1225 static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);
1226 static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
1227                                      JSObject *p, JSAtom prop);
1228 static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);
1229 static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,
1230                             JS_MarkFunc *mark_func);
1231 static void JS_AddIntrinsicBasicObjects(JSContext *ctx);
1232 static void js_free_shape(JSRuntime *rt, JSShape *sh);
1233 static void js_free_shape_null(JSRuntime *rt, JSShape *sh);
1234 static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
1235                                    JSShapeProperty **pprs);
1236 static int init_shape_hash(JSRuntime *rt);
1237 static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
1238                                        JSValueConst obj);
1239 static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len);
1240 static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
1241                                JSValueConst array_arg);
1242 static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
1243                               JSValue **arrpp, uint32_t *countp);
1244 static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
1245                                               JSValueConst sync_iter);
1246 static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val);
1247 static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
1248                                     JS_MarkFunc *mark_func);
1249 static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
1250                                        JSValueConst this_val,
1251                                        int argc, JSValueConst *argv, int flags);
1252 static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val);
1253 static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
1254                           JSGCObjectTypeEnum type);
1255 static void remove_gc_object(JSGCObjectHeader *h);
1256 static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s);
1257 static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
1258 static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
1259                                  void *opaque);
1260 static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
1261                                                JSAtom atom, void *opaque);
1262 void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag);
1263 
1264 static const JSClassExoticMethods js_arguments_exotic_methods;
1265 static const JSClassExoticMethods js_string_exotic_methods;
1266 static const JSClassExoticMethods js_proxy_exotic_methods;
1267 static const JSClassExoticMethods js_module_ns_exotic_methods;
1268 static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
1269 
js_trigger_gc(JSRuntime * rt,size_t size)1270 static void js_trigger_gc(JSRuntime *rt, size_t size)
1271 {
1272     BOOL force_gc;
1273 #ifdef FORCE_GC_AT_MALLOC
1274     force_gc = TRUE;
1275 #else
1276     force_gc = ((rt->malloc_state.malloc_size + size) >
1277                 rt->malloc_gc_threshold);
1278 #endif
1279     if (force_gc) {
1280 #ifdef DUMP_GC
1281         printf("GC: size=%" PRIu64 "\n",
1282                (uint64_t)rt->malloc_state.malloc_size);
1283 #endif
1284         JS_RunGC(rt);
1285         rt->malloc_gc_threshold = rt->malloc_state.malloc_size +
1286             (rt->malloc_state.malloc_size >> 1);
1287     }
1288 }
1289 
js_malloc_usable_size_unknown(const void * ptr)1290 static size_t js_malloc_usable_size_unknown(const void *ptr)
1291 {
1292     return 0;
1293 }
1294 
js_malloc_rt(JSRuntime * rt,size_t size)1295 void *js_malloc_rt(JSRuntime *rt, size_t size)
1296 {
1297     return rt->mf.js_malloc(&rt->malloc_state, size);
1298 }
1299 
js_free_rt(JSRuntime * rt,void * ptr)1300 void js_free_rt(JSRuntime *rt, void *ptr)
1301 {
1302     rt->mf.js_free(&rt->malloc_state, ptr);
1303 }
1304 
js_realloc_rt(JSRuntime * rt,void * ptr,size_t size)1305 void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)
1306 {
1307     return rt->mf.js_realloc(&rt->malloc_state, ptr, size);
1308 }
1309 
js_malloc_usable_size_rt(JSRuntime * rt,const void * ptr)1310 size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr)
1311 {
1312     return rt->mf.js_malloc_usable_size(ptr);
1313 }
1314 
js_mallocz_rt(JSRuntime * rt,size_t size)1315 void *js_mallocz_rt(JSRuntime *rt, size_t size)
1316 {
1317     void *ptr;
1318     ptr = js_malloc_rt(rt, size);
1319     if (!ptr)
1320         return NULL;
1321     return memset(ptr, 0, size);
1322 }
1323 
1324 #ifdef CONFIG_BIGNUM
1325 /* called by libbf */
js_bf_realloc(void * opaque,void * ptr,size_t size)1326 static void *js_bf_realloc(void *opaque, void *ptr, size_t size)
1327 {
1328     JSRuntime *rt = opaque;
1329     return js_realloc_rt(rt, ptr, size);
1330 }
1331 #endif /* CONFIG_BIGNUM */
1332 
1333 /* Throw out of memory in case of error */
js_malloc(JSContext * ctx,size_t size)1334 void *js_malloc(JSContext *ctx, size_t size)
1335 {
1336     void *ptr;
1337     ptr = js_malloc_rt(ctx->rt, size);
1338     if (unlikely(!ptr)) {
1339         JS_ThrowOutOfMemory(ctx);
1340         return NULL;
1341     }
1342     return ptr;
1343 }
1344 
1345 /* Throw out of memory in case of error */
js_mallocz(JSContext * ctx,size_t size)1346 void *js_mallocz(JSContext *ctx, size_t size)
1347 {
1348     void *ptr;
1349     ptr = js_mallocz_rt(ctx->rt, size);
1350     if (unlikely(!ptr)) {
1351         JS_ThrowOutOfMemory(ctx);
1352         return NULL;
1353     }
1354     return ptr;
1355 }
1356 
js_free(JSContext * ctx,void * ptr)1357 void js_free(JSContext *ctx, void *ptr)
1358 {
1359     js_free_rt(ctx->rt, ptr);
1360 }
1361 
1362 /* Throw out of memory in case of error */
js_realloc(JSContext * ctx,void * ptr,size_t size)1363 void *js_realloc(JSContext *ctx, void *ptr, size_t size)
1364 {
1365     void *ret;
1366     ret = js_realloc_rt(ctx->rt, ptr, size);
1367     if (unlikely(!ret && size != 0)) {
1368         JS_ThrowOutOfMemory(ctx);
1369         return NULL;
1370     }
1371     return ret;
1372 }
1373 
1374 /* store extra allocated size in *pslack if successful */
js_realloc2(JSContext * ctx,void * ptr,size_t size,size_t * pslack)1375 void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack)
1376 {
1377     void *ret;
1378     ret = js_realloc_rt(ctx->rt, ptr, size);
1379     if (unlikely(!ret && size != 0)) {
1380         JS_ThrowOutOfMemory(ctx);
1381         return NULL;
1382     }
1383     if (pslack) {
1384         size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret);
1385         *pslack = (new_size > size) ? new_size - size : 0;
1386     }
1387     return ret;
1388 }
1389 
js_malloc_usable_size(JSContext * ctx,const void * ptr)1390 size_t js_malloc_usable_size(JSContext *ctx, const void *ptr)
1391 {
1392     return js_malloc_usable_size_rt(ctx->rt, ptr);
1393 }
1394 
1395 /* Throw out of memory exception in case of error */
js_strndup(JSContext * ctx,const char * s,size_t n)1396 char *js_strndup(JSContext *ctx, const char *s, size_t n)
1397 {
1398     char *ptr;
1399     ptr = js_malloc(ctx, n + 1);
1400     if (ptr) {
1401         memcpy(ptr, s, n);
1402         ptr[n] = '\0';
1403     }
1404     return ptr;
1405 }
1406 
js_strdup(JSContext * ctx,const char * str)1407 char *js_strdup(JSContext *ctx, const char *str)
1408 {
1409     return js_strndup(ctx, str, strlen(str));
1410 }
1411 
js_realloc_array(JSContext * ctx,void ** parray,int elem_size,int * psize,int req_size)1412 static no_inline int js_realloc_array(JSContext *ctx, void **parray,
1413                                       int elem_size, int *psize, int req_size)
1414 {
1415     int new_size;
1416     size_t slack;
1417     void *new_array;
1418     /* XXX: potential arithmetic overflow */
1419     new_size = max_int(req_size, *psize * 3 / 2);
1420     new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack);
1421     if (!new_array)
1422         return -1;
1423     new_size += slack / elem_size;
1424     *psize = new_size;
1425     *parray = new_array;
1426     return 0;
1427 }
1428 
1429 /* 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)1430 static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size,
1431                                   int *psize, int req_size)
1432 {
1433     if (unlikely(req_size > *psize))
1434         return js_realloc_array(ctx, parray, elem_size, psize, req_size);
1435     else
1436         return 0;
1437 }
1438 
js_dbuf_init(JSContext * ctx,DynBuf * s)1439 static inline void js_dbuf_init(JSContext *ctx, DynBuf *s)
1440 {
1441     dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
1442 }
1443 
is_digit(int c)1444 static inline int is_digit(int c) {
1445     return c >= '0' && c <= '9';
1446 }
1447 
1448 typedef struct JSClassShortDef {
1449     JSAtom class_name;
1450     JSClassFinalizer *finalizer;
1451     JSClassGCMark *gc_mark;
1452 } JSClassShortDef;
1453 
1454 static JSClassShortDef const js_std_class_def[] = {
1455     { JS_ATOM_Object, NULL, NULL },                             /* JS_CLASS_OBJECT */
1456     { JS_ATOM_Array, js_array_finalizer, js_array_mark },       /* JS_CLASS_ARRAY */
1457     { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */
1458     { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */
1459     { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */
1460     { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */
1461     { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */
1462     { JS_ATOM_Arguments, js_array_finalizer, js_array_mark },   /* JS_CLASS_ARGUMENTS */
1463     { JS_ATOM_Arguments, NULL, NULL },                          /* JS_CLASS_MAPPED_ARGUMENTS */
1464     { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */
1465     { JS_ATOM_Object, NULL, NULL },                             /* JS_CLASS_MODULE_NS */
1466     { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */
1467     { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */
1468     { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */
1469     { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */
1470     { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark },  /* JS_CLASS_GENERATOR_FUNCTION */
1471     { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark },      /* JS_CLASS_FOR_IN_ITERATOR */
1472     { JS_ATOM_RegExp, js_regexp_finalizer, NULL },                              /* JS_CLASS_REGEXP */
1473     { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL },                   /* JS_CLASS_ARRAY_BUFFER */
1474     { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL },             /* JS_CLASS_SHARED_ARRAY_BUFFER */
1475     { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */
1476     { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark },       /* JS_CLASS_INT8_ARRAY */
1477     { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark },      /* JS_CLASS_UINT8_ARRAY */
1478     { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark },      /* JS_CLASS_INT16_ARRAY */
1479     { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark },     /* JS_CLASS_UINT16_ARRAY */
1480     { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark },      /* JS_CLASS_INT32_ARRAY */
1481     { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark },     /* JS_CLASS_UINT32_ARRAY */
1482 #ifdef CONFIG_BIGNUM
1483     { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark },   /* JS_CLASS_BIG_INT64_ARRAY */
1484     { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark },  /* JS_CLASS_BIG_UINT64_ARRAY */
1485 #endif
1486     { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark },    /* JS_CLASS_FLOAT32_ARRAY */
1487     { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark },    /* JS_CLASS_FLOAT64_ARRAY */
1488     { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark },        /* JS_CLASS_DATAVIEW */
1489 #ifdef CONFIG_BIGNUM
1490     { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark },      /* JS_CLASS_BIG_INT */
1491     { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark },    /* JS_CLASS_BIG_FLOAT */
1492     { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL },      /* JS_CLASS_FLOAT_ENV */
1493     { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark },    /* JS_CLASS_BIG_DECIMAL */
1494     { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark },    /* JS_CLASS_OPERATOR_SET */
1495 #endif
1496     { JS_ATOM_Map, js_map_finalizer, js_map_mark },             /* JS_CLASS_MAP */
1497     { JS_ATOM_Set, js_map_finalizer, js_map_mark },             /* JS_CLASS_SET */
1498     { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark },         /* JS_CLASS_WEAKMAP */
1499     { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark },         /* JS_CLASS_WEAKSET */
1500     { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */
1501     { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */
1502     { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */
1503     { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */
1504     { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */
1505     { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */
1506 };
1507 
init_class_range(JSRuntime * rt,JSClassShortDef const * tab,int start,int count)1508 static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,
1509                             int start, int count)
1510 {
1511     JSClassDef cm_s, *cm = &cm_s;
1512     int i, class_id;
1513 
1514     for(i = 0; i < count; i++) {
1515         class_id = i + start;
1516         memset(cm, 0, sizeof(*cm));
1517         cm->finalizer = tab[i].finalizer;
1518         cm->gc_mark = tab[i].gc_mark;
1519         if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0)
1520             return -1;
1521     }
1522     return 0;
1523 }
1524 
1525 #ifdef CONFIG_BIGNUM
JS_ThrowUnsupportedOperation(JSContext * ctx)1526 static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx)
1527 {
1528     return JS_ThrowTypeError(ctx, "unsupported operation");
1529 }
1530 
invalid_to_string(JSContext * ctx,JSValueConst val)1531 static JSValue invalid_to_string(JSContext *ctx, JSValueConst val)
1532 {
1533     return JS_ThrowUnsupportedOperation(ctx);
1534 }
1535 
invalid_from_string(JSContext * ctx,const char * buf,int radix,int flags,slimb_t * pexponent)1536 static JSValue invalid_from_string(JSContext *ctx, const char *buf,
1537                                    int radix, int flags, slimb_t *pexponent)
1538 {
1539     return JS_NAN;
1540 }
1541 
invalid_unary_arith(JSContext * ctx,JSValue * pres,OPCodeEnum op,JSValue op1)1542 static int invalid_unary_arith(JSContext *ctx,
1543                                JSValue *pres, OPCodeEnum op, JSValue op1)
1544 {
1545     JS_FreeValue(ctx, op1);
1546     JS_ThrowUnsupportedOperation(ctx);
1547     return -1;
1548 }
1549 
invalid_binary_arith(JSContext * ctx,OPCodeEnum op,JSValue * pres,JSValue op1,JSValue op2)1550 static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op,
1551                                 JSValue *pres, JSValue op1, JSValue op2)
1552 {
1553     JS_FreeValue(ctx, op1);
1554     JS_FreeValue(ctx, op2);
1555     JS_ThrowUnsupportedOperation(ctx);
1556     return -1;
1557 }
1558 
invalid_mul_pow10_to_float64(JSContext * ctx,const bf_t * a,int64_t exponent)1559 static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
1560                                             int64_t exponent)
1561 {
1562     return JS_ThrowUnsupportedOperation(ctx);
1563 }
1564 
invalid_mul_pow10(JSContext * ctx,JSValue * sp)1565 static int invalid_mul_pow10(JSContext *ctx, JSValue *sp)
1566 {
1567     JS_ThrowUnsupportedOperation(ctx);
1568     return -1;
1569 }
1570 
set_dummy_numeric_ops(JSNumericOperations * ops)1571 static void set_dummy_numeric_ops(JSNumericOperations *ops)
1572 {
1573     ops->to_string = invalid_to_string;
1574     ops->from_string = invalid_from_string;
1575     ops->unary_arith = invalid_unary_arith;
1576     ops->binary_arith = invalid_binary_arith;
1577     ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64;
1578     ops->mul_pow10 = invalid_mul_pow10;
1579 }
1580 
1581 #endif /* CONFIG_BIGNUM */
1582 
1583 #if !defined(CONFIG_STACK_CHECK)
1584 /* no stack limitation */
js_get_stack_pointer(void)1585 static inline uint8_t *js_get_stack_pointer(void)
1586 {
1587     return NULL;
1588 }
1589 
js_check_stack_overflow(JSRuntime * rt,size_t alloca_size)1590 static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
1591 {
1592     return FALSE;
1593 }
1594 #else
1595 /* Note: OS and CPU dependent */
js_get_stack_pointer(void)1596 static inline uint8_t *js_get_stack_pointer(void)
1597 {
1598     return __builtin_frame_address(0);
1599 }
1600 
js_check_stack_overflow(JSRuntime * rt,size_t alloca_size)1601 static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
1602 {
1603     size_t size;
1604     size = rt->stack_top - js_get_stack_pointer();
1605     return unlikely((size + alloca_size) > rt->stack_size);
1606 }
1607 #endif
1608 
JS_NewRuntime2(const JSMallocFunctions * mf,void * opaque)1609 JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
1610 {
1611     JSRuntime *rt;
1612     JSMallocState ms;
1613 
1614     memset(&ms, 0, sizeof(ms));
1615     ms.opaque = opaque;
1616     ms.malloc_limit = -1;
1617 
1618     rt = mf->js_malloc(&ms, sizeof(JSRuntime));
1619     if (!rt)
1620         return NULL;
1621     memset(rt, 0, sizeof(*rt));
1622     rt->mf = *mf;
1623     if (!rt->mf.js_malloc_usable_size) {
1624         /* use dummy function if none provided */
1625         rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;
1626     }
1627     rt->malloc_state = ms;
1628     rt->malloc_gc_threshold = 256 * 1024;
1629 
1630 #ifdef CONFIG_BIGNUM
1631     bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);
1632     set_dummy_numeric_ops(&rt->bigint_ops);
1633     set_dummy_numeric_ops(&rt->bigfloat_ops);
1634     set_dummy_numeric_ops(&rt->bigdecimal_ops);
1635 #endif
1636 
1637     init_list_head(&rt->context_list);
1638     init_list_head(&rt->gc_obj_list);
1639     init_list_head(&rt->gc_zero_ref_count_list);
1640     rt->gc_phase = JS_GC_PHASE_NONE;
1641 
1642 #ifdef DUMP_LEAKS
1643     init_list_head(&rt->string_list);
1644 #endif
1645     init_list_head(&rt->job_list);
1646 
1647     if (JS_InitAtoms(rt))
1648         goto fail;
1649 
1650     /* create the object, array and function classes */
1651     if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT,
1652                          countof(js_std_class_def)) < 0)
1653         goto fail;
1654     rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods;
1655     rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods;
1656     rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods;
1657 
1658     rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;
1659     rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call;
1660     rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;
1661     rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call;
1662     if (init_shape_hash(rt))
1663         goto fail;
1664 
1665     rt->stack_top = js_get_stack_pointer();
1666     rt->stack_size = JS_DEFAULT_STACK_SIZE;
1667     rt->current_exception = JS_NULL;
1668 
1669     return rt;
1670  fail:
1671     JS_FreeRuntime(rt);
1672     return NULL;
1673 }
1674 
JS_GetRuntimeOpaque(JSRuntime * rt)1675 void *JS_GetRuntimeOpaque(JSRuntime *rt)
1676 {
1677     return rt->user_opaque;
1678 }
1679 
JS_SetRuntimeOpaque(JSRuntime * rt,void * opaque)1680 void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)
1681 {
1682     rt->user_opaque = opaque;
1683 }
1684 
1685 /* default memory allocation functions with memory limitation */
js_def_malloc_usable_size(void * ptr)1686 static inline size_t js_def_malloc_usable_size(void *ptr)
1687 {
1688 #if defined(__APPLE__)
1689     return malloc_size(ptr);
1690 #elif defined(_WIN32)
1691     return _msize(ptr);
1692 #elif defined(EMSCRIPTEN)
1693     return 0;
1694 #elif defined(__linux__)
1695     return malloc_usable_size(ptr);
1696 #else
1697     /* change this to `return 0;` if compilation fails */
1698     return malloc_usable_size(ptr);
1699 #endif
1700 }
1701 
js_def_malloc(JSMallocState * s,size_t size)1702 static void *js_def_malloc(JSMallocState *s, size_t size)
1703 {
1704     void *ptr;
1705 
1706     /* Do not allocate zero bytes: behavior is platform dependent */
1707     assert(size != 0);
1708 
1709     if (unlikely(s->malloc_size + size > s->malloc_limit))
1710         return NULL;
1711 
1712     ptr = malloc(size);
1713     if (!ptr)
1714         return NULL;
1715 
1716     s->malloc_count++;
1717     s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
1718     return ptr;
1719 }
1720 
js_def_free(JSMallocState * s,void * ptr)1721 static void js_def_free(JSMallocState *s, void *ptr)
1722 {
1723     if (!ptr)
1724         return;
1725 
1726     s->malloc_count--;
1727     s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
1728     free(ptr);
1729 }
1730 
js_def_realloc(JSMallocState * s,void * ptr,size_t size)1731 static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)
1732 {
1733     size_t old_size;
1734 
1735     if (!ptr) {
1736         if (size == 0)
1737             return NULL;
1738         return js_def_malloc(s, size);
1739     }
1740     old_size = js_def_malloc_usable_size(ptr);
1741     if (size == 0) {
1742         s->malloc_count--;
1743         s->malloc_size -= old_size + MALLOC_OVERHEAD;
1744         free(ptr);
1745         return NULL;
1746     }
1747     if (s->malloc_size + size - old_size > s->malloc_limit)
1748         return NULL;
1749 
1750     ptr = realloc(ptr, size);
1751     if (!ptr)
1752         return NULL;
1753 
1754     s->malloc_size += js_def_malloc_usable_size(ptr) - old_size;
1755     return ptr;
1756 }
1757 
1758 static const JSMallocFunctions def_malloc_funcs = {
1759     js_def_malloc,
1760     js_def_free,
1761     js_def_realloc,
1762 #if defined(__APPLE__)
1763     malloc_size,
1764 #elif defined(_WIN32)
1765     (size_t (*)(const void *))_msize,
1766 #elif defined(EMSCRIPTEN)
1767     NULL,
1768 #elif defined(__linux__)
1769     (size_t (*)(const void *))malloc_usable_size,
1770 #else
1771     /* change this to `NULL,` if compilation fails */
1772     malloc_usable_size,
1773 #endif
1774 };
1775 
JS_NewRuntime(void)1776 JSRuntime *JS_NewRuntime(void)
1777 {
1778     return JS_NewRuntime2(&def_malloc_funcs, NULL);
1779 }
1780 
JS_SetMemoryLimit(JSRuntime * rt,size_t limit)1781 void JS_SetMemoryLimit(JSRuntime *rt, size_t limit)
1782 {
1783     rt->malloc_state.malloc_limit = limit;
1784 }
1785 
1786 /* use -1 to disable automatic GC */
JS_SetGCThreshold(JSRuntime * rt,size_t gc_threshold)1787 void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold)
1788 {
1789     rt->malloc_gc_threshold = gc_threshold;
1790 }
1791 
1792 #define malloc(s) malloc_is_forbidden(s)
1793 #define free(p) free_is_forbidden(p)
1794 #define realloc(p,s) realloc_is_forbidden(p,s)
1795 
JS_SetInterruptHandler(JSRuntime * rt,JSInterruptHandler * cb,void * opaque)1796 void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque)
1797 {
1798     rt->interrupt_handler = cb;
1799     rt->interrupt_opaque = opaque;
1800 }
1801 
JS_SetCanBlock(JSRuntime * rt,BOOL can_block)1802 void JS_SetCanBlock(JSRuntime *rt, BOOL can_block)
1803 {
1804     rt->can_block = can_block;
1805 }
1806 
JS_SetSharedArrayBufferFunctions(JSRuntime * rt,const JSSharedArrayBufferFunctions * sf)1807 void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
1808                                       const JSSharedArrayBufferFunctions *sf)
1809 {
1810     rt->sab_funcs = *sf;
1811 }
1812 
1813 /* return 0 if OK, < 0 if exception */
JS_EnqueueJob(JSContext * ctx,JSJobFunc * job_func,int argc,JSValueConst * argv)1814 int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
1815                   int argc, JSValueConst *argv)
1816 {
1817     JSRuntime *rt = ctx->rt;
1818     JSJobEntry *e;
1819     int i;
1820 
1821     e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));
1822     if (!e)
1823         return -1;
1824     e->ctx = ctx;
1825     e->job_func = job_func;
1826     e->argc = argc;
1827     for(i = 0; i < argc; i++) {
1828         e->argv[i] = JS_DupValue(ctx, argv[i]);
1829     }
1830     list_add_tail(&e->link, &rt->job_list);
1831     return 0;
1832 }
1833 
JS_IsJobPending(JSRuntime * rt)1834 BOOL JS_IsJobPending(JSRuntime *rt)
1835 {
1836     return !list_empty(&rt->job_list);
1837 }
1838 
1839 /* return < 0 if exception, 0 if no job pending, 1 if a job was
1840    executed successfully. the context of the job is stored in '*pctx' */
JS_ExecutePendingJob(JSRuntime * rt,JSContext ** pctx)1841 int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)
1842 {
1843     JSContext *ctx;
1844     JSJobEntry *e;
1845     JSValue res;
1846     int i, ret;
1847 
1848     if (list_empty(&rt->job_list)) {
1849         *pctx = NULL;
1850         return 0;
1851     }
1852 
1853     /* get the first pending job and execute it */
1854     e = list_entry(rt->job_list.next, JSJobEntry, link);
1855     list_del(&e->link);
1856     ctx = e->ctx;
1857     res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv);
1858     for(i = 0; i < e->argc; i++)
1859         JS_FreeValue(ctx, e->argv[i]);
1860     if (JS_IsException(res))
1861         ret = -1;
1862     else
1863         ret = 1;
1864     JS_FreeValue(ctx, res);
1865     js_free(ctx, e);
1866     *pctx = ctx;
1867     return ret;
1868 }
1869 
JS_GetCurrentException(JSRuntime * rt)1870 JSValue JS_GetCurrentException(JSRuntime *rt)
1871 {
1872     return rt->current_exception;
1873 }
1874 
JS_GetStringFromObject(JSValue obj)1875 uint8_t *JS_GetStringFromObject(JSValue obj)
1876 {
1877     if (!JS_IsString(obj)) {
1878         return NULL;
1879     }
1880     JSString *str = str = JS_VALUE_GET_STRING(obj);
1881     return str->u.str8;
1882 }
1883 
atom_get_free(const JSAtomStruct * p)1884 static inline uint32_t atom_get_free(const JSAtomStruct *p)
1885 {
1886     return (uintptr_t)p >> 1;
1887 }
1888 
atom_is_free(const JSAtomStruct * p)1889 static inline BOOL atom_is_free(const JSAtomStruct *p)
1890 {
1891     return (uintptr_t)p & 1;
1892 }
1893 
atom_set_free(uint32_t v)1894 static inline JSAtomStruct *atom_set_free(uint32_t v)
1895 {
1896     return (JSAtomStruct *)(((uintptr_t)v << 1) | 1);
1897 }
1898 
1899 /* Note: the string contents are uninitialized */
js_alloc_string_rt(JSRuntime * rt,int max_len,int is_wide_char)1900 static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char)
1901 {
1902     JSString *str;
1903     str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char);
1904     if (unlikely(!str))
1905         return NULL;
1906     str->header.ref_count = 1;
1907     str->is_wide_char = is_wide_char;
1908     str->len = max_len;
1909     str->atom_type = 0;
1910     str->hash = 0;          /* optional but costless */
1911     str->hash_next = 0;     /* optional */
1912 #ifdef DUMP_LEAKS
1913     list_add_tail(&str->link, &rt->string_list);
1914 #endif
1915     return str;
1916 }
1917 
js_alloc_string(JSContext * ctx,int max_len,int is_wide_char)1918 static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)
1919 {
1920     JSString *p;
1921     p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char);
1922     if (unlikely(!p)) {
1923         JS_ThrowOutOfMemory(ctx);
1924         return NULL;
1925     }
1926     return p;
1927 }
1928 
1929 /* same as JS_FreeValueRT() but faster */
js_free_string(JSRuntime * rt,JSString * str)1930 static inline void js_free_string(JSRuntime *rt, JSString *str)
1931 {
1932     if (--str->header.ref_count <= 0) {
1933         if (str->atom_type) {
1934             JS_FreeAtomStruct(rt, str);
1935         } else {
1936 #ifdef DUMP_LEAKS
1937             list_del(&str->link);
1938 #endif
1939             js_free_rt(rt, str);
1940         }
1941     }
1942 }
1943 
JS_SetRuntimeInfo(JSRuntime * rt,const char * s)1944 void JS_SetRuntimeInfo(JSRuntime *rt, const char *s)
1945 {
1946     if (rt)
1947         rt->rt_info = s;
1948 }
1949 
JS_FreeRuntime(JSRuntime * rt)1950 void JS_FreeRuntime(JSRuntime *rt)
1951 {
1952     struct list_head *el, *el1;
1953     int i;
1954 
1955     JS_FreeValueRT(rt, rt->current_exception);
1956 
1957     list_for_each_safe(el, el1, &rt->job_list) {
1958         JSJobEntry *e = list_entry(el, JSJobEntry, link);
1959         for(i = 0; i < e->argc; i++)
1960             JS_FreeValueRT(rt, e->argv[i]);
1961         js_free_rt(rt, e);
1962     }
1963     init_list_head(&rt->job_list);
1964 
1965     JS_RunGC(rt);
1966 
1967 #ifdef DUMP_LEAKS
1968     /* leaking objects */
1969     {
1970         BOOL header_done;
1971         JSGCObjectHeader *p;
1972         int count;
1973 
1974         /* remove the internal refcounts to display only the object
1975            referenced externally */
1976         list_for_each(el, &rt->gc_obj_list) {
1977             p = list_entry(el, JSGCObjectHeader, link);
1978             p->mark = 0;
1979         }
1980         gc_decref(rt);
1981 
1982         header_done = FALSE;
1983         list_for_each(el, &rt->gc_obj_list) {
1984             p = list_entry(el, JSGCObjectHeader, link);
1985             if (p->ref_count != 0) {
1986                 if (!header_done) {
1987                     printf("Object leaks:\n");
1988                     JS_DumpObjectHeader(rt);
1989                     header_done = TRUE;
1990                 }
1991                 JS_DumpGCObject(rt, p);
1992             }
1993         }
1994 
1995         count = 0;
1996         list_for_each(el, &rt->gc_obj_list) {
1997             p = list_entry(el, JSGCObjectHeader, link);
1998             if (p->ref_count == 0) {
1999                 count++;
2000             }
2001         }
2002         if (count != 0)
2003             printf("Secondary object leaks: %d\n", count);
2004     }
2005 #endif
2006     assert(list_empty(&rt->gc_obj_list));
2007 
2008     /* free the classes */
2009     for(i = 0; i < rt->class_count; i++) {
2010         JSClass *cl = &rt->class_array[i];
2011         if (cl->class_id != 0) {
2012             JS_FreeAtomRT(rt, cl->class_name);
2013         }
2014     }
2015     js_free_rt(rt, rt->class_array);
2016 
2017 #ifdef CONFIG_BIGNUM
2018     bf_context_end(&rt->bf_ctx);
2019 #endif
2020 
2021 #ifdef DUMP_LEAKS
2022     /* only the atoms defined in JS_InitAtoms() should be left */
2023     {
2024         BOOL header_done = FALSE;
2025 
2026         for(i = 0; i < rt->atom_size; i++) {
2027             JSAtomStruct *p = rt->atom_array[i];
2028             if (!atom_is_free(p) /* && p->str*/) {
2029                 if (i >= JS_ATOM_END || p->header.ref_count != 1) {
2030                     if (!header_done) {
2031                         header_done = TRUE;
2032                         if (rt->rt_info) {
2033                             printf("%s:1: atom leakage:", rt->rt_info);
2034                         } else {
2035                             printf("Atom leaks:\n"
2036                                    "    %6s %6s %s\n",
2037                                    "ID", "REFCNT", "NAME");
2038                         }
2039                     }
2040                     if (rt->rt_info) {
2041                         printf(" ");
2042                     } else {
2043                         printf("    %6u %6u ", i, p->header.ref_count);
2044                     }
2045                     switch (p->atom_type) {
2046                     case JS_ATOM_TYPE_STRING:
2047                         JS_DumpString(rt, p);
2048                         break;
2049                     case JS_ATOM_TYPE_GLOBAL_SYMBOL:
2050                         printf("Symbol.for(");
2051                         JS_DumpString(rt, p);
2052                         printf(")");
2053                         break;
2054                     case JS_ATOM_TYPE_SYMBOL:
2055                         if (p->hash == JS_ATOM_HASH_SYMBOL) {
2056                             printf("Symbol(");
2057                             JS_DumpString(rt, p);
2058                             printf(")");
2059                         } else {
2060                             printf("Private(");
2061                             JS_DumpString(rt, p);
2062                             printf(")");
2063                         }
2064                         break;
2065                     }
2066                     if (rt->rt_info) {
2067                         printf(":%u", p->header.ref_count);
2068                     } else {
2069                         printf("\n");
2070                     }
2071                 }
2072             }
2073         }
2074         if (rt->rt_info && header_done)
2075             printf("\n");
2076     }
2077 #endif
2078 
2079     /* free the atoms */
2080     for(i = 0; i < rt->atom_size; i++) {
2081         JSAtomStruct *p = rt->atom_array[i];
2082         if (!atom_is_free(p)) {
2083 #ifdef DUMP_LEAKS
2084             list_del(&p->link);
2085 #endif
2086             js_free_rt(rt, p);
2087         }
2088     }
2089     js_free_rt(rt, rt->atom_array);
2090     js_free_rt(rt, rt->atom_hash);
2091     js_free_rt(rt, rt->shape_hash);
2092 #ifdef DUMP_LEAKS
2093     if (!list_empty(&rt->string_list)) {
2094         if (rt->rt_info) {
2095             printf("%s:1: string leakage:", rt->rt_info);
2096         } else {
2097             printf("String leaks:\n"
2098                    "    %6s %s\n",
2099                    "REFCNT", "VALUE");
2100         }
2101         list_for_each_safe(el, el1, &rt->string_list) {
2102             JSString *str = list_entry(el, JSString, link);
2103             if (rt->rt_info) {
2104                 printf(" ");
2105             } else {
2106                 printf("    %6u ", str->header.ref_count);
2107             }
2108             JS_DumpString(rt, str);
2109             if (rt->rt_info) {
2110                 printf(":%u", str->header.ref_count);
2111             } else {
2112                 printf("\n");
2113             }
2114             list_del(&str->link);
2115             js_free_rt(rt, str);
2116         }
2117         if (rt->rt_info)
2118             printf("\n");
2119     }
2120     {
2121         JSMallocState *s = &rt->malloc_state;
2122         if (s->malloc_count > 1) {
2123             if (rt->rt_info)
2124                 printf("%s:1: ", rt->rt_info);
2125             printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n",
2126                    (uint64_t)(s->malloc_size - sizeof(JSRuntime)),
2127                    (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]);
2128         }
2129     }
2130 #endif
2131 
2132     {
2133         JSMallocState ms = rt->malloc_state;
2134         rt->mf.js_free(&ms, rt);
2135     }
2136 }
2137 
JS_NewContextRaw(JSRuntime * rt)2138 JSContext *JS_NewContextRaw(JSRuntime *rt)
2139 {
2140     JSContext *ctx;
2141     int i;
2142 
2143     ctx = js_mallocz_rt(rt, sizeof(JSContext));
2144     if (!ctx)
2145         return NULL;
2146     ctx->header.ref_count = 1;
2147     add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT);
2148 
2149 #ifdef ENABLE_JS_DEBUG
2150     ctx->debugger_info = js_malloc_rt(rt, sizeof(DebuggerInfo));
2151     if (!ctx->debugger_info) {
2152         DEBUGGER_LOGE("Create debugger_info failed!");
2153         return NULL;
2154     }
2155 #endif
2156 
2157     ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) *
2158                                     rt->class_count);
2159 
2160     if (!ctx->class_proto) {
2161         js_free_rt(rt, ctx);
2162         return NULL;
2163     }
2164     ctx->rt = rt;
2165     list_add_tail(&ctx->link, &rt->context_list);
2166 #ifdef CONFIG_BIGNUM
2167     ctx->bf_ctx = &rt->bf_ctx;
2168     ctx->fp_env.prec = 113;
2169     ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL;
2170 #endif
2171     for(i = 0; i < rt->class_count; i++)
2172         ctx->class_proto[i] = JS_NULL;
2173     ctx->array_ctor = JS_NULL;
2174     ctx->regexp_ctor = JS_NULL;
2175     ctx->promise_ctor = JS_NULL;
2176     init_list_head(&ctx->loaded_modules);
2177 
2178     JS_AddIntrinsicBasicObjects(ctx);
2179     return ctx;
2180 }
2181 
JS_NewContext(JSRuntime * rt)2182 JSContext *JS_NewContext(JSRuntime *rt)
2183 {
2184     JSContext *ctx;
2185 
2186     ctx = JS_NewContextRaw(rt);
2187     if (!ctx)
2188         return NULL;
2189 
2190     JS_AddIntrinsicBaseObjects(ctx);
2191     JS_AddIntrinsicDate(ctx);
2192     JS_AddIntrinsicEval(ctx);
2193     JS_AddIntrinsicStringNormalize(ctx);
2194     JS_AddIntrinsicRegExp(ctx);
2195     JS_AddIntrinsicJSON(ctx);
2196     JS_AddIntrinsicProxy(ctx);
2197     JS_AddIntrinsicMapSet(ctx);
2198     JS_AddIntrinsicTypedArrays(ctx);
2199     JS_AddIntrinsicPromise(ctx);
2200 #ifdef CONFIG_BIGNUM
2201     JS_AddIntrinsicBigInt(ctx);
2202 #endif
2203     return ctx;
2204 }
2205 
JS_GetContextOpaque(JSContext * ctx)2206 void *JS_GetContextOpaque(JSContext *ctx)
2207 {
2208     return ctx->user_opaque;
2209 }
2210 
JS_SetContextOpaque(JSContext * ctx,void * opaque)2211 void JS_SetContextOpaque(JSContext *ctx, void *opaque)
2212 {
2213     ctx->user_opaque = opaque;
2214 }
2215 
2216 /* set the new value and free the old value after (freeing the value
2217    can reallocate the object data) */
set_value(JSContext * ctx,JSValue * pval,JSValue new_val)2218 static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val)
2219 {
2220     JSValue old_val;
2221     old_val = *pval;
2222     *pval = new_val;
2223     JS_FreeValue(ctx, old_val);
2224 }
2225 
JS_SetClassProto(JSContext * ctx,JSClassID class_id,JSValue obj)2226 void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj)
2227 {
2228     JSRuntime *rt = ctx->rt;
2229     assert(class_id < rt->class_count);
2230     set_value(ctx, &ctx->class_proto[class_id], obj);
2231 }
2232 
JS_GetClassProto(JSContext * ctx,JSClassID class_id)2233 JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id)
2234 {
2235     JSRuntime *rt = ctx->rt;
2236     assert(class_id < rt->class_count);
2237     return JS_DupValue(ctx, ctx->class_proto[class_id]);
2238 }
2239 
2240 typedef enum JSFreeModuleEnum {
2241     JS_FREE_MODULE_ALL,
2242     JS_FREE_MODULE_NOT_RESOLVED,
2243     JS_FREE_MODULE_NOT_EVALUATED,
2244 } JSFreeModuleEnum;
2245 
2246 /* XXX: would be more efficient with separate module lists */
js_free_modules(JSContext * ctx,JSFreeModuleEnum flag)2247 static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)
2248 {
2249     struct list_head *el, *el1;
2250     list_for_each_safe(el, el1, &ctx->loaded_modules) {
2251         JSModuleDef *m = list_entry(el, JSModuleDef, link);
2252         if (flag == JS_FREE_MODULE_ALL ||
2253             (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) ||
2254             (flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) {
2255             js_free_module_def(ctx, m);
2256         }
2257     }
2258 }
2259 
JS_DupContext(JSContext * ctx)2260 JSContext *JS_DupContext(JSContext *ctx)
2261 {
2262     ctx->header.ref_count++;
2263     return ctx;
2264 }
2265 
2266 /* used by the GC */
JS_MarkContext(JSRuntime * rt,JSContext * ctx,JS_MarkFunc * mark_func)2267 static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,
2268                            JS_MarkFunc *mark_func)
2269 {
2270     int i;
2271     struct list_head *el;
2272 
2273     /* modules are not seen by the GC, so we directly mark the objects
2274        referenced by each module */
2275     list_for_each(el, &ctx->loaded_modules) {
2276         JSModuleDef *m = list_entry(el, JSModuleDef, link);
2277         js_mark_module_def(rt, m, mark_func);
2278     }
2279 
2280     JS_MarkValue(rt, ctx->global_obj, mark_func);
2281     JS_MarkValue(rt, ctx->global_var_obj, mark_func);
2282 
2283     JS_MarkValue(rt, ctx->throw_type_error, mark_func);
2284     JS_MarkValue(rt, ctx->eval_obj, mark_func);
2285 
2286     JS_MarkValue(rt, ctx->array_proto_values, mark_func);
2287     for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
2288         JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);
2289     }
2290     for(i = 0; i < rt->class_count; i++) {
2291         JS_MarkValue(rt, ctx->class_proto[i], mark_func);
2292     }
2293     JS_MarkValue(rt, ctx->iterator_proto, mark_func);
2294     JS_MarkValue(rt, ctx->async_iterator_proto, mark_func);
2295     JS_MarkValue(rt, ctx->promise_ctor, mark_func);
2296     JS_MarkValue(rt, ctx->array_ctor, mark_func);
2297     JS_MarkValue(rt, ctx->regexp_ctor, mark_func);
2298     JS_MarkValue(rt, ctx->function_ctor, mark_func);
2299     JS_MarkValue(rt, ctx->function_proto, mark_func);
2300 
2301     if (ctx->array_shape)
2302         mark_func(rt, &ctx->array_shape->header);
2303 }
2304 
JS_FreeContext(JSContext * ctx)2305 void JS_FreeContext(JSContext *ctx)
2306 {
2307     JSRuntime *rt = ctx->rt;
2308     int i;
2309 
2310     if (--ctx->header.ref_count > 0)
2311         return;
2312     assert(ctx->header.ref_count == 0);
2313 
2314 #ifdef DUMP_ATOMS
2315     JS_DumpAtoms(ctx->rt);
2316 #endif
2317 #ifdef DUMP_SHAPES
2318     JS_DumpShapes(ctx->rt);
2319 #endif
2320 #ifdef DUMP_OBJECTS
2321     {
2322         struct list_head *el;
2323         JSGCObjectHeader *p;
2324         printf("JSObjects: {\n");
2325         JS_DumpObjectHeader(ctx->rt);
2326         list_for_each(el, &rt->gc_obj_list) {
2327             p = list_entry(el, JSGCObjectHeader, link);
2328             JS_DumpGCObject(rt, p);
2329         }
2330         printf("}\n");
2331     }
2332 #endif
2333 #ifdef DUMP_MEM
2334     {
2335         JSMemoryUsage stats;
2336         JS_ComputeMemoryUsage(rt, &stats);
2337         JS_DumpMemoryUsage(stdout, &stats, rt);
2338     }
2339 #endif
2340 
2341     js_free_modules(ctx, JS_FREE_MODULE_ALL);
2342 
2343     JS_FreeValue(ctx, ctx->global_obj);
2344     JS_FreeValue(ctx, ctx->global_var_obj);
2345 
2346     JS_FreeValue(ctx, ctx->throw_type_error);
2347     JS_FreeValue(ctx, ctx->eval_obj);
2348 
2349     JS_FreeValue(ctx, ctx->array_proto_values);
2350     for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
2351         JS_FreeValue(ctx, ctx->native_error_proto[i]);
2352     }
2353     for(i = 0; i < rt->class_count; i++) {
2354         JS_FreeValue(ctx, ctx->class_proto[i]);
2355     }
2356     js_free_rt(rt, ctx->class_proto);
2357     JS_FreeValue(ctx, ctx->iterator_proto);
2358     JS_FreeValue(ctx, ctx->async_iterator_proto);
2359     JS_FreeValue(ctx, ctx->promise_ctor);
2360     JS_FreeValue(ctx, ctx->array_ctor);
2361     JS_FreeValue(ctx, ctx->regexp_ctor);
2362     JS_FreeValue(ctx, ctx->function_ctor);
2363     JS_FreeValue(ctx, ctx->function_proto);
2364 
2365     js_free_shape_null(ctx->rt, ctx->array_shape);
2366 
2367     list_del(&ctx->link);
2368     remove_gc_object(&ctx->header);
2369     js_free_rt(ctx->rt, ctx);
2370 }
2371 
JS_GetRuntime(JSContext * ctx)2372 JSRuntime *JS_GetRuntime(JSContext *ctx)
2373 {
2374     return ctx->rt;
2375 }
2376 
JS_SetMaxStackSize(JSContext * ctx,size_t stack_size)2377 void JS_SetMaxStackSize(JSContext *ctx, size_t stack_size)
2378 {
2379     ctx->rt->stack_size = stack_size;
2380 }
2381 
is_strict_mode(JSContext * ctx)2382 static inline BOOL is_strict_mode(JSContext *ctx)
2383 {
2384     JSStackFrame *sf = ctx->rt->current_stack_frame;
2385     return (sf && (sf->js_mode & JS_MODE_STRICT));
2386 }
2387 
2388 #ifdef CONFIG_BIGNUM
is_math_mode(JSContext * ctx)2389 static inline BOOL is_math_mode(JSContext *ctx)
2390 {
2391     JSStackFrame *sf = ctx->rt->current_stack_frame;
2392     return (sf && (sf->js_mode & JS_MODE_MATH));
2393 }
2394 #endif
2395 
2396 /* JSAtom support */
2397 
2398 #define JS_ATOM_TAG_INT (1U << 31)
2399 #define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1)
2400 #define JS_ATOM_MAX     ((1U << 30) - 1)
2401 
2402 /* return the max count from the hash size */
2403 #define JS_ATOM_COUNT_RESIZE(n) ((n) * 2)
2404 
__JS_AtomIsConst(JSAtom v)2405 static inline BOOL __JS_AtomIsConst(JSAtom v)
2406 {
2407 #if defined(DUMP_LEAKS) && DUMP_LEAKS > 1
2408         return (int32_t)v <= 0;
2409 #else
2410         return (int32_t)v < JS_ATOM_END;
2411 #endif
2412 }
2413 
__JS_AtomIsTaggedInt(JSAtom v)2414 static inline BOOL __JS_AtomIsTaggedInt(JSAtom v)
2415 {
2416     return (v & JS_ATOM_TAG_INT) != 0;
2417 }
2418 
__JS_AtomFromUInt32(uint32_t v)2419 static inline JSAtom __JS_AtomFromUInt32(uint32_t v)
2420 {
2421     return v | JS_ATOM_TAG_INT;
2422 }
2423 
__JS_AtomToUInt32(JSAtom atom)2424 static inline uint32_t __JS_AtomToUInt32(JSAtom atom)
2425 {
2426     return atom & ~JS_ATOM_TAG_INT;
2427 }
2428 
is_num(int c)2429 static inline int is_num(int c)
2430 {
2431     return c >= '0' && c <= '9';
2432 }
2433 
2434 /* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */
is_num_string(uint32_t * pval,const JSString * p)2435 static inline BOOL is_num_string(uint32_t *pval, const JSString *p)
2436 {
2437     uint32_t n;
2438     uint64_t n64;
2439     int c, i, len;
2440 
2441     len = p->len;
2442     if (len == 0 || len > 10)
2443         return FALSE;
2444     if (p->is_wide_char)
2445         c = p->u.str16[0];
2446     else
2447         c = p->u.str8[0];
2448     if (is_num(c)) {
2449         if (c == '0') {
2450             if (len != 1)
2451                 return FALSE;
2452             n = 0;
2453         } else {
2454             n = c - '0';
2455             for(i = 1; i < len; i++) {
2456                 if (p->is_wide_char)
2457                     c = p->u.str16[i];
2458                 else
2459                     c = p->u.str8[i];
2460                 if (!is_num(c))
2461                     return FALSE;
2462                 n64 = (uint64_t)n * 10 + (c - '0');
2463                 if ((n64 >> 32) != 0)
2464                     return FALSE;
2465                 n = n64;
2466             }
2467         }
2468         *pval = n;
2469         return TRUE;
2470     } else {
2471         return FALSE;
2472     }
2473 }
2474 
2475 /* XXX: could use faster version ? */
hash_string8(const uint8_t * str,size_t len,uint32_t h)2476 static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h)
2477 {
2478     size_t i;
2479 
2480     for(i = 0; i < len; i++)
2481         h = h * 263 + str[i];
2482     return h;
2483 }
2484 
hash_string16(const uint16_t * str,size_t len,uint32_t h)2485 static inline uint32_t hash_string16(const uint16_t *str,
2486                                      size_t len, uint32_t h)
2487 {
2488     size_t i;
2489 
2490     for(i = 0; i < len; i++)
2491         h = h * 263 + str[i];
2492     return h;
2493 }
2494 
hash_string(const JSString * str,uint32_t h)2495 static uint32_t hash_string(const JSString *str, uint32_t h)
2496 {
2497     if (str->is_wide_char)
2498         h = hash_string16(str->u.str16, str->len, h);
2499     else
2500         h = hash_string8(str->u.str8, str->len, h);
2501     return h;
2502 }
2503 
JS_DumpString(JSRuntime * rt,const JSString * p)2504 static __maybe_unused void JS_DumpString(JSRuntime *rt,
2505                                                   const JSString *p)
2506 {
2507     int i, c, sep;
2508 
2509     if (p == NULL) {
2510         printf("<null>");
2511         return;
2512     }
2513     printf("%d", p->header.ref_count);
2514     sep = (p->header.ref_count == 1) ? '\"' : '\'';
2515     putchar(sep);
2516     for(i = 0; i < p->len; i++) {
2517         if (p->is_wide_char)
2518             c = p->u.str16[i];
2519         else
2520             c = p->u.str8[i];
2521         if (c == sep || c == '\\') {
2522             putchar('\\');
2523             putchar(c);
2524         } else if (c >= ' ' && c <= 126) {
2525             putchar(c);
2526         } else if (c == '\n') {
2527             putchar('\\');
2528             putchar('n');
2529         } else {
2530             printf("\\u%04x", c);
2531         }
2532     }
2533     putchar(sep);
2534 }
2535 
JS_DumpAtoms(JSRuntime * rt)2536 static __maybe_unused void JS_DumpAtoms(JSRuntime *rt)
2537 {
2538     JSAtomStruct *p;
2539     int h, i;
2540     /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */
2541     printf("JSAtom count=%d size=%d hash_size=%d:\n",
2542            rt->atom_count, rt->atom_size, rt->atom_hash_size);
2543     printf("JSAtom hash table: {\n");
2544     for(i = 0; i < rt->atom_hash_size; i++) {
2545         h = rt->atom_hash[i];
2546         if (h) {
2547             printf("  %d:", i);
2548             while (h) {
2549                 p = rt->atom_array[h];
2550                 printf(" ");
2551                 JS_DumpString(rt, p);
2552                 h = p->hash_next;
2553             }
2554             printf("\n");
2555         }
2556     }
2557     printf("}\n");
2558     printf("JSAtom table: {\n");
2559     for(i = 0; i < rt->atom_size; i++) {
2560         p = rt->atom_array[i];
2561         if (!atom_is_free(p)) {
2562             printf("  %d: { %d %08x ", i, p->atom_type, p->hash);
2563             if (!(p->len == 0 && p->is_wide_char != 0))
2564                 JS_DumpString(rt, p);
2565             printf(" %d }\n", p->hash_next);
2566         }
2567     }
2568     printf("}\n");
2569 }
2570 
JS_ResizeAtomHash(JSRuntime * rt,int new_hash_size)2571 static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size)
2572 {
2573     JSAtomStruct *p;
2574     uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash;
2575 
2576     assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */
2577     new_hash_mask = new_hash_size - 1;
2578     new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size);
2579     if (!new_hash)
2580         return -1;
2581     for(i = 0; i < rt->atom_hash_size; i++) {
2582         h = rt->atom_hash[i];
2583         while (h != 0) {
2584             p = rt->atom_array[h];
2585             hash_next1 = p->hash_next;
2586             /* add in new hash table */
2587             j = p->hash & new_hash_mask;
2588             p->hash_next = new_hash[j];
2589             new_hash[j] = h;
2590             h = hash_next1;
2591         }
2592     }
2593     js_free_rt(rt, rt->atom_hash);
2594     rt->atom_hash = new_hash;
2595     rt->atom_hash_size = new_hash_size;
2596     rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size);
2597     //    JS_DumpAtoms(rt);
2598     return 0;
2599 }
2600 
JS_InitAtoms(JSRuntime * rt)2601 static int JS_InitAtoms(JSRuntime *rt)
2602 {
2603     int i, len, atom_type;
2604     const char *p;
2605 
2606     rt->atom_hash_size = 0;
2607     rt->atom_hash = NULL;
2608     rt->atom_count = 0;
2609     rt->atom_size = 0;
2610     rt->atom_free_index = 0;
2611     if (JS_ResizeAtomHash(rt, 256))     /* there are at least 195 predefined atoms */
2612         return -1;
2613 
2614     p = js_atom_init;
2615     for(i = 1; i < JS_ATOM_END; i++) {
2616         if (i == JS_ATOM_Private_brand)
2617             atom_type = JS_ATOM_TYPE_PRIVATE;
2618         else if (i >= JS_ATOM_Symbol_toPrimitive)
2619             atom_type = JS_ATOM_TYPE_SYMBOL;
2620         else
2621             atom_type = JS_ATOM_TYPE_STRING;
2622         len = strlen(p);
2623         if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL)
2624             return -1;
2625         p = p + len + 1;
2626     }
2627     return 0;
2628 }
2629 
JS_DupAtomRT(JSRuntime * rt,JSAtom v)2630 static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)
2631 {
2632     JSAtomStruct *p;
2633 
2634     if (!__JS_AtomIsConst(v)) {
2635         p = rt->atom_array[v];
2636         p->header.ref_count++;
2637     }
2638     return v;
2639 }
2640 
JS_DupAtom(JSContext * ctx,JSAtom v)2641 JSAtom JS_DupAtom(JSContext *ctx, JSAtom v)
2642 {
2643     JSRuntime *rt;
2644     JSAtomStruct *p;
2645 
2646     if (!__JS_AtomIsConst(v)) {
2647         rt = ctx->rt;
2648         p = rt->atom_array[v];
2649         p->header.ref_count++;
2650     }
2651     return v;
2652 }
2653 
JS_AtomGetKind(JSContext * ctx,JSAtom v)2654 static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v)
2655 {
2656     JSRuntime *rt;
2657     JSAtomStruct *p;
2658 
2659     rt = ctx->rt;
2660     if (__JS_AtomIsTaggedInt(v))
2661         return JS_ATOM_KIND_STRING;
2662     p = rt->atom_array[v];
2663     switch(p->atom_type) {
2664     case JS_ATOM_TYPE_STRING:
2665         return JS_ATOM_KIND_STRING;
2666     case JS_ATOM_TYPE_GLOBAL_SYMBOL:
2667         return JS_ATOM_KIND_SYMBOL;
2668     case JS_ATOM_TYPE_SYMBOL:
2669         switch(p->hash) {
2670         case JS_ATOM_HASH_SYMBOL:
2671             return JS_ATOM_KIND_SYMBOL;
2672         case JS_ATOM_HASH_PRIVATE:
2673             return JS_ATOM_KIND_PRIVATE;
2674         default:
2675             abort();
2676         }
2677     default:
2678         abort();
2679     }
2680 }
2681 
JS_AtomIsString(JSContext * ctx,JSAtom v)2682 static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v)
2683 {
2684     return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING;
2685 }
2686 
js_get_atom_index(JSRuntime * rt,JSAtomStruct * p)2687 static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)
2688 {
2689     uint32_t i = p->hash_next;  /* atom_index */
2690     if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
2691         JSAtomStruct *p1;
2692 
2693         i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)];
2694         p1 = rt->atom_array[i];
2695         while (p1 != p) {
2696             assert(i != 0);
2697             i = p1->hash_next;
2698             p1 = rt->atom_array[i];
2699         }
2700     }
2701     return i;
2702 }
2703 
2704 /* string case (internal). Return JS_ATOM_NULL if error. 'str' is
2705    freed. */
__JS_NewAtom(JSRuntime * rt,JSString * str,int atom_type)2706 static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
2707 {
2708     uint32_t h, h1, i;
2709     JSAtomStruct *p;
2710     int len;
2711 
2712 #if 0
2713     printf("__JS_NewAtom: ");  JS_DumpString(rt, str); printf("\n");
2714 #endif
2715     if (atom_type < JS_ATOM_TYPE_SYMBOL) {
2716         /* str is not NULL */
2717         if (str->atom_type == atom_type) {
2718             /* str is the atom, return its index */
2719             i = js_get_atom_index(rt, str);
2720             /* reduce string refcount and increase atom's unless constant */
2721             if (__JS_AtomIsConst(i))
2722                 str->header.ref_count--;
2723             return i;
2724         }
2725         /* try and locate an already registered atom */
2726         len = str->len;
2727         h = hash_string(str, atom_type);
2728         h &= JS_ATOM_HASH_MASK;
2729         h1 = h & (rt->atom_hash_size - 1);
2730         i = rt->atom_hash[h1];
2731         while (i != 0) {
2732             p = rt->atom_array[i];
2733             if (p->hash == h &&
2734                 p->atom_type == atom_type &&
2735                 p->len == len &&
2736                 js_string_memcmp(p, str, len) == 0) {
2737                 if (!__JS_AtomIsConst(i))
2738                     p->header.ref_count++;
2739                 goto done;
2740             }
2741             i = p->hash_next;
2742         }
2743     } else {
2744         h1 = 0; /* avoid warning */
2745         if (atom_type == JS_ATOM_TYPE_SYMBOL) {
2746             h = JS_ATOM_HASH_SYMBOL;
2747         } else {
2748             h = JS_ATOM_HASH_PRIVATE;
2749             atom_type = JS_ATOM_TYPE_SYMBOL;
2750         }
2751     }
2752 
2753     if (rt->atom_free_index == 0) {
2754         /* allow new atom entries */
2755         uint32_t new_size, start;
2756         JSAtomStruct **new_array;
2757 
2758         /* alloc new with size progression 3/2:
2759            4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092
2760            preallocating space for predefined atoms (at least 195).
2761          */
2762         new_size = max_int(211, rt->atom_size * 3 / 2);
2763         if (new_size > JS_ATOM_MAX)
2764             goto fail;
2765         /* XXX: should use realloc2 to use slack space */
2766         new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size);
2767         if (!new_array)
2768             goto fail;
2769         /* Note: the atom 0 is not used */
2770         start = rt->atom_size;
2771         if (start == 0) {
2772             /* JS_ATOM_NULL entry */
2773             p = js_mallocz_rt(rt, sizeof(JSAtomStruct));
2774             if (!p) {
2775                 js_free_rt(rt, new_array);
2776                 goto fail;
2777             }
2778             p->header.ref_count = 1;  /* not refcounted */
2779             p->atom_type = JS_ATOM_TYPE_SYMBOL;
2780 #ifdef DUMP_LEAKS
2781             list_add_tail(&p->link, &rt->string_list);
2782 #endif
2783             new_array[0] = p;
2784             rt->atom_count++;
2785             start = 1;
2786         }
2787         rt->atom_size = new_size;
2788         rt->atom_array = new_array;
2789         rt->atom_free_index = start;
2790         for(i = start; i < new_size; i++) {
2791             uint32_t next;
2792             if (i == (new_size - 1))
2793                 next = 0;
2794             else
2795                 next = i + 1;
2796             rt->atom_array[i] = atom_set_free(next);
2797         }
2798     }
2799 
2800     if (str) {
2801         if (str->atom_type == 0) {
2802             p = str;
2803             p->atom_type = atom_type;
2804         } else {
2805             p = js_malloc_rt(rt, sizeof(JSString) +
2806                              (str->len << str->is_wide_char) +
2807                              1 - str->is_wide_char);
2808             if (unlikely(!p))
2809                 goto fail;
2810             p->header.ref_count = 1;
2811             p->is_wide_char = str->is_wide_char;
2812             p->len = str->len;
2813 #ifdef DUMP_LEAKS
2814             list_add_tail(&p->link, &rt->string_list);
2815 #endif
2816             memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) +
2817                    1 - str->is_wide_char);
2818             js_free_string(rt, str);
2819         }
2820     } else {
2821         p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */
2822         if (!p)
2823             return JS_ATOM_NULL;
2824         p->header.ref_count = 1;
2825         p->is_wide_char = 1;    /* Hack to represent NULL as a JSString */
2826         p->len = 0;
2827 #ifdef DUMP_LEAKS
2828         list_add_tail(&p->link, &rt->string_list);
2829 #endif
2830     }
2831 
2832     /* use an already free entry */
2833     i = rt->atom_free_index;
2834     rt->atom_free_index = atom_get_free(rt->atom_array[i]);
2835     rt->atom_array[i] = p;
2836 
2837     p->hash = h;
2838     p->hash_next = i;   /* atom_index */
2839     p->atom_type = atom_type;
2840 
2841     rt->atom_count++;
2842 
2843     if (atom_type != JS_ATOM_TYPE_SYMBOL) {
2844         p->hash_next = rt->atom_hash[h1];
2845         rt->atom_hash[h1] = i;
2846         if (unlikely(rt->atom_count >= rt->atom_count_resize))
2847             JS_ResizeAtomHash(rt, rt->atom_hash_size * 2);
2848     }
2849 
2850     //    JS_DumpAtoms(rt);
2851     return i;
2852 
2853  fail:
2854     i = JS_ATOM_NULL;
2855  done:
2856     if (str)
2857         js_free_string(rt, str);
2858     return i;
2859 }
2860 
2861 /* only works with zero terminated 8 bit strings */
__JS_NewAtomInit(JSRuntime * rt,const char * str,int len,int atom_type)2862 static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
2863                                int atom_type)
2864 {
2865     JSString *p;
2866     p = js_alloc_string_rt(rt, len, 0);
2867     if (!p)
2868         return JS_ATOM_NULL;
2869     memcpy(p->u.str8, str, len);
2870     p->u.str8[len] = '\0';
2871     return __JS_NewAtom(rt, p, atom_type);
2872 }
2873 
__JS_FindAtom(JSRuntime * rt,const char * str,size_t len,int atom_type)2874 static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
2875                             int atom_type)
2876 {
2877     uint32_t h, h1, i;
2878     JSAtomStruct *p;
2879 
2880     h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING);
2881     h &= JS_ATOM_HASH_MASK;
2882     h1 = h & (rt->atom_hash_size - 1);
2883     i = rt->atom_hash[h1];
2884     while (i != 0) {
2885         p = rt->atom_array[i];
2886         if (p->hash == h &&
2887             p->atom_type == JS_ATOM_TYPE_STRING &&
2888             p->len == len &&
2889             p->is_wide_char == 0 &&
2890             memcmp(p->u.str8, str, len) == 0) {
2891             if (!__JS_AtomIsConst(i))
2892                 p->header.ref_count++;
2893             return i;
2894         }
2895         i = p->hash_next;
2896     }
2897     return JS_ATOM_NULL;
2898 }
2899 
JS_FreeAtomStruct(JSRuntime * rt,JSAtomStruct * p)2900 static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)
2901 {
2902 #if 0   /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */
2903     if (unlikely(i == JS_ATOM_NULL)) {
2904         p->header.ref_count = INT32_MAX / 2;
2905         return;
2906     }
2907 #endif
2908     uint32_t i = p->hash_next;  /* atom_index */
2909     if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
2910         JSAtomStruct *p0, *p1;
2911         uint32_t h0;
2912 
2913         h0 = p->hash & (rt->atom_hash_size - 1);
2914         i = rt->atom_hash[h0];
2915         p1 = rt->atom_array[i];
2916         if (p1 == p) {
2917             rt->atom_hash[h0] = p1->hash_next;
2918         } else {
2919             for(;;) {
2920                 assert(i != 0);
2921                 p0 = p1;
2922                 i = p1->hash_next;
2923                 p1 = rt->atom_array[i];
2924                 if (p1 == p) {
2925                     p0->hash_next = p1->hash_next;
2926                     break;
2927                 }
2928             }
2929         }
2930     }
2931     /* insert in free atom list */
2932     rt->atom_array[i] = atom_set_free(rt->atom_free_index);
2933     rt->atom_free_index = i;
2934     /* free the string structure */
2935 #ifdef DUMP_LEAKS
2936     list_del(&p->link);
2937 #endif
2938     js_free_rt(rt, p);
2939     rt->atom_count--;
2940     assert(rt->atom_count >= 0);
2941 }
2942 
__JS_FreeAtom(JSRuntime * rt,uint32_t i)2943 static void __JS_FreeAtom(JSRuntime *rt, uint32_t i)
2944 {
2945     JSAtomStruct *p;
2946 
2947     p = rt->atom_array[i];
2948     if (--p->header.ref_count > 0)
2949         return;
2950     JS_FreeAtomStruct(rt, p);
2951 }
2952 
2953 /* Warning: 'p' is freed */
JS_NewAtomStr(JSContext * ctx,JSString * p)2954 static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)
2955 {
2956     JSRuntime *rt = ctx->rt;
2957     uint32_t n;
2958     if (is_num_string(&n, p)) {
2959         if (n <= JS_ATOM_MAX_INT) {
2960             js_free_string(rt, p);
2961             return __JS_AtomFromUInt32(n);
2962         }
2963     }
2964     /* XXX: should generate an exception */
2965     return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING);
2966 }
2967 
JS_NewAtomLen(JSContext * ctx,const char * str,size_t len)2968 JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
2969 {
2970     JSValue val;
2971 
2972     if (len == 0 || !is_digit(*str)) {
2973         JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
2974         if (atom)
2975             return atom;
2976     }
2977     val = JS_NewStringLen(ctx, str, len);
2978     if (JS_IsException(val))
2979         return JS_ATOM_NULL;
2980     return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val));
2981 }
2982 
JS_NewAtom(JSContext * ctx,const char * str)2983 JSAtom JS_NewAtom(JSContext *ctx, const char *str)
2984 {
2985     return JS_NewAtomLen(ctx, str, strlen(str));
2986 }
2987 
JS_NewAtomUInt32(JSContext * ctx,uint32_t n)2988 JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
2989 {
2990     if (n <= JS_ATOM_MAX_INT) {
2991         return __JS_AtomFromUInt32(n);
2992     } else {
2993         char buf[11];
2994         JSValue val;
2995         snprintf(buf, sizeof(buf), "%u", n);
2996         val = JS_NewString(ctx, buf);
2997         if (JS_IsException(val))
2998             return JS_ATOM_NULL;
2999         return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
3000                             JS_ATOM_TYPE_STRING);
3001     }
3002 }
3003 
JS_NewAtomInt64(JSContext * ctx,int64_t n)3004 static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)
3005 {
3006     if ((uint64_t)n <= JS_ATOM_MAX_INT) {
3007         return __JS_AtomFromUInt32((uint32_t)n);
3008     } else {
3009         char buf[24];
3010         JSValue val;
3011         snprintf(buf, sizeof(buf), "%" PRId64 , n);
3012         val = JS_NewString(ctx, buf);
3013         if (JS_IsException(val))
3014             return JS_ATOM_NULL;
3015         return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
3016                             JS_ATOM_TYPE_STRING);
3017     }
3018 }
3019 
3020 /* 'p' is freed */
JS_NewSymbol(JSContext * ctx,JSString * p,int atom_type)3021 static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type)
3022 {
3023     JSRuntime *rt = ctx->rt;
3024     JSAtom atom;
3025     atom = __JS_NewAtom(rt, p, atom_type);
3026     if (atom == JS_ATOM_NULL)
3027         return JS_ThrowOutOfMemory(ctx);
3028     return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]);
3029 }
3030 
3031 /* descr must be a non-numeric string atom */
JS_NewSymbolFromAtom(JSContext * ctx,JSAtom descr,int atom_type)3032 static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
3033                                     int atom_type)
3034 {
3035     JSRuntime *rt = ctx->rt;
3036     JSString *p;
3037 
3038     assert(!__JS_AtomIsTaggedInt(descr));
3039     assert(descr < rt->atom_size);
3040     p = rt->atom_array[descr];
3041     JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
3042     return JS_NewSymbol(ctx, p, atom_type);
3043 }
3044 
3045 #define ATOM_GET_STR_BUF_SIZE 64
3046 
3047 /* Should only be used for debug. */
JS_AtomGetStrRT(JSRuntime * rt,char * buf,int buf_size,JSAtom atom)3048 static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size,
3049                                    JSAtom atom)
3050 {
3051     if (__JS_AtomIsTaggedInt(atom)) {
3052         snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom));
3053     } else {
3054         JSAtomStruct *p;
3055         assert(atom < rt->atom_size);
3056         if (atom == JS_ATOM_NULL) {
3057             snprintf(buf, buf_size, "<null>");
3058         } else {
3059             int i, c;
3060             char *q;
3061             JSString *str;
3062 
3063             q = buf;
3064             p = rt->atom_array[atom];
3065             assert(!atom_is_free(p));
3066             str = p;
3067             if (str) {
3068                 if (!str->is_wide_char) {
3069                     /* special case ASCII strings */
3070                     c = 0;
3071                     for(i = 0; i < str->len; i++) {
3072                         c |= str->u.str8[i];
3073                     }
3074                     if (c < 0x80)
3075                         return (const char *)str->u.str8;
3076                 }
3077                 for(i = 0; i < str->len; i++) {
3078                     if (str->is_wide_char)
3079                         c = str->u.str16[i];
3080                     else
3081                         c = str->u.str8[i];
3082                     if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX)
3083                         break;
3084                     if (c < 128) {
3085                         *q++ = c;
3086                     } else {
3087                         q += unicode_to_utf8((uint8_t *)q, c);
3088                     }
3089                 }
3090             }
3091             *q = '\0';
3092         }
3093     }
3094     return buf;
3095 }
3096 
JS_AtomGetStr(JSContext * ctx,char * buf,int buf_size,JSAtom atom)3097 static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom)
3098 {
3099     return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);
3100 }
3101 
__JS_AtomToValue(JSContext * ctx,JSAtom atom,BOOL force_string)3102 static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)
3103 {
3104     char buf[ATOM_GET_STR_BUF_SIZE];
3105 
3106     if (__JS_AtomIsTaggedInt(atom)) {
3107         snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom));
3108         return JS_NewString(ctx, buf);
3109     } else {
3110         JSRuntime *rt = ctx->rt;
3111         JSAtomStruct *p;
3112         assert(atom < rt->atom_size);
3113         p = rt->atom_array[atom];
3114         if (p->atom_type == JS_ATOM_TYPE_STRING) {
3115             goto ret_string;
3116         } else if (force_string) {
3117             if (p->len == 0 && p->is_wide_char != 0) {
3118                 /* no description string */
3119                 p = rt->atom_array[JS_ATOM_empty_string];
3120             }
3121         ret_string:
3122             return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
3123         } else {
3124             return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p));
3125         }
3126     }
3127 }
3128 
JS_AtomToValue(JSContext * ctx,JSAtom atom)3129 JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)
3130 {
3131     return __JS_AtomToValue(ctx, atom, FALSE);
3132 }
3133 
JS_AtomToString(JSContext * ctx,JSAtom atom)3134 JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)
3135 {
3136     return __JS_AtomToValue(ctx, atom, TRUE);
3137 }
3138 
3139 /* return TRUE if the atom is an array index (i.e. 0 <= index <=
3140    2^32-2 and return its value */
JS_AtomIsArrayIndex(JSContext * ctx,uint32_t * pval,JSAtom atom)3141 static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)
3142 {
3143     if (__JS_AtomIsTaggedInt(atom)) {
3144         *pval = __JS_AtomToUInt32(atom);
3145         return TRUE;
3146     } else {
3147         JSRuntime *rt = ctx->rt;
3148         JSAtomStruct *p;
3149         uint32_t val;
3150 
3151         assert(atom < rt->atom_size);
3152         p = rt->atom_array[atom];
3153         if (p->atom_type == JS_ATOM_TYPE_STRING &&
3154             is_num_string(&val, p) && val != -1) {
3155             *pval = val;
3156             return TRUE;
3157         } else {
3158             *pval = 0;
3159             return FALSE;
3160         }
3161     }
3162 }
3163 
3164 /* This test must be fast if atom is not a numeric index (e.g. a
3165    method name). Return JS_UNDEFINED if not a numeric
3166    index. JS_EXCEPTION can also be returned. */
JS_AtomIsNumericIndex1(JSContext * ctx,JSAtom atom)3167 static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
3168 {
3169     JSRuntime *rt = ctx->rt;
3170     JSAtomStruct *p1;
3171     JSString *p;
3172     int c, len, ret;
3173     JSValue num, str;
3174 
3175     if (__JS_AtomIsTaggedInt(atom))
3176         return JS_NewInt32(ctx, __JS_AtomToUInt32(atom));
3177     assert(atom < rt->atom_size);
3178     p1 = rt->atom_array[atom];
3179     if (p1->atom_type != JS_ATOM_TYPE_STRING)
3180         return JS_UNDEFINED;
3181     p = p1;
3182     len = p->len;
3183     if (p->is_wide_char) {
3184         const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len;
3185         if (r >= r_end)
3186             return JS_UNDEFINED;
3187         c = *r;
3188         if (c == '-') {
3189             if (r >= r_end)
3190                 return JS_UNDEFINED;
3191             r++;
3192             c = *r;
3193             /* -0 case is specific */
3194             if (c == '0' && len == 2)
3195                 goto minus_zero;
3196         }
3197         /* XXX: should test NaN, but the tests do not check it */
3198         if (!is_num(c)) {
3199             /* XXX: String should be normalized, therefore 8-bit only */
3200             const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' };
3201             if (!(c =='I' && (r_end - r) == 8 &&
3202                   !memcmp(r + 1, nfinity16, sizeof(nfinity16))))
3203                 return JS_UNDEFINED;
3204         }
3205     } else {
3206         const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len;
3207         if (r >= r_end)
3208             return JS_UNDEFINED;
3209         c = *r;
3210         if (c == '-') {
3211             if (r >= r_end)
3212                 return JS_UNDEFINED;
3213             r++;
3214             c = *r;
3215             /* -0 case is specific */
3216             if (c == '0' && len == 2) {
3217             minus_zero:
3218                 return __JS_NewFloat64(ctx, -0.0);
3219             }
3220         }
3221         if (!is_num(c)) {
3222             if (!(c =='I' && (r_end - r) == 8 &&
3223                   !memcmp(r + 1, "nfinity", 7)))
3224                 return JS_UNDEFINED;
3225         }
3226     }
3227     /* XXX: bignum: would be better to only accept integer to avoid
3228        relying on current floating point precision */
3229     /* this is ECMA CanonicalNumericIndexString primitive */
3230     num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p));
3231     if (JS_IsException(num))
3232         return num;
3233     str = JS_ToString(ctx, num);
3234     if (JS_IsException(str)) {
3235         JS_FreeValue(ctx, num);
3236         return str;
3237     }
3238     ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str));
3239     JS_FreeValue(ctx, str);
3240     if (ret == 0) {
3241         return num;
3242     } else {
3243         JS_FreeValue(ctx, num);
3244         return JS_UNDEFINED;
3245     }
3246 }
3247 
3248 /* return -1 if exception or TRUE/FALSE */
JS_AtomIsNumericIndex(JSContext * ctx,JSAtom atom)3249 static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)
3250 {
3251     JSValue num;
3252     num = JS_AtomIsNumericIndex1(ctx, atom);
3253     if (likely(JS_IsUndefined(num)))
3254         return FALSE;
3255     if (JS_IsException(num))
3256         return -1;
3257     JS_FreeValue(ctx, num);
3258     return TRUE;
3259 }
3260 
JS_FreeAtom(JSContext * ctx,JSAtom v)3261 void JS_FreeAtom(JSContext *ctx, JSAtom v)
3262 {
3263     if (!__JS_AtomIsConst(v))
3264         __JS_FreeAtom(ctx->rt, v);
3265 }
3266 
JS_FreeAtomRT(JSRuntime * rt,JSAtom v)3267 void JS_FreeAtomRT(JSRuntime *rt, JSAtom v)
3268 {
3269     if (!__JS_AtomIsConst(v))
3270         __JS_FreeAtom(rt, v);
3271 }
3272 
3273 /* return TRUE if 'v' is a symbol with a string description */
JS_AtomSymbolHasDescription(JSContext * ctx,JSAtom v)3274 static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)
3275 {
3276     JSRuntime *rt;
3277     JSAtomStruct *p;
3278 
3279     rt = ctx->rt;
3280     if (__JS_AtomIsTaggedInt(v))
3281         return FALSE;
3282     p = rt->atom_array[v];
3283     return (((p->atom_type == JS_ATOM_TYPE_SYMBOL &&
3284               p->hash == JS_ATOM_HASH_SYMBOL) ||
3285              p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) &&
3286             !(p->len == 0 && p->is_wide_char != 0));
3287 }
3288 
print_atom(JSContext * ctx,JSAtom atom)3289 static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom)
3290 {
3291     char buf[ATOM_GET_STR_BUF_SIZE];
3292     const char *p;
3293     int i;
3294 
3295     /* XXX: should handle embedded null characters */
3296     /* XXX: should move encoding code to JS_AtomGetStr */
3297     p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom);
3298     for (i = 0; p[i]; i++) {
3299         int c = (unsigned char)p[i];
3300         if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
3301               (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))
3302             break;
3303     }
3304     if (i > 0 && p[i] == '\0') {
3305         printf("%s", p);
3306     } else {
3307         putchar('"');
3308         printf("%.*s", i, p);
3309         for (; p[i]; i++) {
3310             int c = (unsigned char)p[i];
3311             if (c == '\"' || c == '\\') {
3312                 putchar('\\');
3313                 putchar(c);
3314             } else if (c >= ' ' && c <= 126) {
3315                 putchar(c);
3316             } else if (c == '\n') {
3317                 putchar('\\');
3318                 putchar('n');
3319             } else {
3320                 printf("\\u%04x", c);
3321             }
3322         }
3323         putchar('\"');
3324     }
3325 }
3326 
3327 /* free with JS_FreeCString() */
JS_AtomToCString(JSContext * ctx,JSAtom atom)3328 const char *JS_AtomToCString(JSContext *ctx, JSAtom atom)
3329 {
3330     JSValue str;
3331     const char *cstr;
3332 
3333     str = JS_AtomToString(ctx, atom);
3334     if (JS_IsException(str))
3335         return NULL;
3336     cstr = JS_ToCString(ctx, str);
3337     JS_FreeValue(ctx, str);
3338     return cstr;
3339 }
3340 
3341 /* return a string atom containing name concatenated with str1 */
js_atom_concat_str(JSContext * ctx,JSAtom name,const char * str1)3342 static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1)
3343 {
3344     JSValue str;
3345     JSAtom atom;
3346     const char *cstr;
3347     char *cstr2;
3348     size_t len, len1;
3349 
3350     str = JS_AtomToString(ctx, name);
3351     if (JS_IsException(str))
3352         return JS_ATOM_NULL;
3353     cstr = JS_ToCStringLen(ctx, &len, str);
3354     if (!cstr)
3355         goto fail;
3356     len1 = strlen(str1);
3357     cstr2 = js_malloc(ctx, len + len1 + 1);
3358     if (!cstr2)
3359         goto fail;
3360     memcpy(cstr2, cstr, len);
3361     memcpy(cstr2 + len, str1, len1);
3362     cstr2[len + len1] = '\0';
3363     atom = JS_NewAtomLen(ctx, cstr2, len + len1);
3364     js_free(ctx, cstr2);
3365     JS_FreeCString(ctx, cstr);
3366     JS_FreeValue(ctx, str);
3367     return atom;
3368  fail:
3369     JS_FreeCString(ctx, cstr);
3370     JS_FreeValue(ctx, str);
3371     return JS_ATOM_NULL;
3372 }
3373 
js_atom_concat_num(JSContext * ctx,JSAtom name,uint32_t n)3374 static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n)
3375 {
3376     char buf[16];
3377     snprintf(buf, sizeof(buf), "%u", n);
3378     return js_atom_concat_str(ctx, name, buf);
3379 }
3380 
JS_IsEmptyString(JSValueConst v)3381 static inline BOOL JS_IsEmptyString(JSValueConst v)
3382 {
3383     return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0;
3384 }
3385 
3386 /* JSClass support */
3387 
3388 /* a new class ID is allocated if *pclass_id != 0 */
JS_NewClassID(JSClassID * pclass_id)3389 JSClassID JS_NewClassID(JSClassID *pclass_id)
3390 {
3391     JSClassID class_id;
3392     /* XXX: make it thread safe */
3393     class_id = *pclass_id;
3394     if (class_id == 0) {
3395         class_id = js_class_id_alloc++;
3396         *pclass_id = class_id;
3397     }
3398     return class_id;
3399 }
3400 
JS_IsRegisteredClass(JSRuntime * rt,JSClassID class_id)3401 BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id)
3402 {
3403     return (class_id < rt->class_count &&
3404             rt->class_array[class_id].class_id != 0);
3405 }
3406 
3407 /* create a new object internal class. Return -1 if error, 0 if
3408    OK. The finalizer can be NULL if none is needed. */
JS_NewClass1(JSRuntime * rt,JSClassID class_id,const JSClassDef * class_def,JSAtom name)3409 static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
3410                         const JSClassDef *class_def, JSAtom name)
3411 {
3412     int new_size, i;
3413     JSClass *cl, *new_class_array;
3414     struct list_head *el;
3415 
3416     if (class_id < rt->class_count &&
3417         rt->class_array[class_id].class_id != 0)
3418         return -1;
3419 
3420     if (class_id >= rt->class_count) {
3421         new_size = max_int(JS_CLASS_INIT_COUNT,
3422                            max_int(class_id + 1, rt->class_count * 3 / 2));
3423 
3424         /* reallocate the context class prototype array, if any */
3425         list_for_each(el, &rt->context_list) {
3426             JSContext *ctx = list_entry(el, JSContext, link);
3427             JSValue *new_tab;
3428             new_tab = js_realloc_rt(rt, ctx->class_proto,
3429                                     sizeof(ctx->class_proto[0]) * new_size);
3430             if (!new_tab)
3431                 return -1;
3432             for(i = rt->class_count; i < new_size; i++)
3433                 new_tab[i] = JS_NULL;
3434             ctx->class_proto = new_tab;
3435         }
3436         /* reallocate the class array */
3437         new_class_array = js_realloc_rt(rt, rt->class_array,
3438                                         sizeof(JSClass) * new_size);
3439         if (!new_class_array)
3440             return -1;
3441         memset(new_class_array + rt->class_count, 0,
3442                (new_size - rt->class_count) * sizeof(JSClass));
3443         rt->class_array = new_class_array;
3444         rt->class_count = new_size;
3445     }
3446     cl = &rt->class_array[class_id];
3447     cl->class_id = class_id;
3448     cl->class_name = JS_DupAtomRT(rt, name);
3449     cl->finalizer = class_def->finalizer;
3450     cl->gc_mark = class_def->gc_mark;
3451     cl->call = class_def->call;
3452     cl->exotic = class_def->exotic;
3453     return 0;
3454 }
3455 
JS_NewClass(JSRuntime * rt,JSClassID class_id,const JSClassDef * class_def)3456 int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def)
3457 {
3458     int ret, len;
3459     JSAtom name;
3460 
3461     len = strlen(class_def->class_name);
3462     name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
3463     if (name == JS_ATOM_NULL) {
3464         name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
3465         if (name == JS_ATOM_NULL)
3466             return -1;
3467     }
3468     ret = JS_NewClass1(rt, class_id, class_def, name);
3469     JS_FreeAtomRT(rt, name);
3470     return ret;
3471 }
3472 
js_new_string8(JSContext * ctx,const uint8_t * buf,int len)3473 static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len)
3474 {
3475     JSString *str;
3476 
3477     if (len <= 0) {
3478         return JS_AtomToString(ctx, JS_ATOM_empty_string);
3479     }
3480     str = js_alloc_string(ctx, len, 0);
3481     if (!str)
3482         return JS_EXCEPTION;
3483     memcpy(str->u.str8, buf, len);
3484     str->u.str8[len] = '\0';
3485     return JS_MKPTR(JS_TAG_STRING, str);
3486 }
3487 
js_new_string16(JSContext * ctx,const uint16_t * buf,int len)3488 static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len)
3489 {
3490     JSString *str;
3491     str = js_alloc_string(ctx, len, 1);
3492     if (!str)
3493         return JS_EXCEPTION;
3494     memcpy(str->u.str16, buf, len * 2);
3495     return JS_MKPTR(JS_TAG_STRING, str);
3496 }
3497 
js_new_string_char(JSContext * ctx,uint16_t c)3498 static JSValue js_new_string_char(JSContext *ctx, uint16_t c)
3499 {
3500     if (c < 0x100) {
3501         uint8_t ch8 = c;
3502         return js_new_string8(ctx, &ch8, 1);
3503     } else {
3504         uint16_t ch16 = c;
3505         return js_new_string16(ctx, &ch16, 1);
3506     }
3507 }
3508 
js_sub_string(JSContext * ctx,JSString * p,int start,int end)3509 static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end)
3510 {
3511     int len = end - start;
3512     if (start == 0 && end == p->len) {
3513         return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
3514     }
3515     if (p->is_wide_char && len > 0) {
3516         JSString *str;
3517         int i;
3518         uint16_t c = 0;
3519         for (i = start; i < end; i++) {
3520             c |= p->u.str16[i];
3521         }
3522         if (c > 0xFF)
3523             return js_new_string16(ctx, p->u.str16 + start, len);
3524 
3525         str = js_alloc_string(ctx, len, 0);
3526         if (!str)
3527             return JS_EXCEPTION;
3528         for (i = 0; i < len; i++) {
3529             str->u.str8[i] = p->u.str16[start + i];
3530         }
3531         str->u.str8[len] = '\0';
3532         return JS_MKPTR(JS_TAG_STRING, str);
3533     } else {
3534         return js_new_string8(ctx, p->u.str8 + start, len);
3535     }
3536 }
3537 
3538 typedef struct StringBuffer {
3539     JSContext *ctx;
3540     JSString *str;
3541     int len;
3542     int size;
3543     int is_wide_char;
3544     int error_status;
3545 } StringBuffer;
3546 
3547 /* It is valid to call string_buffer_end() and all string_buffer functions even
3548    if string_buffer_init() or another string_buffer function returns an error.
3549    If the error_status is set, string_buffer_end() returns JS_EXCEPTION.
3550  */
string_buffer_init2(JSContext * ctx,StringBuffer * s,int size,int is_wide)3551 static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size,
3552                                int is_wide)
3553 {
3554     s->ctx = ctx;
3555     s->size = size;
3556     s->len = 0;
3557     s->is_wide_char = is_wide;
3558     s->error_status = 0;
3559     s->str = js_alloc_string(ctx, size, is_wide);
3560     if (unlikely(!s->str)) {
3561         s->size = 0;
3562         return s->error_status = -1;
3563     }
3564 #ifdef DUMP_LEAKS
3565     /* the StringBuffer may reallocate the JSString, only link it at the end */
3566     list_del(&s->str->link);
3567 #endif
3568     return 0;
3569 }
3570 
string_buffer_init(JSContext * ctx,StringBuffer * s,int size)3571 static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)
3572 {
3573     return string_buffer_init2(ctx, s, size, 0);
3574 }
3575 
string_buffer_free(StringBuffer * s)3576 static void string_buffer_free(StringBuffer *s)
3577 {
3578     js_free(s->ctx, s->str);
3579     s->str = NULL;
3580 }
3581 
string_buffer_set_error(StringBuffer * s)3582 static int string_buffer_set_error(StringBuffer *s)
3583 {
3584     js_free(s->ctx, s->str);
3585     s->str = NULL;
3586     s->size = 0;
3587     s->len = 0;
3588     return s->error_status = -1;
3589 }
3590 
string_buffer_widen(StringBuffer * s,int size)3591 static no_inline int string_buffer_widen(StringBuffer *s, int size)
3592 {
3593     JSString *str;
3594     size_t slack;
3595     int i;
3596 
3597     if (s->error_status)
3598         return -1;
3599 
3600     str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);
3601     if (!str)
3602         return string_buffer_set_error(s);
3603     size += slack >> 1;
3604     for(i = s->len; i-- > 0;) {
3605         str->u.str16[i] = str->u.str8[i];
3606     }
3607     s->is_wide_char = 1;
3608     s->size = size;
3609     s->str = str;
3610     return 0;
3611 }
3612 
string_buffer_realloc(StringBuffer * s,int new_len,int c)3613 static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c)
3614 {
3615     JSString *new_str;
3616     int new_size;
3617     size_t new_size_bytes, slack;
3618 
3619     if (s->error_status)
3620         return -1;
3621 
3622     if (new_len > JS_STRING_LEN_MAX) {
3623         JS_ThrowInternalError(s->ctx, "string too long");
3624         return string_buffer_set_error(s);
3625     }
3626     new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);
3627     if (!s->is_wide_char && c >= 0x100) {
3628         return string_buffer_widen(s, new_size);
3629     }
3630     new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;
3631     new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);
3632     if (!new_str)
3633         return string_buffer_set_error(s);
3634     new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);
3635     s->size = new_size;
3636     s->str = new_str;
3637     return 0;
3638 }
3639 
string_buffer_putc_slow(StringBuffer * s,uint32_t c)3640 static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c)
3641 {
3642     if (unlikely(s->len >= s->size)) {
3643         if (string_buffer_realloc(s, s->len + 1, c))
3644             return -1;
3645     }
3646     if (s->is_wide_char) {
3647         s->str->u.str16[s->len++] = c;
3648     } else if (c < 0x100) {
3649         s->str->u.str8[s->len++] = c;
3650     } else {
3651         if (string_buffer_widen(s, s->size))
3652             return -1;
3653         s->str->u.str16[s->len++] = c;
3654     }
3655     return 0;
3656 }
3657 
3658 /* 0 <= c <= 0xff */
string_buffer_putc8(StringBuffer * s,uint32_t c)3659 static int string_buffer_putc8(StringBuffer *s, uint32_t c)
3660 {
3661     if (unlikely(s->len >= s->size)) {
3662         if (string_buffer_realloc(s, s->len + 1, c))
3663             return -1;
3664     }
3665     if (s->is_wide_char) {
3666         s->str->u.str16[s->len++] = c;
3667     } else {
3668         s->str->u.str8[s->len++] = c;
3669     }
3670     return 0;
3671 }
3672 
3673 /* 0 <= c <= 0xffff */
string_buffer_putc16(StringBuffer * s,uint32_t c)3674 static int string_buffer_putc16(StringBuffer *s, uint32_t c)
3675 {
3676     if (likely(s->len < s->size)) {
3677         if (s->is_wide_char) {
3678             s->str->u.str16[s->len++] = c;
3679             return 0;
3680         } else if (c < 0x100) {
3681             s->str->u.str8[s->len++] = c;
3682             return 0;
3683         }
3684     }
3685     return string_buffer_putc_slow(s, c);
3686 }
3687 
3688 /* 0 <= c <= 0x10ffff */
string_buffer_putc(StringBuffer * s,uint32_t c)3689 static int string_buffer_putc(StringBuffer *s, uint32_t c)
3690 {
3691     if (unlikely(c >= 0x10000)) {
3692         /* surrogate pair */
3693         c -= 0x10000;
3694         if (string_buffer_putc16(s, (c >> 10) + 0xd800))
3695             return -1;
3696         c = (c & 0x3ff) + 0xdc00;
3697     }
3698     return string_buffer_putc16(s, c);
3699 }
3700 
string_get(const JSString * p,int idx)3701 static int string_get(const JSString *p, int idx) {
3702     return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx];
3703 }
3704 
string_getc(const JSString * p,int * pidx)3705 static int string_getc(const JSString *p, int *pidx)
3706 {
3707     int idx, c, c1;
3708     idx = *pidx;
3709     if (p->is_wide_char) {
3710         c = p->u.str16[idx++];
3711         if (c >= 0xd800 && c < 0xdc00 && idx < p->len) {
3712             c1 = p->u.str16[idx];
3713             if (c1 >= 0xdc00 && c1 < 0xe000) {
3714                 c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
3715                 idx++;
3716             }
3717         }
3718     } else {
3719         c = p->u.str8[idx++];
3720     }
3721     *pidx = idx;
3722     return c;
3723 }
3724 
string_buffer_write8(StringBuffer * s,const uint8_t * p,int len)3725 static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)
3726 {
3727     int i;
3728 
3729     if (s->len + len > s->size) {
3730         if (string_buffer_realloc(s, s->len + len, 0))
3731             return -1;
3732     }
3733     if (s->is_wide_char) {
3734         for (i = 0; i < len; i++) {
3735             s->str->u.str16[s->len + i] = p[i];
3736         }
3737         s->len += len;
3738     } else {
3739         memcpy(&s->str->u.str8[s->len], p, len);
3740         s->len += len;
3741     }
3742     return 0;
3743 }
3744 
string_buffer_write16(StringBuffer * s,const uint16_t * p,int len)3745 static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)
3746 {
3747     int c = 0, i;
3748 
3749     for (i = 0; i < len; i++) {
3750         c |= p[i];
3751     }
3752     if (s->len + len > s->size) {
3753         if (string_buffer_realloc(s, s->len + len, c))
3754             return -1;
3755     } else if (!s->is_wide_char && c >= 0x100) {
3756         if (string_buffer_widen(s, s->size))
3757             return -1;
3758     }
3759     if (s->is_wide_char) {
3760         memcpy(&s->str->u.str16[s->len], p, len << 1);
3761         s->len += len;
3762     } else {
3763         for (i = 0; i < len; i++) {
3764             s->str->u.str8[s->len + i] = p[i];
3765         }
3766         s->len += len;
3767     }
3768     return 0;
3769 }
3770 
3771 /* appending an ASCII string */
string_buffer_puts8(StringBuffer * s,const char * str)3772 static int string_buffer_puts8(StringBuffer *s, const char *str)
3773 {
3774     return string_buffer_write8(s, (const uint8_t *)str, strlen(str));
3775 }
3776 
string_buffer_concat(StringBuffer * s,const JSString * p,uint32_t from,uint32_t to)3777 static int string_buffer_concat(StringBuffer *s, const JSString *p,
3778                                 uint32_t from, uint32_t to)
3779 {
3780     if (to <= from)
3781         return 0;
3782     if (p->is_wide_char)
3783         return string_buffer_write16(s, p->u.str16 + from, to - from);
3784     else
3785         return string_buffer_write8(s, p->u.str8 + from, to - from);
3786 }
3787 
string_buffer_concat_value(StringBuffer * s,JSValueConst v)3788 static int string_buffer_concat_value(StringBuffer *s, JSValueConst v)
3789 {
3790     JSString *p;
3791     JSValue v1;
3792     int res;
3793 
3794     if (s->error_status) {
3795         /* prevent exception overload */
3796         return -1;
3797     }
3798     if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
3799         v1 = JS_ToString(s->ctx, v);
3800         if (JS_IsException(v1))
3801             return string_buffer_set_error(s);
3802         p = JS_VALUE_GET_STRING(v1);
3803         res = string_buffer_concat(s, p, 0, p->len);
3804         JS_FreeValue(s->ctx, v1);
3805         return res;
3806     }
3807     p = JS_VALUE_GET_STRING(v);
3808     return string_buffer_concat(s, p, 0, p->len);
3809 }
3810 
string_buffer_concat_value_free(StringBuffer * s,JSValue v)3811 static int string_buffer_concat_value_free(StringBuffer *s, JSValue v)
3812 {
3813     JSString *p;
3814     int res;
3815 
3816     if (s->error_status) {
3817         /* prevent exception overload */
3818         JS_FreeValue(s->ctx, v);
3819         return -1;
3820     }
3821     if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
3822         v = JS_ToStringFree(s->ctx, v);
3823         if (JS_IsException(v))
3824             return string_buffer_set_error(s);
3825     }
3826     p = JS_VALUE_GET_STRING(v);
3827     res = string_buffer_concat(s, p, 0, p->len);
3828     JS_FreeValue(s->ctx, v);
3829     return res;
3830 }
3831 
string_buffer_fill(StringBuffer * s,int c,int count)3832 static int string_buffer_fill(StringBuffer *s, int c, int count)
3833 {
3834     /* XXX: optimize */
3835     if (s->len + count > s->size) {
3836         if (string_buffer_realloc(s, s->len + count, c))
3837             return -1;
3838     }
3839     while (count-- > 0) {
3840         if (string_buffer_putc16(s, c))
3841             return -1;
3842     }
3843     return 0;
3844 }
3845 
string_buffer_end(StringBuffer * s)3846 static JSValue string_buffer_end(StringBuffer *s)
3847 {
3848     JSString *str;
3849     str = s->str;
3850     if (s->error_status)
3851         return JS_EXCEPTION;
3852     if (s->len == 0) {
3853         js_free(s->ctx, str);
3854         s->str = NULL;
3855         return JS_AtomToString(s->ctx, JS_ATOM_empty_string);
3856     }
3857     if (s->len < s->size) {
3858         /* smaller size so js_realloc should not fail, but OK if it does */
3859         /* XXX: should add some slack to avoid unnecessary calls */
3860         /* XXX: might need to use malloc+free to ensure smaller size */
3861         str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) +
3862                             (s->len << s->is_wide_char) + 1 - s->is_wide_char);
3863         if (str == NULL)
3864             str = s->str;
3865         s->str = str;
3866     }
3867     if (!s->is_wide_char)
3868         str->u.str8[s->len] = 0;
3869 #ifdef DUMP_LEAKS
3870     list_add_tail(&str->link, &s->ctx->rt->string_list);
3871 #endif
3872     str->is_wide_char = s->is_wide_char;
3873     str->len = s->len;
3874     s->str = NULL;
3875     return JS_MKPTR(JS_TAG_STRING, str);
3876 }
3877 
3878 /* create a string from a UTF-8 buffer */
JS_NewStringLen(JSContext * ctx,const char * buf,size_t buf_len)3879 JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
3880 {
3881     const uint8_t *p, *p_end, *p_start, *p_next;
3882     uint32_t c;
3883     StringBuffer b_s, *b = &b_s;
3884     size_t len1;
3885 
3886     p_start = (const uint8_t *)buf;
3887     p_end = p_start + buf_len;
3888     p = p_start;
3889     while (p < p_end && *p < 128)
3890         p++;
3891     len1 = p - p_start;
3892     if (len1 > JS_STRING_LEN_MAX)
3893         return JS_ThrowInternalError(ctx, "string too long");
3894     if (p == p_end) {
3895         /* ASCII string */
3896         return js_new_string8(ctx, (const uint8_t *)buf, buf_len);
3897     } else {
3898         if (string_buffer_init(ctx, b, buf_len))
3899             goto fail;
3900         string_buffer_write8(b, p_start, len1);
3901         while (p < p_end) {
3902             if (*p < 128) {
3903                 string_buffer_putc8(b, *p++);
3904             } else {
3905                 /* parse utf-8 sequence, return 0xFFFFFFFF for error */
3906                 c = unicode_from_utf8(p, p_end - p, &p_next);
3907                 if (c < 0x10000) {
3908                     p = p_next;
3909                 } else if (c <= 0x10FFFF) {
3910                     p = p_next;
3911                     /* surrogate pair */
3912                     c -= 0x10000;
3913                     string_buffer_putc16(b, (c >> 10) + 0xd800);
3914                     c = (c & 0x3ff) + 0xdc00;
3915                 } else {
3916                     /* invalid char */
3917                     c = 0xfffd;
3918                     /* skip the invalid chars */
3919                     /* XXX: seems incorrect. Why not just use c = *p++; ? */
3920                     while (p < p_end && (*p >= 0x80 && *p < 0xc0))
3921                         p++;
3922                     if (p < p_end) {
3923                         p++;
3924                         while (p < p_end && (*p >= 0x80 && *p < 0xc0))
3925                             p++;
3926                     }
3927                 }
3928                 string_buffer_putc16(b, c);
3929             }
3930         }
3931     }
3932     return string_buffer_end(b);
3933 
3934  fail:
3935     string_buffer_free(b);
3936     return JS_EXCEPTION;
3937 }
3938 
JS_ConcatString3(JSContext * ctx,const char * str1,JSValue str2,const char * str3)3939 static JSValue JS_ConcatString3(JSContext *ctx, const char *str1,
3940                                 JSValue str2, const char *str3)
3941 {
3942     StringBuffer b_s, *b = &b_s;
3943     int len1, len3;
3944     JSString *p;
3945 
3946     if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) {
3947         str2 = JS_ToStringFree(ctx, str2);
3948         if (JS_IsException(str2))
3949             goto fail;
3950     }
3951     p = JS_VALUE_GET_STRING(str2);
3952     len1 = strlen(str1);
3953     len3 = strlen(str3);
3954 
3955     if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char))
3956         goto fail;
3957 
3958     string_buffer_write8(b, (const uint8_t *)str1, len1);
3959     string_buffer_concat(b, p, 0, p->len);
3960     string_buffer_write8(b, (const uint8_t *)str3, len3);
3961 
3962     JS_FreeValue(ctx, str2);
3963     return string_buffer_end(b);
3964 
3965  fail:
3966     JS_FreeValue(ctx, str2);
3967     return JS_EXCEPTION;
3968 }
3969 
JS_NewString(JSContext * ctx,const char * str)3970 JSValue JS_NewString(JSContext *ctx, const char *str)
3971 {
3972     return JS_NewStringLen(ctx, str, strlen(str));
3973 }
3974 
JS_NewAtomString(JSContext * ctx,const char * str)3975 JSValue JS_NewAtomString(JSContext *ctx, const char *str)
3976 {
3977     JSAtom atom = JS_NewAtom(ctx, str);
3978     if (atom == JS_ATOM_NULL)
3979         return JS_EXCEPTION;
3980     JSValue val = JS_AtomToString(ctx, atom);
3981     JS_FreeAtom(ctx, atom);
3982     return val;
3983 }
3984 
3985 /* return (NULL, 0) if exception. */
3986 /* return pointer into a JSString with a live ref_count */
3987 /* 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)3988 const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8)
3989 {
3990     JSValue val;
3991     JSString *str, *str_new;
3992     int pos, len, c, c1;
3993     uint8_t *q;
3994 
3995     if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) {
3996         val = JS_ToString(ctx, val1);
3997         if (JS_IsException(val))
3998             goto fail;
3999     } else {
4000         val = JS_DupValue(ctx, val1);
4001     }
4002 
4003     str = JS_VALUE_GET_STRING(val);
4004     len = str->len;
4005     if (!str->is_wide_char) {
4006         const uint8_t *src = str->u.str8;
4007         int count;
4008 
4009         /* count the number of non-ASCII characters */
4010         /* Scanning the whole string is required for ASCII strings,
4011            and computing the number of non-ASCII bytes is less expensive
4012            than testing each byte, hence this method is faster for ASCII
4013            strings, which is the most common case.
4014          */
4015         count = 0;
4016         for (pos = 0; pos < len; pos++) {
4017             count += src[pos] >> 7;
4018         }
4019         if (count == 0) {
4020             if (plen)
4021                 *plen = len;
4022             return (const char *)src;
4023         }
4024         str_new = js_alloc_string(ctx, len + count, 0);
4025         if (!str_new)
4026             goto fail;
4027         q = str_new->u.str8;
4028         for (pos = 0; pos < len; pos++) {
4029             c = src[pos];
4030             if (c < 0x80) {
4031                 *q++ = c;
4032             } else {
4033                 *q++ = (c >> 6) | 0xc0;
4034                 *q++ = (c & 0x3f) | 0x80;
4035             }
4036         }
4037     } else {
4038         const uint16_t *src = str->u.str16;
4039         /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may
4040            produce 4 bytes but use 2 code points.
4041          */
4042         str_new = js_alloc_string(ctx, len * 3, 0);
4043         if (!str_new)
4044             goto fail;
4045         q = str_new->u.str8;
4046         pos = 0;
4047         while (pos < len) {
4048             c = src[pos++];
4049             if (c < 0x80) {
4050                 *q++ = c;
4051             } else {
4052                 if (c >= 0xd800 && c < 0xdc00) {
4053                     if (pos < len && !cesu8) {
4054                         c1 = src[pos];
4055                         if (c1 >= 0xdc00 && c1 < 0xe000) {
4056                             pos++;
4057                             /* surrogate pair */
4058                             c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
4059                         } else {
4060                             /* Keep unmatched surrogate code points */
4061                             /* c = 0xfffd; */ /* error */
4062                         }
4063                     } else {
4064                         /* Keep unmatched surrogate code points */
4065                         /* c = 0xfffd; */ /* error */
4066                     }
4067                 }
4068                 q += unicode_to_utf8(q, c);
4069             }
4070         }
4071     }
4072 
4073     *q = '\0';
4074     str_new->len = q - str_new->u.str8;
4075     JS_FreeValue(ctx, val);
4076     if (plen)
4077         *plen = str_new->len;
4078     return (const char *)str_new->u.str8;
4079  fail:
4080     if (plen)
4081         *plen = 0;
4082     return NULL;
4083 }
4084 
JS_FreeCString(JSContext * ctx,const char * ptr)4085 void JS_FreeCString(JSContext *ctx, const char *ptr)
4086 {
4087     JSString *p;
4088     if (!ptr)
4089         return;
4090     /* purposely removing constness */
4091     p = (JSString *)(void *)(ptr - offsetof(JSString, u));
4092     JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
4093 }
4094 
memcmp16_8(const uint16_t * src1,const uint8_t * src2,int len)4095 static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len)
4096 {
4097     int c, i;
4098     for(i = 0; i < len; i++) {
4099         c = src1[i] - src2[i];
4100         if (c != 0)
4101             return c;
4102     }
4103     return 0;
4104 }
4105 
memcmp16(const uint16_t * src1,const uint16_t * src2,int len)4106 static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len)
4107 {
4108     int c, i;
4109     for(i = 0; i < len; i++) {
4110         c = src1[i] - src2[i];
4111         if (c != 0)
4112             return c;
4113     }
4114     return 0;
4115 }
4116 
js_string_memcmp(const JSString * p1,const JSString * p2,int len)4117 static int js_string_memcmp(const JSString *p1, const JSString *p2, int len)
4118 {
4119     int res;
4120 
4121     if (likely(!p1->is_wide_char)) {
4122         if (likely(!p2->is_wide_char))
4123             res = memcmp(p1->u.str8, p2->u.str8, len);
4124         else
4125             res = -memcmp16_8(p2->u.str16, p1->u.str8, len);
4126     } else {
4127         if (!p2->is_wide_char)
4128             res = memcmp16_8(p1->u.str16, p2->u.str8, len);
4129         else
4130             res = memcmp16(p1->u.str16, p2->u.str16, len);
4131     }
4132     return res;
4133 }
4134 
4135 /* return < 0, 0 or > 0 */
js_string_compare(JSContext * ctx,const JSString * p1,const JSString * p2)4136 static int js_string_compare(JSContext *ctx,
4137                              const JSString *p1, const JSString *p2)
4138 {
4139     int res, len;
4140     len = min_int(p1->len, p2->len);
4141     res = js_string_memcmp(p1, p2, len);
4142     if (res == 0) {
4143         if (p1->len == p2->len)
4144             res = 0;
4145         else if (p1->len < p2->len)
4146             res = -1;
4147         else
4148             res = 1;
4149     }
4150     return res;
4151 }
4152 
copy_str16(uint16_t * dst,const JSString * p,int offset,int len)4153 static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len)
4154 {
4155     if (p->is_wide_char) {
4156         memcpy(dst, p->u.str16 + offset, len * 2);
4157     } else {
4158         const uint8_t *src1 = p->u.str8 + offset;
4159         int i;
4160 
4161         for(i = 0; i < len; i++)
4162             dst[i] = src1[i];
4163     }
4164 }
4165 
JS_ConcatString1(JSContext * ctx,const JSString * p1,const JSString * p2)4166 static JSValue JS_ConcatString1(JSContext *ctx,
4167                                 const JSString *p1, const JSString *p2)
4168 {
4169     JSString *p;
4170     uint32_t len;
4171     int is_wide_char;
4172 
4173     len = p1->len + p2->len;
4174     if (len > JS_STRING_LEN_MAX)
4175         return JS_ThrowInternalError(ctx, "string too long");
4176     is_wide_char = p1->is_wide_char | p2->is_wide_char;
4177     p = js_alloc_string(ctx, len, is_wide_char);
4178     if (!p)
4179         return JS_EXCEPTION;
4180     if (!is_wide_char) {
4181         memcpy(p->u.str8, p1->u.str8, p1->len);
4182         memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len);
4183         p->u.str8[len] = '\0';
4184     } else {
4185         copy_str16(p->u.str16, p1, 0, p1->len);
4186         copy_str16(p->u.str16 + p1->len, p2, 0, p2->len);
4187     }
4188     return JS_MKPTR(JS_TAG_STRING, p);
4189 }
4190 
4191 /* op1 and op2 are converted to strings. For convience, op1 or op2 =
4192    JS_EXCEPTION are accepted and return JS_EXCEPTION.  */
JS_ConcatString(JSContext * ctx,JSValue op1,JSValue op2)4193 static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2)
4194 {
4195     JSValue ret;
4196     JSString *p1, *p2;
4197 
4198     if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) {
4199         op1 = JS_ToStringFree(ctx, op1);
4200         if (JS_IsException(op1)) {
4201             JS_FreeValue(ctx, op2);
4202             return JS_EXCEPTION;
4203         }
4204     }
4205     if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) {
4206         op2 = JS_ToStringFree(ctx, op2);
4207         if (JS_IsException(op2)) {
4208             JS_FreeValue(ctx, op1);
4209             return JS_EXCEPTION;
4210         }
4211     }
4212     p1 = JS_VALUE_GET_STRING(op1);
4213     p2 = JS_VALUE_GET_STRING(op2);
4214 
4215     /* XXX: could also check if p1 is empty */
4216     if (p2->len == 0) {
4217         goto ret_op1;
4218     }
4219     if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char
4220     &&  js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) {
4221         /* Concatenate in place in available space at the end of p1 */
4222         if (p1->is_wide_char) {
4223             memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1);
4224             p1->len += p2->len;
4225         } else {
4226             memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len);
4227             p1->len += p2->len;
4228             p1->u.str8[p1->len] = '\0';
4229         }
4230     ret_op1:
4231         JS_FreeValue(ctx, op2);
4232         return op1;
4233     }
4234     ret = JS_ConcatString1(ctx, p1, p2);
4235     JS_FreeValue(ctx, op1);
4236     JS_FreeValue(ctx, op2);
4237     return ret;
4238 }
4239 
4240 /* Shape support */
4241 
get_shape_size(size_t hash_size,size_t prop_size)4242 static inline size_t get_shape_size(size_t hash_size, size_t prop_size)
4243 {
4244     return hash_size * sizeof(uint32_t) + sizeof(JSShape) +
4245         prop_size * sizeof(JSShapeProperty);
4246 }
4247 
get_shape_from_alloc(void * sh_alloc,size_t hash_size)4248 static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size)
4249 {
4250     return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size);
4251 }
4252 
prop_hash_end(JSShape * sh)4253 static inline uint32_t *prop_hash_end(JSShape *sh)
4254 {
4255     return (uint32_t *)sh;
4256 }
4257 
get_alloc_from_shape(JSShape * sh)4258 static inline void *get_alloc_from_shape(JSShape *sh)
4259 {
4260     return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1);
4261 }
4262 
get_shape_prop(JSShape * sh)4263 static inline JSShapeProperty *get_shape_prop(JSShape *sh)
4264 {
4265     return sh->prop;
4266 }
4267 
init_shape_hash(JSRuntime * rt)4268 static int init_shape_hash(JSRuntime *rt)
4269 {
4270     rt->shape_hash_bits = 4;   /* 16 shapes */
4271     rt->shape_hash_size = 1 << rt->shape_hash_bits;
4272     rt->shape_hash_count = 0;
4273     rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
4274                                    rt->shape_hash_size);
4275     if (!rt->shape_hash)
4276         return -1;
4277     return 0;
4278 }
4279 
4280 /* same magic hash multiplier as the Linux kernel */
shape_hash(uint32_t h,uint32_t val)4281 static uint32_t shape_hash(uint32_t h, uint32_t val)
4282 {
4283     return (h + val) * 0x9e370001;
4284 }
4285 
4286 /* truncate the shape hash to 'hash_bits' bits */
get_shape_hash(uint32_t h,int hash_bits)4287 static uint32_t get_shape_hash(uint32_t h, int hash_bits)
4288 {
4289     return h >> (32 - hash_bits);
4290 }
4291 
shape_initial_hash(JSObject * proto)4292 static uint32_t shape_initial_hash(JSObject *proto)
4293 {
4294     uint32_t h;
4295     h = shape_hash(1, (uintptr_t)proto);
4296     if (sizeof(proto) > 4)
4297         h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32);
4298     return h;
4299 }
4300 
resize_shape_hash(JSRuntime * rt,int new_shape_hash_bits)4301 static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits)
4302 {
4303     int new_shape_hash_size, i;
4304     uint32_t h;
4305     JSShape **new_shape_hash, *sh, *sh_next;
4306 
4307     new_shape_hash_size = 1 << new_shape_hash_bits;
4308     new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
4309                                    new_shape_hash_size);
4310     if (!new_shape_hash)
4311         return -1;
4312     for(i = 0; i < rt->shape_hash_size; i++) {
4313         for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) {
4314             sh_next = sh->shape_hash_next;
4315             h = get_shape_hash(sh->hash, new_shape_hash_bits);
4316             sh->shape_hash_next = new_shape_hash[h];
4317             new_shape_hash[h] = sh;
4318         }
4319     }
4320     js_free_rt(rt, rt->shape_hash);
4321     rt->shape_hash_bits = new_shape_hash_bits;
4322     rt->shape_hash_size = new_shape_hash_size;
4323     rt->shape_hash = new_shape_hash;
4324     return 0;
4325 }
4326 
js_shape_hash_link(JSRuntime * rt,JSShape * sh)4327 static void js_shape_hash_link(JSRuntime *rt, JSShape *sh)
4328 {
4329     uint32_t h;
4330     h = get_shape_hash(sh->hash, rt->shape_hash_bits);
4331     sh->shape_hash_next = rt->shape_hash[h];
4332     rt->shape_hash[h] = sh;
4333     rt->shape_hash_count++;
4334 }
4335 
js_shape_hash_unlink(JSRuntime * rt,JSShape * sh)4336 static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh)
4337 {
4338     uint32_t h;
4339     JSShape **psh;
4340 
4341     h = get_shape_hash(sh->hash, rt->shape_hash_bits);
4342     psh = &rt->shape_hash[h];
4343     while (*psh != sh)
4344         psh = &(*psh)->shape_hash_next;
4345     *psh = sh->shape_hash_next;
4346     rt->shape_hash_count--;
4347 }
4348 
4349 /* create a new empty shape with prototype 'proto' */
js_new_shape2(JSContext * ctx,JSObject * proto,int hash_size,int prop_size)4350 static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,
4351                                         int hash_size, int prop_size)
4352 {
4353     JSRuntime *rt = ctx->rt;
4354     void *sh_alloc;
4355     JSShape *sh;
4356 
4357     /* resize the shape hash table if necessary */
4358     if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) {
4359         resize_shape_hash(rt, rt->shape_hash_bits + 1);
4360     }
4361 
4362     sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size));
4363     if (!sh_alloc)
4364         return NULL;
4365     sh = get_shape_from_alloc(sh_alloc, hash_size);
4366     sh->header.ref_count = 1;
4367     add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
4368     if (proto)
4369         JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));
4370     sh->proto = proto;
4371     memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) *
4372            hash_size);
4373     sh->prop_hash_mask = hash_size - 1;
4374     sh->prop_size = prop_size;
4375     sh->prop_count = 0;
4376     sh->deleted_prop_count = 0;
4377 
4378     /* insert in the hash table */
4379     sh->hash = shape_initial_hash(proto);
4380     sh->is_hashed = TRUE;
4381     sh->has_small_array_index = FALSE;
4382     js_shape_hash_link(ctx->rt, sh);
4383     return sh;
4384 }
4385 
js_new_shape(JSContext * ctx,JSObject * proto)4386 static JSShape *js_new_shape(JSContext *ctx, JSObject *proto)
4387 {
4388     return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE,
4389                          JS_PROP_INITIAL_SIZE);
4390 }
4391 
4392 /* The shape is cloned. The new shape is not inserted in the shape
4393    hash table */
js_clone_shape(JSContext * ctx,JSShape * sh1)4394 static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)
4395 {
4396     JSShape *sh;
4397     void *sh_alloc, *sh_alloc1;
4398     size_t size;
4399     JSShapeProperty *pr;
4400     uint32_t i, hash_size;
4401 
4402     hash_size = sh1->prop_hash_mask + 1;
4403     size = get_shape_size(hash_size, sh1->prop_size);
4404     sh_alloc = js_malloc(ctx, size);
4405     if (!sh_alloc)
4406         return NULL;
4407     sh_alloc1 = get_alloc_from_shape(sh1);
4408     memcpy(sh_alloc, sh_alloc1, size);
4409     sh = get_shape_from_alloc(sh_alloc, hash_size);
4410     sh->header.ref_count = 1;
4411     add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
4412     sh->is_hashed = FALSE;
4413     if (sh->proto) {
4414         JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
4415     }
4416     for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
4417         JS_DupAtom(ctx, pr->atom);
4418     }
4419     return sh;
4420 }
4421 
js_dup_shape(JSShape * sh)4422 static JSShape *js_dup_shape(JSShape *sh)
4423 {
4424     sh->header.ref_count++;
4425     return sh;
4426 }
4427 
js_free_shape0(JSRuntime * rt,JSShape * sh)4428 static void js_free_shape0(JSRuntime *rt, JSShape *sh)
4429 {
4430     uint32_t i;
4431     JSShapeProperty *pr;
4432 
4433     assert(sh->header.ref_count == 0);
4434     if (sh->is_hashed)
4435         js_shape_hash_unlink(rt, sh);
4436     if (sh->proto != NULL) {
4437         JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
4438     }
4439     pr = get_shape_prop(sh);
4440     for(i = 0; i < sh->prop_count; i++) {
4441         JS_FreeAtomRT(rt, pr->atom);
4442         pr++;
4443     }
4444     remove_gc_object(&sh->header);
4445     js_free_rt(rt, get_alloc_from_shape(sh));
4446 }
4447 
js_free_shape(JSRuntime * rt,JSShape * sh)4448 static void js_free_shape(JSRuntime *rt, JSShape *sh)
4449 {
4450     if (unlikely(--sh->header.ref_count <= 0)) {
4451         js_free_shape0(rt, sh);
4452     }
4453 }
4454 
js_free_shape_null(JSRuntime * rt,JSShape * sh)4455 static void js_free_shape_null(JSRuntime *rt, JSShape *sh)
4456 {
4457     if (sh)
4458         js_free_shape(rt, sh);
4459 }
4460 
4461 /* make space to hold at least 'count' properties */
resize_properties(JSContext * ctx,JSShape ** psh,JSObject * p,uint32_t count)4462 static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
4463                                        JSObject *p, uint32_t count)
4464 {
4465     JSShape *sh;
4466     uint32_t new_size, new_hash_size, new_hash_mask, i;
4467     JSShapeProperty *pr;
4468     void *sh_alloc;
4469     intptr_t h;
4470 
4471     sh = *psh;
4472     new_size = max_int(count, sh->prop_size * 3 / 2);
4473     /* Reallocate prop array first to avoid crash or size inconsistency
4474        in case of memory allocation failure */
4475     if (p) {
4476         JSProperty *new_prop;
4477         new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
4478         if (unlikely(!new_prop))
4479             return -1;
4480         p->prop = new_prop;
4481     }
4482     new_hash_size = sh->prop_hash_mask + 1;
4483     while (new_hash_size < new_size)
4484         new_hash_size = 2 * new_hash_size;
4485     if (new_hash_size != (sh->prop_hash_mask + 1)) {
4486         JSShape *old_sh;
4487         /* resize the hash table and the properties */
4488         old_sh = sh;
4489         sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
4490         if (!sh_alloc)
4491             return -1;
4492         sh = get_shape_from_alloc(sh_alloc, new_hash_size);
4493         list_del(&old_sh->header.link);
4494         /* copy all the fields and the properties */
4495         memcpy(sh, old_sh,
4496                sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);
4497         list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
4498         new_hash_mask = new_hash_size - 1;
4499         sh->prop_hash_mask = new_hash_mask;
4500         memset(prop_hash_end(sh) - new_hash_size, 0,
4501                sizeof(prop_hash_end(sh)[0]) * new_hash_size);
4502         for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) {
4503             if (pr->atom != JS_ATOM_NULL) {
4504                 h = ((uintptr_t)pr->atom & new_hash_mask);
4505                 pr->hash_next = prop_hash_end(sh)[-h - 1];
4506                 prop_hash_end(sh)[-h - 1] = i + 1;
4507             }
4508         }
4509         js_free(ctx, get_alloc_from_shape(old_sh));
4510     } else {
4511         /* only resize the properties */
4512         list_del(&sh->header.link);
4513         sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh),
4514                               get_shape_size(new_hash_size, new_size));
4515         if (unlikely(!sh_alloc)) {
4516             /* insert again in the GC list */
4517             list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
4518             return -1;
4519         }
4520         sh = get_shape_from_alloc(sh_alloc, new_hash_size);
4521         list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
4522     }
4523     *psh = sh;
4524     sh->prop_size = new_size;
4525     return 0;
4526 }
4527 
4528 /* remove the deleted properties. */
compact_properties(JSContext * ctx,JSObject * p)4529 static int compact_properties(JSContext *ctx, JSObject *p)
4530 {
4531     JSShape *sh, *old_sh;
4532     void *sh_alloc;
4533     intptr_t h;
4534     uint32_t new_hash_size, i, j, new_hash_mask, new_size;
4535     JSShapeProperty *old_pr, *pr;
4536     JSProperty *prop, *new_prop;
4537 
4538     sh = p->shape;
4539     assert(!sh->is_hashed);
4540 
4541     new_size = max_int(JS_PROP_INITIAL_SIZE,
4542                        sh->prop_count - sh->deleted_prop_count);
4543     assert(new_size <= sh->prop_size);
4544 
4545     new_hash_size = sh->prop_hash_mask + 1;
4546     while ((new_hash_size / 2) >= new_size)
4547         new_hash_size = new_hash_size / 2;
4548     new_hash_mask = new_hash_size - 1;
4549 
4550     /* resize the hash table and the properties */
4551     old_sh = sh;
4552     sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
4553     if (!sh_alloc)
4554         return -1;
4555     sh = get_shape_from_alloc(sh_alloc, new_hash_size);
4556     list_del(&old_sh->header.link);
4557     memcpy(sh, old_sh, sizeof(JSShape));
4558     list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
4559 
4560     memset(prop_hash_end(sh) - new_hash_size, 0,
4561            sizeof(prop_hash_end(sh)[0]) * new_hash_size);
4562 
4563     j = 0;
4564     old_pr = old_sh->prop;
4565     pr = sh->prop;
4566     prop = p->prop;
4567     for(i = 0; i < sh->prop_count; i++) {
4568         if (old_pr->atom != JS_ATOM_NULL) {
4569             pr->atom = old_pr->atom;
4570             pr->flags = old_pr->flags;
4571             h = ((uintptr_t)old_pr->atom & new_hash_mask);
4572             pr->hash_next = prop_hash_end(sh)[-h - 1];
4573             prop_hash_end(sh)[-h - 1] = j + 1;
4574             prop[j] = prop[i];
4575             j++;
4576             pr++;
4577         }
4578         old_pr++;
4579     }
4580     assert(j == (sh->prop_count - sh->deleted_prop_count));
4581     sh->prop_hash_mask = new_hash_mask;
4582     sh->prop_size = new_size;
4583     sh->deleted_prop_count = 0;
4584     sh->prop_count = j;
4585 
4586     p->shape = sh;
4587     js_free(ctx, get_alloc_from_shape(old_sh));
4588 
4589     /* reduce the size of the object properties */
4590     new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
4591     if (new_prop)
4592         p->prop = new_prop;
4593     return 0;
4594 }
4595 
add_shape_property(JSContext * ctx,JSShape ** psh,JSObject * p,JSAtom atom,int prop_flags)4596 static int add_shape_property(JSContext *ctx, JSShape **psh,
4597                               JSObject *p, JSAtom atom, int prop_flags)
4598 {
4599     JSRuntime *rt = ctx->rt;
4600     JSShape *sh = *psh;
4601     JSShapeProperty *pr, *prop;
4602     uint32_t hash_mask, new_shape_hash = 0;
4603     intptr_t h;
4604 
4605     /* update the shape hash */
4606     if (sh->is_hashed) {
4607         js_shape_hash_unlink(rt, sh);
4608         new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags);
4609     }
4610 
4611     if (unlikely(sh->prop_count >= sh->prop_size)) {
4612         if (resize_properties(ctx, psh, p, sh->prop_count + 1)) {
4613             /* in case of error, reinsert in the hash table.
4614                sh is still valid if resize_properties() failed */
4615             if (sh->is_hashed)
4616                 js_shape_hash_link(rt, sh);
4617             return -1;
4618         }
4619         sh = *psh;
4620     }
4621     if (sh->is_hashed) {
4622         sh->hash = new_shape_hash;
4623         js_shape_hash_link(rt, sh);
4624     }
4625     /* Initialize the new shape property.
4626        The object property at p->prop[sh->prop_count] is uninitialized */
4627     prop = get_shape_prop(sh);
4628     pr = &prop[sh->prop_count++];
4629     pr->atom = JS_DupAtom(ctx, atom);
4630     pr->flags = prop_flags;
4631     sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom);
4632     /* add in hash table */
4633     hash_mask = sh->prop_hash_mask;
4634     h = atom & hash_mask;
4635     pr->hash_next = prop_hash_end(sh)[-h - 1];
4636     prop_hash_end(sh)[-h - 1] = sh->prop_count;
4637     return 0;
4638 }
4639 
4640 /* find a hashed empty shape matching the prototype. Return NULL if
4641    not found */
find_hashed_shape_proto(JSRuntime * rt,JSObject * proto)4642 static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto)
4643 {
4644     JSShape *sh1;
4645     uint32_t h, h1;
4646 
4647     h = shape_initial_hash(proto);
4648     h1 = get_shape_hash(h, rt->shape_hash_bits);
4649     for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
4650         if (sh1->hash == h &&
4651             sh1->proto == proto &&
4652             sh1->prop_count == 0) {
4653             return sh1;
4654         }
4655     }
4656     return NULL;
4657 }
4658 
4659 /* find a hashed shape matching sh + (prop, prop_flags). Return NULL if
4660    not found */
find_hashed_shape_prop(JSRuntime * rt,JSShape * sh,JSAtom atom,int prop_flags)4661 static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,
4662                                        JSAtom atom, int prop_flags)
4663 {
4664     JSShape *sh1;
4665     uint32_t h, h1, i, n;
4666 
4667     h = sh->hash;
4668     h = shape_hash(h, atom);
4669     h = shape_hash(h, prop_flags);
4670     h1 = get_shape_hash(h, rt->shape_hash_bits);
4671     for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
4672         /* we test the hash first so that the rest is done only if the
4673            shapes really match */
4674         if (sh1->hash == h &&
4675             sh1->proto == sh->proto &&
4676             sh1->prop_count == ((n = sh->prop_count) + 1)) {
4677             for(i = 0; i < n; i++) {
4678                 if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) ||
4679                     unlikely(sh1->prop[i].flags != sh->prop[i].flags))
4680                     goto next;
4681             }
4682             if (unlikely(sh1->prop[n].atom != atom) ||
4683                 unlikely(sh1->prop[n].flags != prop_flags))
4684                 goto next;
4685             return sh1;
4686         }
4687     next: ;
4688     }
4689     return NULL;
4690 }
4691 
JS_DumpShape(JSRuntime * rt,int i,JSShape * sh)4692 static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
4693 {
4694     char atom_buf[ATOM_GET_STR_BUF_SIZE];
4695     int j;
4696 
4697     /* XXX: should output readable class prototype */
4698     printf("%5d %3d%c %14p %5d %5d", i,
4699            sh->header.ref_count, " *"[sh->is_hashed],
4700            (void *)sh->proto, sh->prop_size, sh->prop_count);
4701     for(j = 0; j < sh->prop_count; j++) {
4702         printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf),
4703                                       sh->prop[j].atom));
4704     }
4705     printf("\n");
4706 }
4707 
JS_DumpShapes(JSRuntime * rt)4708 static __maybe_unused void JS_DumpShapes(JSRuntime *rt)
4709 {
4710     int i;
4711     JSShape *sh;
4712     struct list_head *el;
4713     JSObject *p;
4714     JSGCObjectHeader *gp;
4715 
4716     printf("JSShapes: {\n");
4717     printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS");
4718     for(i = 0; i < rt->shape_hash_size; i++) {
4719         for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
4720             JS_DumpShape(rt, i, sh);
4721             assert(sh->is_hashed);
4722         }
4723     }
4724     /* dump non-hashed shapes */
4725     list_for_each(el, &rt->gc_obj_list) {
4726         gp = list_entry(el, JSGCObjectHeader, link);
4727         if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
4728             p = (JSObject *)gp;
4729             if (!p->shape->is_hashed) {
4730                 JS_DumpShape(rt, -1, p->shape);
4731             }
4732         }
4733     }
4734     printf("}\n");
4735 }
4736 
JS_NewObjectFromShape(JSContext * ctx,JSShape * sh,JSClassID class_id)4737 static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id)
4738 {
4739     JSObject *p;
4740 
4741     js_trigger_gc(ctx->rt, sizeof(JSObject));
4742     p = js_malloc(ctx, sizeof(JSObject));
4743     if (unlikely(!p))
4744         goto fail;
4745     p->class_id = class_id;
4746     p->extensible = TRUE;
4747     p->free_mark = 0;
4748     p->is_exotic = 0;
4749     p->fast_array = 0;
4750     p->is_constructor = 0;
4751     p->is_uncatchable_error = 0;
4752     p->tmp_mark = 0;
4753     p->is_HTMLDDA = 0;
4754     p->first_weak_ref = NULL;
4755     p->u.opaque = NULL;
4756     p->shape = sh;
4757     p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);
4758     if (unlikely(!p->prop)) {
4759         js_free(ctx, p);
4760     fail:
4761         js_free_shape(ctx->rt, sh);
4762         return JS_EXCEPTION;
4763     }
4764 
4765     switch(class_id) {
4766     case JS_CLASS_OBJECT:
4767         break;
4768     case JS_CLASS_ARRAY:
4769         {
4770             JSProperty *pr;
4771             p->is_exotic = 1;
4772             p->fast_array = 1;
4773             p->u.array.u.values = NULL;
4774             p->u.array.count = 0;
4775             p->u.array.u1.size = 0;
4776             /* the length property is always the first one */
4777             if (likely(sh == ctx->array_shape)) {
4778                 pr = &p->prop[0];
4779             } else {
4780                 /* only used for the first array */
4781                 /* cannot fail */
4782                 pr = add_property(ctx, p, JS_ATOM_length,
4783                                   JS_PROP_WRITABLE | JS_PROP_LENGTH);
4784             }
4785             pr->u.value = JS_NewInt32(ctx, 0);
4786         }
4787         break;
4788     case JS_CLASS_C_FUNCTION:
4789         p->prop[0].u.value = JS_UNDEFINED;
4790         break;
4791     case JS_CLASS_ARGUMENTS:
4792     case JS_CLASS_UINT8C_ARRAY:
4793     case JS_CLASS_INT8_ARRAY:
4794     case JS_CLASS_UINT8_ARRAY:
4795     case JS_CLASS_INT16_ARRAY:
4796     case JS_CLASS_UINT16_ARRAY:
4797     case JS_CLASS_INT32_ARRAY:
4798     case JS_CLASS_UINT32_ARRAY:
4799 #ifdef CONFIG_BIGNUM
4800     case JS_CLASS_BIG_INT64_ARRAY:
4801     case JS_CLASS_BIG_UINT64_ARRAY:
4802 #endif
4803     case JS_CLASS_FLOAT32_ARRAY:
4804     case JS_CLASS_FLOAT64_ARRAY:
4805         p->is_exotic = 1;
4806         p->fast_array = 1;
4807         p->u.array.u.ptr = NULL;
4808         p->u.array.count = 0;
4809         break;
4810     case JS_CLASS_DATAVIEW:
4811         p->u.array.u.ptr = NULL;
4812         p->u.array.count = 0;
4813         break;
4814     case JS_CLASS_NUMBER:
4815     case JS_CLASS_STRING:
4816     case JS_CLASS_BOOLEAN:
4817     case JS_CLASS_SYMBOL:
4818     case JS_CLASS_DATE:
4819 #ifdef CONFIG_BIGNUM
4820     case JS_CLASS_BIG_INT:
4821     case JS_CLASS_BIG_FLOAT:
4822     case JS_CLASS_BIG_DECIMAL:
4823 #endif
4824         p->u.object_data = JS_UNDEFINED;
4825         goto set_exotic;
4826     case JS_CLASS_REGEXP:
4827         p->u.regexp.pattern = NULL;
4828         p->u.regexp.bytecode = NULL;
4829         goto set_exotic;
4830     default:
4831     set_exotic:
4832         if (ctx->rt->class_array[class_id].exotic) {
4833             p->is_exotic = 1;
4834         }
4835         break;
4836     }
4837     p->header.ref_count = 1;
4838     add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT);
4839     return JS_MKPTR(JS_TAG_OBJECT, p);
4840 }
4841 
get_proto_obj(JSValueConst proto_val)4842 static JSObject *get_proto_obj(JSValueConst proto_val)
4843 {
4844     if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT)
4845         return NULL;
4846     else
4847         return JS_VALUE_GET_OBJ(proto_val);
4848 }
4849 
4850 /* WARNING: proto must be an object or JS_NULL */
JS_NewObjectProtoClass(JSContext * ctx,JSValueConst proto_val,JSClassID class_id)4851 JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val,
4852                                JSClassID class_id)
4853 {
4854     JSShape *sh;
4855     JSObject *proto;
4856 
4857     proto = get_proto_obj(proto_val);
4858     sh = find_hashed_shape_proto(ctx->rt, proto);
4859     if (likely(sh)) {
4860         sh = js_dup_shape(sh);
4861     } else {
4862         sh = js_new_shape(ctx, proto);
4863         if (!sh)
4864             return JS_EXCEPTION;
4865     }
4866     return JS_NewObjectFromShape(ctx, sh, class_id);
4867 }
4868 
4869 #if 0
4870 static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj)
4871 {
4872     JSObject *p;
4873 
4874     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
4875         p = JS_VALUE_GET_OBJ(obj);
4876         switch(p->class_id) {
4877         case JS_CLASS_NUMBER:
4878         case JS_CLASS_STRING:
4879         case JS_CLASS_BOOLEAN:
4880         case JS_CLASS_SYMBOL:
4881         case JS_CLASS_DATE:
4882 #ifdef CONFIG_BIGNUM
4883         case JS_CLASS_BIG_INT:
4884         case JS_CLASS_BIG_FLOAT:
4885         case JS_CLASS_BIG_DECIMAL:
4886 #endif
4887             return JS_DupValue(ctx, p->u.object_data);
4888         }
4889     }
4890     return JS_UNDEFINED;
4891 }
4892 #endif
4893 
JS_SetObjectData(JSContext * ctx,JSValueConst obj,JSValue val)4894 static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val)
4895 {
4896     JSObject *p;
4897 
4898     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
4899         p = JS_VALUE_GET_OBJ(obj);
4900         switch(p->class_id) {
4901         case JS_CLASS_NUMBER:
4902         case JS_CLASS_STRING:
4903         case JS_CLASS_BOOLEAN:
4904         case JS_CLASS_SYMBOL:
4905         case JS_CLASS_DATE:
4906 #ifdef CONFIG_BIGNUM
4907         case JS_CLASS_BIG_INT:
4908         case JS_CLASS_BIG_FLOAT:
4909         case JS_CLASS_BIG_DECIMAL:
4910 #endif
4911             JS_FreeValue(ctx, p->u.object_data);
4912             p->u.object_data = val;
4913             return 0;
4914         }
4915     }
4916     JS_FreeValue(ctx, val);
4917     if (!JS_IsException(obj))
4918         JS_ThrowTypeError(ctx, "invalid object type");
4919     return -1;
4920 }
4921 
JS_NewObjectClass(JSContext * ctx,int class_id)4922 JSValue JS_NewObjectClass(JSContext *ctx, int class_id)
4923 {
4924     return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id);
4925 }
4926 
JS_NewObjectProto(JSContext * ctx,JSValueConst proto)4927 JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto)
4928 {
4929     return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT);
4930 }
4931 
JS_NewArray(JSContext * ctx)4932 JSValue JS_NewArray(JSContext *ctx)
4933 {
4934     return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape),
4935                                  JS_CLASS_ARRAY);
4936 }
4937 
JS_NewObject(JSContext * ctx)4938 JSValue JS_NewObject(JSContext *ctx)
4939 {
4940     /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */
4941     return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT);
4942 }
4943 
js_function_set_properties(JSContext * ctx,JSValueConst func_obj,JSAtom name,int len)4944 static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj,
4945                                        JSAtom name, int len)
4946 {
4947     /* ES6 feature non compatible with ES5.1: length is configurable */
4948     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len),
4949                            JS_PROP_CONFIGURABLE);
4950     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name,
4951                            JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE);
4952 }
4953 
js_class_has_bytecode(JSClassID class_id)4954 static BOOL js_class_has_bytecode(JSClassID class_id)
4955 {
4956     return (class_id == JS_CLASS_BYTECODE_FUNCTION ||
4957             class_id == JS_CLASS_GENERATOR_FUNCTION ||
4958             class_id == JS_CLASS_ASYNC_FUNCTION ||
4959             class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION);
4960 }
4961 
4962 /* return NULL without exception if not a function or no bytecode */
JS_GetFunctionBytecode(JSValueConst val)4963 static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val)
4964 {
4965     JSObject *p;
4966     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
4967         return NULL;
4968     p = JS_VALUE_GET_OBJ(val);
4969     if (!js_class_has_bytecode(p->class_id))
4970         return NULL;
4971     return p->u.func.function_bytecode;
4972 }
4973 
js_method_set_home_object(JSContext * ctx,JSValueConst func_obj,JSValueConst home_obj)4974 static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj,
4975                                       JSValueConst home_obj)
4976 {
4977     JSObject *p, *p1;
4978     JSFunctionBytecode *b;
4979 
4980     if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
4981         return;
4982     p = JS_VALUE_GET_OBJ(func_obj);
4983     if (!js_class_has_bytecode(p->class_id))
4984         return;
4985     b = p->u.func.function_bytecode;
4986     if (b->need_home_object) {
4987         p1 = p->u.func.home_object;
4988         if (p1) {
4989             JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
4990         }
4991         if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT)
4992             p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj));
4993         else
4994             p1 = NULL;
4995         p->u.func.home_object = p1;
4996     }
4997 }
4998 
js_get_function_name(JSContext * ctx,JSAtom name)4999 static JSValue js_get_function_name(JSContext *ctx, JSAtom name)
5000 {
5001     JSValue name_str;
5002 
5003     name_str = JS_AtomToString(ctx, name);
5004     if (JS_AtomSymbolHasDescription(ctx, name)) {
5005         name_str = JS_ConcatString3(ctx, "[", name_str, "]");
5006     }
5007     return name_str;
5008 }
5009 
5010 /* Modify the name of a method according to the atom and
5011    'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and
5012    JS_PROP_HAS_SET. Also set the home object of the method.
5013    Return < 0 if exception. */
js_method_set_properties(JSContext * ctx,JSValueConst func_obj,JSAtom name,int flags,JSValueConst home_obj)5014 static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj,
5015                                     JSAtom name, int flags, JSValueConst home_obj)
5016 {
5017     JSValue name_str;
5018 
5019     name_str = js_get_function_name(ctx, name);
5020     if (flags & JS_PROP_HAS_GET) {
5021         name_str = JS_ConcatString3(ctx, "get ", name_str, "");
5022     } else if (flags & JS_PROP_HAS_SET) {
5023         name_str = JS_ConcatString3(ctx, "set ", name_str, "");
5024     }
5025     if (JS_IsException(name_str))
5026         return -1;
5027     if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str,
5028                                JS_PROP_CONFIGURABLE) < 0)
5029         return -1;
5030     js_method_set_home_object(ctx, func_obj, home_obj);
5031     return 0;
5032 }
5033 
5034 /* 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)5035 static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func,
5036                                 const char *name,
5037                                 int length, JSCFunctionEnum cproto, int magic,
5038                                 JSValueConst proto_val)
5039 {
5040     JSValue func_obj;
5041     JSObject *p;
5042     JSAtom name_atom;
5043 
5044     func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION);
5045     if (JS_IsException(func_obj))
5046         return func_obj;
5047     p = JS_VALUE_GET_OBJ(func_obj);
5048     p->u.cfunc.realm = JS_DupContext(ctx);
5049     p->u.cfunc.c_function.generic = func;
5050     p->u.cfunc.length = length;
5051     p->u.cfunc.cproto = cproto;
5052     p->u.cfunc.magic = magic;
5053     p->is_constructor = (cproto == JS_CFUNC_constructor ||
5054                          cproto == JS_CFUNC_constructor_magic ||
5055                          cproto == JS_CFUNC_constructor_or_func ||
5056                          cproto == JS_CFUNC_constructor_or_func_magic);
5057     if (!name)
5058         name = "";
5059     name_atom = JS_NewAtom(ctx, name);
5060     js_function_set_properties(ctx, func_obj, name_atom, length);
5061     JS_FreeAtom(ctx, name_atom);
5062     return func_obj;
5063 }
5064 
5065 /* 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)5066 JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,
5067                          const char *name,
5068                          int length, JSCFunctionEnum cproto, int magic)
5069 {
5070     return JS_NewCFunction3(ctx, func, name, length, cproto, magic,
5071                             ctx->function_proto);
5072 }
5073 
5074 typedef struct JSCFunctionDataRecord {
5075     JSCFunctionData *func;
5076     uint8_t length;
5077     uint8_t data_len;
5078     uint16_t magic;
5079     JSValue data[0];
5080 } JSCFunctionDataRecord;
5081 
js_c_function_data_finalizer(JSRuntime * rt,JSValue val)5082 static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val)
5083 {
5084     JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
5085     int i;
5086 
5087     if (s) {
5088         for(i = 0; i < s->data_len; i++) {
5089             JS_FreeValueRT(rt, s->data[i]);
5090         }
5091         js_free_rt(rt, s);
5092     }
5093 }
5094 
js_c_function_data_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5095 static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
5096                                     JS_MarkFunc *mark_func)
5097 {
5098     JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
5099     int i;
5100 
5101     if (s) {
5102         for(i = 0; i < s->data_len; i++) {
5103             JS_MarkValue(rt, s->data[i], mark_func);
5104         }
5105     }
5106 }
5107 
js_c_function_data_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_val,int argc,JSValueConst * argv,int flags)5108 static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
5109                                        JSValueConst this_val,
5110                                        int argc, JSValueConst *argv, int flags)
5111 {
5112     JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA);
5113     JSValueConst *arg_buf;
5114     int i;
5115 
5116     /* XXX: could add the function on the stack for debug */
5117     if (unlikely(argc < s->length)) {
5118         arg_buf = alloca(sizeof(arg_buf[0]) * s->length);
5119         for(i = 0; i < argc; i++)
5120             arg_buf[i] = argv[i];
5121         for(i = argc; i < s->length; i++)
5122             arg_buf[i] = JS_UNDEFINED;
5123     } else {
5124         arg_buf = argv;
5125     }
5126 
5127     return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data);
5128 }
5129 
JS_NewCFunctionData(JSContext * ctx,JSCFunctionData * func,int length,int magic,int data_len,JSValueConst * data)5130 JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,
5131                             int length, int magic, int data_len,
5132                             JSValueConst *data)
5133 {
5134     JSCFunctionDataRecord *s;
5135     JSValue func_obj;
5136     int i;
5137 
5138     func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
5139                                       JS_CLASS_C_FUNCTION_DATA);
5140     if (JS_IsException(func_obj))
5141         return func_obj;
5142     s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue));
5143     if (!s) {
5144         JS_FreeValue(ctx, func_obj);
5145         return JS_EXCEPTION;
5146     }
5147     s->func = func;
5148     s->length = length;
5149     s->data_len = data_len;
5150     s->magic = magic;
5151     for(i = 0; i < data_len; i++)
5152         s->data[i] = JS_DupValue(ctx, data[i]);
5153     JS_SetOpaque(func_obj, s);
5154     js_function_set_properties(ctx, func_obj,
5155                                JS_ATOM_empty_string, length);
5156     return func_obj;
5157 }
5158 
js_autoinit_get_realm(JSProperty * pr)5159 static JSContext *js_autoinit_get_realm(JSProperty *pr)
5160 {
5161     return (JSContext *)(pr->u.init.realm_and_id & ~3);
5162 }
5163 
js_autoinit_get_id(JSProperty * pr)5164 static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr)
5165 {
5166     return pr->u.init.realm_and_id & 3;
5167 }
5168 
js_autoinit_free(JSRuntime * rt,JSProperty * pr)5169 static void js_autoinit_free(JSRuntime *rt, JSProperty *pr)
5170 {
5171     JS_FreeContext(js_autoinit_get_realm(pr));
5172 }
5173 
js_autoinit_mark(JSRuntime * rt,JSProperty * pr,JS_MarkFunc * mark_func)5174 static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,
5175                              JS_MarkFunc *mark_func)
5176 {
5177     mark_func(rt, &js_autoinit_get_realm(pr)->header);
5178 }
5179 
free_property(JSRuntime * rt,JSProperty * pr,int prop_flags)5180 static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)
5181 {
5182     if (unlikely(prop_flags & JS_PROP_TMASK)) {
5183         if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
5184             if (pr->u.getset.getter)
5185                 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
5186             if (pr->u.getset.setter)
5187                 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
5188         } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
5189             free_var_ref(rt, pr->u.var_ref);
5190         } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
5191             js_autoinit_free(rt, pr);
5192         }
5193     } else {
5194         JS_FreeValueRT(rt, pr->u.value);
5195     }
5196 }
5197 
find_own_property1(JSObject * p,JSAtom atom)5198 static force_inline JSShapeProperty *find_own_property1(JSObject *p,
5199                                                         JSAtom atom)
5200 {
5201     JSShape *sh;
5202     JSShapeProperty *pr, *prop;
5203     intptr_t h;
5204     sh = p->shape;
5205     h = (uintptr_t)atom & sh->prop_hash_mask;
5206     h = prop_hash_end(sh)[-h - 1];
5207     prop = get_shape_prop(sh);
5208     while (h) {
5209         pr = &prop[h - 1];
5210         if (likely(pr->atom == atom)) {
5211             return pr;
5212         }
5213         h = pr->hash_next;
5214     }
5215     return NULL;
5216 }
5217 
find_own_property(JSProperty ** ppr,JSObject * p,JSAtom atom)5218 static force_inline JSShapeProperty *find_own_property(JSProperty **ppr,
5219                                                        JSObject *p,
5220                                                        JSAtom atom)
5221 {
5222     JSShape *sh;
5223     JSShapeProperty *pr, *prop;
5224     intptr_t h;
5225     sh = p->shape;
5226     h = (uintptr_t)atom & sh->prop_hash_mask;
5227     h = prop_hash_end(sh)[-h - 1];
5228     prop = get_shape_prop(sh);
5229     while (h) {
5230         pr = &prop[h - 1];
5231         if (likely(pr->atom == atom)) {
5232             *ppr = &p->prop[h - 1];
5233             /* the compiler should be able to assume that pr != NULL here */
5234             return pr;
5235         }
5236         h = pr->hash_next;
5237     }
5238     *ppr = NULL;
5239     return NULL;
5240 }
5241 
5242 /* indicate that the object may be part of a function prototype cycle */
set_cycle_flag(JSContext * ctx,JSValueConst obj)5243 static void set_cycle_flag(JSContext *ctx, JSValueConst obj)
5244 {
5245 }
5246 
free_var_ref(JSRuntime * rt,JSVarRef * var_ref)5247 static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)
5248 {
5249     if (var_ref) {
5250         assert(var_ref->header.ref_count > 0);
5251         if (--var_ref->header.ref_count == 0) {
5252             if (var_ref->is_detached) {
5253                 JS_FreeValueRT(rt, var_ref->value);
5254                 remove_gc_object(&var_ref->header);
5255             } else {
5256                 list_del(&var_ref->header.link); /* still on the stack */
5257             }
5258             js_free_rt(rt, var_ref);
5259         }
5260     }
5261 }
5262 
js_array_finalizer(JSRuntime * rt,JSValue val)5263 static void js_array_finalizer(JSRuntime *rt, JSValue val)
5264 {
5265     JSObject *p = JS_VALUE_GET_OBJ(val);
5266     int i;
5267 
5268     for(i = 0; i < p->u.array.count; i++) {
5269         JS_FreeValueRT(rt, p->u.array.u.values[i]);
5270     }
5271     js_free_rt(rt, p->u.array.u.values);
5272 }
5273 
js_array_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5274 static void js_array_mark(JSRuntime *rt, JSValueConst val,
5275                           JS_MarkFunc *mark_func)
5276 {
5277     JSObject *p = JS_VALUE_GET_OBJ(val);
5278     int i;
5279 
5280     for(i = 0; i < p->u.array.count; i++) {
5281         JS_MarkValue(rt, p->u.array.u.values[i], mark_func);
5282     }
5283 }
5284 
js_object_data_finalizer(JSRuntime * rt,JSValue val)5285 static void js_object_data_finalizer(JSRuntime *rt, JSValue val)
5286 {
5287     JSObject *p = JS_VALUE_GET_OBJ(val);
5288     JS_FreeValueRT(rt, p->u.object_data);
5289     p->u.object_data = JS_UNDEFINED;
5290 }
5291 
js_object_data_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5292 static void js_object_data_mark(JSRuntime *rt, JSValueConst val,
5293                                 JS_MarkFunc *mark_func)
5294 {
5295     JSObject *p = JS_VALUE_GET_OBJ(val);
5296     JS_MarkValue(rt, p->u.object_data, mark_func);
5297 }
5298 
js_c_function_finalizer(JSRuntime * rt,JSValue val)5299 static void js_c_function_finalizer(JSRuntime *rt, JSValue val)
5300 {
5301     JSObject *p = JS_VALUE_GET_OBJ(val);
5302 
5303     if (p->u.cfunc.realm)
5304         JS_FreeContext(p->u.cfunc.realm);
5305 }
5306 
js_c_function_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5307 static void js_c_function_mark(JSRuntime *rt, JSValueConst val,
5308                                JS_MarkFunc *mark_func)
5309 {
5310     JSObject *p = JS_VALUE_GET_OBJ(val);
5311 
5312     if (p->u.cfunc.realm)
5313         mark_func(rt, &p->u.cfunc.realm->header);
5314 }
5315 
js_bytecode_function_finalizer(JSRuntime * rt,JSValue val)5316 static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val)
5317 {
5318     JSObject *p1, *p = JS_VALUE_GET_OBJ(val);
5319     JSFunctionBytecode *b;
5320     JSVarRef **var_refs;
5321     int i;
5322 
5323     p1 = p->u.func.home_object;
5324     if (p1) {
5325         JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1));
5326     }
5327     b = p->u.func.function_bytecode;
5328     if (b) {
5329         var_refs = p->u.func.var_refs;
5330         if (var_refs) {
5331             for(i = 0; i < b->closure_var_count; i++)
5332                 free_var_ref(rt, var_refs[i]);
5333             js_free_rt(rt, var_refs);
5334         }
5335         JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b));
5336     }
5337 }
5338 
js_bytecode_function_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5339 static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
5340                                       JS_MarkFunc *mark_func)
5341 {
5342     JSObject *p = JS_VALUE_GET_OBJ(val);
5343     JSVarRef **var_refs = p->u.func.var_refs;
5344     JSFunctionBytecode *b = p->u.func.function_bytecode;
5345     int i;
5346 
5347     if (p->u.func.home_object) {
5348         JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object),
5349                      mark_func);
5350     }
5351     if (b) {
5352         if (var_refs) {
5353             for(i = 0; i < b->closure_var_count; i++) {
5354                 JSVarRef *var_ref = var_refs[i];
5355                 if (var_ref && var_ref->is_detached) {
5356                     mark_func(rt, &var_ref->header);
5357                 }
5358             }
5359         }
5360         /* must mark the function bytecode because template objects may be
5361            part of a cycle */
5362         JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func);
5363     }
5364 }
5365 
js_bound_function_finalizer(JSRuntime * rt,JSValue val)5366 static void js_bound_function_finalizer(JSRuntime *rt, JSValue val)
5367 {
5368     JSObject *p = JS_VALUE_GET_OBJ(val);
5369     JSBoundFunction *bf = p->u.bound_function;
5370     int i;
5371 
5372     JS_FreeValueRT(rt, bf->func_obj);
5373     JS_FreeValueRT(rt, bf->this_val);
5374     for(i = 0; i < bf->argc; i++) {
5375         JS_FreeValueRT(rt, bf->argv[i]);
5376     }
5377     js_free_rt(rt, bf);
5378 }
5379 
js_bound_function_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5380 static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
5381                                 JS_MarkFunc *mark_func)
5382 {
5383     JSObject *p = JS_VALUE_GET_OBJ(val);
5384     JSBoundFunction *bf = p->u.bound_function;
5385     int i;
5386 
5387     JS_MarkValue(rt, bf->func_obj, mark_func);
5388     JS_MarkValue(rt, bf->this_val, mark_func);
5389     for(i = 0; i < bf->argc; i++)
5390         JS_MarkValue(rt, bf->argv[i], mark_func);
5391 }
5392 
js_for_in_iterator_finalizer(JSRuntime * rt,JSValue val)5393 static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val)
5394 {
5395     JSObject *p = JS_VALUE_GET_OBJ(val);
5396     JSForInIterator *it = p->u.for_in_iterator;
5397     JS_FreeValueRT(rt, it->obj);
5398     js_free_rt(rt, it);
5399 }
5400 
js_for_in_iterator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5401 static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
5402                                 JS_MarkFunc *mark_func)
5403 {
5404     JSObject *p = JS_VALUE_GET_OBJ(val);
5405     JSForInIterator *it = p->u.for_in_iterator;
5406     JS_MarkValue(rt, it->obj, mark_func);
5407 }
5408 
free_object(JSRuntime * rt,JSObject * p)5409 static void free_object(JSRuntime *rt, JSObject *p)
5410 {
5411     int i;
5412     JSClassFinalizer *finalizer;
5413     JSShape *sh;
5414     JSShapeProperty *pr;
5415 
5416     p->free_mark = 1; /* used to tell the object is invalid when
5417                          freeing cycles */
5418     /* free all the fields */
5419     sh = p->shape;
5420     pr = get_shape_prop(sh);
5421     for(i = 0; i < sh->prop_count; i++) {
5422         free_property(rt, &p->prop[i], pr->flags);
5423         pr++;
5424     }
5425     js_free_rt(rt, p->prop);
5426     /* as an optimization we destroy the shape immediately without
5427        putting it in gc_zero_ref_count_list */
5428     js_free_shape(rt, sh);
5429 
5430     /* fail safe */
5431     p->shape = NULL;
5432     p->prop = NULL;
5433 
5434     if (unlikely(p->first_weak_ref)) {
5435         reset_weak_ref(rt, p);
5436     }
5437 
5438     finalizer = rt->class_array[p->class_id].finalizer;
5439     if (finalizer)
5440         (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p));
5441 
5442     /* fail safe */
5443     p->class_id = 0;
5444     p->u.opaque = NULL;
5445     p->u.func.var_refs = NULL;
5446     p->u.func.home_object = NULL;
5447 
5448     remove_gc_object(&p->header);
5449     if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) {
5450         list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);
5451     } else {
5452         js_free_rt(rt, p);
5453     }
5454 }
5455 
free_gc_object(JSRuntime * rt,JSGCObjectHeader * gp)5456 static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)
5457 {
5458     switch(gp->gc_obj_type) {
5459     case JS_GC_OBJ_TYPE_JS_OBJECT:
5460         free_object(rt, (JSObject *)gp);
5461         break;
5462     case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
5463         free_function_bytecode(rt, (JSFunctionBytecode *)gp);
5464         break;
5465     default:
5466         abort();
5467     }
5468 }
5469 
free_zero_refcount(JSRuntime * rt)5470 static void free_zero_refcount(JSRuntime *rt)
5471 {
5472     struct list_head *el;
5473     JSGCObjectHeader *p;
5474 
5475     rt->gc_phase = JS_GC_PHASE_DECREF;
5476     for(;;) {
5477         el = rt->gc_zero_ref_count_list.next;
5478         if (el == &rt->gc_zero_ref_count_list)
5479             break;
5480         p = list_entry(el, JSGCObjectHeader, link);
5481         assert(p->ref_count == 0);
5482         free_gc_object(rt, p);
5483     }
5484     rt->gc_phase = JS_GC_PHASE_NONE;
5485 }
5486 
5487 /* called with the ref_count of 'v' reaches zero. */
__JS_FreeValueRT(JSRuntime * rt,JSValue v)5488 void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
5489 {
5490     uint32_t tag = JS_VALUE_GET_TAG(v);
5491 
5492 #ifdef DUMP_FREE
5493     {
5494         printf("Freeing ");
5495         if (tag == JS_TAG_OBJECT) {
5496             JS_DumpObject(rt, JS_VALUE_GET_OBJ(v));
5497         } else {
5498             JS_DumpValueShort(rt, v);
5499             printf("\n");
5500         }
5501     }
5502 #endif
5503 
5504     switch(tag) {
5505     case JS_TAG_STRING:
5506         {
5507             JSString *p = JS_VALUE_GET_STRING(v);
5508             if (p->atom_type) {
5509                 JS_FreeAtomStruct(rt, p);
5510             } else {
5511 #ifdef DUMP_LEAKS
5512                 list_del(&p->link);
5513 #endif
5514                 js_free_rt(rt, p);
5515             }
5516         }
5517         break;
5518     case JS_TAG_OBJECT:
5519     case JS_TAG_FUNCTION_BYTECODE:
5520         {
5521             JSGCObjectHeader *p = JS_VALUE_GET_PTR(v);
5522             if (p->mark) {
5523                 return;
5524             }
5525             if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
5526                 list_del(&p->link);
5527                 list_add(&p->link, &rt->gc_zero_ref_count_list);
5528                 if (rt->gc_phase == JS_GC_PHASE_NONE) {
5529                     free_zero_refcount(rt);
5530                 }
5531             }
5532         }
5533         break;
5534     case JS_TAG_MODULE:
5535         abort(); /* never freed here */
5536         break;
5537 #ifdef CONFIG_BIGNUM
5538     case JS_TAG_BIG_INT:
5539     case JS_TAG_BIG_FLOAT:
5540         {
5541             JSBigFloat *bf = JS_VALUE_GET_PTR(v);
5542             bf_delete(&bf->num);
5543             js_free_rt(rt, bf);
5544         }
5545         break;
5546     case JS_TAG_BIG_DECIMAL:
5547         {
5548             JSBigDecimal *bf = JS_VALUE_GET_PTR(v);
5549             bfdec_delete(&bf->num);
5550             js_free_rt(rt, bf);
5551         }
5552         break;
5553 #endif
5554     case JS_TAG_SYMBOL:
5555         {
5556             JSAtomStruct *p = JS_VALUE_GET_PTR(v);
5557             JS_FreeAtomStruct(rt, p);
5558         }
5559         break;
5560     default:
5561         printf("__JS_FreeValue: unknown tag=%d\n", tag);
5562         abort();
5563     }
5564 }
5565 
__JS_FreeValue(JSContext * ctx,JSValue v)5566 void __JS_FreeValue(JSContext *ctx, JSValue v)
5567 {
5568     __JS_FreeValueRT(ctx->rt, v);
5569 }
5570 
5571 /* garbage collection */
5572 
add_gc_object(JSRuntime * rt,JSGCObjectHeader * h,JSGCObjectTypeEnum type)5573 static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
5574                           JSGCObjectTypeEnum type)
5575 {
5576     h->mark = 0;
5577     h->gc_obj_type = type;
5578     list_add_tail(&h->link, &rt->gc_obj_list);
5579 }
5580 
remove_gc_object(JSGCObjectHeader * h)5581 static void remove_gc_object(JSGCObjectHeader *h)
5582 {
5583     list_del(&h->link);
5584 }
5585 
JS_MarkValue(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)5586 void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
5587 {
5588     if (JS_VALUE_HAS_REF_COUNT(val)) {
5589         switch(JS_VALUE_GET_TAG(val)) {
5590         case JS_TAG_OBJECT:
5591         case JS_TAG_FUNCTION_BYTECODE:
5592             mark_func(rt, JS_VALUE_GET_PTR(val));
5593             break;
5594         default:
5595             break;
5596         }
5597     }
5598 }
5599 
mark_children(JSRuntime * rt,JSGCObjectHeader * gp,JS_MarkFunc * mark_func)5600 static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
5601                           JS_MarkFunc *mark_func)
5602 {
5603     switch(gp->gc_obj_type) {
5604     case JS_GC_OBJ_TYPE_JS_OBJECT:
5605         {
5606             JSObject *p = (JSObject *)gp;
5607             JSShapeProperty *prs;
5608             JSShape *sh;
5609             int i;
5610             sh = p->shape;
5611             mark_func(rt, &sh->header);
5612             /* mark all the fields */
5613             prs = get_shape_prop(sh);
5614             for(i = 0; i < sh->prop_count; i++) {
5615                 JSProperty *pr = &p->prop[i];
5616                 if (prs->atom != JS_ATOM_NULL) {
5617                     if (prs->flags & JS_PROP_TMASK) {
5618                         if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
5619                             if (pr->u.getset.getter)
5620                                 mark_func(rt, &pr->u.getset.getter->header);
5621                             if (pr->u.getset.setter)
5622                                 mark_func(rt, &pr->u.getset.setter->header);
5623                         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
5624                             if (pr->u.var_ref->is_detached) {
5625                                 /* Note: the tag does not matter
5626                                    provided it is a GC object */
5627                                 mark_func(rt, &pr->u.var_ref->header);
5628                             }
5629                         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
5630                             js_autoinit_mark(rt, pr, mark_func);
5631                         }
5632                     } else {
5633                         JS_MarkValue(rt, pr->u.value, mark_func);
5634                     }
5635                 }
5636                 prs++;
5637             }
5638 
5639             if (p->class_id != JS_CLASS_OBJECT) {
5640                 JSClassGCMark *gc_mark;
5641                 gc_mark = rt->class_array[p->class_id].gc_mark;
5642                 if (gc_mark)
5643                     gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func);
5644             }
5645         }
5646         break;
5647     case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
5648         /* the template objects can be part of a cycle */
5649         {
5650             JSFunctionBytecode *b = (JSFunctionBytecode *)gp;
5651             int i;
5652             for(i = 0; i < b->cpool_count; i++) {
5653                 JS_MarkValue(rt, b->cpool[i], mark_func);
5654             }
5655             if (b->realm)
5656                 mark_func(rt, &b->realm->header);
5657         }
5658         break;
5659     case JS_GC_OBJ_TYPE_VAR_REF:
5660         {
5661             JSVarRef *var_ref = (JSVarRef *)gp;
5662             /* only detached variable referenced are taken into account */
5663             assert(var_ref->is_detached);
5664             JS_MarkValue(rt, *var_ref->pvalue, mark_func);
5665         }
5666         break;
5667     case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
5668         {
5669             JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp;
5670             if (s->is_active)
5671                 async_func_mark(rt, &s->func_state, mark_func);
5672             JS_MarkValue(rt, s->resolving_funcs[0], mark_func);
5673             JS_MarkValue(rt, s->resolving_funcs[1], mark_func);
5674         }
5675         break;
5676     case JS_GC_OBJ_TYPE_SHAPE:
5677         {
5678             JSShape *sh = (JSShape *)gp;
5679             if (sh->proto != NULL) {
5680                 mark_func(rt, &sh->proto->header);
5681             }
5682         }
5683         break;
5684     case JS_GC_OBJ_TYPE_JS_CONTEXT:
5685         {
5686             JSContext *ctx = (JSContext *)gp;
5687             JS_MarkContext(rt, ctx, mark_func);
5688         }
5689         break;
5690     default:
5691         abort();
5692     }
5693 }
5694 
gc_decref_child(JSRuntime * rt,JSGCObjectHeader * p)5695 static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p)
5696 {
5697     assert(p->ref_count > 0);
5698     p->ref_count--;
5699     if (p->ref_count == 0 && p->mark == 1) {
5700         list_del(&p->link);
5701         list_add_tail(&p->link, &rt->tmp_obj_list);
5702     }
5703 }
5704 
gc_decref(JSRuntime * rt)5705 static void gc_decref(JSRuntime *rt)
5706 {
5707     struct list_head *el, *el1;
5708     JSGCObjectHeader *p;
5709 
5710     init_list_head(&rt->tmp_obj_list);
5711 
5712     /* decrement the refcount of all the children of all the GC
5713        objects and move the GC objects with zero refcount to
5714        tmp_obj_list */
5715     list_for_each_safe(el, el1, &rt->gc_obj_list) {
5716         p = list_entry(el, JSGCObjectHeader, link);
5717         assert(p->mark == 0);
5718         mark_children(rt, p, gc_decref_child);
5719         p->mark = 1;
5720         if (p->ref_count == 0) {
5721             list_del(&p->link);
5722             list_add_tail(&p->link, &rt->tmp_obj_list);
5723         }
5724     }
5725 }
5726 
gc_scan_incref_child(JSRuntime * rt,JSGCObjectHeader * p)5727 static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p)
5728 {
5729     p->ref_count++;
5730     if (p->ref_count == 1) {
5731         /* ref_count was 0: remove from tmp_obj_list and add at the
5732            end of gc_obj_list */
5733         list_del(&p->link);
5734         list_add_tail(&p->link, &rt->gc_obj_list);
5735         p->mark = 0; /* reset the mark for the next GC call */
5736     }
5737 }
5738 
gc_scan_incref_child2(JSRuntime * rt,JSGCObjectHeader * p)5739 static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p)
5740 {
5741     p->ref_count++;
5742 }
5743 
gc_scan(JSRuntime * rt)5744 static void gc_scan(JSRuntime *rt)
5745 {
5746     struct list_head *el;
5747     JSGCObjectHeader *p;
5748 
5749     /* keep the objects with a refcount > 0 and their children. */
5750     list_for_each(el, &rt->gc_obj_list) {
5751         p = list_entry(el, JSGCObjectHeader, link);
5752         assert(p->ref_count > 0);
5753         p->mark = 0; /* reset the mark for the next GC call */
5754         mark_children(rt, p, gc_scan_incref_child);
5755     }
5756 
5757     /* restore the refcount of the objects to be deleted. */
5758     list_for_each(el, &rt->tmp_obj_list) {
5759         p = list_entry(el, JSGCObjectHeader, link);
5760         mark_children(rt, p, gc_scan_incref_child2);
5761     }
5762 }
5763 
gc_free_cycles(JSRuntime * rt)5764 static void gc_free_cycles(JSRuntime *rt)
5765 {
5766     struct list_head *el, *el1;
5767     JSGCObjectHeader *p;
5768 #ifdef DUMP_GC_FREE
5769     BOOL header_done = FALSE;
5770 #endif
5771 
5772     rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES;
5773 
5774     for(;;) {
5775         el = rt->tmp_obj_list.next;
5776         if (el == &rt->tmp_obj_list)
5777             break;
5778         p = list_entry(el, JSGCObjectHeader, link);
5779         /* Only need to free the GC object associated with JS
5780            values. The rest will be automatically removed because they
5781            must be referenced by them. */
5782         switch(p->gc_obj_type) {
5783         case JS_GC_OBJ_TYPE_JS_OBJECT:
5784         case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
5785 #ifdef DUMP_GC_FREE
5786             if (!header_done) {
5787                 printf("Freeing cycles:\n");
5788                 JS_DumpObjectHeader(rt);
5789                 header_done = TRUE;
5790             }
5791             JS_DumpGCObject(rt, p);
5792 #endif
5793             free_gc_object(rt, p);
5794             break;
5795         default:
5796             list_del(&p->link);
5797             list_add_tail(&p->link, &rt->gc_zero_ref_count_list);
5798             break;
5799         }
5800     }
5801     rt->gc_phase = JS_GC_PHASE_NONE;
5802 
5803     list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {
5804         p = list_entry(el, JSGCObjectHeader, link);
5805         assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
5806                p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
5807         js_free_rt(rt, p);
5808     }
5809 
5810     init_list_head(&rt->gc_zero_ref_count_list);
5811 }
5812 
JS_RunGC(JSRuntime * rt)5813 void JS_RunGC(JSRuntime *rt)
5814 {
5815     /* decrement the reference of the children of each object. mark =
5816        1 after this pass. */
5817     gc_decref(rt);
5818 
5819     /* keep the GC objects with a non zero refcount and their childs */
5820     gc_scan(rt);
5821 
5822     /* free the GC objects in a cycle */
5823     gc_free_cycles(rt);
5824 }
5825 
5826 /* Return false if not an object or if the object has already been
5827    freed (zombie objects are visible in finalizers when freeing
5828    cycles). */
JS_IsLiveObject(JSRuntime * rt,JSValueConst obj)5829 BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj)
5830 {
5831     JSObject *p;
5832     if (!JS_IsObject(obj))
5833         return FALSE;
5834     p = JS_VALUE_GET_OBJ(obj);
5835     return !p->free_mark;
5836 }
5837 
5838 /* Compute memory used by various object types */
5839 /* XXX: poor man's approach to handling multiply referenced objects */
5840 typedef struct JSMemoryUsage_helper {
5841     double memory_used_count;
5842     double str_count;
5843     double str_size;
5844     int64_t js_func_count;
5845     double js_func_size;
5846     int64_t js_func_code_size;
5847     int64_t js_func_pc2line_count;
5848     int64_t js_func_pc2line_size;
5849 } JSMemoryUsage_helper;
5850 
5851 static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp);
5852 
compute_jsstring_size(JSString * str,JSMemoryUsage_helper * hp)5853 static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp)
5854 {
5855     if (!str->atom_type) {  /* atoms are handled separately */
5856         double s_ref_count = str->header.ref_count;
5857         hp->str_count += 1 / s_ref_count;
5858         hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) +
5859                           1 - str->is_wide_char) / s_ref_count);
5860     }
5861 }
5862 
compute_bytecode_size(JSFunctionBytecode * b,JSMemoryUsage_helper * hp)5863 static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp)
5864 {
5865     int memory_used_count, js_func_size, i;
5866 
5867     memory_used_count = 0;
5868     js_func_size = offsetof(JSFunctionBytecode, debug);
5869     if (b->vardefs) {
5870         js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs);
5871     }
5872     if (b->cpool) {
5873         js_func_size += b->cpool_count * sizeof(*b->cpool);
5874         for (i = 0; i < b->cpool_count; i++) {
5875             JSValueConst val = b->cpool[i];
5876             compute_value_size(val, hp);
5877         }
5878     }
5879     if (b->closure_var) {
5880         js_func_size += b->closure_var_count * sizeof(*b->closure_var);
5881     }
5882     if (!b->read_only_bytecode && b->byte_code_buf) {
5883         hp->js_func_code_size += b->byte_code_len;
5884     }
5885     if (b->has_debug) {
5886         js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug);
5887         if (b->debug.source) {
5888             memory_used_count++;
5889             js_func_size += b->debug.source_len + 1;
5890         }
5891         if (b->debug.pc2line_len) {
5892             memory_used_count++;
5893             hp->js_func_pc2line_count += 1;
5894             hp->js_func_pc2line_size += b->debug.pc2line_len;
5895         }
5896     }
5897     hp->js_func_size += js_func_size;
5898     hp->js_func_count += 1;
5899     hp->memory_used_count += memory_used_count;
5900 }
5901 
compute_value_size(JSValueConst val,JSMemoryUsage_helper * hp)5902 static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp)
5903 {
5904     switch(JS_VALUE_GET_TAG(val)) {
5905     case JS_TAG_STRING:
5906         compute_jsstring_size(JS_VALUE_GET_STRING(val), hp);
5907         break;
5908 #ifdef CONFIG_BIGNUM
5909     case JS_TAG_BIG_INT:
5910     case JS_TAG_BIG_FLOAT:
5911     case JS_TAG_BIG_DECIMAL:
5912         /* should track JSBigFloat usage */
5913         break;
5914 #endif
5915     }
5916 }
5917 
JS_ComputeMemoryUsage(JSRuntime * rt,JSMemoryUsage * s)5918 void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
5919 {
5920     struct list_head *el, *el1;
5921     int i;
5922     JSMemoryUsage_helper mem = { 0 }, *hp = &mem;
5923 
5924     memset(s, 0, sizeof(*s));
5925     s->malloc_count = rt->malloc_state.malloc_count;
5926     s->malloc_size = rt->malloc_state.malloc_size;
5927     s->malloc_limit = rt->malloc_state.malloc_limit;
5928 
5929     s->memory_used_count = 2; /* rt + rt->class_array */
5930     s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;
5931 
5932     list_for_each(el, &rt->context_list) {
5933         JSContext *ctx = list_entry(el, JSContext, link);
5934         JSShape *sh = ctx->array_shape;
5935         s->memory_used_count += 2; /* ctx + ctx->class_proto */
5936         s->memory_used_size += sizeof(JSContext) +
5937             sizeof(JSValue) * rt->class_count;
5938         s->binary_object_count += ctx->binary_object_count;
5939         s->binary_object_size += ctx->binary_object_size;
5940 
5941         /* the hashed shapes are counted separately */
5942         if (sh && !sh->is_hashed) {
5943             int hash_size = sh->prop_hash_mask + 1;
5944             s->shape_count++;
5945             s->shape_size += get_shape_size(hash_size, sh->prop_size);
5946         }
5947         list_for_each(el1, &ctx->loaded_modules) {
5948             JSModuleDef *m = list_entry(el1, JSModuleDef, link);
5949             s->memory_used_count += 1;
5950             s->memory_used_size += sizeof(*m);
5951             if (m->req_module_entries) {
5952                 s->memory_used_count += 1;
5953                 s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries);
5954             }
5955             if (m->export_entries) {
5956                 s->memory_used_count += 1;
5957                 s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries);
5958                 for (i = 0; i < m->export_entries_count; i++) {
5959                     JSExportEntry *me = &m->export_entries[i];
5960                     if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) {
5961                         /* potential multiple count */
5962                         s->memory_used_count += 1;
5963                         compute_value_size(me->u.local.var_ref->value, hp);
5964                     }
5965                 }
5966             }
5967             if (m->star_export_entries) {
5968                 s->memory_used_count += 1;
5969                 s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries);
5970             }
5971             if (m->import_entries) {
5972                 s->memory_used_count += 1;
5973                 s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries);
5974             }
5975             compute_value_size(m->module_ns, hp);
5976             compute_value_size(m->func_obj, hp);
5977         }
5978     }
5979 
5980     list_for_each(el, &rt->gc_obj_list) {
5981         JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
5982         JSObject *p;
5983         JSShape *sh;
5984         JSShapeProperty *prs;
5985 
5986         /* XXX: could count the other GC object types too */
5987         if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {
5988             compute_bytecode_size((JSFunctionBytecode *)gp, hp);
5989             continue;
5990         } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) {
5991             continue;
5992         }
5993         p = (JSObject *)gp;
5994         sh = p->shape;
5995         s->obj_count++;
5996         if (p->prop) {
5997             s->memory_used_count++;
5998             s->prop_size += sh->prop_size * sizeof(*p->prop);
5999             s->prop_count += sh->prop_count;
6000             prs = get_shape_prop(sh);
6001             for(i = 0; i < sh->prop_count; i++) {
6002                 JSProperty *pr = &p->prop[i];
6003                 if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) {
6004                     compute_value_size(pr->u.value, hp);
6005                 }
6006                 prs++;
6007             }
6008         }
6009         /* the hashed shapes are counted separately */
6010         if (!sh->is_hashed) {
6011             int hash_size = sh->prop_hash_mask + 1;
6012             s->shape_count++;
6013             s->shape_size += get_shape_size(hash_size, sh->prop_size);
6014         }
6015 
6016         switch(p->class_id) {
6017         case JS_CLASS_ARRAY:             /* u.array | length */
6018         case JS_CLASS_ARGUMENTS:         /* u.array | length */
6019             s->array_count++;
6020             if (p->fast_array) {
6021                 s->fast_array_count++;
6022                 if (p->u.array.u.values) {
6023                     s->memory_used_count++;
6024                     s->memory_used_size += p->u.array.count *
6025                         sizeof(*p->u.array.u.values);
6026                     s->fast_array_elements += p->u.array.count;
6027                     for (i = 0; i < p->u.array.count; i++) {
6028                         compute_value_size(p->u.array.u.values[i], hp);
6029                     }
6030                 }
6031             }
6032             break;
6033         case JS_CLASS_NUMBER:            /* u.object_data */
6034         case JS_CLASS_STRING:            /* u.object_data */
6035         case JS_CLASS_BOOLEAN:           /* u.object_data */
6036         case JS_CLASS_SYMBOL:            /* u.object_data */
6037         case JS_CLASS_DATE:              /* u.object_data */
6038 #ifdef CONFIG_BIGNUM
6039         case JS_CLASS_BIG_INT:           /* u.object_data */
6040         case JS_CLASS_BIG_FLOAT:         /* u.object_data */
6041         case JS_CLASS_BIG_DECIMAL:         /* u.object_data */
6042 #endif
6043             compute_value_size(p->u.object_data, hp);
6044             break;
6045         case JS_CLASS_C_FUNCTION:        /* u.cfunc */
6046             s->c_func_count++;
6047             break;
6048         case JS_CLASS_BYTECODE_FUNCTION: /* u.func */
6049             {
6050                 JSFunctionBytecode *b = p->u.func.function_bytecode;
6051                 JSVarRef **var_refs = p->u.func.var_refs;
6052                 /* home_object: object will be accounted for in list scan */
6053                 if (var_refs) {
6054                     s->memory_used_count++;
6055                     s->js_func_size += b->closure_var_count * sizeof(*var_refs);
6056                     for (i = 0; i < b->closure_var_count; i++) {
6057                         if (var_refs[i]) {
6058                             double ref_count = var_refs[i]->header.ref_count;
6059                             s->memory_used_count += 1 / ref_count;
6060                             s->js_func_size += sizeof(*var_refs[i]) / ref_count;
6061                             /* handle non object closed values */
6062                             if (var_refs[i]->pvalue == &var_refs[i]->value) {
6063                                 /* potential multiple count */
6064                                 compute_value_size(var_refs[i]->value, hp);
6065                             }
6066                         }
6067                     }
6068                 }
6069             }
6070             break;
6071         case JS_CLASS_BOUND_FUNCTION:    /* u.bound_function */
6072             {
6073                 JSBoundFunction *bf = p->u.bound_function;
6074                 /* func_obj and this_val are objects */
6075                 for (i = 0; i < bf->argc; i++) {
6076                     compute_value_size(bf->argv[i], hp);
6077                 }
6078                 s->memory_used_count += 1;
6079                 s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv);
6080             }
6081             break;
6082         case JS_CLASS_C_FUNCTION_DATA:   /* u.c_function_data_record */
6083             {
6084                 JSCFunctionDataRecord *fd = p->u.c_function_data_record;
6085                 if (fd) {
6086                     for (i = 0; i < fd->data_len; i++) {
6087                         compute_value_size(fd->data[i], hp);
6088                     }
6089                     s->memory_used_count += 1;
6090                     s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data);
6091                 }
6092             }
6093             break;
6094         case JS_CLASS_REGEXP:            /* u.regexp */
6095             compute_jsstring_size(p->u.regexp.pattern, hp);
6096             compute_jsstring_size(p->u.regexp.bytecode, hp);
6097             break;
6098 
6099         case JS_CLASS_FOR_IN_ITERATOR:   /* u.for_in_iterator */
6100             {
6101                 JSForInIterator *it = p->u.for_in_iterator;
6102                 if (it) {
6103                     compute_value_size(it->obj, hp);
6104                     s->memory_used_count += 1;
6105                     s->memory_used_size += sizeof(*it);
6106                 }
6107             }
6108             break;
6109         case JS_CLASS_ARRAY_BUFFER:      /* u.array_buffer */
6110         case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */
6111             {
6112                 JSArrayBuffer *abuf = p->u.array_buffer;
6113                 if (abuf) {
6114                     s->memory_used_count += 1;
6115                     s->memory_used_size += sizeof(*abuf);
6116                     if (abuf->data) {
6117                         s->memory_used_count += 1;
6118                         s->memory_used_size += abuf->byte_length;
6119                     }
6120                 }
6121             }
6122             break;
6123         case JS_CLASS_GENERATOR:         /* u.generator_data */
6124         case JS_CLASS_UINT8C_ARRAY:      /* u.typed_array / u.array */
6125         case JS_CLASS_INT8_ARRAY:        /* u.typed_array / u.array */
6126         case JS_CLASS_UINT8_ARRAY:       /* u.typed_array / u.array */
6127         case JS_CLASS_INT16_ARRAY:       /* u.typed_array / u.array */
6128         case JS_CLASS_UINT16_ARRAY:      /* u.typed_array / u.array */
6129         case JS_CLASS_INT32_ARRAY:       /* u.typed_array / u.array */
6130         case JS_CLASS_UINT32_ARRAY:      /* u.typed_array / u.array */
6131 #ifdef CONFIG_BIGNUM
6132         case JS_CLASS_BIG_INT64_ARRAY:   /* u.typed_array / u.array */
6133         case JS_CLASS_BIG_UINT64_ARRAY:  /* u.typed_array / u.array */
6134 #endif
6135         case JS_CLASS_FLOAT32_ARRAY:     /* u.typed_array / u.array */
6136         case JS_CLASS_FLOAT64_ARRAY:     /* u.typed_array / u.array */
6137         case JS_CLASS_DATAVIEW:          /* u.typed_array */
6138 #ifdef CONFIG_BIGNUM
6139         case JS_CLASS_FLOAT_ENV:         /* u.float_env */
6140 #endif
6141         case JS_CLASS_MAP:               /* u.map_state */
6142         case JS_CLASS_SET:               /* u.map_state */
6143         case JS_CLASS_WEAKMAP:           /* u.map_state */
6144         case JS_CLASS_WEAKSET:           /* u.map_state */
6145         case JS_CLASS_MAP_ITERATOR:      /* u.map_iterator_data */
6146         case JS_CLASS_SET_ITERATOR:      /* u.map_iterator_data */
6147         case JS_CLASS_ARRAY_ITERATOR:    /* u.array_iterator_data */
6148         case JS_CLASS_STRING_ITERATOR:   /* u.array_iterator_data */
6149         case JS_CLASS_PROXY:             /* u.proxy_data */
6150         case JS_CLASS_PROMISE:           /* u.promise_data */
6151         case JS_CLASS_PROMISE_RESOLVE_FUNCTION:  /* u.promise_function_data */
6152         case JS_CLASS_PROMISE_REJECT_FUNCTION:   /* u.promise_function_data */
6153         case JS_CLASS_ASYNC_FUNCTION_RESOLVE:    /* u.async_function_data */
6154         case JS_CLASS_ASYNC_FUNCTION_REJECT:     /* u.async_function_data */
6155         case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR:  /* u.async_from_sync_iterator_data */
6156         case JS_CLASS_ASYNC_GENERATOR:   /* u.async_generator_data */
6157             /* TODO */
6158         default:
6159             /* XXX: class definition should have an opaque block size */
6160             if (p->u.opaque) {
6161                 s->memory_used_count += 1;
6162             }
6163             break;
6164         }
6165     }
6166     s->obj_size += s->obj_count * sizeof(JSObject);
6167 
6168     /* hashed shapes */
6169     s->memory_used_count++; /* rt->shape_hash */
6170     s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size;
6171     for(i = 0; i < rt->shape_hash_size; i++) {
6172         JSShape *sh;
6173         for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
6174             int hash_size = sh->prop_hash_mask + 1;
6175             s->shape_count++;
6176             s->shape_size += get_shape_size(hash_size, sh->prop_size);
6177         }
6178     }
6179 
6180     /* atoms */
6181     s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */
6182     s->atom_count = rt->atom_count;
6183     s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size +
6184         sizeof(rt->atom_hash[0]) * rt->atom_hash_size;
6185     for(i = 0; i < rt->atom_size; i++) {
6186         JSAtomStruct *p = rt->atom_array[i];
6187         if (!atom_is_free(p)) {
6188             s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) +
6189                              1 - p->is_wide_char);
6190         }
6191     }
6192     s->str_count = round(mem.str_count);
6193     s->str_size = round(mem.str_size);
6194     s->js_func_count = mem.js_func_count;
6195     s->js_func_size = round(mem.js_func_size);
6196     s->js_func_code_size = mem.js_func_code_size;
6197     s->js_func_pc2line_count = mem.js_func_pc2line_count;
6198     s->js_func_pc2line_size = mem.js_func_pc2line_size;
6199     s->memory_used_count += round(mem.memory_used_count) +
6200         s->atom_count + s->str_count +
6201         s->obj_count + s->shape_count +
6202         s->js_func_count + s->js_func_pc2line_count;
6203     s->memory_used_size += s->atom_size + s->str_size +
6204         s->obj_size + s->prop_size + s->shape_size +
6205         s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size;
6206 }
6207 
JS_DumpMemoryUsage(FILE * fp,const JSMemoryUsage * s,JSRuntime * rt)6208 void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
6209 {
6210     fprintf(fp, "QuickJS memory usage -- "
6211 #ifdef CONFIG_BIGNUM
6212             "BigNum "
6213 #endif
6214             "2020-11-08 version, %d-bit, malloc limit: %"PRId64"\n\n",
6215             (int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit);
6216 #if 1
6217     if (rt) {
6218         static const struct {
6219             const char *name;
6220             size_t size;
6221         } object_types[] = {
6222             { "JSRuntime", sizeof(JSRuntime) },
6223             { "JSContext", sizeof(JSContext) },
6224             { "JSObject", sizeof(JSObject) },
6225             { "JSString", sizeof(JSString) },
6226             { "JSFunctionBytecode", sizeof(JSFunctionBytecode) },
6227         };
6228         int i, usage_size_ok = 0;
6229         for(i = 0; i < countof(object_types); i++) {
6230             unsigned int size = object_types[i].size;
6231             void *p = js_malloc_rt(rt, size);
6232             if (p) {
6233                 unsigned int size1 = js_malloc_usable_size_rt(rt, p);
6234                 if (size1 >= size) {
6235                     usage_size_ok = 1;
6236                     fprintf(fp, "  %3u + %-2u  %s\n",
6237                             size, size1 - size, object_types[i].name);
6238                 }
6239                 js_free_rt(rt, p);
6240             }
6241         }
6242         if (!usage_size_ok) {
6243             fprintf(fp, "  malloc_usable_size unavailable\n");
6244         }
6245         {
6246             int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 };
6247             int class_id;
6248             struct list_head *el;
6249             list_for_each(el, &rt->gc_obj_list) {
6250                 JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
6251                 JSObject *p;
6252                 if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
6253                     p = (JSObject *)gp;
6254                     obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++;
6255                 }
6256             }
6257             fprintf(fp, "\n" "JSObject classes\n");
6258             if (obj_classes[0])
6259                 fprintf(fp, "  %5d  %2.0d %s\n", obj_classes[0], 0, "none");
6260             for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) {
6261                 if (obj_classes[class_id]) {
6262                     char buf[ATOM_GET_STR_BUF_SIZE];
6263                     fprintf(fp, "  %5d  %2.0d %s\n", obj_classes[class_id], class_id,
6264                             JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name));
6265                 }
6266             }
6267             if (obj_classes[JS_CLASS_INIT_COUNT])
6268                 fprintf(fp, "  %5d  %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other");
6269         }
6270         fprintf(fp, "\n");
6271     }
6272 #endif
6273     fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE");
6274 
6275     if (s->malloc_count) {
6276         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per block)\n",
6277                 "memory allocated", s->malloc_count, s->malloc_size,
6278                 (double)s->malloc_size / s->malloc_count);
6279         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%d overhead, %0.1f average slack)\n",
6280                 "memory used", s->memory_used_count, s->memory_used_size,
6281                 MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) /
6282                                   s->memory_used_count));
6283     }
6284     if (s->atom_count) {
6285         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per atom)\n",
6286                 "atoms", s->atom_count, s->atom_size,
6287                 (double)s->atom_size / s->atom_count);
6288     }
6289     if (s->str_count) {
6290         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per string)\n",
6291                 "strings", s->str_count, s->str_size,
6292                 (double)s->str_size / s->str_count);
6293     }
6294     if (s->obj_count) {
6295         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per object)\n",
6296                 "objects", s->obj_count, s->obj_size,
6297                 (double)s->obj_size / s->obj_count);
6298         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per object)\n",
6299                 "  properties", s->prop_count, s->prop_size,
6300                 (double)s->prop_count / s->obj_count);
6301         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per shape)\n",
6302                 "  shapes", s->shape_count, s->shape_size,
6303                 (double)s->shape_size / s->shape_count);
6304     }
6305     if (s->js_func_count) {
6306         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
6307                 "bytecode functions", s->js_func_count, s->js_func_size);
6308         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per function)\n",
6309                 "  bytecode", s->js_func_count, s->js_func_code_size,
6310                 (double)s->js_func_code_size / s->js_func_count);
6311         if (s->js_func_pc2line_count) {
6312             fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per function)\n",
6313                     "  pc2line", s->js_func_pc2line_count,
6314                     s->js_func_pc2line_size,
6315                     (double)s->js_func_pc2line_size / s->js_func_pc2line_count);
6316         }
6317     }
6318     if (s->c_func_count) {
6319         fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);
6320     }
6321     if (s->array_count) {
6322         fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count);
6323         if (s->fast_array_count) {
6324             fprintf(fp, "%-20s %8"PRId64"\n", "  fast arrays", s->fast_array_count);
6325             fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per fast array)\n",
6326                     "  elements", s->fast_array_elements,
6327                     s->fast_array_elements * (int)sizeof(JSValue),
6328                     (double)s->fast_array_elements / s->fast_array_count);
6329         }
6330     }
6331     if (s->binary_object_count) {
6332         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
6333                 "binary objects", s->binary_object_count, s->binary_object_size);
6334     }
6335 }
6336 
JS_GetGlobalObject(JSContext * ctx)6337 JSValue JS_GetGlobalObject(JSContext *ctx)
6338 {
6339     return JS_DupValue(ctx, ctx->global_obj);
6340 }
6341 
6342 /* WARNING: obj is freed */
JS_Throw(JSContext * ctx,JSValue obj)6343 JSValue JS_Throw(JSContext *ctx, JSValue obj)
6344 {
6345     JSRuntime *rt = ctx->rt;
6346     JS_FreeValue(ctx, rt->current_exception);
6347     rt->current_exception = obj;
6348     return JS_EXCEPTION;
6349 }
6350 
6351 /* return the pending exception (cannot be called twice). */
JS_GetException(JSContext * ctx)6352 JSValue JS_GetException(JSContext *ctx)
6353 {
6354     JSValue val;
6355     JSRuntime *rt = ctx->rt;
6356     val = rt->current_exception;
6357     rt->current_exception = JS_NULL;
6358     return val;
6359 }
6360 
dbuf_put_leb128(DynBuf * s,uint32_t v)6361 static void dbuf_put_leb128(DynBuf *s, uint32_t v)
6362 {
6363     uint32_t a;
6364     for(;;) {
6365         a = v & 0x7f;
6366         v >>= 7;
6367         if (v != 0) {
6368             dbuf_putc(s, a | 0x80);
6369         } else {
6370             dbuf_putc(s, a);
6371             break;
6372         }
6373     }
6374 }
6375 
dbuf_put_sleb128(DynBuf * s,int32_t v1)6376 static void dbuf_put_sleb128(DynBuf *s, int32_t v1)
6377 {
6378     uint32_t v = v1;
6379     dbuf_put_leb128(s, (2 * v) ^ -(v >> 31));
6380 }
6381 
get_leb128(uint32_t * pval,const uint8_t * buf,const uint8_t * buf_end)6382 static int get_leb128(uint32_t *pval, const uint8_t *buf,
6383                       const uint8_t *buf_end)
6384 {
6385     const uint8_t *ptr = buf;
6386     uint32_t v, a, i;
6387     v = 0;
6388     for(i = 0; i < 5; i++) {
6389         if (unlikely(ptr >= buf_end))
6390             break;
6391         a = *ptr++;
6392         v |= (a & 0x7f) << (i * 7);
6393         if (!(a & 0x80)) {
6394             *pval = v;
6395             return ptr - buf;
6396         }
6397     }
6398     *pval = 0;
6399     return -1;
6400 }
6401 
get_sleb128(int32_t * pval,const uint8_t * buf,const uint8_t * buf_end)6402 static int get_sleb128(int32_t *pval, const uint8_t *buf,
6403                        const uint8_t *buf_end)
6404 {
6405     int ret;
6406     uint32_t val;
6407     ret = get_leb128(&val, buf, buf_end);
6408     if (ret < 0) {
6409         *pval = 0;
6410         return -1;
6411     }
6412     *pval = (val >> 1) ^ -(val & 1);
6413     return ret;
6414 }
6415 
find_line_num(JSContext * ctx,JSFunctionBytecode * b,uint32_t pc_value)6416 static int find_line_num(JSContext *ctx, JSFunctionBytecode *b,
6417                          uint32_t pc_value)
6418 {
6419     const uint8_t *p_end, *p;
6420     int new_line_num, line_num, pc, v, ret;
6421     unsigned int op;
6422 
6423     if (!b->has_debug || !b->debug.pc2line_buf) {
6424         /* function was stripped */
6425         return -1;
6426     }
6427 
6428     p = b->debug.pc2line_buf;
6429     p_end = p + b->debug.pc2line_len;
6430     pc = 0;
6431     line_num = b->debug.line_num;
6432     while (p < p_end) {
6433         op = *p++;
6434         if (op == 0) {
6435             uint32_t val;
6436             ret = get_leb128(&val, p, p_end);
6437             if (ret < 0)
6438                 goto fail;
6439             pc += val;
6440             p += ret;
6441             ret = get_sleb128(&v, p, p_end);
6442             if (ret < 0) {
6443             fail:
6444                 /* should never happen */
6445                 return b->debug.line_num;
6446             }
6447             p += ret;
6448             new_line_num = line_num + v;
6449         } else {
6450             op -= PC2LINE_OP_FIRST;
6451             pc += (op / PC2LINE_RANGE);
6452             new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE;
6453         }
6454         if (pc_value < pc)
6455             return line_num;
6456         line_num = new_line_num;
6457     }
6458     return line_num;
6459 }
6460 
6461 /* in order to avoid executing arbitrary code during the stack trace
6462    generation, we only look at simple 'name' properties containing a
6463    string. */
get_func_name(JSContext * ctx,JSValueConst func)6464 static const char *get_func_name(JSContext *ctx, JSValueConst func)
6465 {
6466     JSProperty *pr;
6467     JSShapeProperty *prs;
6468     JSValueConst val;
6469 
6470     if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)
6471         return NULL;
6472     prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name);
6473     if (!prs)
6474         return NULL;
6475     if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
6476         return NULL;
6477     val = pr->u.value;
6478     if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
6479         return NULL;
6480     return JS_ToCString(ctx, val);
6481 }
6482 
6483 #define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)
6484 /* only taken into account if filename is provided */
6485 #define JS_BACKTRACE_FLAG_SINGLE_LEVEL     (1 << 1)
6486 
6487 /* if filename != NULL, an additional level is added with the filename
6488    and line number information (used for parse error). */
build_backtrace(JSContext * ctx,JSValueConst error_obj,const char * filename,int line_num,int backtrace_flags)6489 static void build_backtrace(JSContext *ctx, JSValueConst error_obj,
6490                             const char *filename, int line_num,
6491                             int backtrace_flags)
6492 {
6493     JSStackFrame *sf;
6494     JSValue str;
6495     DynBuf dbuf;
6496     const char *func_name_str;
6497     const char *str1;
6498     JSObject *p;
6499     BOOL backtrace_barrier;
6500 
6501     js_dbuf_init(ctx, &dbuf);
6502     if (filename) {
6503         dbuf_printf(&dbuf, "    at %s", filename);
6504         if (line_num != -1) {
6505 #ifdef ENABLE_JS_DEBUG
6506             dbuf_printf(&dbuf, ":%d", (line_num - DEBUG_MODE_LINE_DEVIATION));
6507 #else
6508             dbuf_printf(&dbuf, ":%d", (line_num - RELEASE_MODE_LINE_DEVIATION));
6509 #endif
6510         }
6511         dbuf_putc(&dbuf, '\n');
6512         str = JS_NewString(ctx, filename);
6513         JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str,
6514                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6515         JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num),
6516                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6517         if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)
6518             goto done;
6519     }
6520     for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {
6521         if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {
6522             backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
6523             continue;
6524         }
6525         func_name_str = get_func_name(ctx, sf->cur_func);
6526         if (!func_name_str || func_name_str[0] == '\0')
6527             str1 = "<anonymous>";
6528         else
6529             str1 = func_name_str;
6530         dbuf_printf(&dbuf, "    at %s", str1);
6531         JS_FreeCString(ctx, func_name_str);
6532 
6533         p = JS_VALUE_GET_OBJ(sf->cur_func);
6534         backtrace_barrier = FALSE;
6535         if (js_class_has_bytecode(p->class_id)) {
6536             JSFunctionBytecode *b;
6537             const char *atom_str;
6538             int line_num1;
6539 
6540             b = p->u.func.function_bytecode;
6541             backtrace_barrier = b->backtrace_barrier;
6542             if (b->has_debug) {
6543                 line_num1 = find_line_num(ctx, b,
6544                                           sf->cur_pc - b->byte_code_buf - 1);
6545                 atom_str = JS_AtomToCString(ctx, b->debug.filename);
6546                 dbuf_printf(&dbuf, " (%s",
6547                             atom_str ? atom_str : "<null>");
6548                 JS_FreeCString(ctx, atom_str);
6549                 if (line_num1 != -1) {
6550 #ifdef ENABLE_JS_DEBUG
6551                     dbuf_printf(&dbuf, ":%d", line_num1 - DEBUG_MODE_LINE_DEVIATION);
6552 #else
6553                     dbuf_printf(&dbuf, ":%d", line_num1 - RELEASE_MODE_LINE_DEVIATION);
6554 #endif
6555                 }
6556                 dbuf_putc(&dbuf, ')');
6557             }
6558         } else {
6559             dbuf_printf(&dbuf, " (native)");
6560         }
6561         dbuf_putc(&dbuf, '\n');
6562         /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */
6563         if (backtrace_barrier)
6564             break;
6565     }
6566  done:
6567     dbuf_putc(&dbuf, '\0');
6568     if (dbuf_error(&dbuf))
6569         str = JS_NULL;
6570     else
6571         str = JS_NewString(ctx, (char *)dbuf.buf);
6572     dbuf_free(&dbuf);
6573     JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str,
6574                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6575 }
6576 
6577 /* Note: it is important that no exception is returned by this function */
is_backtrace_needed(JSContext * ctx,JSValueConst obj)6578 static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj)
6579 {
6580     JSObject *p;
6581     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
6582         return FALSE;
6583     p = JS_VALUE_GET_OBJ(obj);
6584     if (p->class_id != JS_CLASS_ERROR)
6585         return FALSE;
6586     if (find_own_property1(p, JS_ATOM_stack))
6587         return FALSE;
6588     return TRUE;
6589 }
6590 
JS_NewError(JSContext * ctx)6591 JSValue JS_NewError(JSContext *ctx)
6592 {
6593     return JS_NewObjectClass(ctx, JS_CLASS_ERROR);
6594 }
6595 
JS_ThrowError2(JSContext * ctx,JSErrorEnum error_num,const char * fmt,va_list ap,BOOL add_backtrace)6596 static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num,
6597                               const char *fmt, va_list ap, BOOL add_backtrace)
6598 {
6599     char buf[256];
6600     JSValue obj, ret;
6601 
6602     vsnprintf(buf, sizeof(buf), fmt, ap);
6603     obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num],
6604                                  JS_CLASS_ERROR);
6605     if (unlikely(JS_IsException(obj))) {
6606         /* out of memory: throw JS_NULL to avoid recursing */
6607         obj = JS_NULL;
6608     } else {
6609         JS_DefinePropertyValue(ctx, obj, JS_ATOM_message,
6610                                JS_NewString(ctx, buf),
6611                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6612     }
6613     if (add_backtrace) {
6614         build_backtrace(ctx, obj, NULL, 0, 0);
6615     }
6616     ret = JS_Throw(ctx, obj);
6617     return ret;
6618 }
6619 
JS_ThrowError(JSContext * ctx,JSErrorEnum error_num,const char * fmt,va_list ap)6620 static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num,
6621                              const char *fmt, va_list ap)
6622 {
6623     JSRuntime *rt = ctx->rt;
6624     JSStackFrame *sf;
6625     BOOL add_backtrace;
6626 
6627     /* the backtrace is added later if called from a bytecode function */
6628     sf = rt->current_stack_frame;
6629     add_backtrace = !rt->in_out_of_memory &&
6630         (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL));
6631     return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace);
6632 }
6633 
JS_ThrowSyntaxError(JSContext * ctx,const char * fmt,...)6634 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)
6635 {
6636     JSValue val;
6637     va_list ap;
6638 
6639     va_start(ap, fmt);
6640     val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap);
6641     va_end(ap);
6642     return val;
6643 }
6644 
JS_ThrowTypeError(JSContext * ctx,const char * fmt,...)6645 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)
6646 {
6647     JSValue val;
6648     va_list ap;
6649 
6650     va_start(ap, fmt);
6651     val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
6652     va_end(ap);
6653     return val;
6654 }
6655 
JS_ThrowTypeErrorOrFalse(JSContext * ctx,int flags,const char * fmt,...)6656 static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)
6657 {
6658     va_list ap;
6659 
6660     if ((flags & JS_PROP_THROW) ||
6661         ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
6662         va_start(ap, fmt);
6663         JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
6664         va_end(ap);
6665         return -1;
6666     } else {
6667         return FALSE;
6668     }
6669 }
6670 
6671 /* never use it directly */
__JS_ThrowTypeErrorAtom(JSContext * ctx,JSAtom atom,const char * fmt,...)6672 static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
6673 {
6674     char buf[ATOM_GET_STR_BUF_SIZE];
6675     return JS_ThrowTypeError(ctx, fmt,
6676                              JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
6677 }
6678 
6679 /* never use it directly */
__JS_ThrowSyntaxErrorAtom(JSContext * ctx,JSAtom atom,const char * fmt,...)6680 static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
6681 {
6682     char buf[ATOM_GET_STR_BUF_SIZE];
6683     return JS_ThrowSyntaxError(ctx, fmt,
6684                              JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
6685 }
6686 
6687 /* %s is replaced by 'atom'. The macro is used so that gcc can check
6688     the format string. */
6689 #define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "")
6690 #define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "")
6691 
JS_ThrowTypeErrorReadOnly(JSContext * ctx,int flags,JSAtom atom)6692 static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)
6693 {
6694     if ((flags & JS_PROP_THROW) ||
6695         ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
6696         JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom);
6697         return -1;
6698     } else {
6699         return FALSE;
6700     }
6701 }
6702 
JS_ThrowReferenceError(JSContext * ctx,const char * fmt,...)6703 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)
6704 {
6705     JSValue val;
6706     va_list ap;
6707 
6708     va_start(ap, fmt);
6709     val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap);
6710     va_end(ap);
6711     return val;
6712 }
6713 
JS_ThrowRangeError(JSContext * ctx,const char * fmt,...)6714 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)
6715 {
6716     JSValue val;
6717     va_list ap;
6718 
6719     va_start(ap, fmt);
6720     val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap);
6721     va_end(ap);
6722     return val;
6723 }
6724 
JS_ThrowInternalError(JSContext * ctx,const char * fmt,...)6725 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)
6726 {
6727     JSValue val;
6728     va_list ap;
6729 
6730     va_start(ap, fmt);
6731     val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap);
6732     va_end(ap);
6733     return val;
6734 }
6735 
JS_ThrowOutOfMemory(JSContext * ctx)6736 JSValue JS_ThrowOutOfMemory(JSContext *ctx)
6737 {
6738     JSRuntime *rt = ctx->rt;
6739     if (!rt->in_out_of_memory) {
6740         rt->in_out_of_memory = TRUE;
6741         JS_ThrowInternalError(ctx, "out of memory");
6742         rt->in_out_of_memory = FALSE;
6743     }
6744     return JS_EXCEPTION;
6745 }
6746 
JS_ThrowStackOverflow(JSContext * ctx)6747 static JSValue JS_ThrowStackOverflow(JSContext *ctx)
6748 {
6749     return JS_ThrowInternalError(ctx, "stack overflow");
6750 }
6751 
JS_ThrowTypeErrorNotAnObject(JSContext * ctx)6752 static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx)
6753 {
6754     return JS_ThrowTypeError(ctx, "not an object");
6755 }
6756 
JS_ThrowTypeErrorNotASymbol(JSContext * ctx)6757 static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx)
6758 {
6759     return JS_ThrowTypeError(ctx, "not a symbol");
6760 }
6761 
JS_ThrowReferenceErrorNotDefined(JSContext * ctx,JSAtom name)6762 static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name)
6763 {
6764     char buf[ATOM_GET_STR_BUF_SIZE];
6765     return JS_ThrowReferenceError(ctx, "'%s' is not defined",
6766                                   JS_AtomGetStr(ctx, buf, sizeof(buf), name));
6767 }
6768 
JS_ThrowReferenceErrorUninitialized(JSContext * ctx,JSAtom name)6769 static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name)
6770 {
6771     char buf[ATOM_GET_STR_BUF_SIZE];
6772     return JS_ThrowReferenceError(ctx, "%s is not initialized",
6773                                   name == JS_ATOM_NULL ? "lexical variable" :
6774                                   JS_AtomGetStr(ctx, buf, sizeof(buf), name));
6775 }
6776 
JS_ThrowReferenceErrorUninitialized2(JSContext * ctx,JSFunctionBytecode * b,int idx,BOOL is_ref)6777 static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx,
6778                                                     JSFunctionBytecode *b,
6779                                                     int idx, BOOL is_ref)
6780 {
6781     JSAtom atom = JS_ATOM_NULL;
6782     if (is_ref) {
6783         atom = b->closure_var[idx].var_name;
6784     } else {
6785         /* not present if the function is stripped and contains no eval() */
6786         if (b->vardefs)
6787             atom = b->vardefs[b->arg_count + idx].var_name;
6788     }
6789     return JS_ThrowReferenceErrorUninitialized(ctx, atom);
6790 }
6791 
JS_ThrowTypeErrorInvalidClass(JSContext * ctx,int class_id)6792 static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)
6793 {
6794     JSRuntime *rt = ctx->rt;
6795     JSAtom name;
6796     name = rt->class_array[class_id].class_name;
6797     return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);
6798 }
6799 
__js_poll_interrupts(JSContext * ctx)6800 static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
6801 {
6802     JSRuntime *rt = ctx->rt;
6803     ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;
6804     if (rt->interrupt_handler) {
6805         if (rt->interrupt_handler(rt, rt->interrupt_opaque)) {
6806             /* XXX: should set a specific flag to avoid catching */
6807             JS_ThrowInternalError(ctx, "interrupted");
6808             JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE);
6809             return -1;
6810         }
6811     }
6812     return 0;
6813 }
6814 
js_poll_interrupts(JSContext * ctx)6815 static inline __exception int js_poll_interrupts(JSContext *ctx)
6816 {
6817     if (unlikely(--ctx->interrupt_counter <= 0)) {
6818         return __js_poll_interrupts(ctx);
6819     } else {
6820         return 0;
6821     }
6822 }
6823 
6824 /* return -1 (exception) or TRUE/FALSE */
JS_SetPrototypeInternal(JSContext * ctx,JSValueConst obj,JSValueConst proto_val,BOOL throw_flag)6825 static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj,
6826                                    JSValueConst proto_val,
6827                                    BOOL throw_flag)
6828 {
6829     JSObject *proto, *p, *p1;
6830     JSShape *sh;
6831 
6832     if (throw_flag) {
6833         if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL ||
6834             JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED)
6835             goto not_obj;
6836     } else {
6837         if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
6838             goto not_obj;
6839     }
6840     p = JS_VALUE_GET_OBJ(obj);
6841     if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) {
6842         if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) {
6843         not_obj:
6844             JS_ThrowTypeErrorNotAnObject(ctx);
6845             return -1;
6846         }
6847         proto = NULL;
6848     } else {
6849         proto = JS_VALUE_GET_OBJ(proto_val);
6850     }
6851 
6852     if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
6853         return TRUE;
6854 
6855     if (unlikely(p->class_id == JS_CLASS_PROXY))
6856         return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag);
6857     sh = p->shape;
6858     if (sh->proto == proto)
6859         return TRUE;
6860     if (!p->extensible) {
6861         if (throw_flag) {
6862             JS_ThrowTypeError(ctx, "object is not extensible");
6863             return -1;
6864         } else {
6865             return FALSE;
6866         }
6867     }
6868     if (proto) {
6869         /* check if there is a cycle */
6870         p1 = proto;
6871         do {
6872             if (p1 == p) {
6873                 if (throw_flag) {
6874                     JS_ThrowTypeError(ctx, "circular prototype chain");
6875                     return -1;
6876                 } else {
6877                     return FALSE;
6878                 }
6879             }
6880             /* Note: for Proxy objects, proto is NULL */
6881             p1 = p1->shape->proto;
6882         } while (p1 != NULL);
6883         JS_DupValue(ctx, proto_val);
6884     }
6885 
6886     if (js_shape_prepare_update(ctx, p, NULL))
6887         return -1;
6888     sh = p->shape;
6889     if (sh->proto)
6890         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
6891     sh->proto = proto;
6892     return TRUE;
6893 }
6894 
6895 /* return -1 (exception) or TRUE/FALSE */
JS_SetPrototype(JSContext * ctx,JSValueConst obj,JSValueConst proto_val)6896 int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val)
6897 {
6898     return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE);
6899 }
6900 
6901 /* Only works for primitive types, otherwise return JS_NULL. */
JS_GetPrototypePrimitive(JSContext * ctx,JSValueConst val)6902 static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)
6903 {
6904     switch(JS_VALUE_GET_NORM_TAG(val)) {
6905 #ifdef CONFIG_BIGNUM
6906     case JS_TAG_BIG_INT:
6907         val = ctx->class_proto[JS_CLASS_BIG_INT];
6908         break;
6909     case JS_TAG_BIG_FLOAT:
6910         val = ctx->class_proto[JS_CLASS_BIG_FLOAT];
6911         break;
6912     case JS_TAG_BIG_DECIMAL:
6913         val = ctx->class_proto[JS_CLASS_BIG_DECIMAL];
6914         break;
6915 #endif
6916     case JS_TAG_INT:
6917     case JS_TAG_FLOAT64:
6918         val = ctx->class_proto[JS_CLASS_NUMBER];
6919         break;
6920     case JS_TAG_BOOL:
6921         val = ctx->class_proto[JS_CLASS_BOOLEAN];
6922         break;
6923     case JS_TAG_STRING:
6924         val = ctx->class_proto[JS_CLASS_STRING];
6925         break;
6926     case JS_TAG_SYMBOL:
6927         val = ctx->class_proto[JS_CLASS_SYMBOL];
6928         break;
6929     case JS_TAG_OBJECT:
6930     case JS_TAG_NULL:
6931     case JS_TAG_UNDEFINED:
6932     default:
6933         val = JS_NULL;
6934         break;
6935     }
6936     return val;
6937 }
6938 
6939 /* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */
JS_GetPrototype(JSContext * ctx,JSValueConst obj)6940 JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj)
6941 {
6942     JSValue val;
6943     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
6944         JSObject *p;
6945         p = JS_VALUE_GET_OBJ(obj);
6946         if (unlikely(p->class_id == JS_CLASS_PROXY)) {
6947             val = js_proxy_getPrototypeOf(ctx, obj);
6948         } else {
6949             p = p->shape->proto;
6950             if (!p)
6951                 val = JS_NULL;
6952             else
6953                 val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
6954         }
6955     } else {
6956         val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj));
6957     }
6958     return val;
6959 }
6960 
JS_GetPrototypeFree(JSContext * ctx,JSValue obj)6961 static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj)
6962 {
6963     JSValue obj1;
6964     obj1 = JS_GetPrototype(ctx, obj);
6965     JS_FreeValue(ctx, obj);
6966     return obj1;
6967 }
6968 
6969 /* return TRUE, FALSE or (-1) in case of exception */
JS_OrdinaryIsInstanceOf(JSContext * ctx,JSValueConst val,JSValueConst obj)6970 static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,
6971                                    JSValueConst obj)
6972 {
6973     JSValue obj_proto;
6974     JSObject *proto;
6975     const JSObject *p, *proto1;
6976     BOOL ret;
6977 
6978     if (!JS_IsFunction(ctx, obj))
6979         return FALSE;
6980     p = JS_VALUE_GET_OBJ(obj);
6981     if (p->class_id == JS_CLASS_BOUND_FUNCTION) {
6982         JSBoundFunction *s = p->u.bound_function;
6983         return JS_IsInstanceOf(ctx, val, s->func_obj);
6984     }
6985 
6986     /* Only explicitly boxed values are instances of constructors */
6987     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
6988         return FALSE;
6989     obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype);
6990     if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) {
6991         if (!JS_IsException(obj_proto))
6992             JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object");
6993         ret = -1;
6994         goto done;
6995     }
6996     proto = JS_VALUE_GET_OBJ(obj_proto);
6997     p = JS_VALUE_GET_OBJ(val);
6998     for(;;) {
6999         proto1 = p->shape->proto;
7000         if (!proto1) {
7001             /* slow case if proxy in the prototype chain */
7002             if (unlikely(p->class_id == JS_CLASS_PROXY)) {
7003                 JSValue obj1;
7004                 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p));
7005                 for(;;) {
7006                     obj1 = JS_GetPrototypeFree(ctx, obj1);
7007                     if (JS_IsException(obj1)) {
7008                         ret = -1;
7009                         break;
7010                     }
7011                     if (JS_IsNull(obj1)) {
7012                         ret = FALSE;
7013                         break;
7014                     }
7015                     if (proto == JS_VALUE_GET_OBJ(obj1)) {
7016                         JS_FreeValue(ctx, obj1);
7017                         ret = TRUE;
7018                         break;
7019                     }
7020                     /* must check for timeout to avoid infinite loop */
7021                     if (js_poll_interrupts(ctx)) {
7022                         JS_FreeValue(ctx, obj1);
7023                         ret = -1;
7024                         break;
7025                     }
7026                 }
7027             } else {
7028                 ret = FALSE;
7029             }
7030             break;
7031         }
7032         p = proto1;
7033         if (proto == p) {
7034             ret = TRUE;
7035             break;
7036         }
7037     }
7038 done:
7039     JS_FreeValue(ctx, obj_proto);
7040     return ret;
7041 }
7042 
7043 /* return TRUE, FALSE or (-1) in case of exception */
JS_IsInstanceOf(JSContext * ctx,JSValueConst val,JSValueConst obj)7044 int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj)
7045 {
7046     JSValue method;
7047 
7048     if (!JS_IsObject(obj))
7049         goto fail;
7050     method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance);
7051     if (JS_IsException(method))
7052         return -1;
7053     if (!JS_IsNull(method) && !JS_IsUndefined(method)) {
7054         JSValue ret;
7055         ret = JS_CallFree(ctx, method, obj, 1, &val);
7056         return JS_ToBoolFree(ctx, ret);
7057     }
7058 
7059     /* legacy case */
7060     if (!JS_IsFunction(ctx, obj)) {
7061     fail:
7062         JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand");
7063         return -1;
7064     }
7065     return JS_OrdinaryIsInstanceOf(ctx, val, obj);
7066 }
7067 
7068 /* return the value associated to the autoinit property or an exception */
7069 typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
7070 
7071 static JSAutoInitFunc *js_autoinit_func_table[] = {
7072     js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */
7073     js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */
7074     JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */
7075 };
7076 
7077 /* warning: 'prs' is reallocated after it */
JS_AutoInitProperty(JSContext * ctx,JSObject * p,JSAtom prop,JSProperty * pr,JSShapeProperty * prs)7078 static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,
7079                                JSProperty *pr, JSShapeProperty *prs)
7080 {
7081     JSValue val;
7082     JSContext *realm;
7083     JSAutoInitFunc *func;
7084 
7085     if (js_shape_prepare_update(ctx, p, &prs))
7086         return -1;
7087 
7088     realm = js_autoinit_get_realm(pr);
7089     func = js_autoinit_func_table[js_autoinit_get_id(pr)];
7090     /* 'func' shall not modify the object properties 'pr' */
7091     val = func(realm, p, prop, pr->u.init.opaque);
7092     js_autoinit_free(ctx->rt, pr);
7093     prs->flags &= ~JS_PROP_TMASK;
7094     pr->u.value = JS_UNDEFINED;
7095     if (JS_IsException(val))
7096         return -1;
7097     pr->u.value = val;
7098     return 0;
7099 }
7100 
JS_GetPropertyInternal(JSContext * ctx,JSValueConst obj,JSAtom prop,JSValueConst this_obj,BOOL throw_ref_error)7101 JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
7102                                JSAtom prop, JSValueConst this_obj,
7103                                BOOL throw_ref_error)
7104 {
7105     JSObject *p;
7106     JSProperty *pr;
7107     JSShapeProperty *prs;
7108     uint32_t tag;
7109 
7110     tag = JS_VALUE_GET_TAG(obj);
7111     if (unlikely(tag != JS_TAG_OBJECT)) {
7112         switch(tag) {
7113         case JS_TAG_NULL:
7114             return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop);
7115         case JS_TAG_UNDEFINED:
7116             return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop);
7117         case JS_TAG_EXCEPTION:
7118             return JS_EXCEPTION;
7119         case JS_TAG_STRING:
7120             {
7121                 JSString *p1 = JS_VALUE_GET_STRING(obj);
7122                 if (__JS_AtomIsTaggedInt(prop)) {
7123                     uint32_t idx, ch;
7124                     idx = __JS_AtomToUInt32(prop);
7125                     if (idx < p1->len) {
7126                         if (p1->is_wide_char)
7127                             ch = p1->u.str16[idx];
7128                         else
7129                             ch = p1->u.str8[idx];
7130                         return js_new_string_char(ctx, ch);
7131                     }
7132                 } else if (prop == JS_ATOM_length) {
7133                     return JS_NewInt32(ctx, p1->len);
7134                 }
7135             }
7136             break;
7137         default:
7138             break;
7139         }
7140         /* cannot raise an exception */
7141         p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));
7142         if (!p)
7143             return JS_UNDEFINED;
7144     } else {
7145         p = JS_VALUE_GET_OBJ(obj);
7146     }
7147 
7148     for(;;) {
7149         prs = find_own_property(&pr, p, prop);
7150         if (prs) {
7151             /* found */
7152             if (unlikely(prs->flags & JS_PROP_TMASK)) {
7153                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
7154                     if (unlikely(!pr->u.getset.getter)) {
7155                         return JS_UNDEFINED;
7156                     } else {
7157                         JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter);
7158                         /* Note: the field could be removed in the getter */
7159                         func = JS_DupValue(ctx, func);
7160                         return JS_CallFree(ctx, func, this_obj, 0, NULL);
7161                     }
7162                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
7163                     JSValue val = *pr->u.var_ref->pvalue;
7164                     if (unlikely(JS_IsUninitialized(val)))
7165                         return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
7166                     return JS_DupValue(ctx, val);
7167                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
7168                     /* Instantiate property and retry */
7169                     if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
7170                         return JS_EXCEPTION;
7171                     continue;
7172                 }
7173             } else {
7174                 return JS_DupValue(ctx, pr->u.value);
7175             }
7176         }
7177         if (unlikely(p->is_exotic)) {
7178             /* exotic behaviors */
7179             if (p->fast_array) {
7180                 if (__JS_AtomIsTaggedInt(prop)) {
7181                     uint32_t idx = __JS_AtomToUInt32(prop);
7182                     if (idx < p->u.array.count) {
7183                         /* we avoid duplicating the code */
7184                         return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
7185                     } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
7186                                p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
7187                         return JS_UNDEFINED;
7188                     }
7189                 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
7190                            p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
7191                     int ret;
7192                     ret = JS_AtomIsNumericIndex(ctx, prop);
7193                     if (ret != 0) {
7194                         if (ret < 0)
7195                             return JS_EXCEPTION;
7196                         return JS_UNDEFINED;
7197                     }
7198                 }
7199             } else {
7200                 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7201                 if (em) {
7202                     if (em->get_property) {
7203                         JSValue obj1, retval;
7204                         /* XXX: should pass throw_ref_error */
7205                         /* Note: if 'p' is a prototype, it can be
7206                            freed in the called function */
7207                         obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7208                         retval = em->get_property(ctx, obj1, prop, this_obj);
7209                         JS_FreeValue(ctx, obj1);
7210                         return retval;
7211                     }
7212                     if (em->get_own_property) {
7213                         JSPropertyDescriptor desc;
7214                         int ret;
7215                         JSValue obj1;
7216 
7217                         /* Note: if 'p' is a prototype, it can be
7218                            freed in the called function */
7219                         obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7220                         ret = em->get_own_property(ctx, &desc, obj1, prop);
7221                         JS_FreeValue(ctx, obj1);
7222                         if (ret < 0)
7223                             return JS_EXCEPTION;
7224                         if (ret) {
7225                             if (desc.flags & JS_PROP_GETSET) {
7226                                 JS_FreeValue(ctx, desc.setter);
7227                                 return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL);
7228                             } else {
7229                                 return desc.value;
7230                             }
7231                         }
7232                     }
7233                 }
7234             }
7235         }
7236         p = p->shape->proto;
7237         if (!p)
7238             break;
7239     }
7240     if (unlikely(throw_ref_error)) {
7241         return JS_ThrowReferenceErrorNotDefined(ctx, prop);
7242     } else {
7243         return JS_UNDEFINED;
7244     }
7245 }
7246 
JS_ThrowTypeErrorPrivateNotFound(JSContext * ctx,JSAtom atom)7247 static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom)
7248 {
7249     return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist",
7250                                  atom);
7251 }
7252 
7253 /* Private fields can be added even on non extensible objects or
7254    Proxies */
JS_DefinePrivateField(JSContext * ctx,JSValueConst obj,JSValueConst name,JSValue val)7255 static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,
7256                                  JSValueConst name, JSValue val)
7257 {
7258     JSObject *p;
7259     JSShapeProperty *prs;
7260     JSProperty *pr;
7261     JSAtom prop;
7262 
7263     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
7264         JS_ThrowTypeErrorNotAnObject(ctx);
7265         goto fail;
7266     }
7267     /* safety check */
7268     if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
7269         JS_ThrowTypeErrorNotASymbol(ctx);
7270         goto fail;
7271     }
7272     prop = js_symbol_to_atom(ctx, (JSValue)name);
7273     p = JS_VALUE_GET_OBJ(obj);
7274     prs = find_own_property(&pr, p, prop);
7275     if (prs) {
7276         JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists",
7277                               prop);
7278         goto fail;
7279     }
7280     pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
7281     if (unlikely(!pr)) {
7282     fail:
7283         JS_FreeValue(ctx, val);
7284         return -1;
7285     }
7286     pr->u.value = val;
7287     return 0;
7288 }
7289 
JS_GetPrivateField(JSContext * ctx,JSValueConst obj,JSValueConst name)7290 static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj,
7291                                   JSValueConst name)
7292 {
7293     JSObject *p;
7294     JSShapeProperty *prs;
7295     JSProperty *pr;
7296     JSAtom prop;
7297 
7298     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
7299         return JS_ThrowTypeErrorNotAnObject(ctx);
7300     /* safety check */
7301     if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL))
7302         return JS_ThrowTypeErrorNotASymbol(ctx);
7303     prop = js_symbol_to_atom(ctx, (JSValue)name);
7304     p = JS_VALUE_GET_OBJ(obj);
7305     prs = find_own_property(&pr, p, prop);
7306     if (!prs) {
7307         JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
7308         return JS_EXCEPTION;
7309     }
7310     return JS_DupValue(ctx, pr->u.value);
7311 }
7312 
JS_SetPrivateField(JSContext * ctx,JSValueConst obj,JSValueConst name,JSValue val)7313 static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,
7314                               JSValueConst name, JSValue val)
7315 {
7316     JSObject *p;
7317     JSShapeProperty *prs;
7318     JSProperty *pr;
7319     JSAtom prop;
7320 
7321     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
7322         JS_ThrowTypeErrorNotAnObject(ctx);
7323         goto fail;
7324     }
7325     /* safety check */
7326     if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
7327         JS_ThrowTypeErrorNotASymbol(ctx);
7328         goto fail;
7329     }
7330     prop = js_symbol_to_atom(ctx, (JSValue)name);
7331     p = JS_VALUE_GET_OBJ(obj);
7332     prs = find_own_property(&pr, p, prop);
7333     if (!prs) {
7334         JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
7335     fail:
7336         JS_FreeValue(ctx, val);
7337         return -1;
7338     }
7339     set_value(ctx, &pr->u.value, val);
7340     return 0;
7341 }
7342 
JS_AddBrand(JSContext * ctx,JSValueConst obj,JSValueConst home_obj)7343 static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
7344 {
7345     JSObject *p, *p1;
7346     JSShapeProperty *prs;
7347     JSProperty *pr;
7348     JSValue brand;
7349     JSAtom brand_atom;
7350 
7351     if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) {
7352         JS_ThrowTypeErrorNotAnObject(ctx);
7353         return -1;
7354     }
7355     p = JS_VALUE_GET_OBJ(home_obj);
7356     prs = find_own_property(&pr, p, JS_ATOM_Private_brand);
7357     if (!prs) {
7358         brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE);
7359         if (JS_IsException(brand))
7360             return -1;
7361         /* if the brand is not present, add it */
7362         pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E);
7363         if (!pr) {
7364             JS_FreeValue(ctx, brand);
7365             return -1;
7366         }
7367         pr->u.value = JS_DupValue(ctx, brand);
7368     } else {
7369         brand = JS_DupValue(ctx, pr->u.value);
7370     }
7371     brand_atom = js_symbol_to_atom(ctx, brand);
7372 
7373     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
7374         JS_ThrowTypeErrorNotAnObject(ctx);
7375         JS_FreeAtom(ctx, brand_atom);
7376         return -1;
7377     }
7378     p1 = JS_VALUE_GET_OBJ(obj);
7379     pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);
7380     JS_FreeAtom(ctx, brand_atom);
7381     if (!pr)
7382         return -1;
7383     pr->u.value = JS_UNDEFINED;
7384     return 0;
7385 }
7386 
JS_CheckBrand(JSContext * ctx,JSValueConst obj,JSValueConst func)7387 static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
7388 {
7389     JSObject *p, *p1, *home_obj;
7390     JSShapeProperty *prs;
7391     JSProperty *pr;
7392     JSValueConst brand;
7393 
7394     /* get the home object of 'func' */
7395     if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) {
7396     not_obj:
7397         JS_ThrowTypeErrorNotAnObject(ctx);
7398         return -1;
7399     }
7400     p1 = JS_VALUE_GET_OBJ(func);
7401     if (!js_class_has_bytecode(p1->class_id))
7402         goto not_obj;
7403     home_obj = p1->u.func.home_object;
7404     if (!home_obj)
7405         goto not_obj;
7406     prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand);
7407     if (!prs) {
7408         JS_ThrowTypeError(ctx, "expecting <brand> private field");
7409         return -1;
7410     }
7411     brand = pr->u.value;
7412     /* safety check */
7413     if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL))
7414         goto not_obj;
7415 
7416     /* get the brand array of 'obj' */
7417     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
7418         goto not_obj;
7419     p = JS_VALUE_GET_OBJ(obj);
7420     prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));
7421     if (!prs) {
7422         JS_ThrowTypeError(ctx, "invalid brand on object");
7423         return -1;
7424     }
7425     return 0;
7426 }
7427 
js_string_obj_get_length(JSContext * ctx,JSValueConst obj)7428 static uint32_t js_string_obj_get_length(JSContext *ctx,
7429                                          JSValueConst obj)
7430 {
7431     JSObject *p;
7432     JSString *p1;
7433     uint32_t len = 0;
7434 
7435     /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
7436     p = JS_VALUE_GET_OBJ(obj);
7437     if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
7438         p1 = JS_VALUE_GET_STRING(p->u.object_data);
7439         len = p1->len;
7440     }
7441     return len;
7442 }
7443 
num_keys_cmp(const void * p1,const void * p2,void * opaque)7444 static int num_keys_cmp(const void *p1, const void *p2, void *opaque)
7445 {
7446     JSContext *ctx = opaque;
7447     JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom;
7448     JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom;
7449     uint32_t v1, v2;
7450     BOOL atom1_is_integer, atom2_is_integer;
7451 
7452     atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1);
7453     atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2);
7454     assert(atom1_is_integer && atom2_is_integer);
7455     if (v1 < v2)
7456         return -1;
7457     else if (v1 == v2)
7458         return 0;
7459     else
7460         return 1;
7461 }
7462 
js_free_prop_enum(JSContext * ctx,JSPropertyEnum * tab,uint32_t len)7463 static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len)
7464 {
7465     uint32_t i;
7466     if (tab) {
7467         for(i = 0; i < len; i++)
7468             JS_FreeAtom(ctx, tab[i].atom);
7469         js_free(ctx, tab);
7470     }
7471 }
7472 
7473 /* return < 0 in case if exception, 0 if OK. ptab and its atoms must
7474    be freed by the user. */
JS_GetOwnPropertyNamesInternal(JSContext * ctx,JSPropertyEnum ** ptab,uint32_t * plen,JSObject * p,int flags)7475 static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,
7476                                                       JSPropertyEnum **ptab,
7477                                                       uint32_t *plen,
7478                                                       JSObject *p, int flags)
7479 {
7480     int i, j;
7481     JSShape *sh;
7482     JSShapeProperty *prs;
7483     JSPropertyEnum *tab_atom, *tab_exotic;
7484     JSAtom atom;
7485     uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count;
7486     uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count;
7487     BOOL is_enumerable, num_sorted;
7488     uint32_t num_key;
7489     JSAtomKindEnum kind;
7490 
7491     /* clear pointer for consistency in case of failure */
7492     *ptab = NULL;
7493     *plen = 0;
7494 
7495     /* compute the number of returned properties */
7496     num_keys_count = 0;
7497     str_keys_count = 0;
7498     sym_keys_count = 0;
7499     exotic_keys_count = 0;
7500     exotic_count = 0;
7501     tab_exotic = NULL;
7502     sh = p->shape;
7503     for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
7504         atom = prs->atom;
7505         if (atom != JS_ATOM_NULL) {
7506             is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
7507             kind = JS_AtomGetKind(ctx, atom);
7508             if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
7509                 ((flags >> kind) & 1) != 0) {
7510                 /* need to raise an exception in case of the module
7511                    name space (implicit GetOwnProperty) */
7512                 if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) &&
7513                     (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) {
7514                     JSVarRef *var_ref = p->prop[i].u.var_ref;
7515                     if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) {
7516                         JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
7517                         return -1;
7518                     }
7519                 }
7520                 if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
7521                     num_keys_count++;
7522                 } else if (kind == JS_ATOM_KIND_STRING) {
7523                     str_keys_count++;
7524                 } else {
7525                     sym_keys_count++;
7526                 }
7527             }
7528         }
7529     }
7530 
7531     if (p->is_exotic) {
7532         if (p->fast_array) {
7533             if (flags & JS_GPN_STRING_MASK) {
7534                 num_keys_count += p->u.array.count;
7535             }
7536         } else if (p->class_id == JS_CLASS_STRING) {
7537             if (flags & JS_GPN_STRING_MASK) {
7538                 num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7539             }
7540         } else {
7541             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7542             if (em && em->get_own_property_names) {
7543                 if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count,
7544                                                JS_MKPTR(JS_TAG_OBJECT, p)))
7545                     return -1;
7546                 for(i = 0; i < exotic_count; i++) {
7547                     atom = tab_exotic[i].atom;
7548                     kind = JS_AtomGetKind(ctx, atom);
7549                     if (((flags >> kind) & 1) != 0) {
7550                         is_enumerable = FALSE;
7551                         if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) {
7552                             JSPropertyDescriptor desc;
7553                             int res;
7554                             /* set the "is_enumerable" field if necessary */
7555                             res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
7556                             if (res < 0) {
7557                                 js_free_prop_enum(ctx, tab_exotic, exotic_count);
7558                                 return -1;
7559                             }
7560                             if (res) {
7561                                 is_enumerable =
7562                                     ((desc.flags & JS_PROP_ENUMERABLE) != 0);
7563                                 js_free_desc(ctx, &desc);
7564                             }
7565                             tab_exotic[i].is_enumerable = is_enumerable;
7566                         }
7567                         if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) {
7568                             exotic_keys_count++;
7569                         }
7570                     }
7571                 }
7572             }
7573         }
7574     }
7575 
7576     /* fill them */
7577 
7578     atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count;
7579     /* avoid allocating 0 bytes */
7580     tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1));
7581     if (!tab_atom) {
7582         js_free_prop_enum(ctx, tab_exotic, exotic_count);
7583         return -1;
7584     }
7585 
7586     num_index = 0;
7587     str_index = num_keys_count;
7588     sym_index = str_index + str_keys_count;
7589 
7590     num_sorted = TRUE;
7591     sh = p->shape;
7592     for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
7593         atom = prs->atom;
7594         if (atom != JS_ATOM_NULL) {
7595             is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
7596             kind = JS_AtomGetKind(ctx, atom);
7597             if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
7598                 ((flags >> kind) & 1) != 0) {
7599                 if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
7600                     j = num_index++;
7601                     num_sorted = FALSE;
7602                 } else if (kind == JS_ATOM_KIND_STRING) {
7603                     j = str_index++;
7604                 } else {
7605                     j = sym_index++;
7606                 }
7607                 tab_atom[j].atom = JS_DupAtom(ctx, atom);
7608                 tab_atom[j].is_enumerable = is_enumerable;
7609             }
7610         }
7611     }
7612 
7613     if (p->is_exotic) {
7614         int len;
7615         if (p->fast_array) {
7616             if (flags & JS_GPN_STRING_MASK) {
7617                 len = p->u.array.count;
7618                 goto add_array_keys;
7619             }
7620         } else if (p->class_id == JS_CLASS_STRING) {
7621             if (flags & JS_GPN_STRING_MASK) {
7622                 len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7623             add_array_keys:
7624                 for(i = 0; i < len; i++) {
7625                     tab_atom[num_index].atom = __JS_AtomFromUInt32(i);
7626                     if (tab_atom[num_index].atom == JS_ATOM_NULL) {
7627                         js_free_prop_enum(ctx, tab_atom, num_index);
7628                         return -1;
7629                     }
7630                     tab_atom[num_index].is_enumerable = TRUE;
7631                     num_index++;
7632                 }
7633             }
7634         } else {
7635             /* Note: exotic keys are not reordered and comes after the object own properties. */
7636             for(i = 0; i < exotic_count; i++) {
7637                 atom = tab_exotic[i].atom;
7638                 is_enumerable = tab_exotic[i].is_enumerable;
7639                 kind = JS_AtomGetKind(ctx, atom);
7640                 if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
7641                     ((flags >> kind) & 1) != 0) {
7642                     tab_atom[sym_index].atom = atom;
7643                     tab_atom[sym_index].is_enumerable = is_enumerable;
7644                     sym_index++;
7645                 } else {
7646                     JS_FreeAtom(ctx, atom);
7647                 }
7648             }
7649             js_free(ctx, tab_exotic);
7650         }
7651     }
7652 
7653     assert(num_index == num_keys_count);
7654     assert(str_index == num_keys_count + str_keys_count);
7655     assert(sym_index == atom_count);
7656 
7657     if (num_keys_count != 0 && !num_sorted) {
7658         rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp,
7659                ctx);
7660     }
7661     *ptab = tab_atom;
7662     *plen = atom_count;
7663     return 0;
7664 }
7665 
JS_GetOwnPropertyNames(JSContext * ctx,JSPropertyEnum ** ptab,uint32_t * plen,JSValueConst obj,int flags)7666 int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
7667                            uint32_t *plen, JSValueConst obj, int flags)
7668 {
7669     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
7670         JS_ThrowTypeErrorNotAnObject(ctx);
7671         return -1;
7672     }
7673     return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
7674                                           JS_VALUE_GET_OBJ(obj), flags);
7675 }
7676 
7677 /* Return -1 if exception,
7678    FALSE if the property does not exist, TRUE if it exists. If TRUE is
7679    returned, the property descriptor 'desc' is filled present. */
JS_GetOwnPropertyInternal(JSContext * ctx,JSPropertyDescriptor * desc,JSObject * p,JSAtom prop)7680 static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
7681                                      JSObject *p, JSAtom prop)
7682 {
7683     JSShapeProperty *prs;
7684     JSProperty *pr;
7685 
7686 retry:
7687     prs = find_own_property(&pr, p, prop);
7688     if (prs) {
7689         if (desc) {
7690             desc->flags = prs->flags & JS_PROP_C_W_E;
7691             desc->getter = JS_UNDEFINED;
7692             desc->setter = JS_UNDEFINED;
7693             desc->value = JS_UNDEFINED;
7694             if (unlikely(prs->flags & JS_PROP_TMASK)) {
7695                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
7696                     desc->flags |= JS_PROP_GETSET;
7697                     if (pr->u.getset.getter)
7698                         desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
7699                     if (pr->u.getset.setter)
7700                         desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
7701                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
7702                     JSValue val = *pr->u.var_ref->pvalue;
7703                     if (unlikely(JS_IsUninitialized(val))) {
7704                         JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
7705                         return -1;
7706                     }
7707                     desc->value = JS_DupValue(ctx, val);
7708                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
7709                     /* Instantiate property and retry */
7710                     if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
7711                         return -1;
7712                     goto retry;
7713                 }
7714             } else {
7715                 desc->value = JS_DupValue(ctx, pr->u.value);
7716             }
7717         } else {
7718             /* for consistency, send the exception even if desc is NULL */
7719             if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) {
7720                 if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) {
7721                     JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
7722                     return -1;
7723                 }
7724             } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
7725                 /* nothing to do: delay instantiation until actual value and/or attributes are read */
7726             }
7727         }
7728         return TRUE;
7729     }
7730     if (p->is_exotic) {
7731         if (p->fast_array) {
7732             /* specific case for fast arrays */
7733             if (__JS_AtomIsTaggedInt(prop)) {
7734                 uint32_t idx;
7735                 idx = __JS_AtomToUInt32(prop);
7736                 if (idx < p->u.array.count) {
7737                     if (desc) {
7738                         desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE |
7739                             JS_PROP_CONFIGURABLE;
7740                         desc->getter = JS_UNDEFINED;
7741                         desc->setter = JS_UNDEFINED;
7742                         desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
7743                     }
7744                     return TRUE;
7745                 }
7746             }
7747         } else {
7748             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7749             if (em && em->get_own_property) {
7750                 return em->get_own_property(ctx, desc,
7751                                             JS_MKPTR(JS_TAG_OBJECT, p), prop);
7752             }
7753         }
7754     }
7755     return FALSE;
7756 }
7757 
JS_GetOwnProperty(JSContext * ctx,JSPropertyDescriptor * desc,JSValueConst obj,JSAtom prop)7758 int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,
7759                       JSValueConst obj, JSAtom prop)
7760 {
7761     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
7762         JS_ThrowTypeErrorNotAnObject(ctx);
7763         return -1;
7764     }
7765     return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop);
7766 }
7767 
7768 /* return -1 if exception (Proxy object only) or TRUE/FALSE */
JS_IsExtensible(JSContext * ctx,JSValueConst obj)7769 int JS_IsExtensible(JSContext *ctx, JSValueConst obj)
7770 {
7771     JSObject *p;
7772 
7773     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
7774         return FALSE;
7775     p = JS_VALUE_GET_OBJ(obj);
7776     if (unlikely(p->class_id == JS_CLASS_PROXY))
7777         return js_proxy_isExtensible(ctx, obj);
7778     else
7779         return p->extensible;
7780 }
7781 
7782 /* return -1 if exception (Proxy object only) or TRUE/FALSE */
JS_PreventExtensions(JSContext * ctx,JSValueConst obj)7783 int JS_PreventExtensions(JSContext *ctx, JSValueConst obj)
7784 {
7785     JSObject *p;
7786 
7787     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
7788         return FALSE;
7789     p = JS_VALUE_GET_OBJ(obj);
7790     if (unlikely(p->class_id == JS_CLASS_PROXY))
7791         return js_proxy_preventExtensions(ctx, obj);
7792     p->extensible = FALSE;
7793     return TRUE;
7794 }
7795 
7796 /* return -1 if exception otherwise TRUE or FALSE */
JS_HasProperty(JSContext * ctx,JSValueConst obj,JSAtom prop)7797 int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop)
7798 {
7799     JSObject *p;
7800     int ret;
7801     JSValue obj1;
7802 
7803     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
7804         return FALSE;
7805     p = JS_VALUE_GET_OBJ(obj);
7806     for(;;) {
7807         if (p->is_exotic) {
7808             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7809             if (em && em->has_property) {
7810                 /* has_property can free the prototype */
7811                 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7812                 ret = em->has_property(ctx, obj1, prop);
7813                 JS_FreeValue(ctx, obj1);
7814                 return ret;
7815             }
7816         }
7817         /* JS_GetOwnPropertyInternal can free the prototype */
7818         JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7819         ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop);
7820         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7821         if (ret != 0)
7822             return ret;
7823         if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
7824             p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
7825             ret = JS_AtomIsNumericIndex(ctx, prop);
7826             if (ret != 0) {
7827                 if (ret < 0)
7828                     return -1;
7829                 return FALSE;
7830             }
7831         }
7832         p = p->shape->proto;
7833         if (!p)
7834             break;
7835     }
7836     return FALSE;
7837 }
7838 
7839 /* val must be a symbol */
js_symbol_to_atom(JSContext * ctx,JSValue val)7840 static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val)
7841 {
7842     JSAtomStruct *p = JS_VALUE_GET_PTR(val);
7843     return js_get_atom_index(ctx->rt, p);
7844 }
7845 
7846 /* return JS_ATOM_NULL in case of exception */
JS_ValueToAtom(JSContext * ctx,JSValueConst val)7847 JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)
7848 {
7849     JSAtom atom;
7850     uint32_t tag;
7851     tag = JS_VALUE_GET_TAG(val);
7852     if (tag == JS_TAG_INT &&
7853         (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) {
7854         /* fast path for integer values */
7855         atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val));
7856     } else if (tag == JS_TAG_SYMBOL) {
7857         JSAtomStruct *p = JS_VALUE_GET_PTR(val);
7858         atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p));
7859     } else {
7860         JSValue str;
7861         str = JS_ToPropertyKey(ctx, val);
7862         if (JS_IsException(str))
7863             return JS_ATOM_NULL;
7864         if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) {
7865             atom = js_symbol_to_atom(ctx, str);
7866         } else {
7867             atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str));
7868         }
7869     }
7870     return atom;
7871 }
7872 
JS_GetPropertyValue(JSContext * ctx,JSValueConst this_obj,JSValue prop)7873 static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
7874                                    JSValue prop)
7875 {
7876     JSAtom atom;
7877     JSValue ret;
7878 
7879     if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
7880                JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
7881         JSObject *p;
7882         uint32_t idx, len;
7883         /* fast path for array access */
7884         p = JS_VALUE_GET_OBJ(this_obj);
7885         idx = JS_VALUE_GET_INT(prop);
7886         len = (uint32_t)p->u.array.count;
7887         if (unlikely(idx >= len))
7888             goto slow_path;
7889         switch(p->class_id) {
7890         case JS_CLASS_ARRAY:
7891         case JS_CLASS_ARGUMENTS:
7892             return JS_DupValue(ctx, p->u.array.u.values[idx]);
7893         case JS_CLASS_INT8_ARRAY:
7894             return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]);
7895         case JS_CLASS_UINT8C_ARRAY:
7896         case JS_CLASS_UINT8_ARRAY:
7897             return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]);
7898         case JS_CLASS_INT16_ARRAY:
7899             return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]);
7900         case JS_CLASS_UINT16_ARRAY:
7901             return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]);
7902         case JS_CLASS_INT32_ARRAY:
7903             return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]);
7904         case JS_CLASS_UINT32_ARRAY:
7905             return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]);
7906 #ifdef CONFIG_BIGNUM
7907         case JS_CLASS_BIG_INT64_ARRAY:
7908             return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]);
7909         case JS_CLASS_BIG_UINT64_ARRAY:
7910             return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);
7911 #endif
7912         case JS_CLASS_FLOAT32_ARRAY:
7913             return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]);
7914         case JS_CLASS_FLOAT64_ARRAY:
7915             return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]);
7916         default:
7917             goto slow_path;
7918         }
7919     } else {
7920     slow_path:
7921         atom = JS_ValueToAtom(ctx, prop);
7922         JS_FreeValue(ctx, prop);
7923         if (unlikely(atom == JS_ATOM_NULL))
7924             return JS_EXCEPTION;
7925         ret = JS_GetProperty(ctx, this_obj, atom);
7926         JS_FreeAtom(ctx, atom);
7927         return ret;
7928     }
7929 }
7930 
JS_GetPropertyUint32(JSContext * ctx,JSValueConst this_obj,uint32_t idx)7931 JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
7932                              uint32_t idx)
7933 {
7934     return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx));
7935 }
7936 
7937 /* Check if an object has a generalized numeric property. Return value:
7938    -1 for exception,
7939    TRUE if property exists, stored into *pval,
7940    FALSE if proprty does not exist.
7941  */
JS_TryGetPropertyInt64(JSContext * ctx,JSValueConst obj,int64_t idx,JSValue * pval)7942 static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval)
7943 {
7944     JSValue val = JS_UNDEFINED;
7945     JSAtom prop;
7946     int present;
7947 
7948     if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) {
7949         /* fast path */
7950         present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx));
7951         if (present > 0) {
7952             val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
7953             if (unlikely(JS_IsException(val)))
7954                 present = -1;
7955         }
7956     } else {
7957         prop = JS_NewAtomInt64(ctx, idx);
7958         present = -1;
7959         if (likely(prop != JS_ATOM_NULL)) {
7960             present = JS_HasProperty(ctx, obj, prop);
7961             if (present > 0) {
7962                 val = JS_GetProperty(ctx, obj, prop);
7963                 if (unlikely(JS_IsException(val)))
7964                     present = -1;
7965             }
7966             JS_FreeAtom(ctx, prop);
7967         }
7968     }
7969     *pval = val;
7970     return present;
7971 }
7972 
JS_GetPropertyInt64(JSContext * ctx,JSValueConst obj,int64_t idx)7973 JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx)
7974 {
7975     JSAtom prop;
7976     JSValue val;
7977 
7978     if ((uint64_t)idx <= INT32_MAX) {
7979         /* fast path for fast arrays */
7980         return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
7981     }
7982     prop = JS_NewAtomInt64(ctx, idx);
7983     if (prop == JS_ATOM_NULL)
7984         return JS_EXCEPTION;
7985 
7986     val = JS_GetProperty(ctx, obj, prop);
7987     JS_FreeAtom(ctx, prop);
7988     return val;
7989 }
7990 
JS_GetPropertyStr(JSContext * ctx,JSValueConst this_obj,const char * prop)7991 JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,
7992                           const char *prop)
7993 {
7994     JSAtom atom;
7995     JSValue ret;
7996     atom = JS_NewAtom(ctx, prop);
7997     ret = JS_GetProperty(ctx, this_obj, atom);
7998     JS_FreeAtom(ctx, atom);
7999     return ret;
8000 }
8001 
8002 /* Note: the property value is not initialized. Return NULL if memory
8003    error. */
add_property(JSContext * ctx,JSObject * p,JSAtom prop,int prop_flags)8004 static JSProperty *add_property(JSContext *ctx,
8005                                 JSObject *p, JSAtom prop, int prop_flags)
8006 {
8007     JSShape *sh, *new_sh;
8008 
8009     sh = p->shape;
8010     if (sh->is_hashed) {
8011         /* try to find an existing shape */
8012         new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags);
8013         if (new_sh) {
8014             /* matching shape found: use it */
8015             /*  the property array may need to be resized */
8016             if (new_sh->prop_size != sh->prop_size) {
8017                 JSProperty *new_prop;
8018                 new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) *
8019                                       new_sh->prop_size);
8020                 if (!new_prop)
8021                     return NULL;
8022                 p->prop = new_prop;
8023             }
8024             p->shape = js_dup_shape(new_sh);
8025             js_free_shape(ctx->rt, sh);
8026             return &p->prop[new_sh->prop_count - 1];
8027         } else if (sh->header.ref_count != 1) {
8028             /* if the shape is shared, clone it */
8029             new_sh = js_clone_shape(ctx, sh);
8030             if (!new_sh)
8031                 return NULL;
8032             /* hash the cloned shape */
8033             new_sh->is_hashed = TRUE;
8034             js_shape_hash_link(ctx->rt, new_sh);
8035             js_free_shape(ctx->rt, p->shape);
8036             p->shape = new_sh;
8037         }
8038     }
8039     assert(p->shape->header.ref_count == 1);
8040     if (add_shape_property(ctx, &p->shape, p, prop, prop_flags))
8041         return NULL;
8042     return &p->prop[p->shape->prop_count - 1];
8043 }
8044 
8045 /* can be called on Array or Arguments objects. return < 0 if
8046    memory alloc error. */
convert_fast_array_to_array(JSContext * ctx,JSObject * p)8047 static no_inline __exception int convert_fast_array_to_array(JSContext *ctx,
8048                                                              JSObject *p)
8049 {
8050     JSProperty *pr;
8051     JSShape *sh;
8052     JSValue *tab;
8053     uint32_t i, len, new_count;
8054 
8055     if (js_shape_prepare_update(ctx, p, NULL))
8056         return -1;
8057     len = p->u.array.count;
8058     /* resize the properties once to simplify the error handling */
8059     sh = p->shape;
8060     new_count = sh->prop_count + len;
8061     if (new_count > sh->prop_size) {
8062         if (resize_properties(ctx, &p->shape, p, new_count))
8063             return -1;
8064     }
8065 
8066     tab = p->u.array.u.values;
8067     for(i = 0; i < len; i++) {
8068         /* add_property cannot fail here but
8069            __JS_AtomFromUInt32(i) fails for i > INT32_MAX */
8070         pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E);
8071         pr->u.value = *tab++;
8072     }
8073     js_free(ctx, p->u.array.u.values);
8074     p->u.array.count = 0;
8075     p->u.array.u.values = NULL; /* fail safe */
8076     p->u.array.u1.size = 0;
8077     p->fast_array = 0;
8078     return 0;
8079 }
8080 
delete_property(JSContext * ctx,JSObject * p,JSAtom atom)8081 static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
8082 {
8083     JSShape *sh;
8084     JSShapeProperty *pr, *lpr, *prop;
8085     JSProperty *pr1;
8086     uint32_t lpr_idx;
8087     intptr_t h, h1;
8088 
8089  redo:
8090     sh = p->shape;
8091     h1 = atom & sh->prop_hash_mask;
8092     h = prop_hash_end(sh)[-h1 - 1];
8093     prop = get_shape_prop(sh);
8094     lpr = NULL;
8095     lpr_idx = 0;   /* prevent warning */
8096     while (h != 0) {
8097         pr = &prop[h - 1];
8098         if (likely(pr->atom == atom)) {
8099             /* found ! */
8100             if (!(pr->flags & JS_PROP_CONFIGURABLE))
8101                 return FALSE;
8102             /* realloc the shape if needed */
8103             if (lpr)
8104                 lpr_idx = lpr - get_shape_prop(sh);
8105             if (js_shape_prepare_update(ctx, p, &pr))
8106                 return -1;
8107             sh = p->shape;
8108             /* remove property */
8109             if (lpr) {
8110                 lpr = get_shape_prop(sh) + lpr_idx;
8111                 lpr->hash_next = pr->hash_next;
8112             } else {
8113                 prop_hash_end(sh)[-h1 - 1] = pr->hash_next;
8114             }
8115             sh->deleted_prop_count++;
8116             /* free the entry */
8117             pr1 = &p->prop[h - 1];
8118             free_property(ctx->rt, pr1, pr->flags);
8119             JS_FreeAtom(ctx, pr->atom);
8120             /* put default values */
8121             pr->flags = 0;
8122             pr->atom = JS_ATOM_NULL;
8123             pr1->u.value = JS_UNDEFINED;
8124 
8125             /* compact the properties if too many deleted properties */
8126             if (sh->deleted_prop_count >= 8 &&
8127                 sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) {
8128                 compact_properties(ctx, p);
8129             }
8130             return TRUE;
8131         }
8132         lpr = pr;
8133         h = pr->hash_next;
8134     }
8135 
8136     if (p->is_exotic) {
8137         if (p->fast_array) {
8138             uint32_t idx;
8139             if (JS_AtomIsArrayIndex(ctx, &idx, atom) &&
8140                 idx < p->u.array.count) {
8141                 if (p->class_id == JS_CLASS_ARRAY ||
8142                     p->class_id == JS_CLASS_ARGUMENTS) {
8143                     /* Special case deleting the last element of a fast Array */
8144                     if (idx == p->u.array.count - 1) {
8145                         JS_FreeValue(ctx, p->u.array.u.values[idx]);
8146                         p->u.array.count = idx;
8147                         return TRUE;
8148                     }
8149                     if (convert_fast_array_to_array(ctx, p))
8150                         return -1;
8151                     goto redo;
8152                 } else {
8153                     return FALSE;
8154                 }
8155             }
8156         } else {
8157             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
8158             if (em && em->delete_property) {
8159                 return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom);
8160             }
8161         }
8162     }
8163     /* not found */
8164     return TRUE;
8165 }
8166 
call_setter(JSContext * ctx,JSObject * setter,JSValueConst this_obj,JSValue val,int flags)8167 static int call_setter(JSContext *ctx, JSObject *setter,
8168                        JSValueConst this_obj, JSValue val, int flags)
8169 {
8170     JSValue ret, func;
8171     if (likely(setter)) {
8172         func = JS_MKPTR(JS_TAG_OBJECT, setter);
8173         /* Note: the field could be removed in the setter */
8174         func = JS_DupValue(ctx, func);
8175         ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val);
8176         JS_FreeValue(ctx, val);
8177         if (JS_IsException(ret))
8178             return -1;
8179         JS_FreeValue(ctx, ret);
8180         return TRUE;
8181     } else {
8182         JS_FreeValue(ctx, val);
8183         if ((flags & JS_PROP_THROW) ||
8184             ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
8185             JS_ThrowTypeError(ctx, "no setter for property");
8186             return -1;
8187         }
8188         return FALSE;
8189     }
8190 }
8191 
8192 /* set the array length and remove the array elements if necessary. */
set_array_length(JSContext * ctx,JSObject * p,JSValue val,int flags)8193 static int set_array_length(JSContext *ctx, JSObject *p, JSValue val,
8194                             int flags)
8195 {
8196     uint32_t len, idx, cur_len;
8197     int i, ret;
8198 
8199     /* Note: this call can reallocate the properties of 'p' */
8200     ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE);
8201     if (ret)
8202         return -1;
8203     /* JS_ToArrayLengthFree() must be done before the read-only test */
8204     if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE)))
8205         return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
8206 
8207     if (likely(p->fast_array)) {
8208         uint32_t old_len = p->u.array.count;
8209         if (len < old_len) {
8210             for(i = len; i < old_len; i++) {
8211                 JS_FreeValue(ctx, p->u.array.u.values[i]);
8212             }
8213             p->u.array.count = len;
8214         }
8215         p->prop[0].u.value = JS_NewUint32(ctx, len);
8216     } else {
8217         /* Note: length is always a uint32 because the object is an
8218            array */
8219         JS_ToUint32(ctx, &cur_len, p->prop[0].u.value);
8220         if (len < cur_len) {
8221             uint32_t d;
8222             JSShape *sh;
8223             JSShapeProperty *pr;
8224 
8225             d = cur_len - len;
8226             sh = p->shape;
8227             if (d <= sh->prop_count) {
8228                 JSAtom atom;
8229 
8230                 /* faster to iterate */
8231                 while (cur_len > len) {
8232                     atom = JS_NewAtomUInt32(ctx, cur_len - 1);
8233                     ret = delete_property(ctx, p, atom);
8234                     JS_FreeAtom(ctx, atom);
8235                     if (unlikely(!ret)) {
8236                         /* unlikely case: property is not
8237                            configurable */
8238                         break;
8239                     }
8240                     cur_len--;
8241                 }
8242             } else {
8243                 /* faster to iterate thru all the properties. Need two
8244                    passes in case one of the property is not
8245                    configurable */
8246                 cur_len = len;
8247                 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
8248                     i++, pr++) {
8249                     if (pr->atom != JS_ATOM_NULL &&
8250                         JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
8251                         if (idx >= cur_len &&
8252                             !(pr->flags & JS_PROP_CONFIGURABLE)) {
8253                             cur_len = idx + 1;
8254                         }
8255                     }
8256                 }
8257 
8258                 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
8259                     i++, pr++) {
8260                     if (pr->atom != JS_ATOM_NULL &&
8261                         JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
8262                         if (idx >= cur_len) {
8263                             /* remove the property */
8264                             delete_property(ctx, p, pr->atom);
8265                             /* WARNING: the shape may have been modified */
8266                             sh = p->shape;
8267                             pr = get_shape_prop(sh) + i;
8268                         }
8269                     }
8270                 }
8271             }
8272         } else {
8273             cur_len = len;
8274         }
8275         set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len));
8276         if (unlikely(cur_len > len)) {
8277             return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable");
8278         }
8279     }
8280     return TRUE;
8281 }
8282 
8283 /* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array =
8284    TRUE and p->extensible = TRUE */
add_fast_array_element(JSContext * ctx,JSObject * p,JSValue val,int flags)8285 static int add_fast_array_element(JSContext *ctx, JSObject *p,
8286                                   JSValue val, int flags)
8287 {
8288     uint32_t new_len, array_len;
8289     /* extend the array by one */
8290     /* XXX: convert to slow array if new_len > 2^31-1 elements */
8291     new_len = p->u.array.count + 1;
8292     /* update the length if necessary. We assume that if the length is
8293        not an integer, then if it >= 2^31.  */
8294     if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) {
8295         array_len = JS_VALUE_GET_INT(p->prop[0].u.value);
8296         if (new_len > array_len) {
8297             if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) {
8298                 JS_FreeValue(ctx, val);
8299                 return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
8300             }
8301             p->prop[0].u.value = JS_NewInt32(ctx, new_len);
8302         }
8303     }
8304     if (unlikely(new_len > p->u.array.u1.size)) {
8305         uint32_t new_size;
8306         size_t slack;
8307         JSValue *new_array_prop;
8308         /* XXX: potential arithmetic overflow */
8309         new_size = max_int(new_len, p->u.array.u1.size * 3 / 2);
8310         new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack);
8311         if (!new_array_prop) {
8312             JS_FreeValue(ctx, val);
8313             return -1;
8314         }
8315         new_size += slack / sizeof(*new_array_prop);
8316         p->u.array.u.values = new_array_prop;
8317         p->u.array.u1.size = new_size;
8318     }
8319     p->u.array.u.values[new_len - 1] = val;
8320     p->u.array.count = new_len;
8321     return TRUE;
8322 }
8323 
js_free_desc(JSContext * ctx,JSPropertyDescriptor * desc)8324 static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)
8325 {
8326     JS_FreeValue(ctx, desc->getter);
8327     JS_FreeValue(ctx, desc->setter);
8328     JS_FreeValue(ctx, desc->value);
8329 }
8330 
8331 /* generic (and slower) version of JS_SetProperty() for Reflect.set() */
JS_SetPropertyGeneric(JSContext * ctx,JSObject * p,JSAtom prop,JSValue val,JSValueConst this_obj,int flags)8332 static int JS_SetPropertyGeneric(JSContext *ctx,
8333                                  JSObject *p, JSAtom prop,
8334                                  JSValue val, JSValueConst this_obj,
8335                                  int flags)
8336 {
8337     int ret;
8338     JSPropertyDescriptor desc;
8339 
8340     while (p != NULL) {
8341         if (p->is_exotic) {
8342             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
8343             if (em && em->set_property) {
8344                 JSValue obj1;
8345                 /* set_property can free the prototype */
8346                 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
8347                 ret = em->set_property(ctx, obj1, prop,
8348                                        val, this_obj, flags);
8349                 JS_FreeValue(ctx, obj1);
8350                 JS_FreeValue(ctx, val);
8351                 return ret;
8352             }
8353         }
8354 
8355         ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
8356         if (ret < 0)
8357             return ret;
8358         if (ret) {
8359             if (desc.flags & JS_PROP_GETSET) {
8360                 JSObject *setter;
8361                 if (JS_IsUndefined(desc.setter))
8362                     setter = NULL;
8363                 else
8364                     setter = JS_VALUE_GET_OBJ(desc.setter);
8365                 ret = call_setter(ctx, setter, this_obj, val, flags);
8366                 JS_FreeValue(ctx, desc.getter);
8367                 JS_FreeValue(ctx, desc.setter);
8368                 return ret;
8369             } else {
8370                 JS_FreeValue(ctx, desc.value);
8371                 if (!(desc.flags & JS_PROP_WRITABLE)) {
8372                     goto read_only_error;
8373                 }
8374             }
8375             break;
8376         }
8377         p = p->shape->proto;
8378     }
8379 
8380     if (!JS_IsObject(this_obj))
8381         return JS_ThrowTypeErrorOrFalse(ctx, flags, "receiver is not an object");
8382 
8383     p = JS_VALUE_GET_OBJ(this_obj);
8384 
8385     /* modify the property in this_obj if it already exists */
8386     ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
8387     if (ret < 0)
8388         return ret;
8389     if (ret) {
8390         if (desc.flags & JS_PROP_GETSET) {
8391             JS_FreeValue(ctx, desc.getter);
8392             JS_FreeValue(ctx, desc.setter);
8393             JS_FreeValue(ctx, val);
8394             return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden");
8395         } else {
8396             JS_FreeValue(ctx, desc.value);
8397             if (!(desc.flags & JS_PROP_WRITABLE) ||
8398                 p->class_id == JS_CLASS_MODULE_NS) {
8399             read_only_error:
8400                 JS_FreeValue(ctx, val);
8401                 return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
8402             }
8403         }
8404         ret = JS_DefineProperty(ctx, this_obj, prop, val,
8405                                 JS_UNDEFINED, JS_UNDEFINED,
8406                                 JS_PROP_HAS_VALUE);
8407         JS_FreeValue(ctx, val);
8408         return ret;
8409     }
8410 
8411     ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
8412                             flags |
8413                             JS_PROP_HAS_VALUE |
8414                             JS_PROP_HAS_ENUMERABLE |
8415                             JS_PROP_HAS_WRITABLE |
8416                             JS_PROP_HAS_CONFIGURABLE |
8417                             JS_PROP_C_W_E);
8418     JS_FreeValue(ctx, val);
8419     return ret;
8420 }
8421 
8422 /* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is
8423    freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD,
8424    JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set,
8425    the new property is not added and an error is raised. */
JS_SetPropertyInternal(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSValue val,int flags)8426 int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,
8427                            JSAtom prop, JSValue val, int flags)
8428 {
8429     JSObject *p, *p1;
8430     JSShapeProperty *prs;
8431     JSProperty *pr;
8432     uint32_t tag;
8433     JSPropertyDescriptor desc;
8434     int ret;
8435 #if 0
8436     printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n");
8437 #endif
8438     tag = JS_VALUE_GET_TAG(this_obj);
8439     if (unlikely(tag != JS_TAG_OBJECT)) {
8440         switch(tag) {
8441         case JS_TAG_NULL:
8442             JS_FreeValue(ctx, val);
8443             JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop);
8444             return -1;
8445         case JS_TAG_UNDEFINED:
8446             JS_FreeValue(ctx, val);
8447             JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop);
8448             return -1;
8449         default:
8450             /* even on a primitive type we can have setters on the prototype */
8451             p = NULL;
8452             p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj));
8453             goto prototype_lookup;
8454         }
8455     }
8456     p = JS_VALUE_GET_OBJ(this_obj);
8457 retry:
8458     prs = find_own_property(&pr, p, prop);
8459     if (prs) {
8460         if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
8461                                   JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
8462             /* fast case */
8463             set_value(ctx, &pr->u.value, val);
8464             return TRUE;
8465         } else if (prs->flags & JS_PROP_LENGTH) {
8466             assert(p->class_id == JS_CLASS_ARRAY);
8467             assert(prop == JS_ATOM_length);
8468             return set_array_length(ctx, p, val, flags);
8469         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
8470             return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
8471         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
8472             /* JS_PROP_WRITABLE is always true for variable
8473                references, but they are write protected in module name
8474                spaces. */
8475             if (p->class_id == JS_CLASS_MODULE_NS)
8476                 goto read_only_prop;
8477             set_value(ctx, pr->u.var_ref->pvalue, val);
8478             return TRUE;
8479         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
8480             /* Instantiate property and retry (potentially useless) */
8481             if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) {
8482                 JS_FreeValue(ctx, val);
8483                 return -1;
8484             }
8485             goto retry;
8486         } else {
8487             goto read_only_prop;
8488         }
8489     }
8490 
8491     p1 = p;
8492     for(;;) {
8493         if (p1->is_exotic) {
8494             if (p1->fast_array) {
8495                 if (__JS_AtomIsTaggedInt(prop)) {
8496                     uint32_t idx = __JS_AtomToUInt32(prop);
8497                     if (idx < p1->u.array.count) {
8498                         if (unlikely(p == p1))
8499                             return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags);
8500                         else
8501                             break;
8502                     } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
8503                                p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
8504                         goto typed_array_oob;
8505                     }
8506                 } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
8507                            p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
8508                     ret = JS_AtomIsNumericIndex(ctx, prop);
8509                     if (ret != 0) {
8510                         if (ret < 0) {
8511                             JS_FreeValue(ctx, val);
8512                             return -1;
8513                         }
8514                     typed_array_oob:
8515                         val = JS_ToNumberFree(ctx, val);
8516                         JS_FreeValue(ctx, val);
8517                         if (JS_IsException(val))
8518                             return -1;
8519                         return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");
8520                     }
8521                 }
8522             } else {
8523                 const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic;
8524                 if (em) {
8525                     JSValue obj1;
8526                     if (em->set_property) {
8527                         /* set_property can free the prototype */
8528                         obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
8529                         ret = em->set_property(ctx, obj1, prop,
8530                                                val, this_obj, flags);
8531                         JS_FreeValue(ctx, obj1);
8532                         JS_FreeValue(ctx, val);
8533                         return ret;
8534                     }
8535                     if (em->get_own_property) {
8536                         /* get_own_property can free the prototype */
8537                         obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
8538                         ret = em->get_own_property(ctx, &desc,
8539                                                    obj1, prop);
8540                         JS_FreeValue(ctx, obj1);
8541                         if (ret < 0) {
8542                             JS_FreeValue(ctx, val);
8543                             return ret;
8544                         }
8545                         if (ret) {
8546                             if (desc.flags & JS_PROP_GETSET) {
8547                                 JSObject *setter;
8548                                 if (JS_IsUndefined(desc.setter))
8549                                     setter = NULL;
8550                                 else
8551                                     setter = JS_VALUE_GET_OBJ(desc.setter);
8552                                 ret = call_setter(ctx, setter, this_obj, val, flags);
8553                                 JS_FreeValue(ctx, desc.getter);
8554                                 JS_FreeValue(ctx, desc.setter);
8555                                 return ret;
8556                             } else {
8557                                 JS_FreeValue(ctx, desc.value);
8558                                 if (!(desc.flags & JS_PROP_WRITABLE))
8559                                     goto read_only_prop;
8560                                 if (likely(p == p1)) {
8561                                     ret = JS_DefineProperty(ctx, this_obj, prop, val,
8562                                                             JS_UNDEFINED, JS_UNDEFINED,
8563                                                             JS_PROP_HAS_VALUE);
8564                                     JS_FreeValue(ctx, val);
8565                                     return ret;
8566                                 } else {
8567                                     break;
8568                                 }
8569                             }
8570                         }
8571                     }
8572                 }
8573             }
8574         }
8575         p1 = p1->shape->proto;
8576     prototype_lookup:
8577         if (!p1)
8578             break;
8579 
8580     retry2:
8581         prs = find_own_property(&pr, p1, prop);
8582         if (prs) {
8583             if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
8584                 return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
8585             } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
8586                 /* Instantiate property and retry (potentially useless) */
8587                 if (JS_AutoInitProperty(ctx, p1, prop, pr, prs))
8588                     return -1;
8589                 goto retry2;
8590             } else if (!(prs->flags & JS_PROP_WRITABLE)) {
8591             read_only_prop:
8592                 JS_FreeValue(ctx, val);
8593                 return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
8594             }
8595         }
8596     }
8597 
8598     if (unlikely(flags & JS_PROP_NO_ADD)) {
8599         JS_FreeValue(ctx, val);
8600         JS_ThrowReferenceErrorNotDefined(ctx, prop);
8601         return -1;
8602     }
8603 
8604     if (unlikely(!p)) {
8605         JS_FreeValue(ctx, val);
8606         return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object");
8607     }
8608 
8609     if (unlikely(!p->extensible)) {
8610         JS_FreeValue(ctx, val);
8611         return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
8612     }
8613 
8614     if (p->is_exotic) {
8615         if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&
8616             __JS_AtomIsTaggedInt(prop)) {
8617             uint32_t idx = __JS_AtomToUInt32(prop);
8618             if (idx == p->u.array.count) {
8619                 /* fast case */
8620                 return add_fast_array_element(ctx, p, val, flags);
8621             } else {
8622                 goto generic_create_prop;
8623             }
8624         } else {
8625         generic_create_prop:
8626             ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
8627                                     flags |
8628                                     JS_PROP_HAS_VALUE |
8629                                     JS_PROP_HAS_ENUMERABLE |
8630                                     JS_PROP_HAS_WRITABLE |
8631                                     JS_PROP_HAS_CONFIGURABLE |
8632                                     JS_PROP_C_W_E);
8633             JS_FreeValue(ctx, val);
8634             return ret;
8635         }
8636     }
8637 
8638     pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
8639     if (unlikely(!pr)) {
8640         JS_FreeValue(ctx, val);
8641         return -1;
8642     }
8643     pr->u.value = val;
8644     return TRUE;
8645 }
8646 
8647 /* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */
JS_SetPropertyValue(JSContext * ctx,JSValueConst this_obj,JSValue prop,JSValue val,int flags)8648 static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
8649                                JSValue prop, JSValue val, int flags)
8650 {
8651     if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
8652                JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
8653         JSObject *p;
8654         uint32_t idx;
8655         double d;
8656         int32_t v;
8657 
8658         /* fast path for array access */
8659         p = JS_VALUE_GET_OBJ(this_obj);
8660         idx = JS_VALUE_GET_INT(prop);
8661         switch(p->class_id) {
8662         case JS_CLASS_ARRAY:
8663             if (unlikely(idx >= (uint32_t)p->u.array.count)) {
8664                 JSObject *p1;
8665                 JSShape *sh1;
8666 
8667                 /* fast path to add an element to the array */
8668                 if (idx != (uint32_t)p->u.array.count ||
8669                     !p->fast_array || !p->extensible)
8670                     goto slow_path;
8671                 /* check if prototype chain has a numeric property */
8672                 p1 = p->shape->proto;
8673                 while (p1 != NULL) {
8674                     sh1 = p1->shape;
8675                     if (p1->class_id == JS_CLASS_ARRAY) {
8676                         if (unlikely(!p1->fast_array))
8677                             goto slow_path;
8678                     } else if (p1->class_id == JS_CLASS_OBJECT) {
8679                         if (unlikely(sh1->has_small_array_index))
8680                             goto slow_path;
8681                     } else {
8682                         goto slow_path;
8683                     }
8684                     p1 = sh1->proto;
8685                 }
8686                 /* add element */
8687                 return add_fast_array_element(ctx, p, val, flags);
8688             }
8689             set_value(ctx, &p->u.array.u.values[idx], val);
8690             break;
8691         case JS_CLASS_ARGUMENTS:
8692             if (unlikely(idx >= (uint32_t)p->u.array.count))
8693                 goto slow_path;
8694             set_value(ctx, &p->u.array.u.values[idx], val);
8695             break;
8696         case JS_CLASS_UINT8C_ARRAY:
8697             if (JS_ToUint8ClampFree(ctx, &v, val))
8698                 return -1;
8699             /* Note: the conversion can detach the typed array, so the
8700                array bound check must be done after */
8701             if (unlikely(idx >= (uint32_t)p->u.array.count))
8702                 goto ta_out_of_bound;
8703             p->u.array.u.uint8_ptr[idx] = v;
8704             break;
8705         case JS_CLASS_INT8_ARRAY:
8706         case JS_CLASS_UINT8_ARRAY:
8707             if (JS_ToInt32Free(ctx, &v, val))
8708                 return -1;
8709             if (unlikely(idx >= (uint32_t)p->u.array.count))
8710                 goto ta_out_of_bound;
8711             p->u.array.u.uint8_ptr[idx] = v;
8712             break;
8713         case JS_CLASS_INT16_ARRAY:
8714         case JS_CLASS_UINT16_ARRAY:
8715             if (JS_ToInt32Free(ctx, &v, val))
8716                 return -1;
8717             if (unlikely(idx >= (uint32_t)p->u.array.count))
8718                 goto ta_out_of_bound;
8719             p->u.array.u.uint16_ptr[idx] = v;
8720             break;
8721         case JS_CLASS_INT32_ARRAY:
8722         case JS_CLASS_UINT32_ARRAY:
8723             if (JS_ToInt32Free(ctx, &v, val))
8724                 return -1;
8725             if (unlikely(idx >= (uint32_t)p->u.array.count))
8726                 goto ta_out_of_bound;
8727             p->u.array.u.uint32_ptr[idx] = v;
8728             break;
8729 #ifdef CONFIG_BIGNUM
8730         case JS_CLASS_BIG_INT64_ARRAY:
8731         case JS_CLASS_BIG_UINT64_ARRAY:
8732             /* XXX: need specific conversion function */
8733             {
8734                 int64_t v;
8735                 if (JS_ToBigInt64Free(ctx, &v, val))
8736                     return -1;
8737                 if (unlikely(idx >= (uint32_t)p->u.array.count))
8738                     goto ta_out_of_bound;
8739                 p->u.array.u.uint64_ptr[idx] = v;
8740             }
8741             break;
8742 #endif
8743         case JS_CLASS_FLOAT32_ARRAY:
8744             if (JS_ToFloat64Free(ctx, &d, val))
8745                 return -1;
8746             if (unlikely(idx >= (uint32_t)p->u.array.count))
8747                 goto ta_out_of_bound;
8748             p->u.array.u.float_ptr[idx] = d;
8749             break;
8750         case JS_CLASS_FLOAT64_ARRAY:
8751             if (JS_ToFloat64Free(ctx, &d, val))
8752                 return -1;
8753             if (unlikely(idx >= (uint32_t)p->u.array.count)) {
8754             ta_out_of_bound:
8755                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");
8756             }
8757             p->u.array.u.double_ptr[idx] = d;
8758             break;
8759         default:
8760             goto slow_path;
8761         }
8762         return TRUE;
8763     } else {
8764         JSAtom atom;
8765         int ret;
8766     slow_path:
8767         atom = JS_ValueToAtom(ctx, prop);
8768         JS_FreeValue(ctx, prop);
8769         if (unlikely(atom == JS_ATOM_NULL)) {
8770             JS_FreeValue(ctx, val);
8771             return -1;
8772         }
8773         ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags);
8774         JS_FreeAtom(ctx, atom);
8775         return ret;
8776     }
8777 }
8778 
JS_SetPropertyUint32(JSContext * ctx,JSValueConst this_obj,uint32_t idx,JSValue val)8779 int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
8780                          uint32_t idx, JSValue val)
8781 {
8782     return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val,
8783                                JS_PROP_THROW);
8784 }
8785 
JS_SetPropertyInt64(JSContext * ctx,JSValueConst this_obj,int64_t idx,JSValue val)8786 int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,
8787                         int64_t idx, JSValue val)
8788 {
8789     JSAtom prop;
8790     int res;
8791 
8792     if ((uint64_t)idx <= INT32_MAX) {
8793         /* fast path for fast arrays */
8794         return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val,
8795                                    JS_PROP_THROW);
8796     }
8797     prop = JS_NewAtomInt64(ctx, idx);
8798     if (prop == JS_ATOM_NULL) {
8799         JS_FreeValue(ctx, val);
8800         return -1;
8801     }
8802     res = JS_SetProperty(ctx, this_obj, prop, val);
8803     JS_FreeAtom(ctx, prop);
8804     return res;
8805 }
8806 
JS_SetPropertyStr(JSContext * ctx,JSValueConst this_obj,const char * prop,JSValue val)8807 int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,
8808                       const char *prop, JSValue val)
8809 {
8810     JSAtom atom;
8811     int ret;
8812     atom = JS_NewAtom(ctx, prop);
8813     ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW);
8814     JS_FreeAtom(ctx, atom);
8815     return ret;
8816 }
8817 
8818 /* compute the property flags. For each flag: (JS_PROP_HAS_x forces
8819    it, otherwise def_flags is used)
8820    Note: makes assumption about the bit pattern of the flags
8821 */
get_prop_flags(int flags,int def_flags)8822 static int get_prop_flags(int flags, int def_flags)
8823 {
8824     int mask;
8825     mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E;
8826     return (flags & mask) | (def_flags & ~mask);
8827 }
8828 
JS_CreateProperty(JSContext * ctx,JSObject * p,JSAtom prop,JSValueConst val,JSValueConst getter,JSValueConst setter,int flags)8829 static int JS_CreateProperty(JSContext *ctx, JSObject *p,
8830                              JSAtom prop, JSValueConst val,
8831                              JSValueConst getter, JSValueConst setter,
8832                              int flags)
8833 {
8834     JSProperty *pr;
8835     int ret, prop_flags;
8836 
8837     /* add a new property or modify an existing exotic one */
8838     if (p->is_exotic) {
8839         if (p->class_id == JS_CLASS_ARRAY) {
8840             uint32_t idx, len;
8841 
8842             if (p->fast_array) {
8843                 if (__JS_AtomIsTaggedInt(prop)) {
8844                     idx = __JS_AtomToUInt32(prop);
8845                     if (idx == p->u.array.count) {
8846                         if (!p->extensible)
8847                             goto not_extensible;
8848                         if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET))
8849                             goto convert_to_array;
8850                         prop_flags = get_prop_flags(flags, 0);
8851                         if (prop_flags != JS_PROP_C_W_E)
8852                             goto convert_to_array;
8853                         return add_fast_array_element(ctx, p,
8854                                                       JS_DupValue(ctx, val), flags);
8855                     } else {
8856                         goto convert_to_array;
8857                     }
8858                 } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
8859                     /* convert the fast array to normal array */
8860                 convert_to_array:
8861                     if (convert_fast_array_to_array(ctx, p))
8862                         return -1;
8863                     goto generic_array;
8864                 }
8865             } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
8866                 JSProperty *plen;
8867                 JSShapeProperty *pslen;
8868             generic_array:
8869                 /* update the length field */
8870                 plen = &p->prop[0];
8871                 JS_ToUint32(ctx, &len, plen->u.value);
8872                 if ((idx + 1) > len) {
8873                     pslen = get_shape_prop(p->shape);
8874                     if (unlikely(!(pslen->flags & JS_PROP_WRITABLE)))
8875                         return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
8876                     /* XXX: should update the length after defining
8877                        the property */
8878                     len = idx + 1;
8879                     set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len));
8880                 }
8881             }
8882         } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
8883                    p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
8884             ret = JS_AtomIsNumericIndex(ctx, prop);
8885             if (ret != 0) {
8886                 if (ret < 0)
8887                     return -1;
8888                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array");
8889             }
8890         } else if (!(flags & JS_PROP_NO_EXOTIC)) {
8891             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
8892             if (em) {
8893                 if (em->define_own_property) {
8894                     return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p),
8895                                                    prop, val, getter, setter, flags);
8896                 }
8897                 ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
8898                 if (ret < 0)
8899                     return -1;
8900                 if (!ret)
8901                     goto not_extensible;
8902             }
8903         }
8904     }
8905 
8906     if (!p->extensible) {
8907     not_extensible:
8908         return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
8909     }
8910 
8911     if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
8912         prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
8913             JS_PROP_GETSET;
8914     } else {
8915         prop_flags = flags & JS_PROP_C_W_E;
8916     }
8917     pr = add_property(ctx, p, prop, prop_flags);
8918     if (unlikely(!pr))
8919         return -1;
8920     if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
8921         pr->u.getset.getter = NULL;
8922         if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) {
8923             pr->u.getset.getter =
8924                 JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter));
8925         }
8926         pr->u.getset.setter = NULL;
8927         if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) {
8928             pr->u.getset.setter =
8929                 JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter));
8930         }
8931     } else {
8932         if (flags & JS_PROP_HAS_VALUE) {
8933             pr->u.value = JS_DupValue(ctx, val);
8934         } else {
8935             pr->u.value = JS_UNDEFINED;
8936         }
8937     }
8938     return TRUE;
8939 }
8940 
8941 /* return FALSE if not OK */
check_define_prop_flags(int prop_flags,int flags)8942 static BOOL check_define_prop_flags(int prop_flags, int flags)
8943 {
8944     BOOL has_accessor, is_getset;
8945 
8946     if (!(prop_flags & JS_PROP_CONFIGURABLE)) {
8947         if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) ==
8948             (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) {
8949             return FALSE;
8950         }
8951         if ((flags & JS_PROP_HAS_ENUMERABLE) &&
8952             (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE))
8953             return FALSE;
8954     }
8955     if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
8956                  JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
8957         if (!(prop_flags & JS_PROP_CONFIGURABLE)) {
8958             has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0);
8959             is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET);
8960             if (has_accessor != is_getset)
8961                 return FALSE;
8962             if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) {
8963                 /* not writable: cannot set the writable bit */
8964                 if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
8965                     (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE))
8966                     return FALSE;
8967             }
8968         }
8969     }
8970     return TRUE;
8971 }
8972 
8973 /* ensure that the shape can be safely modified */
js_shape_prepare_update(JSContext * ctx,JSObject * p,JSShapeProperty ** pprs)8974 static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
8975                                    JSShapeProperty **pprs)
8976 {
8977     JSShape *sh;
8978     uint32_t idx = 0;    /* prevent warning */
8979 
8980     sh = p->shape;
8981     if (sh->is_hashed) {
8982         if (sh->header.ref_count != 1) {
8983             if (pprs)
8984                 idx = *pprs - get_shape_prop(sh);
8985             /* clone the shape (the resulting one is no longer hashed) */
8986             sh = js_clone_shape(ctx, sh);
8987             if (!sh)
8988                 return -1;
8989             js_free_shape(ctx->rt, p->shape);
8990             p->shape = sh;
8991             if (pprs)
8992                 *pprs = get_shape_prop(sh) + idx;
8993         } else {
8994             js_shape_hash_unlink(ctx->rt, sh);
8995             sh->is_hashed = FALSE;
8996         }
8997     }
8998     return 0;
8999 }
9000 
js_update_property_flags(JSContext * ctx,JSObject * p,JSShapeProperty ** pprs,int flags)9001 static int js_update_property_flags(JSContext *ctx, JSObject *p,
9002                                     JSShapeProperty **pprs, int flags)
9003 {
9004     if (flags != (*pprs)->flags) {
9005         if (js_shape_prepare_update(ctx, p, pprs))
9006             return -1;
9007         (*pprs)->flags = flags;
9008     }
9009     return 0;
9010 }
9011 
9012 /* allowed flags:
9013    JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE
9014    JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE,
9015    JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE,
9016    JS_PROP_THROW, JS_PROP_NO_EXOTIC.
9017    If JS_PROP_THROW is set, return an exception instead of FALSE.
9018    if JS_PROP_NO_EXOTIC is set, do not call the exotic
9019    define_own_property callback.
9020    return -1 (exception), FALSE or TRUE.
9021 */
JS_DefineProperty(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSValueConst val,JSValueConst getter,JSValueConst setter,int flags)9022 int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
9023                       JSAtom prop, JSValueConst val,
9024                       JSValueConst getter, JSValueConst setter, int flags)
9025 {
9026     JSObject *p;
9027     JSShapeProperty *prs;
9028     JSProperty *pr;
9029     int mask, res;
9030 
9031     if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) {
9032         JS_ThrowTypeErrorNotAnObject(ctx);
9033         return -1;
9034     }
9035     p = JS_VALUE_GET_OBJ(this_obj);
9036 
9037  redo_prop_update:
9038     prs = find_own_property(&pr, p, prop);
9039     if (prs) {
9040         /* the range of the Array length property is always tested before */
9041         if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) {
9042             uint32_t array_length;
9043             if (JS_ToArrayLengthFree(ctx, &array_length,
9044                                      JS_DupValue(ctx, val), FALSE)) {
9045                 return -1;
9046             }
9047             /* this code relies on the fact that Uint32 are never allocated */
9048             val = (JSValueConst)JS_NewUint32(ctx, array_length);
9049             /* prs may have been modified */
9050             prs = find_own_property(&pr, p, prop);
9051             assert(prs != NULL);
9052         }
9053         /* property already exists */
9054         if (!check_define_prop_flags(prs->flags, flags)) {
9055         not_configurable:
9056             return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
9057         }
9058 
9059         if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
9060             /* Instantiate property and retry */
9061             if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
9062                 return -1;
9063             goto redo_prop_update;
9064         }
9065 
9066         if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
9067                      JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
9068             if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
9069                 JSObject *new_getter, *new_setter;
9070 
9071                 if (JS_IsFunction(ctx, getter)) {
9072                     new_getter = JS_VALUE_GET_OBJ(getter);
9073                 } else {
9074                     new_getter = NULL;
9075                 }
9076                 if (JS_IsFunction(ctx, setter)) {
9077                     new_setter = JS_VALUE_GET_OBJ(setter);
9078                 } else {
9079                     new_setter = NULL;
9080                 }
9081 
9082                 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) {
9083                     if (js_shape_prepare_update(ctx, p, &prs))
9084                         return -1;
9085                     /* convert to getset */
9086                     if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
9087                         free_var_ref(ctx->rt, pr->u.var_ref);
9088                     } else {
9089                         JS_FreeValue(ctx, pr->u.value);
9090                     }
9091                     prs->flags = (prs->flags &
9092                                   (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
9093                         JS_PROP_GETSET;
9094                     pr->u.getset.getter = NULL;
9095                     pr->u.getset.setter = NULL;
9096                 } else {
9097                     if (!(prs->flags & JS_PROP_CONFIGURABLE)) {
9098                         if ((flags & JS_PROP_HAS_GET) &&
9099                             new_getter != pr->u.getset.getter) {
9100                             goto not_configurable;
9101                         }
9102                         if ((flags & JS_PROP_HAS_SET) &&
9103                             new_setter != pr->u.getset.setter) {
9104                             goto not_configurable;
9105                         }
9106                     }
9107                 }
9108                 if (flags & JS_PROP_HAS_GET) {
9109                     if (pr->u.getset.getter)
9110                         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
9111                     if (new_getter)
9112                         JS_DupValue(ctx, getter);
9113                     pr->u.getset.getter = new_getter;
9114                 }
9115                 if (flags & JS_PROP_HAS_SET) {
9116                     if (pr->u.getset.setter)
9117                         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
9118                     if (new_setter)
9119                         JS_DupValue(ctx, setter);
9120                     pr->u.getset.setter = new_setter;
9121                 }
9122             } else {
9123                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
9124                     /* convert to data descriptor */
9125                     if (js_shape_prepare_update(ctx, p, &prs))
9126                         return -1;
9127                     if (pr->u.getset.getter)
9128                         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
9129                     if (pr->u.getset.setter)
9130                         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
9131                     prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
9132                     pr->u.value = JS_UNDEFINED;
9133                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
9134                     /* Note: JS_PROP_VARREF is always writable */
9135                 } else {
9136                     if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&
9137                         (flags & JS_PROP_HAS_VALUE)) {
9138                         if (!js_same_value(ctx, val, pr->u.value)) {
9139                             goto not_configurable;
9140                         } else {
9141                             return TRUE;
9142                         }
9143                     }
9144                 }
9145                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
9146                     if (flags & JS_PROP_HAS_VALUE) {
9147                         if (p->class_id == JS_CLASS_MODULE_NS) {
9148                             /* JS_PROP_WRITABLE is always true for variable
9149                                references, but they are write protected in module name
9150                                spaces. */
9151                             if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue))
9152                                 goto not_configurable;
9153                         }
9154                         /* update the reference */
9155                         set_value(ctx, pr->u.var_ref->pvalue,
9156                                   JS_DupValue(ctx, val));
9157                     }
9158                     /* if writable is set to false, no longer a
9159                        reference (for mapped arguments) */
9160                     if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) {
9161                         JSValue val1;
9162                         if (js_shape_prepare_update(ctx, p, &prs))
9163                             return -1;
9164                         val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue);
9165                         free_var_ref(ctx->rt, pr->u.var_ref);
9166                         pr->u.value = val1;
9167                         prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
9168                     }
9169                 } else if (prs->flags & JS_PROP_LENGTH) {
9170                     if (flags & JS_PROP_HAS_VALUE) {
9171                         /* Note: no JS code is executable because
9172                            'val' is guaranted to be a Uint32 */
9173                         res = set_array_length(ctx, p, JS_DupValue(ctx, val),
9174                                                flags);
9175                     } else {
9176                         res = TRUE;
9177                     }
9178                     /* still need to reset the writable flag if
9179                        needed.  The JS_PROP_LENGTH is kept because the
9180                        Uint32 test is still done if the length
9181                        property is read-only. */
9182                     if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
9183                         JS_PROP_HAS_WRITABLE) {
9184                         prs = get_shape_prop(p->shape);
9185                         if (js_update_property_flags(ctx, p, &prs,
9186                                                      prs->flags & ~JS_PROP_WRITABLE))
9187                             return -1;
9188                     }
9189                     return res;
9190                 } else {
9191                     if (flags & JS_PROP_HAS_VALUE) {
9192                         JS_FreeValue(ctx, pr->u.value);
9193                         pr->u.value = JS_DupValue(ctx, val);
9194                     }
9195                     if (flags & JS_PROP_HAS_WRITABLE) {
9196                         if (js_update_property_flags(ctx, p, &prs,
9197                                                      (prs->flags & ~JS_PROP_WRITABLE) |
9198                                                      (flags & JS_PROP_WRITABLE)))
9199                             return -1;
9200                     }
9201                 }
9202             }
9203         }
9204         mask = 0;
9205         if (flags & JS_PROP_HAS_CONFIGURABLE)
9206             mask |= JS_PROP_CONFIGURABLE;
9207         if (flags & JS_PROP_HAS_ENUMERABLE)
9208             mask |= JS_PROP_ENUMERABLE;
9209         if (js_update_property_flags(ctx, p, &prs,
9210                                      (prs->flags & ~mask) | (flags & mask)))
9211             return -1;
9212         return TRUE;
9213     }
9214 
9215     /* handle modification of fast array elements */
9216     if (p->fast_array) {
9217         uint32_t idx;
9218         uint32_t prop_flags;
9219         if (p->class_id == JS_CLASS_ARRAY) {
9220             if (__JS_AtomIsTaggedInt(prop)) {
9221                 idx = __JS_AtomToUInt32(prop);
9222                 if (idx < p->u.array.count) {
9223                     prop_flags = get_prop_flags(flags, JS_PROP_C_W_E);
9224                     if (prop_flags != JS_PROP_C_W_E)
9225                         goto convert_to_slow_array;
9226                     if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
9227                     convert_to_slow_array:
9228                         if (convert_fast_array_to_array(ctx, p))
9229                             return -1;
9230                         else
9231                             goto redo_prop_update;
9232                     }
9233                     if (flags & JS_PROP_HAS_VALUE) {
9234                         set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val));
9235                     }
9236                     return TRUE;
9237                 }
9238             }
9239         } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
9240                    p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
9241             JSValue num;
9242             int ret;
9243 
9244             if (!__JS_AtomIsTaggedInt(prop)) {
9245                 /* slow path with to handle all numeric indexes */
9246                 num = JS_AtomIsNumericIndex1(ctx, prop);
9247                 if (JS_IsUndefined(num))
9248                     goto typed_array_done;
9249                 if (JS_IsException(num))
9250                     return -1;
9251                 ret = JS_NumberIsInteger(ctx, num);
9252                 if (ret < 0) {
9253                     JS_FreeValue(ctx, num);
9254                     return -1;
9255                 }
9256                 if (!ret) {
9257                     JS_FreeValue(ctx, num);
9258                     return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array");
9259                 }
9260                 ret = JS_NumberIsNegativeOrMinusZero(ctx, num);
9261                 JS_FreeValue(ctx, num);
9262                 if (ret) {
9263                     return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array");
9264                 }
9265                 if (!__JS_AtomIsTaggedInt(prop))
9266                     goto typed_array_oob;
9267             }
9268             idx = __JS_AtomToUInt32(prop);
9269             /* if the typed array is detached, p->u.array.count = 0 */
9270             if (idx >= typed_array_get_length(ctx, p)) {
9271             typed_array_oob:
9272                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array");
9273             }
9274             prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
9275             if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) ||
9276                 prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) {
9277                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags");
9278             }
9279             if (flags & JS_PROP_HAS_VALUE) {
9280                 return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags);
9281             }
9282             return TRUE;
9283         typed_array_done: ;
9284         }
9285     }
9286 
9287     return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags);
9288 }
9289 
JS_DefineAutoInitProperty(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSAutoInitIDEnum id,void * opaque,int flags)9290 static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj,
9291                                      JSAtom prop, JSAutoInitIDEnum id,
9292                                      void *opaque, int flags)
9293 {
9294     JSObject *p;
9295     JSProperty *pr;
9296 
9297     if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT)
9298         return FALSE;
9299 
9300     p = JS_VALUE_GET_OBJ(this_obj);
9301 
9302     if (find_own_property(&pr, p, prop)) {
9303         /* property already exists */
9304         abort();
9305         return FALSE;
9306     }
9307 
9308     /* Specialized CreateProperty */
9309     pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT);
9310     if (unlikely(!pr))
9311         return -1;
9312     pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx);
9313     assert((pr->u.init.realm_and_id & 3) == 0);
9314     assert(id <= 3);
9315     pr->u.init.realm_and_id |= id;
9316     pr->u.init.opaque = opaque;
9317     return TRUE;
9318 }
9319 
9320 /* shortcut to add or redefine a new property value */
JS_DefinePropertyValue(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSValue val,int flags)9321 int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,
9322                            JSAtom prop, JSValue val, int flags)
9323 {
9324     int ret;
9325     ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED,
9326                             flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE);
9327     JS_FreeValue(ctx, val);
9328     return ret;
9329 }
9330 
JS_DefinePropertyValueValue(JSContext * ctx,JSValueConst this_obj,JSValue prop,JSValue val,int flags)9331 int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj,
9332                                 JSValue prop, JSValue val, int flags)
9333 {
9334     JSAtom atom;
9335     int ret;
9336     atom = JS_ValueToAtom(ctx, prop);
9337     JS_FreeValue(ctx, prop);
9338     if (unlikely(atom == JS_ATOM_NULL)) {
9339         JS_FreeValue(ctx, val);
9340         return -1;
9341     }
9342     ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
9343     JS_FreeAtom(ctx, atom);
9344     return ret;
9345 }
9346 
JS_DefinePropertyValueUint32(JSContext * ctx,JSValueConst this_obj,uint32_t idx,JSValue val,int flags)9347 int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,
9348                                  uint32_t idx, JSValue val, int flags)
9349 {
9350     return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx),
9351                                        val, flags);
9352 }
9353 
JS_DefinePropertyValueInt64(JSContext * ctx,JSValueConst this_obj,int64_t idx,JSValue val,int flags)9354 int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj,
9355                                 int64_t idx, JSValue val, int flags)
9356 {
9357     return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
9358                                        val, flags);
9359 }
9360 
JS_DefinePropertyValueStr(JSContext * ctx,JSValueConst this_obj,const char * prop,JSValue val,int flags)9361 int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,
9362                               const char *prop, JSValue val, int flags)
9363 {
9364     JSAtom atom;
9365     int ret;
9366     atom = JS_NewAtom(ctx, prop);
9367     ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
9368     JS_FreeAtom(ctx, atom);
9369     return ret;
9370 }
9371 
9372 /* shortcut to add getter & setter */
JS_DefinePropertyGetSet(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSValue getter,JSValue setter,int flags)9373 int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,
9374                             JSAtom prop, JSValue getter, JSValue setter,
9375                             int flags)
9376 {
9377     int ret;
9378     ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter,
9379                             flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET |
9380                             JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE);
9381     JS_FreeValue(ctx, getter);
9382     JS_FreeValue(ctx, setter);
9383     return ret;
9384 }
9385 
JS_CreateDataPropertyUint32(JSContext * ctx,JSValueConst this_obj,int64_t idx,JSValue val,int flags)9386 static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj,
9387                                        int64_t idx, JSValue val, int flags)
9388 {
9389     return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
9390                                        val, flags | JS_PROP_CONFIGURABLE |
9391                                        JS_PROP_ENUMERABLE | JS_PROP_WRITABLE);
9392 }
9393 
9394 
9395 /* return TRUE if 'obj' has a non empty 'name' string */
js_object_has_name(JSContext * ctx,JSValueConst obj)9396 static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj)
9397 {
9398     JSProperty *pr;
9399     JSShapeProperty *prs;
9400     JSValueConst val;
9401     JSString *p;
9402 
9403     prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name);
9404     if (!prs)
9405         return FALSE;
9406     if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
9407         return TRUE;
9408     val = pr->u.value;
9409     if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
9410         return TRUE;
9411     p = JS_VALUE_GET_STRING(val);
9412     return (p->len != 0);
9413 }
9414 
JS_DefineObjectName(JSContext * ctx,JSValueConst obj,JSAtom name,int flags)9415 static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj,
9416                                JSAtom name, int flags)
9417 {
9418     if (name != JS_ATOM_NULL
9419     &&  JS_IsObject(obj)
9420     &&  !js_object_has_name(ctx, obj)
9421     &&  JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) {
9422         return -1;
9423     }
9424     return 0;
9425 }
9426 
JS_DefineObjectNameComputed(JSContext * ctx,JSValueConst obj,JSValueConst str,int flags)9427 static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj,
9428                                        JSValueConst str, int flags)
9429 {
9430     if (JS_IsObject(obj) &&
9431         !js_object_has_name(ctx, obj)) {
9432         JSAtom prop;
9433         JSValue name_str;
9434         prop = JS_ValueToAtom(ctx, str);
9435         if (prop == JS_ATOM_NULL)
9436             return -1;
9437         name_str = js_get_function_name(ctx, prop);
9438         JS_FreeAtom(ctx, prop);
9439         if (JS_IsException(name_str))
9440             return -1;
9441         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0)
9442             return -1;
9443     }
9444     return 0;
9445 }
9446 
9447 #define DEFINE_GLOBAL_LEX_VAR (1 << 7)
9448 #define DEFINE_GLOBAL_FUNC_VAR (1 << 6)
9449 
JS_ThrowSyntaxErrorVarRedeclaration(JSContext * ctx,JSAtom prop)9450 static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop)
9451 {
9452     return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop);
9453 }
9454 
9455 /* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */
9456 /* XXX: could support exotic global object. */
JS_CheckDefineGlobalVar(JSContext * ctx,JSAtom prop,int flags)9457 static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags)
9458 {
9459     JSObject *p;
9460     JSShapeProperty *prs;
9461 
9462     p = JS_VALUE_GET_OBJ(ctx->global_obj);
9463     prs = find_own_property1(p, prop);
9464     /* XXX: should handle JS_PROP_AUTOINIT */
9465     if (flags & DEFINE_GLOBAL_LEX_VAR) {
9466         if (prs && !(prs->flags & JS_PROP_CONFIGURABLE))
9467             goto fail_redeclaration;
9468     } else {
9469         if (!prs && !p->extensible)
9470             goto define_error;
9471         if (flags & DEFINE_GLOBAL_FUNC_VAR) {
9472             if (prs) {
9473                 if (!(prs->flags & JS_PROP_CONFIGURABLE) &&
9474                     ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET ||
9475                      ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) !=
9476                       (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) {
9477                 define_error:
9478                     JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'",
9479                                           prop);
9480                     return -1;
9481                 }
9482             }
9483         }
9484     }
9485     /* check if there already is a lexical declaration */
9486     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9487     prs = find_own_property1(p, prop);
9488     if (prs) {
9489     fail_redeclaration:
9490         JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop);
9491         return -1;
9492     }
9493     return 0;
9494 }
9495 
9496 /* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) |
9497    JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */
9498 /* XXX: could support exotic global object. */
JS_DefineGlobalVar(JSContext * ctx,JSAtom prop,int def_flags)9499 static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags)
9500 {
9501     JSObject *p;
9502     JSShapeProperty *prs;
9503     JSProperty *pr;
9504     JSValue val;
9505     int flags;
9506 
9507     if (def_flags & DEFINE_GLOBAL_LEX_VAR) {
9508         p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9509         flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) |
9510             JS_PROP_CONFIGURABLE;
9511         val = JS_UNINITIALIZED;
9512     } else {
9513         p = JS_VALUE_GET_OBJ(ctx->global_obj);
9514         flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
9515             (def_flags & JS_PROP_CONFIGURABLE);
9516         val = JS_UNDEFINED;
9517     }
9518     prs = find_own_property1(p, prop);
9519     if (prs)
9520         return 0;
9521     if (!p->extensible)
9522         return 0;
9523     pr = add_property(ctx, p, prop, flags);
9524     if (unlikely(!pr))
9525         return -1;
9526     pr->u.value = val;
9527     return 0;
9528 }
9529 
9530 /* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */
9531 /* XXX: could support exotic global object. */
JS_DefineGlobalFunction(JSContext * ctx,JSAtom prop,JSValueConst func,int def_flags)9532 static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,
9533                                    JSValueConst func, int def_flags)
9534 {
9535 
9536     JSObject *p;
9537     JSShapeProperty *prs;
9538     int flags;
9539 
9540     p = JS_VALUE_GET_OBJ(ctx->global_obj);
9541     prs = find_own_property1(p, prop);
9542     flags = JS_PROP_HAS_VALUE | JS_PROP_THROW;
9543     if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) {
9544         flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags |
9545             JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE;
9546     }
9547     if (JS_DefineProperty(ctx, ctx->global_obj, prop, func,
9548                           JS_UNDEFINED, JS_UNDEFINED, flags) < 0)
9549         return -1;
9550     return 0;
9551 }
9552 
JS_GetGlobalVar(JSContext * ctx,JSAtom prop,BOOL throw_ref_error)9553 static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
9554                                BOOL throw_ref_error)
9555 {
9556     JSObject *p;
9557     JSShapeProperty *prs;
9558     JSProperty *pr;
9559 
9560     /* no exotic behavior is possible in global_var_obj */
9561     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9562     prs = find_own_property(&pr, p, prop);
9563     if (prs) {
9564         /* XXX: should handle JS_PROP_TMASK properties */
9565         if (unlikely(JS_IsUninitialized(pr->u.value)))
9566             return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
9567         return JS_DupValue(ctx, pr->u.value);
9568     }
9569     return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,
9570                                  ctx->global_obj, throw_ref_error);
9571 }
9572 
9573 /* construct a reference to a global variable */
JS_GetGlobalVarRef(JSContext * ctx,JSAtom prop,JSValue * sp)9574 static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)
9575 {
9576     JSObject *p;
9577     JSShapeProperty *prs;
9578     JSProperty *pr;
9579 
9580     /* no exotic behavior is possible in global_var_obj */
9581     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9582     prs = find_own_property(&pr, p, prop);
9583     if (prs) {
9584         /* XXX: should handle JS_PROP_AUTOINIT properties? */
9585         /* XXX: conformance: do these tests in
9586            OP_put_var_ref/OP_get_var_ref ? */
9587         if (unlikely(JS_IsUninitialized(pr->u.value))) {
9588             JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
9589             return -1;
9590         }
9591         if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
9592             return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
9593         }
9594         sp[0] = JS_DupValue(ctx, ctx->global_var_obj);
9595     } else {
9596         int ret;
9597         ret = JS_HasProperty(ctx, ctx->global_obj, prop);
9598         if (ret < 0)
9599             return -1;
9600         if (ret) {
9601             sp[0] = JS_DupValue(ctx, ctx->global_obj);
9602         } else {
9603             sp[0] = JS_UNDEFINED;
9604         }
9605     }
9606     sp[1] = JS_AtomToValue(ctx, prop);
9607     return 0;
9608 }
9609 
9610 /* use for strict variable access: test if the variable exists */
JS_CheckGlobalVar(JSContext * ctx,JSAtom prop)9611 static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop)
9612 {
9613     JSObject *p;
9614     JSShapeProperty *prs;
9615     int ret;
9616 
9617     /* no exotic behavior is possible in global_var_obj */
9618     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9619     prs = find_own_property1(p, prop);
9620     if (prs) {
9621         ret = TRUE;
9622     } else {
9623         ret = JS_HasProperty(ctx, ctx->global_obj, prop);
9624         if (ret < 0)
9625             return -1;
9626     }
9627     return ret;
9628 }
9629 
9630 /* flag = 0: normal variable write
9631    flag = 1: initialize lexical variable
9632    flag = 2: normal variable write, strict check was done before
9633 */
JS_SetGlobalVar(JSContext * ctx,JSAtom prop,JSValue val,int flag)9634 static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
9635                            int flag)
9636 {
9637     JSObject *p;
9638     JSShapeProperty *prs;
9639     JSProperty *pr;
9640     int flags;
9641 
9642     /* no exotic behavior is possible in global_var_obj */
9643     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9644     prs = find_own_property(&pr, p, prop);
9645     if (prs) {
9646         /* XXX: should handle JS_PROP_AUTOINIT properties? */
9647         if (flag != 1) {
9648             if (unlikely(JS_IsUninitialized(pr->u.value))) {
9649                 JS_FreeValue(ctx, val);
9650                 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
9651                 return -1;
9652             }
9653             if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
9654                 JS_FreeValue(ctx, val);
9655                 return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
9656             }
9657         }
9658         set_value(ctx, &pr->u.value, val);
9659         return 0;
9660     }
9661     flags = JS_PROP_THROW_STRICT;
9662     if (is_strict_mode(ctx))
9663         flags |= JS_PROP_NO_ADD;
9664     return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags);
9665 }
9666 
9667 /* return -1, FALSE or TRUE. return FALSE if not configurable or
9668    invalid object. return -1 in case of exception.
9669    flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */
JS_DeleteProperty(JSContext * ctx,JSValueConst obj,JSAtom prop,int flags)9670 int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags)
9671 {
9672     JSValue obj1;
9673     JSObject *p;
9674     int res;
9675 
9676     obj1 = JS_ToObject(ctx, obj);
9677     if (JS_IsException(obj1))
9678         return -1;
9679     p = JS_VALUE_GET_OBJ(obj1);
9680     res = delete_property(ctx, p, prop);
9681     JS_FreeValue(ctx, obj1);
9682     if (res != FALSE)
9683         return res;
9684     if ((flags & JS_PROP_THROW) ||
9685         ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
9686         JS_ThrowTypeError(ctx, "could not delete property");
9687         return -1;
9688     }
9689     return FALSE;
9690 }
9691 
JS_DeletePropertyInt64(JSContext * ctx,JSValueConst obj,int64_t idx,int flags)9692 int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags)
9693 {
9694     JSAtom prop;
9695     int res;
9696 
9697     if ((uint64_t)idx <= JS_ATOM_MAX_INT) {
9698         /* fast path for fast arrays */
9699         return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags);
9700     }
9701     prop = JS_NewAtomInt64(ctx, idx);
9702     if (prop == JS_ATOM_NULL)
9703         return -1;
9704     res = JS_DeleteProperty(ctx, obj, prop, flags);
9705     JS_FreeAtom(ctx, prop);
9706     return res;
9707 }
9708 
JS_IsFunction(JSContext * ctx,JSValueConst val)9709 BOOL JS_IsFunction(JSContext *ctx, JSValueConst val)
9710 {
9711     JSObject *p;
9712     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9713         return FALSE;
9714     p = JS_VALUE_GET_OBJ(val);
9715     switch(p->class_id) {
9716     case JS_CLASS_BYTECODE_FUNCTION:
9717         return TRUE;
9718     case JS_CLASS_PROXY:
9719         return p->u.proxy_data->is_func;
9720     default:
9721         return (ctx->rt->class_array[p->class_id].call != NULL);
9722     }
9723 }
9724 
JS_IsCFunction(JSContext * ctx,JSValueConst val,JSCFunction * func,int magic)9725 BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic)
9726 {
9727     JSObject *p;
9728     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9729         return FALSE;
9730     p = JS_VALUE_GET_OBJ(val);
9731     if (p->class_id == JS_CLASS_C_FUNCTION)
9732         return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic);
9733     else
9734         return FALSE;
9735 }
9736 
JS_IsConstructor(JSContext * ctx,JSValueConst val)9737 BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val)
9738 {
9739     JSObject *p;
9740     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9741         return FALSE;
9742     p = JS_VALUE_GET_OBJ(val);
9743     return p->is_constructor;
9744 }
9745 
JS_SetConstructorBit(JSContext * ctx,JSValueConst func_obj,BOOL val)9746 BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val)
9747 {
9748     JSObject *p;
9749     if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
9750         return FALSE;
9751     p = JS_VALUE_GET_OBJ(func_obj);
9752     p->is_constructor = val;
9753     return TRUE;
9754 }
9755 
JS_IsError(JSContext * ctx,JSValueConst val)9756 BOOL JS_IsError(JSContext *ctx, JSValueConst val)
9757 {
9758     JSObject *p;
9759     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9760         return FALSE;
9761     p = JS_VALUE_GET_OBJ(val);
9762     return (p->class_id == JS_CLASS_ERROR);
9763 }
9764 
9765 /* used to avoid catching interrupt exceptions */
JS_IsUncatchableError(JSContext * ctx,JSValueConst val)9766 BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val)
9767 {
9768     JSObject *p;
9769     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9770         return FALSE;
9771     p = JS_VALUE_GET_OBJ(val);
9772     return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error;
9773 }
9774 
JS_SetUncatchableError(JSContext * ctx,JSValueConst val,BOOL flag)9775 void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag)
9776 {
9777     JSObject *p;
9778     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9779         return;
9780     p = JS_VALUE_GET_OBJ(val);
9781     if (p->class_id == JS_CLASS_ERROR)
9782         p->is_uncatchable_error = flag;
9783 }
9784 
JS_ResetUncatchableError(JSContext * ctx)9785 void JS_ResetUncatchableError(JSContext *ctx)
9786 {
9787     JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE);
9788 }
9789 
JS_SetOpaque(JSValue obj,void * opaque)9790 void JS_SetOpaque(JSValue obj, void *opaque)
9791 {
9792    JSObject *p;
9793     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
9794         p = JS_VALUE_GET_OBJ(obj);
9795         p->u.opaque = opaque;
9796     }
9797 }
9798 
9799 /* return NULL if not an object of class class_id */
JS_GetOpaque(JSValueConst obj,JSClassID class_id)9800 void *JS_GetOpaque(JSValueConst obj, JSClassID class_id)
9801 {
9802     JSObject *p;
9803     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
9804         return NULL;
9805     p = JS_VALUE_GET_OBJ(obj);
9806     if (p->class_id != class_id)
9807         return NULL;
9808     return p->u.opaque;
9809 }
9810 
JS_GetOpaque2(JSContext * ctx,JSValueConst obj,JSClassID class_id)9811 void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id)
9812 {
9813     void *p = JS_GetOpaque(obj, class_id);
9814     if (unlikely(!p)) {
9815         JS_ThrowTypeErrorInvalidClass(ctx, class_id);
9816     }
9817     return p;
9818 }
9819 
JS_GetOpaque3(JSValueConst obj)9820 void *JS_GetOpaque3(JSValueConst obj)
9821 {
9822     JSObject *p;
9823     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
9824         return NULL;
9825     p = JS_VALUE_GET_OBJ(obj);
9826     return p->u.opaque;
9827 }
9828 
9829 #define HINT_STRING  0
9830 #define HINT_NUMBER  1
9831 #define HINT_NONE    2
9832 /* don't try Symbol.toPrimitive */
9833 #define HINT_FORCE_ORDINARY (1 << 4)
9834 
JS_ToPrimitiveFree(JSContext * ctx,JSValue val,int hint)9835 static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
9836 {
9837     int i;
9838     BOOL force_ordinary;
9839 
9840     JSAtom method_name;
9841     JSValue method, ret;
9842     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
9843         return val;
9844     force_ordinary = hint & HINT_FORCE_ORDINARY;
9845     hint &= ~HINT_FORCE_ORDINARY;
9846     if (!force_ordinary) {
9847         method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive);
9848         if (JS_IsException(method))
9849             goto exception;
9850         /* ECMA says *If exoticToPrim is not undefined* but tests in
9851            test262 use null as a non callable converter */
9852         if (!JS_IsUndefined(method) && !JS_IsNull(method)) {
9853             JSAtom atom;
9854             JSValue arg;
9855             switch(hint) {
9856             case HINT_STRING:
9857                 atom = JS_ATOM_string;
9858                 break;
9859             case HINT_NUMBER:
9860                 atom = JS_ATOM_number;
9861                 break;
9862             default:
9863             case HINT_NONE:
9864                 atom = JS_ATOM_default;
9865                 break;
9866             }
9867             arg = JS_AtomToString(ctx, atom);
9868             ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg);
9869             JS_FreeValue(ctx, arg);
9870             if (JS_IsException(ret))
9871                 goto exception;
9872             JS_FreeValue(ctx, val);
9873             if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT)
9874                 return ret;
9875             JS_FreeValue(ctx, ret);
9876             return JS_ThrowTypeError(ctx, "toPrimitive");
9877         }
9878     }
9879     if (hint != HINT_STRING)
9880         hint = HINT_NUMBER;
9881     for(i = 0; i < 2; i++) {
9882         if ((i ^ hint) == 0) {
9883             method_name = JS_ATOM_toString;
9884         } else {
9885             method_name = JS_ATOM_valueOf;
9886         }
9887         method = JS_GetProperty(ctx, val, method_name);
9888         if (JS_IsException(method))
9889             goto exception;
9890         if (JS_IsFunction(ctx, method)) {
9891             ret = JS_CallFree(ctx, method, val, 0, NULL);
9892             if (JS_IsException(ret))
9893                 goto exception;
9894             if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
9895                 JS_FreeValue(ctx, val);
9896                 return ret;
9897             }
9898             JS_FreeValue(ctx, ret);
9899         } else {
9900             JS_FreeValue(ctx, method);
9901         }
9902     }
9903     JS_ThrowTypeError(ctx, "toPrimitive");
9904 exception:
9905     JS_FreeValue(ctx, val);
9906     return JS_EXCEPTION;
9907 }
9908 
JS_ToPrimitive(JSContext * ctx,JSValueConst val,int hint)9909 static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint)
9910 {
9911     return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint);
9912 }
9913 
JS_SetIsHTMLDDA(JSContext * ctx,JSValueConst obj)9914 void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj)
9915 {
9916     JSObject *p;
9917     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
9918         return;
9919     p = JS_VALUE_GET_OBJ(obj);
9920     p->is_HTMLDDA = TRUE;
9921 }
9922 
JS_IsHTMLDDA(JSContext * ctx,JSValueConst obj)9923 static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj)
9924 {
9925     JSObject *p;
9926     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
9927         return FALSE;
9928     p = JS_VALUE_GET_OBJ(obj);
9929     return p->is_HTMLDDA;
9930 }
9931 
JS_ToBoolFree(JSContext * ctx,JSValue val)9932 static int JS_ToBoolFree(JSContext *ctx, JSValue val)
9933 {
9934     uint32_t tag = JS_VALUE_GET_TAG(val);
9935     switch(tag) {
9936     case JS_TAG_INT:
9937         return JS_VALUE_GET_INT(val) != 0;
9938     case JS_TAG_BOOL:
9939     case JS_TAG_NULL:
9940     case JS_TAG_UNDEFINED:
9941         return JS_VALUE_GET_INT(val);
9942     case JS_TAG_EXCEPTION:
9943         return -1;
9944     case JS_TAG_STRING:
9945         {
9946             BOOL ret = JS_VALUE_GET_STRING(val)->len != 0;
9947             JS_FreeValue(ctx, val);
9948             return ret;
9949         }
9950 #ifdef CONFIG_BIGNUM
9951     case JS_TAG_BIG_INT:
9952     case JS_TAG_BIG_FLOAT:
9953         {
9954             JSBigFloat *p = JS_VALUE_GET_PTR(val);
9955             BOOL ret;
9956             ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
9957             JS_FreeValue(ctx, val);
9958             return ret;
9959         }
9960     case JS_TAG_BIG_DECIMAL:
9961         {
9962             JSBigDecimal *p = JS_VALUE_GET_PTR(val);
9963             BOOL ret;
9964             ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
9965             JS_FreeValue(ctx, val);
9966             return ret;
9967         }
9968 #endif
9969     case JS_TAG_OBJECT:
9970         {
9971             JSObject *p = JS_VALUE_GET_OBJ(val);
9972             BOOL ret;
9973             ret = !p->is_HTMLDDA;
9974             JS_FreeValue(ctx, val);
9975             return ret;
9976         }
9977         break;
9978     default:
9979         if (JS_TAG_IS_FLOAT64(tag)) {
9980             double d = JS_VALUE_GET_FLOAT64(val);
9981             return !isnan(d) && d != 0;
9982         } else {
9983             JS_FreeValue(ctx, val);
9984             return TRUE;
9985         }
9986     }
9987 }
9988 
JS_ToBool(JSContext * ctx,JSValueConst val)9989 int JS_ToBool(JSContext *ctx, JSValueConst val)
9990 {
9991     return JS_ToBoolFree(ctx, JS_DupValue(ctx, val));
9992 }
9993 
skip_spaces(const char * pc)9994 static int skip_spaces(const char *pc)
9995 {
9996     const uint8_t *p, *p_next, *p_start;
9997     uint32_t c;
9998 
9999     p = p_start = (const uint8_t *)pc;
10000     for (;;) {
10001         c = *p;
10002         if (c < 128) {
10003             if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20)))
10004                 break;
10005             p++;
10006         } else {
10007             c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
10008             if (!lre_is_space(c))
10009                 break;
10010             p = p_next;
10011         }
10012     }
10013     return p - p_start;
10014 }
10015 
to_digit(int c)10016 static inline int to_digit(int c)
10017 {
10018     if (c >= '0' && c <= '9')
10019         return c - '0';
10020     else if (c >= 'A' && c <= 'Z')
10021         return c - 'A' + 10;
10022     else if (c >= 'a' && c <= 'z')
10023         return c - 'a' + 10;
10024     else
10025         return 36;
10026 }
10027 
10028 /* XXX: remove */
js_strtod(const char * p,int radix,BOOL is_float)10029 static double js_strtod(const char *p, int radix, BOOL is_float)
10030 {
10031     double d;
10032     int c;
10033 
10034     if (!is_float || radix != 10) {
10035         uint64_t n_max, n;
10036         int int_exp, is_neg;
10037 
10038         is_neg = 0;
10039         if (*p == '-') {
10040             is_neg = 1;
10041             p++;
10042         }
10043 
10044         /* skip leading zeros */
10045         while (*p == '0')
10046             p++;
10047         n = 0;
10048         if (radix == 10)
10049             n_max = ((uint64_t)-1 - 9) / 10; /* most common case */
10050         else
10051             n_max = ((uint64_t)-1 - (radix - 1)) / radix;
10052         /* XXX: could be more precise */
10053         int_exp = 0;
10054         while (*p != '\0') {
10055             c = to_digit((uint8_t)*p);
10056             if (c >= radix)
10057                 break;
10058             if (n <= n_max) {
10059                 n = n * radix + c;
10060             } else {
10061                 int_exp++;
10062             }
10063             p++;
10064         }
10065         d = n;
10066         if (int_exp != 0) {
10067             d *= pow(radix, int_exp);
10068         }
10069         if (is_neg)
10070             d = -d;
10071     } else {
10072         d = strtod(p, NULL);
10073     }
10074     return d;
10075 }
10076 
10077 #define ATOD_INT_ONLY        (1 << 0)
10078 /* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
10079 #define ATOD_ACCEPT_BIN_OCT  (1 << 2)
10080 /* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
10081 #define ATOD_ACCEPT_LEGACY_OCTAL  (1 << 4)
10082 /* accept _ between digits as a digit separator */
10083 #define ATOD_ACCEPT_UNDERSCORES  (1 << 5)
10084 /* allow a suffix to override the type */
10085 #define ATOD_ACCEPT_SUFFIX    (1 << 6)
10086 /* default type */
10087 #define ATOD_TYPE_MASK        (3 << 7)
10088 #define ATOD_TYPE_FLOAT64     (0 << 7)
10089 #define ATOD_TYPE_BIG_INT     (1 << 7)
10090 #define ATOD_TYPE_BIG_FLOAT   (2 << 7)
10091 #define ATOD_TYPE_BIG_DECIMAL (3 << 7)
10092 /* assume bigint mode: floats are parsed as integers if no decimal
10093    point nor exponent */
10094 #define ATOD_MODE_BIGINT      (1 << 9)
10095 /* accept -0x1 */
10096 #define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)
10097 
10098 #ifdef CONFIG_BIGNUM
js_string_to_bigint(JSContext * ctx,const char * buf,int radix,int flags,slimb_t * pexponent)10099 static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,
10100                                    int radix, int flags, slimb_t *pexponent)
10101 {
10102     bf_t a_s, *a = &a_s;
10103     int ret;
10104     JSValue val;
10105     val = JS_NewBigInt(ctx);
10106     if (JS_IsException(val))
10107         return val;
10108     a = JS_GetBigInt(val);
10109     ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ);
10110     if (ret & BF_ST_MEM_ERROR) {
10111         JS_FreeValue(ctx, val);
10112         return JS_ThrowOutOfMemory(ctx);
10113     }
10114     val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);
10115     return val;
10116 }
10117 
js_string_to_bigfloat(JSContext * ctx,const char * buf,int radix,int flags,slimb_t * pexponent)10118 static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf,
10119                                      int radix, int flags, slimb_t *pexponent)
10120 {
10121     bf_t *a;
10122     int ret;
10123     JSValue val;
10124 
10125     val = JS_NewBigFloat(ctx);
10126     if (JS_IsException(val))
10127         return val;
10128     a = JS_GetBigFloat(val);
10129     if (flags & ATOD_ACCEPT_SUFFIX) {
10130         /* return the exponent to get infinite precision */
10131         ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF,
10132                        BF_RNDZ | BF_ATOF_EXPONENT);
10133     } else {
10134         ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec,
10135                       ctx->fp_env.flags);
10136     }
10137     if (ret & BF_ST_MEM_ERROR) {
10138         JS_FreeValue(ctx, val);
10139         return JS_ThrowOutOfMemory(ctx);
10140     }
10141     return val;
10142 }
10143 
js_string_to_bigdecimal(JSContext * ctx,const char * buf,int radix,int flags,slimb_t * pexponent)10144 static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf,
10145                                        int radix, int flags, slimb_t *pexponent)
10146 {
10147     bfdec_t *a;
10148     int ret;
10149     JSValue val;
10150 
10151     val = JS_NewBigDecimal(ctx);
10152     if (JS_IsException(val))
10153         return val;
10154     a = JS_GetBigDecimal(val);
10155     ret = bfdec_atof(a, buf, NULL, BF_PREC_INF,
10156                      BF_RNDZ | BF_ATOF_NO_NAN_INF);
10157     if (ret & BF_ST_MEM_ERROR) {
10158         JS_FreeValue(ctx, val);
10159         return JS_ThrowOutOfMemory(ctx);
10160     }
10161     return val;
10162 }
10163 
10164 #endif
10165 
10166 /* return an exception in case of memory error. Return JS_NAN if
10167    invalid syntax */
10168 #ifdef CONFIG_BIGNUM
js_atof2(JSContext * ctx,const char * str,const char ** pp,int radix,int flags,slimb_t * pexponent)10169 static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp,
10170                         int radix, int flags, slimb_t *pexponent)
10171 #else
10172 static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
10173                        int radix, int flags)
10174 #endif
10175 {
10176     const char *p, *p_start;
10177     int sep, is_neg;
10178     BOOL is_float, has_legacy_octal;
10179     int atod_type = flags & ATOD_TYPE_MASK;
10180     char buf1[64], *buf;
10181     int i, j, len;
10182     BOOL buf_allocated = FALSE;
10183     JSValue val;
10184 
10185     /* optional separator between digits */
10186     sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
10187     has_legacy_octal = FALSE;
10188 
10189     p = str;
10190     p_start = p;
10191     is_neg = 0;
10192     if (p[0] == '+') {
10193         p++;
10194         p_start++;
10195         if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
10196             goto no_radix_prefix;
10197     } else if (p[0] == '-') {
10198         p++;
10199         p_start++;
10200         is_neg = 1;
10201         if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
10202             goto no_radix_prefix;
10203     }
10204     if (p[0] == '0') {
10205         if ((p[1] == 'x' || p[1] == 'X') &&
10206             (radix == 0 || radix == 16)) {
10207             p += 2;
10208             radix = 16;
10209         } else if ((p[1] == 'o' || p[1] == 'O') &&
10210                    radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
10211             p += 2;
10212             radix = 8;
10213         } else if ((p[1] == 'b' || p[1] == 'B') &&
10214                    radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
10215             p += 2;
10216             radix = 2;
10217         } else if ((p[1] >= '0' && p[1] <= '9') &&
10218                    radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {
10219             int i;
10220             has_legacy_octal = TRUE;
10221             sep = 256;
10222             for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
10223                 continue;
10224             if (p[i] == '8' || p[i] == '9')
10225                 goto no_prefix;
10226             p += 1;
10227             radix = 8;
10228         } else {
10229             goto no_prefix;
10230         }
10231         /* there must be a digit after the prefix */
10232         if (to_digit((uint8_t)*p) >= radix)
10233             goto fail;
10234     no_prefix: ;
10235     } else {
10236  no_radix_prefix:
10237         if (!(flags & ATOD_INT_ONLY) &&
10238             (atod_type == ATOD_TYPE_FLOAT64 ||
10239              atod_type == ATOD_TYPE_BIG_FLOAT) &&
10240             strstart(p, "Infinity", &p)) {
10241 #ifdef CONFIG_BIGNUM
10242             if (atod_type == ATOD_TYPE_BIG_FLOAT) {
10243                 bf_t *a;
10244                 val = JS_NewBigFloat(ctx);
10245                 if (JS_IsException(val))
10246                     goto done;
10247                 a = JS_GetBigFloat(val);
10248                 bf_set_inf(a, is_neg);
10249             } else
10250 #endif
10251             {
10252                 double d = 1.0 / 0.0;
10253                 if (is_neg)
10254                     d = -d;
10255                 val = JS_NewFloat64(ctx, d);
10256             }
10257             goto done;
10258         }
10259     }
10260     if (radix == 0)
10261         radix = 10;
10262     is_float = FALSE;
10263     p_start = p;
10264     while (to_digit((uint8_t)*p) < radix
10265            ||  (*p == sep && (radix != 10 ||
10266                               p != p_start + 1 || p[-1] != '0') &&
10267                 to_digit((uint8_t)p[1]) < radix)) {
10268         p++;
10269     }
10270     if (!(flags & ATOD_INT_ONLY)) {
10271         if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {
10272             is_float = TRUE;
10273             p++;
10274             if (*p == sep)
10275                 goto fail;
10276             while (to_digit((uint8_t)*p) < radix ||
10277                    (*p == sep && to_digit((uint8_t)p[1]) < radix))
10278                 p++;
10279         }
10280         if (p > p_start &&
10281             (((*p == 'e' || *p == 'E') && radix == 10) ||
10282              ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
10283             const char *p1 = p + 1;
10284             is_float = TRUE;
10285             if (*p1 == '+') {
10286                 p1++;
10287             } else if (*p1 == '-') {
10288                 p1++;
10289             }
10290             if (is_digit((uint8_t)*p1)) {
10291                 p = p1 + 1;
10292                 while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))
10293                     p++;
10294             }
10295         }
10296     }
10297     if (p == p_start)
10298         goto fail;
10299 
10300     buf = buf1;
10301     buf_allocated = FALSE;
10302     len = p - p_start;
10303     if (unlikely((len + 2) > sizeof(buf1))) {
10304         buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */
10305         if (!buf)
10306             goto mem_error;
10307         buf_allocated = TRUE;
10308     }
10309     /* remove the separators and the radix prefixes */
10310     j = 0;
10311     if (is_neg)
10312         buf[j++] = '-';
10313     for (i = 0; i < len; i++) {
10314         if (p_start[i] != '_')
10315             buf[j++] = p_start[i];
10316     }
10317     buf[j] = '\0';
10318 
10319 #ifdef CONFIG_BIGNUM
10320     if (flags & ATOD_ACCEPT_SUFFIX) {
10321         if (*p == 'n') {
10322             p++;
10323             atod_type = ATOD_TYPE_BIG_INT;
10324         } else if (*p == 'l') {
10325             p++;
10326             atod_type = ATOD_TYPE_BIG_FLOAT;
10327         } else if (*p == 'm') {
10328             p++;
10329             atod_type = ATOD_TYPE_BIG_DECIMAL;
10330         } else {
10331             if (flags & ATOD_MODE_BIGINT) {
10332                 if (!is_float)
10333                     atod_type = ATOD_TYPE_BIG_INT;
10334                 if (has_legacy_octal)
10335                     goto fail;
10336             } else {
10337                 if (is_float && radix != 10)
10338                     goto fail;
10339             }
10340         }
10341     } else {
10342         if (atod_type == ATOD_TYPE_FLOAT64) {
10343             if (flags & ATOD_MODE_BIGINT) {
10344                 if (!is_float)
10345                     atod_type = ATOD_TYPE_BIG_INT;
10346                 if (has_legacy_octal)
10347                     goto fail;
10348             } else {
10349                 if (is_float && radix != 10)
10350                     goto fail;
10351             }
10352         }
10353     }
10354 
10355     switch(atod_type) {
10356     case ATOD_TYPE_FLOAT64:
10357         {
10358             double d;
10359             d = js_strtod(buf, radix, is_float);
10360             /* return int or float64 */
10361             val = JS_NewFloat64(ctx, d);
10362         }
10363         break;
10364     case ATOD_TYPE_BIG_INT:
10365         if (has_legacy_octal || is_float)
10366             goto fail;
10367         val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL);
10368         break;
10369     case ATOD_TYPE_BIG_FLOAT:
10370         if (has_legacy_octal)
10371             goto fail;
10372         val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags,
10373                                                 pexponent);
10374         break;
10375     case ATOD_TYPE_BIG_DECIMAL:
10376         if (radix != 10)
10377             goto fail;
10378         val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL);
10379         break;
10380     default:
10381         abort();
10382     }
10383 #else
10384     {
10385         double d;
10386         (void)has_legacy_octal;
10387         if (is_float && radix != 10)
10388             goto fail;
10389         d = js_strtod(buf, radix, is_float);
10390         val = JS_NewFloat64(ctx, d);
10391     }
10392 #endif
10393 
10394 done:
10395     if (buf_allocated)
10396         js_free_rt(ctx->rt, buf);
10397     if (pp)
10398         *pp = p;
10399     return val;
10400  fail:
10401     val = JS_NAN;
10402     goto done;
10403  mem_error:
10404     val = JS_ThrowOutOfMemory(ctx);
10405     goto done;
10406 }
10407 
10408 #ifdef CONFIG_BIGNUM
js_atof(JSContext * ctx,const char * str,const char ** pp,int radix,int flags)10409 static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
10410                        int radix, int flags)
10411 {
10412     return js_atof2(ctx, str, pp, radix, flags, NULL);
10413 }
10414 #endif
10415 
10416 typedef enum JSToNumberHintEnum {
10417     TON_FLAG_NUMBER,
10418     TON_FLAG_NUMERIC,
10419 } JSToNumberHintEnum;
10420 
JS_ToNumberHintFree(JSContext * ctx,JSValue val,JSToNumberHintEnum flag)10421 static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
10422                                    JSToNumberHintEnum flag)
10423 {
10424     uint32_t tag;
10425     JSValue ret;
10426 
10427  redo:
10428     tag = JS_VALUE_GET_NORM_TAG(val);
10429     switch(tag) {
10430 #ifdef CONFIG_BIGNUM
10431     case JS_TAG_BIG_DECIMAL:
10432         if (flag != TON_FLAG_NUMERIC) {
10433             JS_FreeValue(ctx, val);
10434             return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number");
10435         }
10436         ret = val;
10437         break;
10438     case JS_TAG_BIG_INT:
10439         if (flag != TON_FLAG_NUMERIC) {
10440             JS_FreeValue(ctx, val);
10441             return JS_ThrowTypeError(ctx, "cannot convert bigint to number");
10442         }
10443         ret = val;
10444         break;
10445     case JS_TAG_BIG_FLOAT:
10446         if (flag != TON_FLAG_NUMERIC) {
10447             JS_FreeValue(ctx, val);
10448             return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number");
10449         }
10450         ret = val;
10451         break;
10452 #endif
10453     case JS_TAG_FLOAT64:
10454     case JS_TAG_INT:
10455     case JS_TAG_EXCEPTION:
10456         ret = val;
10457         break;
10458     case JS_TAG_BOOL:
10459     case JS_TAG_NULL:
10460         ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
10461         break;
10462     case JS_TAG_UNDEFINED:
10463         ret = JS_NAN;
10464         break;
10465     case JS_TAG_OBJECT:
10466         val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
10467         if (JS_IsException(val))
10468             return JS_EXCEPTION;
10469         goto redo;
10470     case JS_TAG_STRING:
10471         {
10472             const char *str;
10473             const char *p;
10474             size_t len;
10475 
10476             str = JS_ToCStringLen(ctx, &len, val);
10477             JS_FreeValue(ctx, val);
10478             if (!str)
10479                 return JS_EXCEPTION;
10480             p = str;
10481             p += skip_spaces(p);
10482             if ((p - str) == len) {
10483                 ret = JS_NewInt32(ctx, 0);
10484             } else {
10485                 int flags = ATOD_ACCEPT_BIN_OCT;
10486                 ret = js_atof(ctx, p, &p, 0, flags);
10487                 if (!JS_IsException(ret)) {
10488                     p += skip_spaces(p);
10489                     if ((p - str) != len) {
10490                         JS_FreeValue(ctx, ret);
10491                         ret = JS_NAN;
10492                     }
10493                 }
10494             }
10495             JS_FreeCString(ctx, str);
10496         }
10497         break;
10498     case JS_TAG_SYMBOL:
10499         JS_FreeValue(ctx, val);
10500         return JS_ThrowTypeError(ctx, "cannot convert symbol to number");
10501     default:
10502         JS_FreeValue(ctx, val);
10503         ret = JS_NAN;
10504         break;
10505     }
10506     return ret;
10507 }
10508 
JS_ToNumberFree(JSContext * ctx,JSValue val)10509 static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
10510 {
10511     return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);
10512 }
10513 
JS_ToNumericFree(JSContext * ctx,JSValue val)10514 static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)
10515 {
10516     return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);
10517 }
10518 
JS_ToNumeric(JSContext * ctx,JSValueConst val)10519 static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
10520 {
10521     return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
10522 }
10523 
__JS_ToFloat64Free(JSContext * ctx,double * pres,JSValue val)10524 static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,
10525                                           JSValue val)
10526 {
10527     double d;
10528     uint32_t tag;
10529 
10530     val = JS_ToNumberFree(ctx, val);
10531     if (JS_IsException(val)) {
10532         *pres = JS_FLOAT64_NAN;
10533         return -1;
10534     }
10535     tag = JS_VALUE_GET_NORM_TAG(val);
10536     switch(tag) {
10537     case JS_TAG_INT:
10538         d = JS_VALUE_GET_INT(val);
10539         break;
10540     case JS_TAG_FLOAT64:
10541         d = JS_VALUE_GET_FLOAT64(val);
10542         break;
10543 #ifdef CONFIG_BIGNUM
10544     case JS_TAG_BIG_INT:
10545     case JS_TAG_BIG_FLOAT:
10546         {
10547             JSBigFloat *p = JS_VALUE_GET_PTR(val);
10548             /* XXX: there can be a double rounding issue with some
10549                primitives (such as JS_ToUint8ClampFree()), but it is
10550                not critical to fix it. */
10551             bf_get_float64(&p->num, &d, BF_RNDN);
10552             JS_FreeValue(ctx, val);
10553         }
10554         break;
10555 #endif
10556     default:
10557         abort();
10558     }
10559     *pres = d;
10560     return 0;
10561 }
10562 
JS_ToFloat64Free(JSContext * ctx,double * pres,JSValue val)10563 static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
10564 {
10565     uint32_t tag;
10566 
10567     tag = JS_VALUE_GET_TAG(val);
10568     if (tag <= JS_TAG_NULL) {
10569         *pres = JS_VALUE_GET_INT(val);
10570         return 0;
10571     } else if (JS_TAG_IS_FLOAT64(tag)) {
10572         *pres = JS_VALUE_GET_FLOAT64(val);
10573         return 0;
10574     } else {
10575         return __JS_ToFloat64Free(ctx, pres, val);
10576     }
10577 }
10578 
JS_ToFloat64(JSContext * ctx,double * pres,JSValueConst val)10579 int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)
10580 {
10581     return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));
10582 }
10583 
JS_ToNumber(JSContext * ctx,JSValueConst val)10584 static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
10585 {
10586     return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
10587 }
10588 
10589 /* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
JS_ToIntegerFree(JSContext * ctx,JSValue val)10590 static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
10591 {
10592     uint32_t tag;
10593     JSValue ret;
10594 
10595  redo:
10596     tag = JS_VALUE_GET_NORM_TAG(val);
10597     switch(tag) {
10598     case JS_TAG_INT:
10599     case JS_TAG_BOOL:
10600     case JS_TAG_NULL:
10601     case JS_TAG_UNDEFINED:
10602         ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
10603         break;
10604     case JS_TAG_FLOAT64:
10605         {
10606             double d = JS_VALUE_GET_FLOAT64(val);
10607             if (isnan(d)) {
10608                 ret = JS_NewInt32(ctx, 0);
10609             } else {
10610                 /* convert -0 to +0 */
10611                 d = trunc(d) + 0.0;
10612                 ret = JS_NewFloat64(ctx, d);
10613             }
10614         }
10615         break;
10616 #ifdef CONFIG_BIGNUM
10617     case JS_TAG_BIG_FLOAT:
10618         {
10619             bf_t a_s, *a, r_s, *r = &r_s;
10620             BOOL is_nan;
10621 
10622             a = JS_ToBigFloat(ctx, &a_s, val);
10623             if (!bf_is_finite(a)) {
10624                 is_nan = bf_is_nan(a);
10625                 if (is_nan)
10626                     ret = JS_NewInt32(ctx, 0);
10627                 else
10628                     ret = JS_DupValue(ctx, val);
10629             } else {
10630                 ret = JS_NewBigInt(ctx);
10631                 if (!JS_IsException(ret)) {
10632                     r = JS_GetBigInt(ret);
10633                     bf_set(r, a);
10634                     bf_rint(r, BF_RNDZ);
10635                     ret = JS_CompactBigInt(ctx, ret);
10636                 }
10637             }
10638             if (a == &a_s)
10639                 bf_delete(a);
10640             JS_FreeValue(ctx, val);
10641         }
10642         break;
10643 #endif
10644     default:
10645         val = JS_ToNumberFree(ctx, val);
10646         if (JS_IsException(val))
10647             return val;
10648         goto redo;
10649     }
10650     return ret;
10651 }
10652 
10653 /* Note: the integer value is satured to 32 bits */
JS_ToInt32SatFree(JSContext * ctx,int * pres,JSValue val)10654 static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
10655 {
10656     uint32_t tag;
10657     int ret;
10658 
10659  redo:
10660     tag = JS_VALUE_GET_NORM_TAG(val);
10661     switch(tag) {
10662     case JS_TAG_INT:
10663     case JS_TAG_BOOL:
10664     case JS_TAG_NULL:
10665     case JS_TAG_UNDEFINED:
10666         ret = JS_VALUE_GET_INT(val);
10667         break;
10668     case JS_TAG_EXCEPTION:
10669         *pres = 0;
10670         return -1;
10671     case JS_TAG_FLOAT64:
10672         {
10673             double d = JS_VALUE_GET_FLOAT64(val);
10674             if (isnan(d)) {
10675                 ret = 0;
10676             } else {
10677                 if (d < INT32_MIN)
10678                     ret = INT32_MIN;
10679                 else if (d > INT32_MAX)
10680                     ret = INT32_MAX;
10681                 else
10682                     ret = (int)d;
10683             }
10684         }
10685         break;
10686 #ifdef CONFIG_BIGNUM
10687     case JS_TAG_BIG_FLOAT:
10688         {
10689             JSBigFloat *p = JS_VALUE_GET_PTR(val);
10690             bf_get_int32(&ret, &p->num, 0);
10691             JS_FreeValue(ctx, val);
10692         }
10693         break;
10694 #endif
10695     default:
10696         val = JS_ToNumberFree(ctx, val);
10697         if (JS_IsException(val)) {
10698             *pres = 0;
10699             return -1;
10700         }
10701         goto redo;
10702     }
10703     *pres = ret;
10704     return 0;
10705 }
10706 
JS_ToInt32Sat(JSContext * ctx,int * pres,JSValueConst val)10707 int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)
10708 {
10709     return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
10710 }
10711 
JS_ToInt32Clamp(JSContext * ctx,int * pres,JSValueConst val,int min,int max,int min_offset)10712 int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,
10713                     int min, int max, int min_offset)
10714 {
10715     int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
10716     if (res == 0) {
10717         if (*pres < min) {
10718             *pres += min_offset;
10719             if (*pres < min)
10720                 *pres = min;
10721         } else {
10722             if (*pres > max)
10723                 *pres = max;
10724         }
10725     }
10726     return res;
10727 }
10728 
JS_ToInt64SatFree(JSContext * ctx,int64_t * pres,JSValue val)10729 static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)
10730 {
10731     uint32_t tag;
10732 
10733  redo:
10734     tag = JS_VALUE_GET_NORM_TAG(val);
10735     switch(tag) {
10736     case JS_TAG_INT:
10737     case JS_TAG_BOOL:
10738     case JS_TAG_NULL:
10739     case JS_TAG_UNDEFINED:
10740         *pres = JS_VALUE_GET_INT(val);
10741         return 0;
10742     case JS_TAG_EXCEPTION:
10743         *pres = 0;
10744         return -1;
10745     case JS_TAG_FLOAT64:
10746         {
10747             double d = JS_VALUE_GET_FLOAT64(val);
10748             if (isnan(d)) {
10749                 *pres = 0;
10750             } else {
10751                 if (d < INT64_MIN)
10752                     *pres = INT64_MIN;
10753                 else if (d > INT64_MAX)
10754                     *pres = INT64_MAX;
10755                 else
10756                     *pres = (int64_t)d;
10757             }
10758         }
10759         return 0;
10760 #ifdef CONFIG_BIGNUM
10761     case JS_TAG_BIG_FLOAT:
10762         {
10763             JSBigFloat *p = JS_VALUE_GET_PTR(val);
10764             bf_get_int64(pres, &p->num, 0);
10765             JS_FreeValue(ctx, val);
10766         }
10767         return 0;
10768 #endif
10769     default:
10770         val = JS_ToNumberFree(ctx, val);
10771         if (JS_IsException(val)) {
10772             *pres = 0;
10773             return -1;
10774         }
10775         goto redo;
10776     }
10777 }
10778 
JS_ToInt64Sat(JSContext * ctx,int64_t * pres,JSValueConst val)10779 int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)
10780 {
10781     return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
10782 }
10783 
JS_ToInt64Clamp(JSContext * ctx,int64_t * pres,JSValueConst val,int64_t min,int64_t max,int64_t neg_offset)10784 int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,
10785                     int64_t min, int64_t max, int64_t neg_offset)
10786 {
10787     int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
10788     if (res == 0) {
10789         if (*pres < 0)
10790             *pres += neg_offset;
10791         if (*pres < min)
10792             *pres = min;
10793         else if (*pres > max)
10794             *pres = max;
10795     }
10796     return res;
10797 }
10798 
10799 /* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)
10800    in case of exception */
JS_ToInt64Free(JSContext * ctx,int64_t * pres,JSValue val)10801 static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
10802 {
10803     uint32_t tag;
10804     int64_t ret;
10805 
10806  redo:
10807     tag = JS_VALUE_GET_NORM_TAG(val);
10808     switch(tag) {
10809     case JS_TAG_INT:
10810     case JS_TAG_BOOL:
10811     case JS_TAG_NULL:
10812     case JS_TAG_UNDEFINED:
10813         ret = JS_VALUE_GET_INT(val);
10814         break;
10815     case JS_TAG_FLOAT64:
10816         {
10817             JSFloat64Union u;
10818             double d;
10819             int e;
10820             d = JS_VALUE_GET_FLOAT64(val);
10821             u.d = d;
10822             /* we avoid doing fmod(x, 2^64) */
10823             e = (u.u64 >> 52) & 0x7ff;
10824             if (likely(e <= (1023 + 62))) {
10825                 /* fast case */
10826                 ret = (int64_t)d;
10827             } else if (e <= (1023 + 62 + 53)) {
10828                 uint64_t v;
10829                 /* remainder modulo 2^64 */
10830                 v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
10831                 ret = v << ((e - 1023) - 52);
10832                 /* take the sign into account */
10833                 if (u.u64 >> 63)
10834                     ret = -ret;
10835             } else {
10836                 ret = 0; /* also handles NaN and +inf */
10837             }
10838         }
10839         break;
10840 #ifdef CONFIG_BIGNUM
10841     case JS_TAG_BIG_FLOAT:
10842         {
10843             JSBigFloat *p = JS_VALUE_GET_PTR(val);
10844             bf_get_int64(&ret, &p->num, BF_GET_INT_MOD);
10845             JS_FreeValue(ctx, val);
10846         }
10847         break;
10848 #endif
10849     default:
10850         val = JS_ToNumberFree(ctx, val);
10851         if (JS_IsException(val)) {
10852             *pres = 0;
10853             return -1;
10854         }
10855         goto redo;
10856     }
10857     *pres = ret;
10858     return 0;
10859 }
10860 
JS_ToInt64(JSContext * ctx,int64_t * pres,JSValueConst val)10861 int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
10862 {
10863     return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));
10864 }
10865 
JS_ToInt64Ext(JSContext * ctx,int64_t * pres,JSValueConst val)10866 int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)
10867 {
10868     if (JS_IsBigInt(ctx, val))
10869         return JS_ToBigInt64(ctx, pres, val);
10870     else
10871         return JS_ToInt64(ctx, pres, val);
10872 }
10873 
10874 /* return (<0, 0) in case of exception */
JS_ToInt32Free(JSContext * ctx,int32_t * pres,JSValue val)10875 static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
10876 {
10877     uint32_t tag;
10878     int32_t ret;
10879 
10880  redo:
10881     tag = JS_VALUE_GET_NORM_TAG(val);
10882     switch(tag) {
10883     case JS_TAG_INT:
10884     case JS_TAG_BOOL:
10885     case JS_TAG_NULL:
10886     case JS_TAG_UNDEFINED:
10887         ret = JS_VALUE_GET_INT(val);
10888         break;
10889     case JS_TAG_FLOAT64:
10890         {
10891             JSFloat64Union u;
10892             double d;
10893             int e;
10894             d = JS_VALUE_GET_FLOAT64(val);
10895             u.d = d;
10896             /* we avoid doing fmod(x, 2^32) */
10897             e = (u.u64 >> 52) & 0x7ff;
10898             if (likely(e <= (1023 + 30))) {
10899                 /* fast case */
10900                 ret = (int32_t)d;
10901             } else if (e <= (1023 + 30 + 53)) {
10902                 uint64_t v;
10903                 /* remainder modulo 2^32 */
10904                 v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
10905                 v = v << ((e - 1023) - 52 + 32);
10906                 ret = v >> 32;
10907                 /* take the sign into account */
10908                 if (u.u64 >> 63)
10909                     ret = -ret;
10910             } else {
10911                 ret = 0; /* also handles NaN and +inf */
10912             }
10913         }
10914         break;
10915 #ifdef CONFIG_BIGNUM
10916     case JS_TAG_BIG_FLOAT:
10917         {
10918             JSBigFloat *p = JS_VALUE_GET_PTR(val);
10919             bf_get_int32(&ret, &p->num, BF_GET_INT_MOD);
10920             JS_FreeValue(ctx, val);
10921         }
10922         break;
10923 #endif
10924     default:
10925         val = JS_ToNumberFree(ctx, val);
10926         if (JS_IsException(val)) {
10927             *pres = 0;
10928             return -1;
10929         }
10930         goto redo;
10931     }
10932     *pres = ret;
10933     return 0;
10934 }
10935 
JS_ToInt32(JSContext * ctx,int32_t * pres,JSValueConst val)10936 int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)
10937 {
10938     return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));
10939 }
10940 
JS_ToUint32Free(JSContext * ctx,uint32_t * pres,JSValue val)10941 static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)
10942 {
10943     return JS_ToInt32Free(ctx, (int32_t *)pres, val);
10944 }
10945 
JS_ToUint8ClampFree(JSContext * ctx,int32_t * pres,JSValue val)10946 static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
10947 {
10948     uint32_t tag;
10949     int res;
10950 
10951  redo:
10952     tag = JS_VALUE_GET_NORM_TAG(val);
10953     switch(tag) {
10954     case JS_TAG_INT:
10955     case JS_TAG_BOOL:
10956     case JS_TAG_NULL:
10957     case JS_TAG_UNDEFINED:
10958         res = JS_VALUE_GET_INT(val);
10959 #ifdef CONFIG_BIGNUM
10960     int_clamp:
10961 #endif
10962         res = max_int(0, min_int(255, res));
10963         break;
10964     case JS_TAG_FLOAT64:
10965         {
10966             double d = JS_VALUE_GET_FLOAT64(val);
10967             if (isnan(d)) {
10968                 res = 0;
10969             } else {
10970                 if (d < 0)
10971                     res = 0;
10972                 else if (d > 255)
10973                     res = 255;
10974                 else
10975                     res = lrint(d);
10976             }
10977         }
10978         break;
10979 #ifdef CONFIG_BIGNUM
10980     case JS_TAG_BIG_FLOAT:
10981         {
10982             JSBigFloat *p = JS_VALUE_GET_PTR(val);
10983             bf_t r_s, *r = &r_s;
10984             bf_init(ctx->bf_ctx, r);
10985             bf_set(r, &p->num);
10986             bf_rint(r, BF_RNDN);
10987             bf_get_int32(&res, r, 0);
10988             bf_delete(r);
10989             JS_FreeValue(ctx, val);
10990         }
10991         goto int_clamp;
10992 #endif
10993     default:
10994         val = JS_ToNumberFree(ctx, val);
10995         if (JS_IsException(val)) {
10996             *pres = 0;
10997             return -1;
10998         }
10999         goto redo;
11000     }
11001     *pres = res;
11002     return 0;
11003 }
11004 
JS_ToArrayLengthFree(JSContext * ctx,uint32_t * plen,JSValue val,BOOL is_array_ctor)11005 static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
11006                                             JSValue val, BOOL is_array_ctor)
11007 {
11008     uint32_t tag, len;
11009 
11010     tag = JS_VALUE_GET_TAG(val);
11011     switch(tag) {
11012     case JS_TAG_INT:
11013     case JS_TAG_BOOL:
11014     case JS_TAG_NULL:
11015         {
11016             int v;
11017             v = JS_VALUE_GET_INT(val);
11018             if (v < 0)
11019                 goto fail;
11020             len = v;
11021         }
11022         break;
11023 #ifdef CONFIG_BIGNUM
11024     case JS_TAG_BIG_INT:
11025     case JS_TAG_BIG_FLOAT:
11026         {
11027             JSBigFloat *p = JS_VALUE_GET_PTR(val);
11028             bf_t a;
11029             BOOL res;
11030             bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD);
11031             bf_init(ctx->bf_ctx, &a);
11032             bf_set_ui(&a, len);
11033             res = bf_cmp_eq(&a, &p->num);
11034             bf_delete(&a);
11035             JS_FreeValue(ctx, val);
11036             if (!res)
11037                 goto fail;
11038         }
11039         break;
11040 #endif
11041     default:
11042         if (JS_TAG_IS_FLOAT64(tag)) {
11043             double d;
11044             d = JS_VALUE_GET_FLOAT64(val);
11045             len = (uint32_t)d;
11046             if (len != d)
11047                 goto fail;
11048         } else {
11049             uint32_t len1;
11050 
11051             if (is_array_ctor) {
11052                 val = JS_ToNumberFree(ctx, val);
11053                 if (JS_IsException(val))
11054                     return -1;
11055                 /* cannot recurse because val is a number */
11056                 if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))
11057                     return -1;
11058             } else {
11059                 /* legacy behavior: must do the conversion twice and compare */
11060                 if (JS_ToUint32(ctx, &len, val)) {
11061                     JS_FreeValue(ctx, val);
11062                     return -1;
11063                 }
11064                 val = JS_ToNumberFree(ctx, val);
11065                 if (JS_IsException(val))
11066                     return -1;
11067                 /* cannot recurse because val is a number */
11068                 if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))
11069                     return -1;
11070                 if (len1 != len) {
11071                 fail:
11072                     JS_ThrowRangeError(ctx, "invalid array length");
11073                     return -1;
11074                 }
11075             }
11076         }
11077         break;
11078     }
11079     *plen = len;
11080     return 0;
11081 }
11082 
11083 #define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
11084 
is_safe_integer(double d)11085 static BOOL is_safe_integer(double d)
11086 {
11087     return isfinite(d) && floor(d) == d &&
11088         fabs(d) <= (double)MAX_SAFE_INTEGER;
11089 }
11090 
JS_ToIndex(JSContext * ctx,uint64_t * plen,JSValueConst val)11091 int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
11092 {
11093     int64_t v;
11094     if (JS_ToInt64Sat(ctx, &v, val))
11095         return -1;
11096     if (v < 0 || v > MAX_SAFE_INTEGER) {
11097         JS_ThrowRangeError(ctx, "invalid array index");
11098         *plen = 0;
11099         return -1;
11100     }
11101     *plen = v;
11102     return 0;
11103 }
11104 
11105 /* convert a value to a length between 0 and MAX_SAFE_INTEGER.
11106    return -1 for exception */
JS_ToLengthFree(JSContext * ctx,int64_t * plen,JSValue val)11107 static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
11108                                        JSValue val)
11109 {
11110     int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);
11111     JS_FreeValue(ctx, val);
11112     return res;
11113 }
11114 
11115 /* Note: can return an exception */
JS_NumberIsInteger(JSContext * ctx,JSValueConst val)11116 static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)
11117 {
11118     double d;
11119     if (!JS_IsNumber(val))
11120         return FALSE;
11121     if (unlikely(JS_ToFloat64(ctx, &d, val)))
11122         return -1;
11123     return isfinite(d) && floor(d) == d;
11124 }
11125 
JS_NumberIsNegativeOrMinusZero(JSContext * ctx,JSValueConst val)11126 static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)
11127 {
11128     uint32_t tag;
11129 
11130     tag = JS_VALUE_GET_NORM_TAG(val);
11131     switch(tag) {
11132     case JS_TAG_INT:
11133         {
11134             int v;
11135             v = JS_VALUE_GET_INT(val);
11136             return (v < 0);
11137         }
11138     case JS_TAG_FLOAT64:
11139         {
11140             JSFloat64Union u;
11141             u.d = JS_VALUE_GET_FLOAT64(val);
11142             return (u.u64 >> 63);
11143         }
11144 #ifdef CONFIG_BIGNUM
11145     case JS_TAG_BIG_INT:
11146         {
11147             JSBigFloat *p = JS_VALUE_GET_PTR(val);
11148             /* Note: integer zeros are not necessarily positive */
11149             return p->num.sign && !bf_is_zero(&p->num);
11150         }
11151     case JS_TAG_BIG_FLOAT:
11152         {
11153             JSBigFloat *p = JS_VALUE_GET_PTR(val);
11154             return p->num.sign;
11155         }
11156         break;
11157     case JS_TAG_BIG_DECIMAL:
11158         {
11159             JSBigDecimal *p = JS_VALUE_GET_PTR(val);
11160             return p->num.sign;
11161         }
11162         break;
11163 #endif
11164     default:
11165         return FALSE;
11166     }
11167 }
11168 
11169 #ifdef CONFIG_BIGNUM
11170 
js_bigint_to_string1(JSContext * ctx,JSValueConst val,int radix)11171 static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)
11172 {
11173     JSValue ret;
11174     bf_t a_s, *a;
11175     char *str;
11176     int saved_sign;
11177 
11178     a = JS_ToBigInt(ctx, &a_s, val);
11179     if (!a)
11180         return JS_EXCEPTION;
11181     saved_sign = a->sign;
11182     if (a->expn == BF_EXP_ZERO)
11183         a->sign = 0;
11184     str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC |
11185                   BF_FTOA_JS_QUIRKS);
11186     a->sign = saved_sign;
11187     JS_FreeBigInt(ctx, a, &a_s);
11188     if (!str)
11189         return JS_ThrowOutOfMemory(ctx);
11190     ret = JS_NewString(ctx, str);
11191     bf_free(ctx->bf_ctx, str);
11192     return ret;
11193 }
11194 
js_bigint_to_string(JSContext * ctx,JSValueConst val)11195 static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)
11196 {
11197     return js_bigint_to_string1(ctx, val, 10);
11198 }
11199 
js_ftoa(JSContext * ctx,JSValueConst val1,int radix,limb_t prec,bf_flags_t flags)11200 static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix,
11201                        limb_t prec, bf_flags_t flags)
11202 {
11203     JSValue val, ret;
11204     bf_t a_s, *a;
11205     char *str;
11206     int saved_sign;
11207 
11208     val = JS_ToNumeric(ctx, val1);
11209     if (JS_IsException(val))
11210         return val;
11211     a = JS_ToBigFloat(ctx, &a_s, val);
11212     saved_sign = a->sign;
11213     if (a->expn == BF_EXP_ZERO)
11214         a->sign = 0;
11215     flags |= BF_FTOA_JS_QUIRKS;
11216     if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) {
11217         /* Note: for floating point numbers with a radix which is not
11218            a power of two, the current precision is used to compute
11219            the number of digits. */
11220         if ((radix & (radix - 1)) != 0) {
11221             bf_t r_s, *r = &r_s;
11222             int prec, flags1;
11223             /* must round first */
11224             if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
11225                 prec = ctx->fp_env.prec;
11226                 flags1 = ctx->fp_env.flags &
11227                     (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT));
11228             } else {
11229                 prec = 53;
11230                 flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL;
11231             }
11232             bf_init(ctx->bf_ctx, r);
11233             bf_set(r, a);
11234             bf_round(r, prec, flags1 | BF_RNDN);
11235             str = bf_ftoa(NULL, r, radix, prec, flags1 | flags);
11236             bf_delete(r);
11237         } else {
11238             str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags);
11239         }
11240     } else {
11241         str = bf_ftoa(NULL, a, radix, prec, flags);
11242     }
11243     a->sign = saved_sign;
11244     if (a == &a_s)
11245         bf_delete(a);
11246     JS_FreeValue(ctx, val);
11247     if (!str)
11248         return JS_ThrowOutOfMemory(ctx);
11249     ret = JS_NewString(ctx, str);
11250     bf_free(ctx->bf_ctx, str);
11251     return ret;
11252 }
11253 
js_bigfloat_to_string(JSContext * ctx,JSValueConst val)11254 static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val)
11255 {
11256     return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
11257 }
11258 
js_bigdecimal_to_string1(JSContext * ctx,JSValueConst val,limb_t prec,int flags)11259 static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val,
11260                                         limb_t prec, int flags)
11261 {
11262     JSValue ret;
11263     bfdec_t *a;
11264     char *str;
11265     int saved_sign;
11266 
11267     a = JS_ToBigDecimal(ctx, val);
11268     saved_sign = a->sign;
11269     if (a->expn == BF_EXP_ZERO)
11270         a->sign = 0;
11271     str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS);
11272     a->sign = saved_sign;
11273     if (!str)
11274         return JS_ThrowOutOfMemory(ctx);
11275     ret = JS_NewString(ctx, str);
11276     bf_free(ctx->bf_ctx, str);
11277     return ret;
11278 }
11279 
js_bigdecimal_to_string(JSContext * ctx,JSValueConst val)11280 static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)
11281 {
11282     return js_bigdecimal_to_string1(ctx, val, 0,
11283                                     BF_RNDZ | BF_FTOA_FORMAT_FREE);
11284 }
11285 
11286 #endif /* CONFIG_BIGNUM */
11287 
11288 /* 2 <= base <= 36 */
i64toa(char * buf_end,int64_t n,unsigned int base)11289 static char *i64toa(char *buf_end, int64_t n, unsigned int base)
11290 {
11291     char *q = buf_end;
11292     int digit, is_neg;
11293 
11294     is_neg = 0;
11295     if (n < 0) {
11296         is_neg = 1;
11297         n = -n;
11298     }
11299     *--q = '\0';
11300     do {
11301         digit = (uint64_t)n % base;
11302         n = (uint64_t)n / base;
11303         if (digit < 10)
11304             digit += '0';
11305         else
11306             digit += 'a' - 10;
11307         *--q = digit;
11308     } while (n != 0);
11309     if (is_neg)
11310         *--q = '-';
11311     return q;
11312 }
11313 
11314 /* 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)11315 static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,
11316                      int rounding_mode, char *buf1, int buf1_size)
11317 {
11318     if (rounding_mode != FE_TONEAREST)
11319         fesetround(rounding_mode);
11320     snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);
11321     if (rounding_mode != FE_TONEAREST)
11322         fesetround(FE_TONEAREST);
11323     *sign = (buf1[0] == '-');
11324     /* mantissa */
11325     buf[0] = buf1[1];
11326     if (n_digits > 1)
11327         memcpy(buf + 1, buf1 + 3, n_digits - 1);
11328     buf[n_digits] = '\0';
11329     /* exponent */
11330     *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;
11331 }
11332 
11333 /* maximum buffer size for js_dtoa */
11334 #define JS_DTOA_BUF_SIZE 128
11335 
11336 /* needed because ecvt usually limits the number of digits to
11337    17. Return the number of digits. */
js_ecvt(double d,int n_digits,int * decpt,int * sign,char * buf,BOOL is_fixed)11338 static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,
11339                    BOOL is_fixed)
11340 {
11341     int rounding_mode;
11342     char buf_tmp[JS_DTOA_BUF_SIZE];
11343 
11344     if (!is_fixed) {
11345         unsigned int n_digits_min, n_digits_max;
11346         /* find the minimum amount of digits (XXX: inefficient but simple) */
11347         n_digits_min = 1;
11348         n_digits_max = 17;
11349         while (n_digits_min < n_digits_max) {
11350             n_digits = (n_digits_min + n_digits_max) / 2;
11351             js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,
11352                      buf_tmp, sizeof(buf_tmp));
11353             if (strtod(buf_tmp, NULL) == d) {
11354                 /* no need to keep the trailing zeros */
11355                 while (n_digits >= 2 && buf[n_digits - 1] == '0')
11356                     n_digits--;
11357                 n_digits_max = n_digits;
11358             } else {
11359                 n_digits_min = n_digits + 1;
11360             }
11361         }
11362         n_digits = n_digits_max;
11363         rounding_mode = FE_TONEAREST;
11364     } else {
11365         rounding_mode = FE_TONEAREST;
11366 #ifdef CONFIG_PRINTF_RNDN
11367         {
11368             char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];
11369             int decpt1, sign1, decpt2, sign2;
11370             /* The JS rounding is specified as round to nearest ties away
11371                from zero (RNDNA), but in printf the "ties" case is not
11372                specified (for example it is RNDN for glibc, RNDNA for
11373                Windows), so we must round manually. */
11374             js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,
11375                      buf_tmp, sizeof(buf_tmp));
11376             /* XXX: could use 2 digits to reduce the average running time */
11377             if (buf1[n_digits] == '5') {
11378                 js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,
11379                          buf_tmp, sizeof(buf_tmp));
11380                 js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,
11381                          buf_tmp, sizeof(buf_tmp));
11382                 if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {
11383                     /* exact result: round away from zero */
11384                     if (sign1)
11385                         rounding_mode = FE_DOWNWARD;
11386                     else
11387                         rounding_mode = FE_UPWARD;
11388                 }
11389             }
11390         }
11391 #endif /* CONFIG_PRINTF_RNDN */
11392     }
11393     js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,
11394              buf_tmp, sizeof(buf_tmp));
11395     return n_digits;
11396 }
11397 
js_fcvt1(char * buf,int buf_size,double d,int n_digits,int rounding_mode)11398 static int js_fcvt1(char *buf, int buf_size, double d, int n_digits,
11399                     int rounding_mode)
11400 {
11401     int n;
11402     if (rounding_mode != FE_TONEAREST)
11403         fesetround(rounding_mode);
11404     n = snprintf(buf, buf_size, "%.*f", n_digits, d);
11405     if (rounding_mode != FE_TONEAREST)
11406         fesetround(FE_TONEAREST);
11407     assert(n < buf_size);
11408     return n;
11409 }
11410 
js_fcvt(char * buf,int buf_size,double d,int n_digits)11411 static void js_fcvt(char *buf, int buf_size, double d, int n_digits)
11412 {
11413     int rounding_mode;
11414     rounding_mode = FE_TONEAREST;
11415 #ifdef CONFIG_PRINTF_RNDN
11416     {
11417         int n1, n2;
11418         char buf1[JS_DTOA_BUF_SIZE];
11419         char buf2[JS_DTOA_BUF_SIZE];
11420 
11421         /* The JS rounding is specified as round to nearest ties away from
11422            zero (RNDNA), but in printf the "ties" case is not specified
11423            (for example it is RNDN for glibc, RNDNA for Windows), so we
11424            must round manually. */
11425         n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST);
11426         rounding_mode = FE_TONEAREST;
11427         /* XXX: could use 2 digits to reduce the average running time */
11428         if (buf1[n1 - 1] == '5') {
11429             n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD);
11430             n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD);
11431             if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {
11432                 /* exact result: round away from zero */
11433                 if (buf1[0] == '-')
11434                     rounding_mode = FE_DOWNWARD;
11435                 else
11436                     rounding_mode = FE_UPWARD;
11437             }
11438         }
11439     }
11440 #endif /* CONFIG_PRINTF_RNDN */
11441     js_fcvt1(buf, buf_size, d, n_digits, rounding_mode);
11442 }
11443 
11444 /* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */
11445 /* use as many digits as necessary */
11446 #define JS_DTOA_VAR_FORMAT   (0 << 0)
11447 /* use n_digits significant digits (1 <= n_digits <= 101) */
11448 #define JS_DTOA_FIXED_FORMAT (1 << 0)
11449 /* force fractional format: [-]dd.dd with n_digits fractional digits */
11450 #define JS_DTOA_FRAC_FORMAT  (2 << 0)
11451 /* force exponential notation either in fixed or variable format */
11452 #define JS_DTOA_FORCE_EXP    (1 << 2)
11453 
11454 /* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.
11455    XXX: radix != 10 is only supported for small integers
11456 */
js_dtoa1(char * buf,double d,int radix,int n_digits,int flags)11457 static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags)
11458 {
11459     char *q;
11460 
11461     if (!isfinite(d)) {
11462         if (isnan(d)) {
11463             strcpy(buf, "NaN");
11464         } else {
11465             q = buf;
11466             if (d < 0)
11467                 *q++ = '-';
11468             strcpy(q, "Infinity");
11469         }
11470     } else if (flags == JS_DTOA_VAR_FORMAT) {
11471         int64_t i64;
11472         char buf1[70], *ptr;
11473         i64 = (int64_t)d;
11474         if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER)
11475             goto generic_conv;
11476         /* fast path for integers */
11477         ptr = i64toa(buf1 + sizeof(buf1), i64, radix);
11478         strcpy(buf, ptr);
11479     } else {
11480         if (d == 0.0)
11481             d = 0.0; /* convert -0 to 0 */
11482         if (flags == JS_DTOA_FRAC_FORMAT) {
11483             js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits);
11484         } else {
11485             char buf1[JS_DTOA_BUF_SIZE];
11486             int sign, decpt, k, n, i, p, n_max;
11487             BOOL is_fixed;
11488         generic_conv:
11489             is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);
11490             if (is_fixed) {
11491                 n_max = n_digits;
11492             } else {
11493                 n_max = 21;
11494             }
11495             /* the number has k digits (k >= 1) */
11496             k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);
11497             n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */
11498             q = buf;
11499             if (sign)
11500                 *q++ = '-';
11501             if (flags & JS_DTOA_FORCE_EXP)
11502                 goto force_exp;
11503             if (n >= 1 && n <= n_max) {
11504                 if (k <= n) {
11505                     memcpy(q, buf1, k);
11506                     q += k;
11507                     for(i = 0; i < (n - k); i++)
11508                         *q++ = '0';
11509                     *q = '\0';
11510                 } else {
11511                     /* k > n */
11512                     memcpy(q, buf1, n);
11513                     q += n;
11514                     *q++ = '.';
11515                     for(i = 0; i < (k - n); i++)
11516                         *q++ = buf1[n + i];
11517                     *q = '\0';
11518                 }
11519             } else if (n >= -5 && n <= 0) {
11520                 *q++ = '0';
11521                 *q++ = '.';
11522                 for(i = 0; i < -n; i++)
11523                     *q++ = '0';
11524                 memcpy(q, buf1, k);
11525                 q += k;
11526                 *q = '\0';
11527             } else {
11528             force_exp:
11529                 /* exponential notation */
11530                 *q++ = buf1[0];
11531                 if (k > 1) {
11532                     *q++ = '.';
11533                     for(i = 1; i < k; i++)
11534                         *q++ = buf1[i];
11535                 }
11536                 *q++ = 'e';
11537                 p = n - 1;
11538                 if (p >= 0)
11539                     *q++ = '+';
11540                 sprintf(q, "%d", p);
11541             }
11542         }
11543     }
11544 }
11545 
js_dtoa(JSContext * ctx,double d,int radix,int n_digits,int flags)11546 static JSValue js_dtoa(JSContext *ctx,
11547                        double d, int radix, int n_digits, int flags)
11548 {
11549     char buf[JS_DTOA_BUF_SIZE];
11550     js_dtoa1(buf, d, radix, n_digits, flags);
11551     return JS_NewString(ctx, buf);
11552 }
11553 
JS_ToStringInternal(JSContext * ctx,JSValueConst val,BOOL is_ToPropertyKey)11554 JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)
11555 {
11556     uint32_t tag;
11557     const char *str;
11558     char buf[32];
11559 
11560     tag = JS_VALUE_GET_NORM_TAG(val);
11561     switch(tag) {
11562     case JS_TAG_STRING:
11563         return JS_DupValue(ctx, val);
11564     case JS_TAG_INT:
11565         snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));
11566         str = buf;
11567         goto new_string;
11568     case JS_TAG_BOOL:
11569         return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
11570                           JS_ATOM_true : JS_ATOM_false);
11571     case JS_TAG_NULL:
11572         return JS_AtomToString(ctx, JS_ATOM_null);
11573     case JS_TAG_UNDEFINED:
11574         return JS_AtomToString(ctx, JS_ATOM_undefined);
11575     case JS_TAG_EXCEPTION:
11576         return JS_EXCEPTION;
11577     case JS_TAG_OBJECT:
11578         {
11579             JSValue val1, ret;
11580             val1 = JS_ToPrimitive(ctx, val, HINT_STRING);
11581             if (JS_IsException(val1))
11582                 return val1;
11583             ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);
11584             JS_FreeValue(ctx, val1);
11585             return ret;
11586         }
11587         break;
11588     case JS_TAG_FUNCTION_BYTECODE:
11589         str = "[function bytecode]";
11590         goto new_string;
11591     case JS_TAG_SYMBOL:
11592         if (is_ToPropertyKey) {
11593             return JS_DupValue(ctx, val);
11594         } else {
11595 #ifdef ENABLE_JS_DEBUG
11596             str = "[object Object]";
11597             goto new_string;
11598 #else
11599             return JS_ThrowTypeError(ctx, "cannot convert symbol to string");
11600 #endif
11601         }
11602     case JS_TAG_FLOAT64:
11603         return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
11604                        JS_DTOA_VAR_FORMAT);
11605 #ifdef CONFIG_BIGNUM
11606     case JS_TAG_BIG_INT:
11607         return ctx->rt->bigint_ops.to_string(ctx, val);
11608     case JS_TAG_BIG_FLOAT:
11609         return ctx->rt->bigfloat_ops.to_string(ctx, val);
11610     case JS_TAG_BIG_DECIMAL:
11611         return ctx->rt->bigdecimal_ops.to_string(ctx, val);
11612 #endif
11613     default:
11614         str = "[unsupported type]";
11615     new_string:
11616         return JS_NewString(ctx, str);
11617     }
11618 }
11619 
JS_ToString(JSContext * ctx,JSValueConst val)11620 JSValue JS_ToString(JSContext *ctx, JSValueConst val)
11621 {
11622     return JS_ToStringInternal(ctx, val, FALSE);
11623 }
11624 
JS_ToStringFree(JSContext * ctx,JSValue val)11625 static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)
11626 {
11627     JSValue ret;
11628     ret = JS_ToString(ctx, val);
11629     JS_FreeValue(ctx, val);
11630     return ret;
11631 }
11632 
JS_ToLocaleStringFree(JSContext * ctx,JSValue val)11633 static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)
11634 {
11635     if (JS_IsUndefined(val) || JS_IsNull(val))
11636         return JS_ToStringFree(ctx, val);
11637     return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);
11638 }
11639 
JS_ToPropertyKey(JSContext * ctx,JSValueConst val)11640 JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
11641 {
11642     return JS_ToStringInternal(ctx, val, TRUE);
11643 }
11644 
JS_ToStringCheckObject(JSContext * ctx,JSValueConst val)11645 static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)
11646 {
11647     uint32_t tag = JS_VALUE_GET_TAG(val);
11648     if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
11649         return JS_ThrowTypeError(ctx, "null or undefined are forbidden");
11650     return JS_ToString(ctx, val);
11651 }
11652 
JS_ToQuotedString(JSContext * ctx,JSValueConst val1)11653 static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
11654 {
11655     JSValue val;
11656     JSString *p;
11657     int i;
11658     uint32_t c;
11659     StringBuffer b_s, *b = &b_s;
11660     char buf[16];
11661 
11662     val = JS_ToStringCheckObject(ctx, val1);
11663     if (JS_IsException(val))
11664         return val;
11665     p = JS_VALUE_GET_STRING(val);
11666 
11667     if (string_buffer_init(ctx, b, p->len + 2))
11668         goto fail;
11669 
11670     if (string_buffer_putc8(b, '\"'))
11671         goto fail;
11672     for(i = 0; i < p->len; ) {
11673         c = string_getc(p, &i);
11674         switch(c) {
11675         case '\t':
11676             c = 't';
11677             goto quote;
11678         case '\r':
11679             c = 'r';
11680             goto quote;
11681         case '\n':
11682             c = 'n';
11683             goto quote;
11684         case '\b':
11685             c = 'b';
11686             goto quote;
11687         case '\f':
11688             c = 'f';
11689             goto quote;
11690         case '\"':
11691         case '\\':
11692         quote:
11693             if (string_buffer_putc8(b, '\\'))
11694                 goto fail;
11695             if (string_buffer_putc8(b, c))
11696                 goto fail;
11697             break;
11698         default:
11699             if (c < 32 || (c >= 0xd800 && c < 0xe000)) {
11700                 snprintf(buf, sizeof(buf), "\\u%04x", c);
11701                 if (string_buffer_puts8(b, buf))
11702                     goto fail;
11703             } else {
11704                 if (string_buffer_putc(b, c))
11705                     goto fail;
11706             }
11707             break;
11708         }
11709     }
11710     if (string_buffer_putc8(b, '\"'))
11711         goto fail;
11712     JS_FreeValue(ctx, val);
11713     return string_buffer_end(b);
11714  fail:
11715     JS_FreeValue(ctx, val);
11716     string_buffer_free(b);
11717     return JS_EXCEPTION;
11718 }
11719 
JS_DumpObjectHeader(JSRuntime * rt)11720 static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
11721 {
11722     printf("%14s %4s %4s %14s %10s %s\n",
11723            "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
11724 }
11725 
11726 /* for debug only: dump an object without side effect */
JS_DumpObject(JSRuntime * rt,JSObject * p)11727 static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
11728 {
11729     uint32_t i;
11730     char atom_buf[ATOM_GET_STR_BUF_SIZE];
11731     JSShape *sh;
11732     JSShapeProperty *prs;
11733     JSProperty *pr;
11734     BOOL is_first = TRUE;
11735 
11736     /* XXX: should encode atoms with special characters */
11737     sh = p->shape; /* the shape can be NULL while freeing an object */
11738     printf("%14p %4d ",
11739            (void *)p,
11740            p->header.ref_count);
11741     if (sh) {
11742         printf("%3d%c %14p ",
11743                sh->header.ref_count,
11744                " *"[sh->is_hashed],
11745                (void *)sh->proto);
11746     } else {
11747         printf("%3s  %14s ", "-", "-");
11748     }
11749     printf("%10s ",
11750            JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));
11751     if (p->is_exotic && p->fast_array) {
11752         printf("[ ");
11753         for(i = 0; i < p->u.array.count; i++) {
11754             if (i != 0)
11755                 printf(", ");
11756             switch (p->class_id) {
11757             case JS_CLASS_ARRAY:
11758             case JS_CLASS_ARGUMENTS:
11759                 JS_DumpValueShort(rt, p->u.array.u.values[i]);
11760                 break;
11761             case JS_CLASS_UINT8C_ARRAY:
11762             case JS_CLASS_INT8_ARRAY:
11763             case JS_CLASS_UINT8_ARRAY:
11764             case JS_CLASS_INT16_ARRAY:
11765             case JS_CLASS_UINT16_ARRAY:
11766             case JS_CLASS_INT32_ARRAY:
11767             case JS_CLASS_UINT32_ARRAY:
11768 #ifdef CONFIG_BIGNUM
11769             case JS_CLASS_BIG_INT64_ARRAY:
11770             case JS_CLASS_BIG_UINT64_ARRAY:
11771 #endif
11772             case JS_CLASS_FLOAT32_ARRAY:
11773             case JS_CLASS_FLOAT64_ARRAY:
11774                 {
11775                     int size = 1 << typed_array_size_log2(p->class_id);
11776                     const uint8_t *b = p->u.array.u.uint8_ptr + i * size;
11777                     while (size-- > 0)
11778                         printf("%02X", *b++);
11779                 }
11780                 break;
11781             }
11782         }
11783         printf(" ] ");
11784     }
11785 
11786     if (sh) {
11787         printf("{ ");
11788         for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
11789             if (prs->atom != JS_ATOM_NULL) {
11790                 pr = &p->prop[i];
11791                 if (!is_first)
11792                     printf(", ");
11793                 printf("%s: ",
11794                        JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));
11795                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
11796                     printf("[getset %p %p]", (void *)pr->u.getset.getter,
11797                            (void *)pr->u.getset.setter);
11798                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
11799                     printf("[varref %p]", (void *)pr->u.var_ref);
11800                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
11801                     printf("[autoinit %p %d %p]",
11802                            (void *)js_autoinit_get_realm(pr),
11803                            js_autoinit_get_id(pr),
11804                            (void *)pr->u.init.opaque);
11805                 } else {
11806                     JS_DumpValueShort(rt, pr->u.value);
11807                 }
11808                 is_first = FALSE;
11809             }
11810         }
11811         printf(" }");
11812     }
11813 
11814     if (js_class_has_bytecode(p->class_id)) {
11815         JSFunctionBytecode *b = p->u.func.function_bytecode;
11816         JSVarRef **var_refs;
11817         if (b->closure_var_count) {
11818             var_refs = p->u.func.var_refs;
11819             printf(" Closure:");
11820             for(i = 0; i < b->closure_var_count; i++) {
11821                 printf(" ");
11822                 JS_DumpValueShort(rt, var_refs[i]->value);
11823             }
11824             if (p->u.func.home_object) {
11825                 printf(" HomeObject: ");
11826                 JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
11827             }
11828         }
11829     }
11830     printf("\n");
11831 }
11832 
JS_DumpGCObject(JSRuntime * rt,JSGCObjectHeader * p)11833 static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
11834 {
11835     if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
11836         JS_DumpObject(rt, (JSObject *)p);
11837     } else {
11838         printf("%14p %4d ",
11839                (void *)p,
11840                p->ref_count);
11841         switch(p->gc_obj_type) {
11842         case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
11843             printf("[function bytecode]");
11844             break;
11845         case JS_GC_OBJ_TYPE_SHAPE:
11846             printf("[shape]");
11847             break;
11848         case JS_GC_OBJ_TYPE_VAR_REF:
11849             printf("[var_ref]");
11850             break;
11851         case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
11852             printf("[async_function]");
11853             break;
11854         case JS_GC_OBJ_TYPE_JS_CONTEXT:
11855             printf("[js_context]");
11856             break;
11857         default:
11858             printf("[unknown %d]", p->gc_obj_type);
11859             break;
11860         }
11861         printf("\n");
11862     }
11863 }
11864 
JS_DumpValueShort(JSRuntime * rt,JSValueConst val)11865 static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
11866                                                       JSValueConst val)
11867 {
11868     uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
11869     const char *str;
11870 
11871     switch(tag) {
11872     case JS_TAG_INT:
11873         printf("%d", JS_VALUE_GET_INT(val));
11874         break;
11875     case JS_TAG_BOOL:
11876         if (JS_VALUE_GET_BOOL(val))
11877             str = "true";
11878         else
11879             str = "false";
11880         goto print_str;
11881     case JS_TAG_NULL:
11882         str = "null";
11883         goto print_str;
11884     case JS_TAG_EXCEPTION:
11885         str = "exception";
11886         goto print_str;
11887     case JS_TAG_UNINITIALIZED:
11888         str = "uninitialized";
11889         goto print_str;
11890     case JS_TAG_UNDEFINED:
11891         str = "undefined";
11892     print_str:
11893         printf("%s", str);
11894         break;
11895     case JS_TAG_FLOAT64:
11896         printf("%.14g", JS_VALUE_GET_FLOAT64(val));
11897         break;
11898 #ifdef CONFIG_BIGNUM
11899     case JS_TAG_BIG_INT:
11900         {
11901             JSBigFloat *p = JS_VALUE_GET_PTR(val);
11902             char *str;
11903             str = bf_ftoa(NULL, &p->num, 10, 0,
11904                           BF_RNDZ | BF_FTOA_FORMAT_FRAC);
11905             printf("%sn", str);
11906             bf_realloc(&rt->bf_ctx, str, 0);
11907         }
11908         break;
11909     case JS_TAG_BIG_FLOAT:
11910         {
11911             JSBigFloat *p = JS_VALUE_GET_PTR(val);
11912             char *str;
11913             str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF,
11914                           BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX);
11915             printf("%sl", str);
11916             bf_free(&rt->bf_ctx, str);
11917         }
11918         break;
11919     case JS_TAG_BIG_DECIMAL:
11920         {
11921             JSBigDecimal *p = JS_VALUE_GET_PTR(val);
11922             char *str;
11923             str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF,
11924                              BF_RNDZ | BF_FTOA_FORMAT_FREE);
11925             printf("%sm", str);
11926             bf_free(&rt->bf_ctx, str);
11927         }
11928         break;
11929 #endif
11930     case JS_TAG_STRING:
11931         {
11932             JSString *p;
11933             p = JS_VALUE_GET_STRING(val);
11934             JS_DumpString(rt, p);
11935         }
11936         break;
11937     case JS_TAG_FUNCTION_BYTECODE:
11938         {
11939             JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);
11940             char buf[ATOM_GET_STR_BUF_SIZE];
11941             printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
11942         }
11943         break;
11944     case JS_TAG_OBJECT:
11945         {
11946             JSObject *p = JS_VALUE_GET_OBJ(val);
11947             JSAtom atom = rt->class_array[p->class_id].class_name;
11948             char atom_buf[ATOM_GET_STR_BUF_SIZE];
11949             printf("[%s %p]",
11950                    JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);
11951         }
11952         break;
11953     case JS_TAG_SYMBOL:
11954         {
11955             JSAtomStruct *p = JS_VALUE_GET_PTR(val);
11956             char atom_buf[ATOM_GET_STR_BUF_SIZE];
11957             printf("Symbol(%s)",
11958                    JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));
11959         }
11960         break;
11961     case JS_TAG_MODULE:
11962         printf("[module]");
11963         break;
11964     default:
11965         printf("[unknown tag %d]", tag);
11966         break;
11967     }
11968 }
11969 
JS_DumpValue(JSContext * ctx,JSValueConst val)11970 static __maybe_unused void JS_DumpValue(JSContext *ctx,
11971                                                  JSValueConst val)
11972 {
11973     JS_DumpValueShort(ctx->rt, val);
11974 }
11975 
JS_PrintValue(JSContext * ctx,const char * str,JSValueConst val)11976 static __maybe_unused void JS_PrintValue(JSContext *ctx,
11977                                                   const char *str,
11978                                                   JSValueConst val)
11979 {
11980     printf("%s=", str);
11981     JS_DumpValueShort(ctx->rt, val);
11982     printf("\n");
11983 }
11984 
11985 /* return -1 if exception (proxy case) or TRUE/FALSE */
JS_IsArray(JSContext * ctx,JSValueConst val)11986 int JS_IsArray(JSContext *ctx, JSValueConst val)
11987 {
11988     JSObject *p;
11989     if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
11990         p = JS_VALUE_GET_OBJ(val);
11991         if (p->is_exotic) {
11992             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
11993             if (em && em->is_array)
11994                 return em->is_array(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
11995         }
11996         if (unlikely(p->class_id == JS_CLASS_PROXY))
11997             return js_proxy_isArray(ctx, val);
11998         else
11999             return p->class_id == JS_CLASS_ARRAY;
12000     } else {
12001         return FALSE;
12002     }
12003 }
12004 
js_pow(double a,double b)12005 static double js_pow(double a, double b)
12006 {
12007     if (unlikely(!isfinite(b)) && fabs(a) == 1) {
12008         /* not compatible with IEEE 754 */
12009         return JS_FLOAT64_NAN;
12010     } else {
12011         return pow(a, b);
12012     }
12013 }
12014 
12015 #ifdef CONFIG_BIGNUM
12016 
JS_NewBigInt64_1(JSContext * ctx,int64_t v)12017 JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v)
12018 {
12019     JSValue val;
12020     bf_t *a;
12021     val = JS_NewBigInt(ctx);
12022     if (JS_IsException(val))
12023         return val;
12024     a = JS_GetBigInt(val);
12025     if (bf_set_si(a, v)) {
12026         JS_FreeValue(ctx, val);
12027         return JS_ThrowOutOfMemory(ctx);
12028     }
12029     return val;
12030 }
12031 
JS_NewBigInt64(JSContext * ctx,int64_t v)12032 JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
12033 {
12034     if (is_math_mode(ctx) &&
12035         v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
12036         return JS_NewInt64(ctx, v);
12037     } else {
12038         return JS_NewBigInt64_1(ctx, v);
12039     }
12040 }
12041 
JS_NewBigUint64(JSContext * ctx,uint64_t v)12042 JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
12043 {
12044     JSValue val;
12045     if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) {
12046         val = JS_NewInt64(ctx, v);
12047     } else {
12048         bf_t *a;
12049         val = JS_NewBigInt(ctx);
12050         if (JS_IsException(val))
12051             return val;
12052         a = JS_GetBigInt(val);
12053         if (bf_set_ui(a, v)) {
12054             JS_FreeValue(ctx, val);
12055             return JS_ThrowOutOfMemory(ctx);
12056         }
12057     }
12058     return val;
12059 }
12060 
12061 /* if the returned bigfloat is allocated it is equal to
12062    'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return
12063    NULL in case of error. */
JS_ToBigFloat(JSContext * ctx,bf_t * buf,JSValueConst val)12064 static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val)
12065 {
12066     uint32_t tag;
12067     bf_t *r;
12068     JSBigFloat *p;
12069 
12070     tag = JS_VALUE_GET_NORM_TAG(val);
12071     switch(tag) {
12072     case JS_TAG_INT:
12073     case JS_TAG_BOOL:
12074     case JS_TAG_NULL:
12075         r = buf;
12076         bf_init(ctx->bf_ctx, r);
12077         if (bf_set_si(r, JS_VALUE_GET_INT(val)))
12078             goto fail;
12079         break;
12080     case JS_TAG_FLOAT64:
12081         r = buf;
12082         bf_init(ctx->bf_ctx, r);
12083         if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) {
12084         fail:
12085             bf_delete(r);
12086             return NULL;
12087         }
12088         break;
12089     case JS_TAG_BIG_INT:
12090     case JS_TAG_BIG_FLOAT:
12091         p = JS_VALUE_GET_PTR(val);
12092         r = &p->num;
12093         break;
12094     case JS_TAG_UNDEFINED:
12095     default:
12096         r = buf;
12097         bf_init(ctx->bf_ctx, r);
12098         bf_set_nan(r);
12099         break;
12100     }
12101     return r;
12102 }
12103 
12104 /* return NULL if invalid type */
JS_ToBigDecimal(JSContext * ctx,JSValueConst val)12105 static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val)
12106 {
12107     uint32_t tag;
12108     JSBigDecimal *p;
12109     bfdec_t *r;
12110 
12111     tag = JS_VALUE_GET_NORM_TAG(val);
12112     switch(tag) {
12113     case JS_TAG_BIG_DECIMAL:
12114         p = JS_VALUE_GET_PTR(val);
12115         r = &p->num;
12116         break;
12117     default:
12118         JS_ThrowTypeError(ctx, "bigdecimal expected");
12119         r = NULL;
12120         break;
12121     }
12122     return r;
12123 }
12124 
12125 /* return NaN if bad bigint literal */
JS_StringToBigInt(JSContext * ctx,JSValue val)12126 static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
12127 {
12128     const char *str, *p;
12129     size_t len;
12130     int flags;
12131 
12132     str = JS_ToCStringLen(ctx, &len, val);
12133     JS_FreeValue(ctx, val);
12134     if (!str)
12135         return JS_EXCEPTION;
12136     p = str;
12137     p += skip_spaces(p);
12138     if ((p - str) == len) {
12139         val = JS_NewBigInt64(ctx, 0);
12140     } else {
12141         flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
12142         if (is_math_mode(ctx))
12143             flags |= ATOD_MODE_BIGINT;
12144         val = js_atof(ctx, p, &p, 0, flags);
12145         p += skip_spaces(p);
12146         if (!JS_IsException(val)) {
12147             if ((p - str) != len) {
12148                 JS_FreeValue(ctx, val);
12149                 val = JS_NAN;
12150             }
12151         }
12152     }
12153     JS_FreeCString(ctx, str);
12154     return val;
12155 }
12156 
JS_StringToBigIntErr(JSContext * ctx,JSValue val)12157 static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)
12158 {
12159     val = JS_StringToBigInt(ctx, val);
12160     if (JS_VALUE_IS_NAN(val))
12161         return JS_ThrowSyntaxError(ctx, "invalid bigint literal");
12162     return val;
12163 }
12164 
12165 /* if the returned bigfloat is allocated it is equal to
12166    'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */
JS_ToBigIntFree(JSContext * ctx,bf_t * buf,JSValue val)12167 static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
12168 {
12169     uint32_t tag;
12170     bf_t *r;
12171     JSBigFloat *p;
12172 
12173  redo:
12174     tag = JS_VALUE_GET_NORM_TAG(val);
12175     switch(tag) {
12176     case JS_TAG_INT:
12177     case JS_TAG_NULL:
12178     case JS_TAG_UNDEFINED:
12179         if (!is_math_mode(ctx))
12180             goto fail;
12181         /* fall tru */
12182     case JS_TAG_BOOL:
12183         r = buf;
12184         bf_init(ctx->bf_ctx, r);
12185         bf_set_si(r, JS_VALUE_GET_INT(val));
12186         break;
12187     case JS_TAG_FLOAT64:
12188         {
12189             double d = JS_VALUE_GET_FLOAT64(val);
12190             if (!is_math_mode(ctx))
12191                 goto fail;
12192             if (!isfinite(d))
12193                 goto fail;
12194             r = buf;
12195             bf_init(ctx->bf_ctx, r);
12196             d = trunc(d);
12197             bf_set_float64(r, d);
12198         }
12199         break;
12200     case JS_TAG_BIG_INT:
12201         p = JS_VALUE_GET_PTR(val);
12202         r = &p->num;
12203         break;
12204     case JS_TAG_BIG_FLOAT:
12205         if (!is_math_mode(ctx))
12206             goto fail;
12207         p = JS_VALUE_GET_PTR(val);
12208         if (!bf_is_finite(&p->num))
12209             goto fail;
12210         r = buf;
12211         bf_init(ctx->bf_ctx, r);
12212         bf_set(r, &p->num);
12213         bf_rint(r, BF_RNDZ);
12214         JS_FreeValue(ctx, val);
12215         break;
12216     case JS_TAG_STRING:
12217         val = JS_StringToBigIntErr(ctx, val);
12218         if (JS_IsException(val))
12219             return NULL;
12220         goto redo;
12221     case JS_TAG_OBJECT:
12222         val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
12223         if (JS_IsException(val))
12224             return NULL;
12225         goto redo;
12226     default:
12227     fail:
12228         JS_FreeValue(ctx, val);
12229         JS_ThrowTypeError(ctx, "cannot convert to bigint");
12230         return NULL;
12231     }
12232     return r;
12233 }
12234 
JS_ToBigInt(JSContext * ctx,bf_t * buf,JSValueConst val)12235 static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val)
12236 {
12237     return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val));
12238 }
12239 
JS_ToBigIntValueFree(JSContext * ctx,JSValue val)12240 static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val)
12241 {
12242     if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) {
12243         return val;
12244     } else {
12245         bf_t a_s, *a, *r;
12246         int ret;
12247         JSValue res;
12248 
12249         res = JS_NewBigInt(ctx);
12250         if (JS_IsException(res))
12251             return JS_EXCEPTION;
12252         a = JS_ToBigIntFree(ctx, &a_s, val);
12253         if (!a) {
12254             JS_FreeValue(ctx, res);
12255             return JS_EXCEPTION;
12256         }
12257         r = JS_GetBigInt(res);
12258         ret = bf_set(r, a);
12259         JS_FreeBigInt(ctx, a, &a_s);
12260         if (ret) {
12261             JS_FreeValue(ctx, res);
12262             return JS_ThrowOutOfMemory(ctx);
12263         }
12264         return JS_CompactBigInt(ctx, res);
12265     }
12266 }
12267 
12268 /* free the bf_t allocated by JS_ToBigInt */
JS_FreeBigInt(JSContext * ctx,bf_t * a,bf_t * buf)12269 static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf)
12270 {
12271     if (a == buf) {
12272         bf_delete(a);
12273     } else {
12274         JSBigFloat *p = (JSBigFloat *)((uint8_t *)a -
12275                                        offsetof(JSBigFloat, num));
12276         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_FLOAT, p));
12277     }
12278 }
12279 
12280 /* XXX: merge with JS_ToInt64Free with a specific flag */
JS_ToBigInt64Free(JSContext * ctx,int64_t * pres,JSValue val)12281 static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
12282 {
12283     bf_t a_s, *a;
12284 
12285     a = JS_ToBigIntFree(ctx, &a_s, val);
12286     if (!a) {
12287         *pres = 0;
12288         return -1;
12289     }
12290     bf_get_int64(pres, a, BF_GET_INT_MOD);
12291     JS_FreeBigInt(ctx, a, &a_s);
12292     return 0;
12293 }
12294 
JS_ToBigInt64(JSContext * ctx,int64_t * pres,JSValueConst val)12295 int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
12296 {
12297     return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));
12298 }
12299 
js_new_bf(JSContext * ctx)12300 static JSBigFloat *js_new_bf(JSContext *ctx)
12301 {
12302     JSBigFloat *p;
12303     p = js_malloc(ctx, sizeof(*p));
12304     if (!p)
12305         return NULL;
12306     p->header.ref_count = 1;
12307     bf_init(ctx->bf_ctx, &p->num);
12308     return p;
12309 }
12310 
JS_NewBigFloat(JSContext * ctx)12311 static JSValue JS_NewBigFloat(JSContext *ctx)
12312 {
12313     JSBigFloat *p;
12314     p = js_malloc(ctx, sizeof(*p));
12315     if (!p)
12316         return JS_EXCEPTION;
12317     p->header.ref_count = 1;
12318     bf_init(ctx->bf_ctx, &p->num);
12319     return JS_MKPTR(JS_TAG_BIG_FLOAT, p);
12320 }
12321 
JS_NewBigDecimal(JSContext * ctx)12322 static JSValue JS_NewBigDecimal(JSContext *ctx)
12323 {
12324     JSBigDecimal *p;
12325     p = js_malloc(ctx, sizeof(*p));
12326     if (!p)
12327         return JS_EXCEPTION;
12328     p->header.ref_count = 1;
12329     bfdec_init(ctx->bf_ctx, &p->num);
12330     return JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
12331 }
12332 
JS_NewBigInt(JSContext * ctx)12333 static JSValue JS_NewBigInt(JSContext *ctx)
12334 {
12335     JSBigFloat *p;
12336     p = js_malloc(ctx, sizeof(*p));
12337     if (!p)
12338         return JS_EXCEPTION;
12339     p->header.ref_count = 1;
12340     bf_init(ctx->bf_ctx, &p->num);
12341     return JS_MKPTR(JS_TAG_BIG_INT, p);
12342 }
12343 
JS_CompactBigInt1(JSContext * ctx,JSValue val,BOOL convert_to_safe_integer)12344 static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
12345                                  BOOL convert_to_safe_integer)
12346 {
12347     int64_t v;
12348     bf_t *a;
12349 
12350     if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT)
12351         return val; /* fail safe */
12352     a = JS_GetBigInt(val);
12353     if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 &&
12354         v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
12355         JS_FreeValue(ctx, val);
12356         return JS_NewInt64(ctx, v);
12357     } else if (a->expn == BF_EXP_ZERO && a->sign) {
12358         JSBigFloat *p = JS_VALUE_GET_PTR(val);
12359         assert(p->header.ref_count == 1);
12360         a->sign = 0;
12361     }
12362     return val;
12363 }
12364 
12365 /* Convert the big int to a safe integer if in math mode. normalize
12366    the zero representation. Could also be used to convert the bigint
12367    to a short bigint value. The reference count of the value must be
12368    1. Cannot fail */
JS_CompactBigInt(JSContext * ctx,JSValue val)12369 static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val)
12370 {
12371     return JS_CompactBigInt1(ctx, val, is_math_mode(ctx));
12372 }
12373 
12374 /* must be kept in sync with JSOverloadableOperatorEnum */
12375 /* XXX: use atoms ? */
12376 static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = {
12377     "+",
12378     "-",
12379     "*",
12380     "/",
12381     "%",
12382     "**",
12383     "|",
12384     "&",
12385     "^",
12386     "<<",
12387     ">>",
12388     ">>>",
12389     "==",
12390     "<",
12391     "pos",
12392     "neg",
12393     "++",
12394     "--",
12395     "~",
12396 };
12397 
get_ovop_from_opcode(OPCodeEnum op)12398 static int get_ovop_from_opcode(OPCodeEnum op)
12399 {
12400     switch(op) {
12401     case OP_add:
12402         return JS_OVOP_ADD;
12403     case OP_sub:
12404         return JS_OVOP_SUB;
12405     case OP_mul:
12406         return JS_OVOP_MUL;
12407     case OP_div:
12408         return JS_OVOP_DIV;
12409     case OP_mod:
12410     case OP_math_mod:
12411         return JS_OVOP_MOD;
12412     case OP_pow:
12413         return JS_OVOP_POW;
12414     case OP_or:
12415         return JS_OVOP_OR;
12416     case OP_and:
12417         return JS_OVOP_AND;
12418     case OP_xor:
12419         return JS_OVOP_XOR;
12420     case OP_shl:
12421         return JS_OVOP_SHL;
12422     case OP_sar:
12423         return JS_OVOP_SAR;
12424     case OP_shr:
12425         return JS_OVOP_SHR;
12426     case OP_eq:
12427     case OP_neq:
12428         return JS_OVOP_EQ;
12429     case OP_lt:
12430     case OP_lte:
12431     case OP_gt:
12432     case OP_gte:
12433         return JS_OVOP_LESS;
12434     case OP_plus:
12435         return JS_OVOP_POS;
12436     case OP_neg:
12437         return JS_OVOP_NEG;
12438     case OP_inc:
12439         return JS_OVOP_INC;
12440     case OP_dec:
12441         return JS_OVOP_DEC;
12442     default:
12443         abort();
12444     }
12445 }
12446 
12447 /* return NULL if not present */
find_binary_op(JSBinaryOperatorDef * def,uint32_t operator_index,JSOverloadableOperatorEnum op)12448 static JSObject *find_binary_op(JSBinaryOperatorDef *def,
12449                                 uint32_t operator_index,
12450                                 JSOverloadableOperatorEnum op)
12451 {
12452     JSBinaryOperatorDefEntry *ent;
12453     int i;
12454     for(i = 0; i < def->count; i++) {
12455         ent = &def->tab[i];
12456         if (ent->operator_index == operator_index)
12457             return ent->ops[op];
12458     }
12459     return NULL;
12460 }
12461 
12462 /* return -1 if exception, 0 if no operator overloading, 1 if
12463    overloaded operator called */
js_call_binary_op_fallback(JSContext * ctx,JSValue * pret,JSValueConst op1,JSValueConst op2,OPCodeEnum op,BOOL is_numeric,int hint)12464 static __exception int js_call_binary_op_fallback(JSContext *ctx,
12465                                                   JSValue *pret,
12466                                                   JSValueConst op1,
12467                                                   JSValueConst op2,
12468                                                   OPCodeEnum op,
12469                                                   BOOL is_numeric,
12470                                                   int hint)
12471 {
12472     JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2;
12473     JSOperatorSetData *opset1, *opset2;
12474     JSOverloadableOperatorEnum ovop;
12475     JSObject *p;
12476     JSValueConst args[2];
12477 
12478     if (!ctx->allow_operator_overloading)
12479         return 0;
12480 
12481     opset2_obj = JS_UNDEFINED;
12482     opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
12483     if (JS_IsException(opset1_obj))
12484         goto exception;
12485     if (JS_IsUndefined(opset1_obj))
12486         return 0;
12487     opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
12488     if (!opset1)
12489         goto exception;
12490 
12491     opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet);
12492     if (JS_IsException(opset2_obj))
12493         goto exception;
12494     if (JS_IsUndefined(opset2_obj)) {
12495         JS_FreeValue(ctx, opset1_obj);
12496         return 0;
12497     }
12498     opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET);
12499     if (!opset2)
12500         goto exception;
12501 
12502     if (opset1->is_primitive && opset2->is_primitive) {
12503         JS_FreeValue(ctx, opset1_obj);
12504         JS_FreeValue(ctx, opset2_obj);
12505         return 0;
12506     }
12507 
12508     ovop = get_ovop_from_opcode(op);
12509 
12510     if (opset1->operator_counter == opset2->operator_counter) {
12511         p = opset1->self_ops[ovop];
12512     } else if (opset1->operator_counter > opset2->operator_counter) {
12513         p = find_binary_op(&opset1->left, opset2->operator_counter, ovop);
12514     } else {
12515         p = find_binary_op(&opset2->right, opset1->operator_counter, ovop);
12516     }
12517     if (!p) {
12518         JS_ThrowTypeError(ctx, "operator %s: no function defined",
12519                           js_overloadable_operator_names[ovop]);
12520         goto exception;
12521     }
12522 
12523     if (opset1->is_primitive) {
12524         if (is_numeric) {
12525             new_op1 = JS_ToNumeric(ctx, op1);
12526         } else {
12527             new_op1 = JS_ToPrimitive(ctx, op1, hint);
12528         }
12529         if (JS_IsException(new_op1))
12530             goto exception;
12531     } else {
12532         new_op1 = JS_DupValue(ctx, op1);
12533     }
12534 
12535     if (opset2->is_primitive) {
12536         if (is_numeric) {
12537             new_op2 = JS_ToNumeric(ctx, op2);
12538         } else {
12539             new_op2 = JS_ToPrimitive(ctx, op2, hint);
12540         }
12541         if (JS_IsException(new_op2)) {
12542             JS_FreeValue(ctx, new_op1);
12543             goto exception;
12544         }
12545     } else {
12546         new_op2 = JS_DupValue(ctx, op2);
12547     }
12548 
12549     /* XXX: could apply JS_ToPrimitive() if primitive type so that the
12550        operator function does not get a value object */
12551 
12552     method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
12553     if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) {
12554         args[0] = new_op2;
12555         args[1] = new_op1;
12556     } else {
12557         args[0] = new_op1;
12558         args[1] = new_op2;
12559     }
12560     ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
12561     JS_FreeValue(ctx, new_op1);
12562     JS_FreeValue(ctx, new_op2);
12563     if (JS_IsException(ret))
12564         goto exception;
12565     if (ovop == JS_OVOP_EQ) {
12566         BOOL res = JS_ToBoolFree(ctx, ret);
12567         if (op == OP_neq)
12568             res ^= 1;
12569         ret = JS_NewBool(ctx, res);
12570     } else if (ovop == JS_OVOP_LESS) {
12571         if (JS_IsUndefined(ret)) {
12572             ret = JS_FALSE;
12573         } else {
12574             BOOL res = JS_ToBoolFree(ctx, ret);
12575             if (op == OP_lte || op == OP_gte)
12576                 res ^= 1;
12577             ret = JS_NewBool(ctx, res);
12578         }
12579     }
12580     JS_FreeValue(ctx, opset1_obj);
12581     JS_FreeValue(ctx, opset2_obj);
12582     *pret = ret;
12583     return 1;
12584  exception:
12585     JS_FreeValue(ctx, opset1_obj);
12586     JS_FreeValue(ctx, opset2_obj);
12587     *pret = JS_UNDEFINED;
12588     return -1;
12589 }
12590 
12591 /* try to call the operation on the operatorSet field of 'obj'. Only
12592    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)12593 static __exception int js_call_binary_op_simple(JSContext *ctx,
12594                                                 JSValue *pret,
12595                                                 JSValueConst obj,
12596                                                 JSValueConst op1,
12597                                                 JSValueConst op2,
12598                                                 OPCodeEnum op)
12599 {
12600     JSValue opset1_obj, method, ret, new_op1, new_op2;
12601     JSOperatorSetData *opset1;
12602     JSOverloadableOperatorEnum ovop;
12603     JSObject *p;
12604     JSValueConst args[2];
12605 
12606     opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
12607     if (JS_IsException(opset1_obj))
12608         goto exception;
12609     if (JS_IsUndefined(opset1_obj))
12610         return 0;
12611     opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
12612     if (!opset1)
12613         goto exception;
12614     ovop = get_ovop_from_opcode(op);
12615 
12616     p = opset1->self_ops[ovop];
12617     if (!p) {
12618         JS_FreeValue(ctx, opset1_obj);
12619         return 0;
12620     }
12621 
12622     new_op1 = JS_ToNumeric(ctx, op1);
12623     if (JS_IsException(new_op1))
12624         goto exception;
12625     new_op2 = JS_ToNumeric(ctx, op2);
12626     if (JS_IsException(new_op2)) {
12627         JS_FreeValue(ctx, new_op1);
12628         goto exception;
12629     }
12630 
12631     method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
12632     args[0] = new_op1;
12633     args[1] = new_op2;
12634     ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
12635     JS_FreeValue(ctx, new_op1);
12636     JS_FreeValue(ctx, new_op2);
12637     if (JS_IsException(ret))
12638         goto exception;
12639     JS_FreeValue(ctx, opset1_obj);
12640     *pret = ret;
12641     return 1;
12642  exception:
12643     JS_FreeValue(ctx, opset1_obj);
12644     *pret = JS_UNDEFINED;
12645     return -1;
12646 }
12647 
12648 /* return -1 if exception, 0 if no operator overloading, 1 if
12649    overloaded operator called */
js_call_unary_op_fallback(JSContext * ctx,JSValue * pret,JSValueConst op1,OPCodeEnum op)12650 static __exception int js_call_unary_op_fallback(JSContext *ctx,
12651                                                  JSValue *pret,
12652                                                  JSValueConst op1,
12653                                                  OPCodeEnum op)
12654 {
12655     JSValue opset1_obj, method, ret;
12656     JSOperatorSetData *opset1;
12657     JSOverloadableOperatorEnum ovop;
12658     JSObject *p;
12659 
12660     if (!ctx->allow_operator_overloading)
12661         return 0;
12662 
12663     opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
12664     if (JS_IsException(opset1_obj))
12665         goto exception;
12666     if (JS_IsUndefined(opset1_obj))
12667         return 0;
12668     opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
12669     if (!opset1)
12670         goto exception;
12671     if (opset1->is_primitive) {
12672         JS_FreeValue(ctx, opset1_obj);
12673         return 0;
12674     }
12675 
12676     ovop = get_ovop_from_opcode(op);
12677 
12678     p = opset1->self_ops[ovop];
12679     if (!p) {
12680         JS_ThrowTypeError(ctx, "no overloaded operator %s",
12681                           js_overloadable_operator_names[ovop]);
12682         goto exception;
12683     }
12684     method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
12685     ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1);
12686     if (JS_IsException(ret))
12687         goto exception;
12688     JS_FreeValue(ctx, opset1_obj);
12689     *pret = ret;
12690     return 1;
12691  exception:
12692     JS_FreeValue(ctx, opset1_obj);
12693     *pret = JS_UNDEFINED;
12694     return -1;
12695 }
12696 
throw_bf_exception(JSContext * ctx,int status)12697 static JSValue throw_bf_exception(JSContext *ctx, int status)
12698 {
12699     const char *str;
12700     if (status & BF_ST_MEM_ERROR)
12701         return JS_ThrowOutOfMemory(ctx);
12702     if (status & BF_ST_DIVIDE_ZERO) {
12703         str = "division by zero";
12704     } else if (status & BF_ST_INVALID_OP) {
12705         str = "invalid operation";
12706     } else {
12707         str = "integer overflow";
12708     }
12709     return JS_ThrowRangeError(ctx, "%s", str);
12710 }
12711 
js_unary_arith_bigint(JSContext * ctx,JSValue * pres,OPCodeEnum op,JSValue op1)12712 static int js_unary_arith_bigint(JSContext *ctx,
12713                                  JSValue *pres, OPCodeEnum op, JSValue op1)
12714 {
12715     bf_t a_s, *r, *a;
12716     int ret, v;
12717     JSValue res;
12718 
12719     if (op == OP_plus && !is_math_mode(ctx)) {
12720         JS_ThrowTypeError(ctx, "bigint argument with unary +");
12721         JS_FreeValue(ctx, op1);
12722         return -1;
12723     }
12724     res = JS_NewBigInt(ctx);
12725     if (JS_IsException(res)) {
12726         JS_FreeValue(ctx, op1);
12727         return -1;
12728     }
12729     r = JS_GetBigInt(res);
12730     a = JS_ToBigInt(ctx, &a_s, op1);
12731     ret = 0;
12732     switch(op) {
12733     case OP_inc:
12734     case OP_dec:
12735         v = 2 * (op - OP_dec) - 1;
12736         ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
12737         break;
12738     case OP_plus:
12739         ret = bf_set(r, a);
12740         break;
12741     case OP_neg:
12742         ret = bf_set(r, a);
12743         bf_neg(r);
12744         break;
12745     case OP_not:
12746         ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ);
12747         bf_neg(r);
12748         break;
12749     default:
12750         abort();
12751     }
12752     JS_FreeBigInt(ctx, a, &a_s);
12753     JS_FreeValue(ctx, op1);
12754     if (unlikely(ret)) {
12755         JS_FreeValue(ctx, res);
12756         throw_bf_exception(ctx, ret);
12757         return -1;
12758     }
12759     res = JS_CompactBigInt(ctx, res);
12760     *pres = res;
12761     return 0;
12762 }
12763 
js_unary_arith_bigfloat(JSContext * ctx,JSValue * pres,OPCodeEnum op,JSValue op1)12764 static int js_unary_arith_bigfloat(JSContext *ctx,
12765                                    JSValue *pres, OPCodeEnum op, JSValue op1)
12766 {
12767     bf_t a_s, *r, *a;
12768     int ret, v;
12769     JSValue res;
12770 
12771     if (op == OP_plus && !is_math_mode(ctx)) {
12772         JS_ThrowTypeError(ctx, "bigfloat argument with unary +");
12773         JS_FreeValue(ctx, op1);
12774         return -1;
12775     }
12776 
12777     res = JS_NewBigFloat(ctx);
12778     if (JS_IsException(res)) {
12779         JS_FreeValue(ctx, op1);
12780         return -1;
12781     }
12782     r = JS_GetBigFloat(res);
12783     a = JS_ToBigFloat(ctx, &a_s, op1);
12784     ret = 0;
12785     switch(op) {
12786     case OP_inc:
12787     case OP_dec:
12788         v = 2 * (op - OP_dec) - 1;
12789         ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags);
12790         break;
12791     case OP_plus:
12792         ret = bf_set(r, a);
12793         break;
12794     case OP_neg:
12795         ret = bf_set(r, a);
12796         bf_neg(r);
12797         break;
12798     default:
12799         abort();
12800     }
12801     if (a == &a_s)
12802         bf_delete(a);
12803     JS_FreeValue(ctx, op1);
12804     if (unlikely(ret & BF_ST_MEM_ERROR)) {
12805         JS_FreeValue(ctx, res);
12806         throw_bf_exception(ctx, ret);
12807         return -1;
12808     }
12809     *pres = res;
12810     return 0;
12811 }
12812 
js_unary_arith_bigdecimal(JSContext * ctx,JSValue * pres,OPCodeEnum op,JSValue op1)12813 static int js_unary_arith_bigdecimal(JSContext *ctx,
12814                                      JSValue *pres, OPCodeEnum op, JSValue op1)
12815 {
12816     bfdec_t *r, *a;
12817     int ret, v;
12818     JSValue res;
12819 
12820     if (op == OP_plus && !is_math_mode(ctx)) {
12821         JS_ThrowTypeError(ctx, "bigdecimal argument with unary +");
12822         JS_FreeValue(ctx, op1);
12823         return -1;
12824     }
12825 
12826     res = JS_NewBigDecimal(ctx);
12827     if (JS_IsException(res)) {
12828         JS_FreeValue(ctx, op1);
12829         return -1;
12830     }
12831     r = JS_GetBigDecimal(res);
12832     a = JS_ToBigDecimal(ctx, op1);
12833     ret = 0;
12834     switch(op) {
12835     case OP_inc:
12836     case OP_dec:
12837         v = 2 * (op - OP_dec) - 1;
12838         ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
12839         break;
12840     case OP_plus:
12841         ret = bfdec_set(r, a);
12842         break;
12843     case OP_neg:
12844         ret = bfdec_set(r, a);
12845         bfdec_neg(r);
12846         break;
12847     default:
12848         abort();
12849     }
12850     JS_FreeValue(ctx, op1);
12851     if (unlikely(ret)) {
12852         JS_FreeValue(ctx, res);
12853         throw_bf_exception(ctx, ret);
12854         return -1;
12855     }
12856     *pres = res;
12857     return 0;
12858 }
12859 
js_unary_arith_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)12860 static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
12861                                                      JSValue *sp,
12862                                                      OPCodeEnum op)
12863 {
12864     JSValue op1, val;
12865     int v, ret;
12866     uint32_t tag;
12867 
12868     op1 = sp[-1];
12869     /* fast path for float64 */
12870     if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))
12871         goto handle_float64;
12872     if (JS_IsObject(op1)) {
12873         ret = js_call_unary_op_fallback(ctx, &val, op1, op);
12874         if (ret < 0)
12875             return -1;
12876         if (ret) {
12877             JS_FreeValue(ctx, op1);
12878             sp[-1] = val;
12879             return 0;
12880         }
12881     }
12882 
12883     op1 = JS_ToNumericFree(ctx, op1);
12884     if (JS_IsException(op1))
12885         goto exception;
12886     tag = JS_VALUE_GET_TAG(op1);
12887     switch(tag) {
12888     case JS_TAG_INT:
12889         {
12890             int64_t v64;
12891             v64 = JS_VALUE_GET_INT(op1);
12892             switch(op) {
12893             case OP_inc:
12894             case OP_dec:
12895                 v = 2 * (op - OP_dec) - 1;
12896                 v64 += v;
12897                 break;
12898             case OP_plus:
12899                 break;
12900             case OP_neg:
12901                 if (v64 == 0) {
12902                     sp[-1] = __JS_NewFloat64(ctx, -0.0);
12903                     return 0;
12904                 } else {
12905                     v64 = -v64;
12906                 }
12907                 break;
12908             default:
12909                 abort();
12910             }
12911             sp[-1] = JS_NewInt64(ctx, v64);
12912         }
12913         break;
12914     case JS_TAG_BIG_INT:
12915     handle_bigint:
12916         if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1))
12917             goto exception;
12918         break;
12919     case JS_TAG_BIG_FLOAT:
12920         if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1))
12921             goto exception;
12922         break;
12923     case JS_TAG_BIG_DECIMAL:
12924         if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1))
12925             goto exception;
12926         break;
12927     default:
12928     handle_float64:
12929         {
12930             double d;
12931             if (is_math_mode(ctx))
12932                 goto handle_bigint;
12933             d = JS_VALUE_GET_FLOAT64(op1);
12934             switch(op) {
12935             case OP_inc:
12936             case OP_dec:
12937                 v = 2 * (op - OP_dec) - 1;
12938                 d += v;
12939                 break;
12940             case OP_plus:
12941                 break;
12942             case OP_neg:
12943                 d = -d;
12944                 break;
12945             default:
12946                 abort();
12947             }
12948             sp[-1] = __JS_NewFloat64(ctx, d);
12949         }
12950         break;
12951     }
12952     return 0;
12953  exception:
12954     sp[-1] = JS_UNDEFINED;
12955     return -1;
12956 }
12957 
js_post_inc_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)12958 static __exception int js_post_inc_slow(JSContext *ctx,
12959                                         JSValue *sp, OPCodeEnum op)
12960 {
12961     JSValue op1;
12962 
12963     /* XXX: allow custom operators */
12964     op1 = sp[-1];
12965     op1 = JS_ToNumericFree(ctx, op1);
12966     if (JS_IsException(op1)) {
12967         sp[-1] = JS_UNDEFINED;
12968         return -1;
12969     }
12970     sp[-1] = op1;
12971     sp[0] = JS_DupValue(ctx, op1);
12972     return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);
12973 }
12974 
js_not_slow(JSContext * ctx,JSValue * sp)12975 static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
12976 {
12977     JSValue op1, val;
12978     int ret;
12979 
12980     op1 = sp[-1];
12981     if (JS_IsObject(op1)) {
12982         ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not);
12983         if (ret < 0)
12984             return -1;
12985         if (ret) {
12986             JS_FreeValue(ctx, op1);
12987             sp[-1] = val;
12988             return 0;
12989         }
12990     }
12991 
12992     op1 = JS_ToNumericFree(ctx, op1);
12993     if (JS_IsException(op1))
12994         goto exception;
12995     if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
12996         if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1))
12997             goto exception;
12998     } else {
12999         int32_t v1;
13000         if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
13001             goto exception;
13002         sp[-1] = JS_NewInt32(ctx, ~v1);
13003     }
13004     return 0;
13005  exception:
13006     sp[-1] = JS_UNDEFINED;
13007     return -1;
13008 }
13009 
js_binary_arith_bigfloat(JSContext * ctx,OPCodeEnum op,JSValue * pres,JSValue op1,JSValue op2)13010 static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,
13011                                     JSValue *pres, JSValue op1, JSValue op2)
13012 {
13013     bf_t a_s, b_s, *r, *a, *b;
13014     int ret;
13015     JSValue res;
13016 
13017     res = JS_NewBigFloat(ctx);
13018     if (JS_IsException(res)) {
13019         JS_FreeValue(ctx, op1);
13020         JS_FreeValue(ctx, op2);
13021         return -1;
13022     }
13023     r = JS_GetBigFloat(res);
13024     a = JS_ToBigFloat(ctx, &a_s, op1);
13025     b = JS_ToBigFloat(ctx, &b_s, op2);
13026     bf_init(ctx->bf_ctx, r);
13027     switch(op) {
13028     case OP_add:
13029         ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
13030         break;
13031     case OP_sub:
13032         ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
13033         break;
13034     case OP_mul:
13035         ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
13036         break;
13037     case OP_div:
13038         ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
13039         break;
13040     case OP_math_mod:
13041         /* Euclidian remainder */
13042         ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
13043                      BF_DIVREM_EUCLIDIAN);
13044         break;
13045     case OP_mod:
13046         ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
13047                      BF_RNDZ);
13048         break;
13049     case OP_pow:
13050         ret = bf_pow(r, a, b, ctx->fp_env.prec,
13051                      ctx->fp_env.flags | BF_POW_JS_QUIRKS);
13052         break;
13053     default:
13054         abort();
13055     }
13056     if (a == &a_s)
13057         bf_delete(a);
13058     if (b == &b_s)
13059         bf_delete(b);
13060     JS_FreeValue(ctx, op1);
13061     JS_FreeValue(ctx, op2);
13062     if (unlikely(ret & BF_ST_MEM_ERROR)) {
13063         JS_FreeValue(ctx, res);
13064         throw_bf_exception(ctx, ret);
13065         return -1;
13066     }
13067     *pres = res;
13068     return 0;
13069 }
13070 
js_binary_arith_bigint(JSContext * ctx,OPCodeEnum op,JSValue * pres,JSValue op1,JSValue op2)13071 static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,
13072                                   JSValue *pres, JSValue op1, JSValue op2)
13073 {
13074     bf_t a_s, b_s, *r, *a, *b;
13075     int ret;
13076     JSValue res;
13077 
13078     res = JS_NewBigInt(ctx);
13079     if (JS_IsException(res))
13080         goto fail;
13081     a = JS_ToBigInt(ctx, &a_s, op1);
13082     if (!a)
13083         goto fail;
13084     b = JS_ToBigInt(ctx, &b_s, op2);
13085     if (!b) {
13086         JS_FreeBigInt(ctx, a, &a_s);
13087         goto fail;
13088     }
13089     r = JS_GetBigInt(res);
13090     ret = 0;
13091     switch(op) {
13092     case OP_add:
13093         ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ);
13094         break;
13095     case OP_sub:
13096         ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
13097         break;
13098     case OP_mul:
13099         ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
13100         break;
13101     case OP_div:
13102         if (!is_math_mode(ctx)) {
13103             bf_t rem_s, *rem = &rem_s;
13104             bf_init(ctx->bf_ctx, rem);
13105             ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ,
13106                             BF_RNDZ);
13107             bf_delete(rem);
13108         } else {
13109             goto math_mode_div_pow;
13110         }
13111         break;
13112     case OP_math_mod:
13113         /* Euclidian remainder */
13114         ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
13115                      BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP;
13116         break;
13117     case OP_mod:
13118         ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
13119                      BF_RNDZ) & BF_ST_INVALID_OP;
13120         break;
13121     case OP_pow:
13122         if (b->sign) {
13123             if (!is_math_mode(ctx)) {
13124                 ret = BF_ST_INVALID_OP;
13125             } else {
13126             math_mode_div_pow:
13127                 JS_FreeValue(ctx, res);
13128                 ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op);
13129                 if (ret != 0) {
13130                     JS_FreeBigInt(ctx, a, &a_s);
13131                     JS_FreeBigInt(ctx, b, &b_s);
13132                     JS_FreeValue(ctx, op1);
13133                     JS_FreeValue(ctx, op2);
13134                     if (ret < 0) {
13135                         return -1;
13136                     } else {
13137                         *pres = res;
13138                         return 0;
13139                     }
13140                 }
13141                 /* if no BigInt power operator defined, return a
13142                    bigfloat */
13143                 res = JS_NewBigFloat(ctx);
13144                 if (JS_IsException(res)) {
13145                     JS_FreeBigInt(ctx, a, &a_s);
13146                     JS_FreeBigInt(ctx, b, &b_s);
13147                     goto fail;
13148                 }
13149                 r = JS_GetBigFloat(res);
13150                 if (op == OP_div) {
13151                     ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR;
13152                 } else {
13153                     ret = bf_pow(r, a, b, ctx->fp_env.prec,
13154                                  ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR;
13155                 }
13156                 JS_FreeBigInt(ctx, a, &a_s);
13157                 JS_FreeBigInt(ctx, b, &b_s);
13158                 JS_FreeValue(ctx, op1);
13159                 JS_FreeValue(ctx, op2);
13160                 if (unlikely(ret)) {
13161                     JS_FreeValue(ctx, res);
13162                     throw_bf_exception(ctx, ret);
13163                     return -1;
13164                 }
13165                 *pres = res;
13166                 return 0;
13167             }
13168         } else {
13169             ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS);
13170         }
13171         break;
13172 
13173         /* logical operations */
13174     case OP_shl:
13175     case OP_sar:
13176         {
13177             slimb_t v2;
13178 #if LIMB_BITS == 32
13179             bf_get_int32(&v2, b, 0);
13180             if (v2 == INT32_MIN)
13181                 v2 = INT32_MIN + 1;
13182 #else
13183             bf_get_int64(&v2, b, 0);
13184             if (v2 == INT64_MIN)
13185                 v2 = INT64_MIN + 1;
13186 #endif
13187             if (op == OP_sar)
13188                 v2 = -v2;
13189             ret = bf_set(r, a);
13190             ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ);
13191             if (v2 < 0) {
13192                 ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR);
13193             }
13194         }
13195         break;
13196     case OP_and:
13197         ret = bf_logic_and(r, a, b);
13198         break;
13199     case OP_or:
13200         ret = bf_logic_or(r, a, b);
13201         break;
13202     case OP_xor:
13203         ret = bf_logic_xor(r, a, b);
13204         break;
13205     default:
13206         abort();
13207     }
13208     JS_FreeBigInt(ctx, a, &a_s);
13209     JS_FreeBigInt(ctx, b, &b_s);
13210     JS_FreeValue(ctx, op1);
13211     JS_FreeValue(ctx, op2);
13212     if (unlikely(ret)) {
13213         JS_FreeValue(ctx, res);
13214         throw_bf_exception(ctx, ret);
13215         return -1;
13216     }
13217     *pres = JS_CompactBigInt(ctx, res);
13218     return 0;
13219  fail:
13220     JS_FreeValue(ctx, res);
13221     JS_FreeValue(ctx, op1);
13222     JS_FreeValue(ctx, op2);
13223     return -1;
13224 }
13225 
13226 /* b must be a positive integer */
js_bfdec_pow(bfdec_t * r,const bfdec_t * a,const bfdec_t * b)13227 static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b)
13228 {
13229     bfdec_t b1;
13230     int32_t b2;
13231     int ret;
13232 
13233     bfdec_init(b->ctx, &b1);
13234     ret = bfdec_set(&b1, b);
13235     if (ret) {
13236         bfdec_delete(&b1);
13237         return ret;
13238     }
13239     ret = bfdec_rint(&b1, BF_RNDZ);
13240     if (ret) {
13241         bfdec_delete(&b1);
13242         return BF_ST_INVALID_OP; /* must be an integer */
13243     }
13244     ret = bfdec_get_int32(&b2, &b1);
13245     bfdec_delete(&b1);
13246     if (ret)
13247         return ret; /* overflow */
13248     if (b2 < 0)
13249         return BF_ST_INVALID_OP; /* must be positive */
13250     return bfdec_pow_ui(r, a, b2);
13251 }
13252 
js_binary_arith_bigdecimal(JSContext * ctx,OPCodeEnum op,JSValue * pres,JSValue op1,JSValue op2)13253 static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
13254                                       JSValue *pres, JSValue op1, JSValue op2)
13255 {
13256     bfdec_t *r, *a, *b;
13257     int ret;
13258     JSValue res;
13259 
13260     res = JS_NewBigDecimal(ctx);
13261     if (JS_IsException(res))
13262         goto fail;
13263     r = JS_GetBigDecimal(res);
13264 
13265     a = JS_ToBigDecimal(ctx, op1);
13266     if (!a)
13267         goto fail;
13268     b = JS_ToBigDecimal(ctx, op2);
13269     if (!b)
13270         goto fail;
13271     switch(op) {
13272     case OP_add:
13273         ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ);
13274         break;
13275     case OP_sub:
13276         ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
13277         break;
13278     case OP_mul:
13279         ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
13280         break;
13281     case OP_div:
13282         ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ);
13283         break;
13284     case OP_math_mod:
13285         /* Euclidian remainder */
13286         ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN);
13287         break;
13288     case OP_mod:
13289         ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ);
13290         break;
13291     case OP_pow:
13292         ret = js_bfdec_pow(r, a, b);
13293         break;
13294     default:
13295         abort();
13296     }
13297     JS_FreeValue(ctx, op1);
13298     JS_FreeValue(ctx, op2);
13299     if (unlikely(ret)) {
13300         JS_FreeValue(ctx, res);
13301         throw_bf_exception(ctx, ret);
13302         return -1;
13303     }
13304     *pres = res;
13305     return 0;
13306  fail:
13307     JS_FreeValue(ctx, res);
13308     JS_FreeValue(ctx, op1);
13309     JS_FreeValue(ctx, op2);
13310     return -1;
13311 }
13312 
js_binary_arith_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)13313 static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
13314                                                       OPCodeEnum op)
13315 {
13316     JSValue op1, op2, res;
13317     uint32_t tag1, tag2;
13318     int ret;
13319     double d1, d2;
13320 
13321     op1 = sp[-2];
13322     op2 = sp[-1];
13323     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13324     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13325     /* fast path for float operations */
13326     if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
13327         d1 = JS_VALUE_GET_FLOAT64(op1);
13328         d2 = JS_VALUE_GET_FLOAT64(op2);
13329         goto handle_float64;
13330     }
13331 
13332     /* try to call an overloaded operator */
13333     if ((tag1 == JS_TAG_OBJECT &&
13334          (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
13335         (tag2 == JS_TAG_OBJECT &&
13336          (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
13337         ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
13338         if (ret != 0) {
13339             JS_FreeValue(ctx, op1);
13340             JS_FreeValue(ctx, op2);
13341             if (ret < 0) {
13342                 goto exception;
13343             } else {
13344                 sp[-2] = res;
13345                 return 0;
13346             }
13347         }
13348     }
13349 
13350     op1 = JS_ToNumericFree(ctx, op1);
13351     if (JS_IsException(op1)) {
13352         JS_FreeValue(ctx, op2);
13353         goto exception;
13354     }
13355     op2 = JS_ToNumericFree(ctx, op2);
13356     if (JS_IsException(op2)) {
13357         JS_FreeValue(ctx, op1);
13358         goto exception;
13359     }
13360     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13361     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13362 
13363     if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
13364         int32_t v1, v2;
13365         int64_t v;
13366         v1 = JS_VALUE_GET_INT(op1);
13367         v2 = JS_VALUE_GET_INT(op2);
13368         switch(op) {
13369         case OP_sub:
13370             v = (int64_t)v1 - (int64_t)v2;
13371             break;
13372         case OP_mul:
13373             v = (int64_t)v1 * (int64_t)v2;
13374             if (is_math_mode(ctx) &&
13375                 (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER))
13376                 goto handle_bigint;
13377             if (v == 0 && (v1 | v2) < 0) {
13378                 sp[-2] = __JS_NewFloat64(ctx, -0.0);
13379                 return 0;
13380             }
13381             break;
13382         case OP_div:
13383             if (is_math_mode(ctx))
13384                 goto handle_bigint;
13385             sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2);
13386             return 0;
13387         case OP_math_mod:
13388             if (unlikely(v2 == 0)) {
13389                 throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO);
13390                 goto exception;
13391             }
13392             v = (int64_t)v1 % (int64_t)v2;
13393             if (v < 0) {
13394                 if (v2 < 0)
13395                     v -= v2;
13396                 else
13397                     v += v2;
13398             }
13399             break;
13400         case OP_mod:
13401             if (v1 < 0 || v2 <= 0) {
13402                 sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));
13403                 return 0;
13404             } else {
13405                 v = (int64_t)v1 % (int64_t)v2;
13406             }
13407             break;
13408         case OP_pow:
13409             if (!is_math_mode(ctx)) {
13410                 sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
13411                 return 0;
13412             } else {
13413                 goto handle_bigint;
13414             }
13415             break;
13416         default:
13417             abort();
13418         }
13419         sp[-2] = JS_NewInt64(ctx, v);
13420     } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
13421         if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2))
13422             goto exception;
13423     } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
13424         if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2))
13425             goto exception;
13426     } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
13427     handle_bigint:
13428         if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
13429             goto exception;
13430     } else {
13431         double dr;
13432         /* float64 result */
13433         if (JS_ToFloat64Free(ctx, &d1, op1)) {
13434             JS_FreeValue(ctx, op2);
13435             goto exception;
13436         }
13437         if (JS_ToFloat64Free(ctx, &d2, op2))
13438             goto exception;
13439     handle_float64:
13440         if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
13441             goto handle_bigint;
13442         switch(op) {
13443         case OP_sub:
13444             dr = d1 - d2;
13445             break;
13446         case OP_mul:
13447             dr = d1 * d2;
13448             break;
13449         case OP_div:
13450             dr = d1 / d2;
13451             break;
13452         case OP_mod:
13453             dr = fmod(d1, d2);
13454             break;
13455         case OP_math_mod:
13456             d2 = fabs(d2);
13457             dr = fmod(d1, d2);
13458             /* XXX: loss of accuracy if dr < 0 */
13459             if (dr < 0)
13460                 dr += d2;
13461             break;
13462         case OP_pow:
13463             dr = js_pow(d1, d2);
13464             break;
13465         default:
13466             abort();
13467         }
13468         sp[-2] = __JS_NewFloat64(ctx, dr);
13469     }
13470     return 0;
13471  exception:
13472     sp[-2] = JS_UNDEFINED;
13473     sp[-1] = JS_UNDEFINED;
13474     return -1;
13475 }
13476 
js_add_slow(JSContext * ctx,JSValue * sp)13477 static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
13478 {
13479     JSValue op1, op2, res;
13480     uint32_t tag1, tag2;
13481     int ret;
13482 
13483     op1 = sp[-2];
13484     op2 = sp[-1];
13485 
13486     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13487     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13488     /* fast path for float64 */
13489     if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
13490         double d1, d2;
13491         d1 = JS_VALUE_GET_FLOAT64(op1);
13492         d2 = JS_VALUE_GET_FLOAT64(op2);
13493         sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
13494         return 0;
13495     }
13496 
13497     if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {
13498         /* try to call an overloaded operator */
13499         if ((tag1 == JS_TAG_OBJECT &&
13500              (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED &&
13501               tag2 != JS_TAG_STRING)) ||
13502             (tag2 == JS_TAG_OBJECT &&
13503              (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED &&
13504               tag1 != JS_TAG_STRING))) {
13505             ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add,
13506                                              FALSE, HINT_NONE);
13507             if (ret != 0) {
13508                 JS_FreeValue(ctx, op1);
13509                 JS_FreeValue(ctx, op2);
13510                 if (ret < 0) {
13511                     goto exception;
13512                 } else {
13513                     sp[-2] = res;
13514                     return 0;
13515                 }
13516             }
13517         }
13518 
13519         op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
13520         if (JS_IsException(op1)) {
13521             JS_FreeValue(ctx, op2);
13522             goto exception;
13523         }
13524 
13525         op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
13526         if (JS_IsException(op2)) {
13527             JS_FreeValue(ctx, op1);
13528             goto exception;
13529         }
13530         tag1 = JS_VALUE_GET_NORM_TAG(op1);
13531         tag2 = JS_VALUE_GET_NORM_TAG(op2);
13532     }
13533 
13534     if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {
13535         sp[-2] = JS_ConcatString(ctx, op1, op2);
13536         if (JS_IsException(sp[-2]))
13537             goto exception;
13538         return 0;
13539     }
13540 
13541     op1 = JS_ToNumericFree(ctx, op1);
13542     if (JS_IsException(op1)) {
13543         JS_FreeValue(ctx, op2);
13544         goto exception;
13545     }
13546     op2 = JS_ToNumericFree(ctx, op2);
13547     if (JS_IsException(op2)) {
13548         JS_FreeValue(ctx, op1);
13549         goto exception;
13550     }
13551     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13552     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13553 
13554     if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
13555         int32_t v1, v2;
13556         int64_t v;
13557         v1 = JS_VALUE_GET_INT(op1);
13558         v2 = JS_VALUE_GET_INT(op2);
13559         v = (int64_t)v1 + (int64_t)v2;
13560         sp[-2] = JS_NewInt64(ctx, v);
13561     } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
13562         if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
13563             goto exception;
13564     } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
13565         if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
13566             goto exception;
13567     } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
13568     handle_bigint:
13569         if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
13570             goto exception;
13571     } else {
13572         double d1, d2;
13573         /* float64 result */
13574         if (JS_ToFloat64Free(ctx, &d1, op1)) {
13575             JS_FreeValue(ctx, op2);
13576             goto exception;
13577         }
13578         if (JS_ToFloat64Free(ctx, &d2, op2))
13579             goto exception;
13580         if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
13581             goto handle_bigint;
13582         sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
13583     }
13584     return 0;
13585  exception:
13586     sp[-2] = JS_UNDEFINED;
13587     sp[-1] = JS_UNDEFINED;
13588     return -1;
13589 }
13590 
js_binary_logic_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)13591 static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
13592                                                       JSValue *sp,
13593                                                       OPCodeEnum op)
13594 {
13595     JSValue op1, op2, res;
13596     int ret;
13597     uint32_t tag1, tag2;
13598     uint32_t v1, v2, r;
13599 
13600     op1 = sp[-2];
13601     op2 = sp[-1];
13602     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13603     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13604 
13605     /* try to call an overloaded operator */
13606     if ((tag1 == JS_TAG_OBJECT &&
13607          (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
13608         (tag2 == JS_TAG_OBJECT &&
13609          (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
13610         ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
13611         if (ret != 0) {
13612             JS_FreeValue(ctx, op1);
13613             JS_FreeValue(ctx, op2);
13614             if (ret < 0) {
13615                 goto exception;
13616             } else {
13617                 sp[-2] = res;
13618                 return 0;
13619             }
13620         }
13621     }
13622 
13623     op1 = JS_ToNumericFree(ctx, op1);
13624     if (JS_IsException(op1)) {
13625         JS_FreeValue(ctx, op2);
13626         goto exception;
13627     }
13628     op2 = JS_ToNumericFree(ctx, op2);
13629     if (JS_IsException(op2)) {
13630         JS_FreeValue(ctx, op1);
13631         goto exception;
13632     }
13633 
13634     if (is_math_mode(ctx))
13635         goto bigint_op;
13636 
13637     tag1 = JS_VALUE_GET_TAG(op1);
13638     tag2 = JS_VALUE_GET_TAG(op2);
13639     if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
13640         if (tag1 != tag2) {
13641             JS_FreeValue(ctx, op1);
13642             JS_FreeValue(ctx, op2);
13643             JS_ThrowTypeError(ctx, "both operands must be bigint");
13644             goto exception;
13645         } else {
13646         bigint_op:
13647             if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
13648                 goto exception;
13649         }
13650     } else {
13651         if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
13652             JS_FreeValue(ctx, op2);
13653             goto exception;
13654         }
13655         if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
13656             goto exception;
13657         switch(op) {
13658         case OP_shl:
13659             r = v1 << (v2 & 0x1f);
13660             break;
13661         case OP_sar:
13662             r = (int)v1 >> (v2 & 0x1f);
13663             break;
13664         case OP_and:
13665             r = v1 & v2;
13666             break;
13667         case OP_or:
13668             r = v1 | v2;
13669             break;
13670         case OP_xor:
13671             r = v1 ^ v2;
13672             break;
13673         default:
13674             abort();
13675         }
13676         sp[-2] = JS_NewInt32(ctx, r);
13677     }
13678     return 0;
13679  exception:
13680     sp[-2] = JS_UNDEFINED;
13681     sp[-1] = JS_UNDEFINED;
13682     return -1;
13683 }
13684 
13685 /* Note: also used for bigint */
js_compare_bigfloat(JSContext * ctx,OPCodeEnum op,JSValue op1,JSValue op2)13686 static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op,
13687                                JSValue op1, JSValue op2)
13688 {
13689     bf_t a_s, b_s, *a, *b;
13690     int res;
13691 
13692     a = JS_ToBigFloat(ctx, &a_s, op1);
13693     if (!a) {
13694         JS_FreeValue(ctx, op2);
13695         return -1;
13696     }
13697     b = JS_ToBigFloat(ctx, &b_s, op2);
13698     if (!b) {
13699         if (a == &a_s)
13700             bf_delete(a);
13701         JS_FreeValue(ctx, op1);
13702         return -1;
13703     }
13704     switch(op) {
13705     case OP_lt:
13706         res = bf_cmp_lt(a, b); /* if NaN return false */
13707         break;
13708     case OP_lte:
13709         res = bf_cmp_le(a, b); /* if NaN return false */
13710         break;
13711     case OP_gt:
13712         res = bf_cmp_lt(b, a); /* if NaN return false */
13713         break;
13714     case OP_gte:
13715         res = bf_cmp_le(b, a); /* if NaN return false */
13716         break;
13717     case OP_eq:
13718         res = bf_cmp_eq(a, b); /* if NaN return false */
13719         break;
13720     default:
13721         abort();
13722     }
13723     if (a == &a_s)
13724         bf_delete(a);
13725     if (b == &b_s)
13726         bf_delete(b);
13727     JS_FreeValue(ctx, op1);
13728     JS_FreeValue(ctx, op2);
13729     return res;
13730 }
13731 
js_compare_bigdecimal(JSContext * ctx,OPCodeEnum op,JSValue op1,JSValue op2)13732 static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op,
13733                                  JSValue op1, JSValue op2)
13734 {
13735     bfdec_t *a, *b;
13736     int res;
13737 
13738     /* Note: binary floats are converted to bigdecimal with
13739        toString(). It is not mathematically correct but is consistent
13740        with the BigDecimal() constructor behavior */
13741     op1 = JS_ToBigDecimalFree(ctx, op1, TRUE);
13742     if (JS_IsException(op1)) {
13743         JS_FreeValue(ctx, op2);
13744         return -1;
13745     }
13746     op2 = JS_ToBigDecimalFree(ctx, op2, TRUE);
13747     if (JS_IsException(op2)) {
13748         JS_FreeValue(ctx, op1);
13749         return -1;
13750     }
13751     a = JS_ToBigDecimal(ctx, op1);
13752     b = JS_ToBigDecimal(ctx, op2);
13753 
13754     switch(op) {
13755     case OP_lt:
13756         res = bfdec_cmp_lt(a, b); /* if NaN return false */
13757         break;
13758     case OP_lte:
13759         res = bfdec_cmp_le(a, b); /* if NaN return false */
13760         break;
13761     case OP_gt:
13762         res = bfdec_cmp_lt(b, a); /* if NaN return false */
13763         break;
13764     case OP_gte:
13765         res = bfdec_cmp_le(b, a); /* if NaN return false */
13766         break;
13767     case OP_eq:
13768         res = bfdec_cmp_eq(a, b); /* if NaN return false */
13769         break;
13770     default:
13771         abort();
13772     }
13773     JS_FreeValue(ctx, op1);
13774     JS_FreeValue(ctx, op2);
13775     return res;
13776 }
13777 
js_relational_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)13778 static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
13779                                         OPCodeEnum op)
13780 {
13781     JSValue op1, op2, ret;
13782     int res;
13783     uint32_t tag1, tag2;
13784 
13785     op1 = sp[-2];
13786     op2 = sp[-1];
13787     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13788     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13789     /* try to call an overloaded operator */
13790     if ((tag1 == JS_TAG_OBJECT &&
13791          (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
13792         (tag2 == JS_TAG_OBJECT &&
13793          (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
13794         res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op,
13795                                          FALSE, HINT_NUMBER);
13796         if (res != 0) {
13797             JS_FreeValue(ctx, op1);
13798             JS_FreeValue(ctx, op2);
13799             if (res < 0) {
13800                 goto exception;
13801             } else {
13802                 sp[-2] = ret;
13803                 return 0;
13804             }
13805         }
13806     }
13807     op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
13808     if (JS_IsException(op1)) {
13809         JS_FreeValue(ctx, op2);
13810         goto exception;
13811     }
13812     op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);
13813     if (JS_IsException(op2)) {
13814         JS_FreeValue(ctx, op1);
13815         goto exception;
13816     }
13817     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13818     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13819 
13820     if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {
13821         JSString *p1, *p2;
13822         p1 = JS_VALUE_GET_STRING(op1);
13823         p2 = JS_VALUE_GET_STRING(op2);
13824         res = js_string_compare(ctx, p1, p2);
13825         switch(op) {
13826         case OP_lt:
13827             res = (res < 0);
13828             break;
13829         case OP_lte:
13830             res = (res <= 0);
13831             break;
13832         case OP_gt:
13833             res = (res > 0);
13834             break;
13835         default:
13836         case OP_gte:
13837             res = (res >= 0);
13838             break;
13839         }
13840         JS_FreeValue(ctx, op1);
13841         JS_FreeValue(ctx, op2);
13842     } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) &&
13843                (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) {
13844         /* fast path for float64/int */
13845         goto float64_compare;
13846     } else {
13847         if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) ||
13848              (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&
13849             !is_math_mode(ctx)) {
13850             if (tag1 == JS_TAG_STRING) {
13851                 op1 = JS_StringToBigInt(ctx, op1);
13852                 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
13853                     goto invalid_bigint_string;
13854             }
13855             if (tag2 == JS_TAG_STRING) {
13856                 op2 = JS_StringToBigInt(ctx, op2);
13857                 if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
13858                 invalid_bigint_string:
13859                     JS_FreeValue(ctx, op1);
13860                     JS_FreeValue(ctx, op2);
13861                     res = FALSE;
13862                     goto done;
13863                 }
13864             }
13865         } else {
13866             op1 = JS_ToNumericFree(ctx, op1);
13867             if (JS_IsException(op1)) {
13868                 JS_FreeValue(ctx, op2);
13869                 goto exception;
13870             }
13871             op2 = JS_ToNumericFree(ctx, op2);
13872             if (JS_IsException(op2)) {
13873                 JS_FreeValue(ctx, op1);
13874                 goto exception;
13875             }
13876         }
13877 
13878         tag1 = JS_VALUE_GET_NORM_TAG(op1);
13879         tag2 = JS_VALUE_GET_NORM_TAG(op2);
13880 
13881         if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
13882             res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2);
13883             if (res < 0)
13884                 goto exception;
13885         } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
13886             res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2);
13887             if (res < 0)
13888                 goto exception;
13889         } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
13890             res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2);
13891             if (res < 0)
13892                 goto exception;
13893         } else {
13894             double d1, d2;
13895 
13896         float64_compare:
13897             /* can use floating point comparison */
13898             if (tag1 == JS_TAG_FLOAT64) {
13899                 d1 = JS_VALUE_GET_FLOAT64(op1);
13900             } else {
13901                 d1 = JS_VALUE_GET_INT(op1);
13902             }
13903             if (tag2 == JS_TAG_FLOAT64) {
13904                 d2 = JS_VALUE_GET_FLOAT64(op2);
13905             } else {
13906                 d2 = JS_VALUE_GET_INT(op2);
13907             }
13908             switch(op) {
13909             case OP_lt:
13910                 res = (d1 < d2); /* if NaN return false */
13911                 break;
13912             case OP_lte:
13913                 res = (d1 <= d2); /* if NaN return false */
13914                 break;
13915             case OP_gt:
13916                 res = (d1 > d2); /* if NaN return false */
13917                 break;
13918             default:
13919             case OP_gte:
13920                 res = (d1 >= d2); /* if NaN return false */
13921                 break;
13922             }
13923         }
13924     }
13925  done:
13926     sp[-2] = JS_NewBool(ctx, res);
13927     return 0;
13928  exception:
13929     sp[-2] = JS_UNDEFINED;
13930     sp[-1] = JS_UNDEFINED;
13931     return -1;
13932 }
13933 
tag_is_number(uint32_t tag)13934 static BOOL tag_is_number(uint32_t tag)
13935 {
13936     return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT ||
13937             tag == JS_TAG_FLOAT64 || tag == JS_TAG_BIG_FLOAT ||
13938             tag == JS_TAG_BIG_DECIMAL);
13939 }
13940 
js_eq_slow(JSContext * ctx,JSValue * sp,BOOL is_neq)13941 static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
13942                                             BOOL is_neq)
13943 {
13944     JSValue op1, op2, ret;
13945     int res;
13946     uint32_t tag1, tag2;
13947 
13948     op1 = sp[-2];
13949     op2 = sp[-1];
13950  redo:
13951     tag1 = JS_VALUE_GET_NORM_TAG(op1);
13952     tag2 = JS_VALUE_GET_NORM_TAG(op2);
13953     if (tag_is_number(tag1) && tag_is_number(tag2)) {
13954         if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
13955             res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
13956         } else if ((tag1 == JS_TAG_FLOAT64 &&
13957                     (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) ||
13958                    (tag2 == JS_TAG_FLOAT64 &&
13959                     (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) {
13960             double d1, d2;
13961             if (tag1 == JS_TAG_FLOAT64) {
13962                 d1 = JS_VALUE_GET_FLOAT64(op1);
13963             } else {
13964                 d1 = JS_VALUE_GET_INT(op1);
13965             }
13966             if (tag2 == JS_TAG_FLOAT64) {
13967                 d2 = JS_VALUE_GET_FLOAT64(op2);
13968             } else {
13969                 d2 = JS_VALUE_GET_INT(op2);
13970             }
13971             res = (d1 == d2);
13972         } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
13973             res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2);
13974             if (res < 0)
13975                 goto exception;
13976         } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
13977             res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2);
13978             if (res < 0)
13979                 goto exception;
13980         } else {
13981             res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2);
13982             if (res < 0)
13983                 goto exception;
13984         }
13985     } else if (tag1 == tag2) {
13986         if (tag1 == JS_TAG_OBJECT) {
13987             /* try the fallback operator */
13988             res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
13989                                              is_neq ? OP_neq : OP_eq,
13990                                              FALSE, HINT_NONE);
13991             if (res != 0) {
13992                 JS_FreeValue(ctx, op1);
13993                 JS_FreeValue(ctx, op2);
13994                 if (res < 0) {
13995                     goto exception;
13996                 } else {
13997                     sp[-2] = ret;
13998                     return 0;
13999                 }
14000             }
14001         }
14002         res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
14003     } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
14004                (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
14005         res = TRUE;
14006     } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) ||
14007                (tag2 == JS_TAG_STRING && tag_is_number(tag1))) {
14008 
14009         if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) &&
14010             !is_math_mode(ctx)) {
14011             if (tag1 == JS_TAG_STRING) {
14012                 op1 = JS_StringToBigInt(ctx, op1);
14013                 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
14014                     goto invalid_bigint_string;
14015             }
14016             if (tag2 == JS_TAG_STRING) {
14017                 op2 = JS_StringToBigInt(ctx, op2);
14018                 if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
14019                 invalid_bigint_string:
14020                     JS_FreeValue(ctx, op1);
14021                     JS_FreeValue(ctx, op2);
14022                     res = FALSE;
14023                     goto done;
14024                 }
14025             }
14026         } else {
14027             op1 = JS_ToNumericFree(ctx, op1);
14028             if (JS_IsException(op1)) {
14029                 JS_FreeValue(ctx, op2);
14030                 goto exception;
14031             }
14032             op2 = JS_ToNumericFree(ctx, op2);
14033             if (JS_IsException(op2)) {
14034                 JS_FreeValue(ctx, op1);
14035                 goto exception;
14036             }
14037         }
14038         res = js_strict_eq(ctx, op1, op2);
14039     } else if (tag1 == JS_TAG_BOOL) {
14040         op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
14041         goto redo;
14042     } else if (tag2 == JS_TAG_BOOL) {
14043         op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
14044         goto redo;
14045     } else if ((tag1 == JS_TAG_OBJECT &&
14046                 (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) ||
14047                (tag2 == JS_TAG_OBJECT &&
14048                 (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) {
14049 
14050         /* try the fallback operator */
14051         res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
14052                                          is_neq ? OP_neq : OP_eq,
14053                                          FALSE, HINT_NONE);
14054         if (res != 0) {
14055             JS_FreeValue(ctx, op1);
14056             JS_FreeValue(ctx, op2);
14057             if (res < 0) {
14058                 goto exception;
14059             } else {
14060                 sp[-2] = ret;
14061                 return 0;
14062             }
14063         }
14064 
14065         op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
14066         if (JS_IsException(op1)) {
14067             JS_FreeValue(ctx, op2);
14068             goto exception;
14069         }
14070         op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
14071         if (JS_IsException(op2)) {
14072             JS_FreeValue(ctx, op1);
14073             goto exception;
14074         }
14075         goto redo;
14076     } else {
14077         /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
14078         if ((JS_IsHTMLDDA(ctx, op1) &&
14079              (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
14080             (JS_IsHTMLDDA(ctx, op2) &&
14081              (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
14082             res = TRUE;
14083         } else {
14084             res = FALSE;
14085         }
14086         JS_FreeValue(ctx, op1);
14087         JS_FreeValue(ctx, op2);
14088     }
14089  done:
14090     sp[-2] = JS_NewBool(ctx, res ^ is_neq);
14091     return 0;
14092  exception:
14093     sp[-2] = JS_UNDEFINED;
14094     sp[-1] = JS_UNDEFINED;
14095     return -1;
14096 }
14097 
js_shr_slow(JSContext * ctx,JSValue * sp)14098 static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
14099 {
14100     JSValue op1, op2;
14101     uint32_t v1, v2, r;
14102 
14103     op1 = sp[-2];
14104     op2 = sp[-1];
14105     op1 = JS_ToNumericFree(ctx, op1);
14106     if (JS_IsException(op1)) {
14107         JS_FreeValue(ctx, op2);
14108         goto exception;
14109     }
14110     op2 = JS_ToNumericFree(ctx, op2);
14111     if (JS_IsException(op2)) {
14112         JS_FreeValue(ctx, op1);
14113         goto exception;
14114     }
14115     /* XXX: could forbid >>> in bignum mode */
14116     if (!is_math_mode(ctx) &&
14117         (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
14118          JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) {
14119         JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");
14120         JS_FreeValue(ctx, op1);
14121         JS_FreeValue(ctx, op2);
14122         goto exception;
14123     }
14124     /* cannot give an exception */
14125     JS_ToUint32Free(ctx, &v1, op1);
14126     JS_ToUint32Free(ctx, &v2, op2);
14127     r = v1 >> (v2 & 0x1f);
14128     sp[-2] = JS_NewUint32(ctx, r);
14129     return 0;
14130  exception:
14131     sp[-2] = JS_UNDEFINED;
14132     sp[-1] = JS_UNDEFINED;
14133     return -1;
14134 }
14135 
js_mul_pow10_to_float64(JSContext * ctx,const bf_t * a,int64_t exponent)14136 static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
14137                                        int64_t exponent)
14138 {
14139     bf_t r_s, *r = &r_s;
14140     double d;
14141     int ret;
14142 
14143     /* always convert to Float64 */
14144     bf_init(ctx->bf_ctx, r);
14145     ret = bf_mul_pow_radix(r, a, 10, exponent,
14146                            53, bf_set_exp_bits(11) | BF_RNDN |
14147                            BF_FLAG_SUBNORMAL);
14148     bf_get_float64(r, &d, BF_RNDN);
14149     bf_delete(r);
14150     if (ret & BF_ST_MEM_ERROR)
14151         return JS_ThrowOutOfMemory(ctx);
14152     else
14153         return __JS_NewFloat64(ctx, d);
14154 }
14155 
js_mul_pow10(JSContext * ctx,JSValue * sp)14156 static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp)
14157 {
14158     bf_t a_s, *a, *r;
14159     JSValue op1, op2, res;
14160     int64_t e;
14161     int ret;
14162 
14163     res = JS_NewBigFloat(ctx);
14164     if (JS_IsException(res))
14165         return -1;
14166     r = JS_GetBigFloat(res);
14167     op1 = sp[-2];
14168     op2 = sp[-1];
14169     a = JS_ToBigFloat(ctx, &a_s, op1);
14170     if (!a)
14171         return -1;
14172     if (JS_IsBigInt(ctx, op2)) {
14173         ret = JS_ToBigInt64(ctx, &e, op2);
14174     } else {
14175         ret = JS_ToInt64(ctx, &e, op2);
14176     }
14177     if (ret) {
14178         if (a == &a_s)
14179             bf_delete(a);
14180         JS_FreeValue(ctx, res);
14181         return -1;
14182     }
14183 
14184     bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags);
14185     if (a == &a_s)
14186         bf_delete(a);
14187     JS_FreeValue(ctx, op1);
14188     JS_FreeValue(ctx, op2);
14189     sp[-2] = res;
14190     return 0;
14191 }
14192 
14193 #else /* !CONFIG_BIGNUM */
14194 
JS_ThrowUnsupportedBigint(JSContext * ctx)14195 static JSValue JS_ThrowUnsupportedBigint(JSContext *ctx)
14196 {
14197     return JS_ThrowTypeError(ctx, "bigint is not supported");
14198 }
14199 
JS_NewBigInt64(JSContext * ctx,int64_t v)14200 JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
14201 {
14202     return JS_ThrowUnsupportedBigint(ctx);
14203 }
14204 
JS_NewBigUint64(JSContext * ctx,uint64_t v)14205 JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
14206 {
14207     return JS_ThrowUnsupportedBigint(ctx);
14208 }
14209 
JS_ToBigInt64(JSContext * ctx,int64_t * pres,JSValueConst val)14210 int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
14211 {
14212     JS_ThrowUnsupportedBigint(ctx);
14213     *pres = 0;
14214     return -1;
14215 }
14216 
js_unary_arith_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)14217 static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
14218                                                      JSValue *sp,
14219                                                      OPCodeEnum op)
14220 {
14221     JSValue op1;
14222     double d;
14223 
14224     op1 = sp[-1];
14225     if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {
14226         sp[-1] = JS_UNDEFINED;
14227         return -1;
14228     }
14229     switch(op) {
14230     case OP_inc:
14231         d++;
14232         break;
14233     case OP_dec:
14234         d--;
14235         break;
14236     case OP_plus:
14237         break;
14238     case OP_neg:
14239         d = -d;
14240         break;
14241     default:
14242         abort();
14243     }
14244     sp[-1] = JS_NewFloat64(ctx, d);
14245     return 0;
14246 }
14247 
14248 /* specific case necessary for correct return value semantics */
js_post_inc_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)14249 static __exception int js_post_inc_slow(JSContext *ctx,
14250                                         JSValue *sp, OPCodeEnum op)
14251 {
14252     JSValue op1;
14253     double d, r;
14254 
14255     op1 = sp[-1];
14256     if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {
14257         sp[-1] = JS_UNDEFINED;
14258         return -1;
14259     }
14260     r = d + 2 * (op - OP_post_dec) - 1;
14261     sp[0] = JS_NewFloat64(ctx, r);
14262     sp[-1] = JS_NewFloat64(ctx, d);
14263     return 0;
14264 }
14265 
js_binary_arith_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)14266 static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
14267                                                       OPCodeEnum op)
14268 {
14269     JSValue op1, op2;
14270     double d1, d2, r;
14271 
14272     op1 = sp[-2];
14273     op2 = sp[-1];
14274     if (unlikely(JS_ToFloat64Free(ctx, &d1, op1))) {
14275         JS_FreeValue(ctx, op2);
14276         goto exception;
14277     }
14278     if (unlikely(JS_ToFloat64Free(ctx, &d2, op2))) {
14279         goto exception;
14280     }
14281     switch(op) {
14282     case OP_sub:
14283         r = d1 - d2;
14284         break;
14285     case OP_mul:
14286         r = d1 * d2;
14287         break;
14288     case OP_div:
14289         r = d1 / d2;
14290         break;
14291     case OP_mod:
14292         r = fmod(d1, d2);
14293         break;
14294     case OP_pow:
14295         r = js_pow(d1, d2);
14296         break;
14297     default:
14298         abort();
14299     }
14300     sp[-2] = JS_NewFloat64(ctx, r);
14301     return 0;
14302  exception:
14303     sp[-2] = JS_UNDEFINED;
14304     sp[-1] = JS_UNDEFINED;
14305     return -1;
14306 }
14307 
js_add_slow(JSContext * ctx,JSValue * sp)14308 static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
14309 {
14310     JSValue op1, op2;
14311     uint32_t tag1, tag2;
14312 
14313     op1 = sp[-2];
14314     op2 = sp[-1];
14315     tag1 = JS_VALUE_GET_TAG(op1);
14316     tag2 = JS_VALUE_GET_TAG(op2);
14317     if ((tag1 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag1)) &&
14318         (tag2 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag2))) {
14319         goto add_numbers;
14320     } else {
14321         op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
14322         if (JS_IsException(op1)) {
14323             JS_FreeValue(ctx, op2);
14324             goto exception;
14325         }
14326         op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
14327         if (JS_IsException(op2)) {
14328             JS_FreeValue(ctx, op1);
14329             goto exception;
14330         }
14331         tag1 = JS_VALUE_GET_TAG(op1);
14332         tag2 = JS_VALUE_GET_TAG(op2);
14333         if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {
14334             sp[-2] = JS_ConcatString(ctx, op1, op2);
14335             if (JS_IsException(sp[-2]))
14336                 goto exception;
14337         } else {
14338             double d1, d2;
14339         add_numbers:
14340             if (JS_ToFloat64Free(ctx, &d1, op1)) {
14341                 JS_FreeValue(ctx, op2);
14342                 goto exception;
14343             }
14344             if (JS_ToFloat64Free(ctx, &d2, op2))
14345                 goto exception;
14346             sp[-2] = JS_NewFloat64(ctx, d1 + d2);
14347         }
14348     }
14349     return 0;
14350  exception:
14351     sp[-2] = JS_UNDEFINED;
14352     sp[-1] = JS_UNDEFINED;
14353     return -1;
14354 }
14355 
js_binary_logic_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)14356 static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
14357                                                       JSValue *sp,
14358                                                       OPCodeEnum op)
14359 {
14360     JSValue op1, op2;
14361     uint32_t v1, v2, r;
14362 
14363     op1 = sp[-2];
14364     op2 = sp[-1];
14365     if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
14366         JS_FreeValue(ctx, op2);
14367         goto exception;
14368     }
14369     if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
14370         goto exception;
14371     switch(op) {
14372     case OP_shl:
14373         r = v1 << (v2 & 0x1f);
14374         break;
14375     case OP_sar:
14376         r = (int)v1 >> (v2 & 0x1f);
14377         break;
14378     case OP_and:
14379         r = v1 & v2;
14380         break;
14381     case OP_or:
14382         r = v1 | v2;
14383         break;
14384     case OP_xor:
14385         r = v1 ^ v2;
14386         break;
14387     default:
14388         abort();
14389     }
14390     sp[-2] = JS_NewInt32(ctx, r);
14391     return 0;
14392  exception:
14393     sp[-2] = JS_UNDEFINED;
14394     sp[-1] = JS_UNDEFINED;
14395     return -1;
14396 }
14397 
js_not_slow(JSContext * ctx,JSValue * sp)14398 static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
14399 {
14400     int32_t v1;
14401 
14402     if (unlikely(JS_ToInt32Free(ctx, &v1, sp[-1]))) {
14403         sp[-1] = JS_UNDEFINED;
14404         return -1;
14405     }
14406     sp[-1] = JS_NewInt32(ctx, ~v1);
14407     return 0;
14408 }
14409 
js_relational_slow(JSContext * ctx,JSValue * sp,OPCodeEnum op)14410 static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
14411                                         OPCodeEnum op)
14412 {
14413     JSValue op1, op2;
14414     int res;
14415 
14416     op1 = sp[-2];
14417     op2 = sp[-1];
14418     op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
14419     if (JS_IsException(op1)) {
14420         JS_FreeValue(ctx, op2);
14421         goto exception;
14422     }
14423     op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);
14424     if (JS_IsException(op2)) {
14425         JS_FreeValue(ctx, op1);
14426         goto exception;
14427     }
14428     if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING &&
14429         JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
14430         JSString *p1, *p2;
14431         p1 = JS_VALUE_GET_STRING(op1);
14432         p2 = JS_VALUE_GET_STRING(op2);
14433         res = js_string_compare(ctx, p1, p2);
14434         JS_FreeValue(ctx, op1);
14435         JS_FreeValue(ctx, op2);
14436         switch(op) {
14437         case OP_lt:
14438             res = (res < 0);
14439             break;
14440         case OP_lte:
14441             res = (res <= 0);
14442             break;
14443         case OP_gt:
14444             res = (res > 0);
14445             break;
14446         default:
14447         case OP_gte:
14448             res = (res >= 0);
14449             break;
14450         }
14451     } else {
14452         double d1, d2;
14453         if (JS_ToFloat64Free(ctx, &d1, op1)) {
14454             JS_FreeValue(ctx, op2);
14455             goto exception;
14456         }
14457         if (JS_ToFloat64Free(ctx, &d2, op2))
14458             goto exception;
14459         switch(op) {
14460         case OP_lt:
14461             res = (d1 < d2); /* if NaN return false */
14462             break;
14463         case OP_lte:
14464             res = (d1 <= d2); /* if NaN return false */
14465             break;
14466         case OP_gt:
14467             res = (d1 > d2); /* if NaN return false */
14468             break;
14469         default:
14470         case OP_gte:
14471             res = (d1 >= d2); /* if NaN return false */
14472             break;
14473         }
14474     }
14475     sp[-2] = JS_NewBool(ctx, res);
14476     return 0;
14477  exception:
14478     sp[-2] = JS_UNDEFINED;
14479     sp[-1] = JS_UNDEFINED;
14480     return -1;
14481 }
14482 
js_eq_slow(JSContext * ctx,JSValue * sp,BOOL is_neq)14483 static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
14484                                             BOOL is_neq)
14485 {
14486     JSValue op1, op2;
14487     int tag1, tag2;
14488     BOOL res;
14489 
14490     op1 = sp[-2];
14491     op2 = sp[-1];
14492  redo:
14493     tag1 = JS_VALUE_GET_NORM_TAG(op1);
14494     tag2 = JS_VALUE_GET_NORM_TAG(op2);
14495     if (tag1 == tag2 ||
14496         (tag1 == JS_TAG_INT && tag2 == JS_TAG_FLOAT64) ||
14497         (tag2 == JS_TAG_INT && tag1 == JS_TAG_FLOAT64)) {
14498         res = js_strict_eq(ctx, op1, op2);
14499     } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
14500                (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
14501         res = TRUE;
14502     } else if ((tag1 == JS_TAG_STRING && (tag2 == JS_TAG_INT ||
14503                                    tag2 == JS_TAG_FLOAT64)) ||
14504         (tag2 == JS_TAG_STRING && (tag1 == JS_TAG_INT ||
14505                                    tag1 == JS_TAG_FLOAT64))) {
14506         double d1;
14507         double d2;
14508         if (JS_ToFloat64Free(ctx, &d1, op1)) {
14509             JS_FreeValue(ctx, op2);
14510             goto exception;
14511         }
14512         if (JS_ToFloat64Free(ctx, &d2, op2))
14513             goto exception;
14514         res = (d1 == d2);
14515     } else if (tag1 == JS_TAG_BOOL) {
14516         op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
14517         goto redo;
14518     } else if (tag2 == JS_TAG_BOOL) {
14519         op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
14520         goto redo;
14521     } else if (tag1 == JS_TAG_OBJECT &&
14522                (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64 || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) {
14523         op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
14524         if (JS_IsException(op1)) {
14525             JS_FreeValue(ctx, op2);
14526             goto exception;
14527         }
14528         goto redo;
14529     } else if (tag2 == JS_TAG_OBJECT &&
14530                (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64 || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL)) {
14531         op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
14532         if (JS_IsException(op2)) {
14533             JS_FreeValue(ctx, op1);
14534             goto exception;
14535         }
14536         goto redo;
14537     } else {
14538         /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
14539         if ((JS_IsHTMLDDA(ctx, op1) &&
14540              (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
14541             (JS_IsHTMLDDA(ctx, op2) &&
14542              (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
14543             res = TRUE;
14544         } else {
14545             res = FALSE;
14546         }
14547         JS_FreeValue(ctx, op1);
14548         JS_FreeValue(ctx, op2);
14549     }
14550     sp[-2] = JS_NewBool(ctx, res ^ is_neq);
14551     return 0;
14552  exception:
14553     sp[-2] = JS_UNDEFINED;
14554     sp[-1] = JS_UNDEFINED;
14555     return -1;
14556 }
14557 
js_shr_slow(JSContext * ctx,JSValue * sp)14558 static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
14559 {
14560     JSValue op1, op2;
14561     uint32_t v1, v2, r;
14562 
14563     op1 = sp[-2];
14564     op2 = sp[-1];
14565     if (unlikely(JS_ToUint32Free(ctx, &v1, op1))) {
14566         JS_FreeValue(ctx, op2);
14567         goto exception;
14568     }
14569     if (unlikely(JS_ToUint32Free(ctx, &v2, op2)))
14570         goto exception;
14571     r = v1 >> (v2 & 0x1f);
14572     sp[-2] = JS_NewUint32(ctx, r);
14573     return 0;
14574  exception:
14575     sp[-2] = JS_UNDEFINED;
14576     sp[-1] = JS_UNDEFINED;
14577     return -1;
14578 }
14579 
14580 #endif /* !CONFIG_BIGNUM */
14581 
14582 /* XXX: Should take JSValueConst arguments */
js_strict_eq2(JSContext * ctx,JSValue op1,JSValue op2,JSStrictEqModeEnum eq_mode)14583 static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
14584                           JSStrictEqModeEnum eq_mode)
14585 {
14586     BOOL res;
14587     int tag1, tag2;
14588     double d1, d2;
14589 
14590     tag1 = JS_VALUE_GET_NORM_TAG(op1);
14591     tag2 = JS_VALUE_GET_NORM_TAG(op2);
14592     switch(tag1) {
14593     case JS_TAG_BOOL:
14594         if (tag1 != tag2) {
14595             res = FALSE;
14596         } else {
14597             res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
14598             goto done_no_free;
14599         }
14600         break;
14601     case JS_TAG_NULL:
14602     case JS_TAG_UNDEFINED:
14603         res = (tag1 == tag2);
14604         break;
14605     case JS_TAG_STRING:
14606         {
14607             JSString *p1, *p2;
14608             if (tag1 != tag2) {
14609                 res = FALSE;
14610             } else {
14611                 p1 = JS_VALUE_GET_STRING(op1);
14612                 p2 = JS_VALUE_GET_STRING(op2);
14613                 res = (js_string_compare(ctx, p1, p2) == 0);
14614             }
14615         }
14616         break;
14617     case JS_TAG_SYMBOL:
14618         {
14619             JSAtomStruct *p1, *p2;
14620             if (tag1 != tag2) {
14621                 res = FALSE;
14622             } else {
14623                 p1 = JS_VALUE_GET_PTR(op1);
14624                 p2 = JS_VALUE_GET_PTR(op2);
14625                 res = (p1 == p2);
14626             }
14627         }
14628         break;
14629     case JS_TAG_OBJECT:
14630         if (tag1 != tag2)
14631             res = FALSE;
14632         else
14633             res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2);
14634         break;
14635     case JS_TAG_INT:
14636         d1 = JS_VALUE_GET_INT(op1);
14637         if (tag2 == JS_TAG_INT) {
14638             d2 = JS_VALUE_GET_INT(op2);
14639             goto number_test;
14640         } else if (tag2 == JS_TAG_FLOAT64) {
14641             d2 = JS_VALUE_GET_FLOAT64(op2);
14642             goto number_test;
14643         } else {
14644             res = FALSE;
14645         }
14646         break;
14647     case JS_TAG_FLOAT64:
14648         d1 = JS_VALUE_GET_FLOAT64(op1);
14649         if (tag2 == JS_TAG_FLOAT64) {
14650             d2 = JS_VALUE_GET_FLOAT64(op2);
14651         } else if (tag2 == JS_TAG_INT) {
14652             d2 = JS_VALUE_GET_INT(op2);
14653         } else {
14654             res = FALSE;
14655             break;
14656         }
14657     number_test:
14658         if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
14659             JSFloat64Union u1, u2;
14660             /* NaN is not always normalized, so this test is necessary */
14661             if (isnan(d1) || isnan(d2)) {
14662                 res = isnan(d1) == isnan(d2);
14663             } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) {
14664                 res = (d1 == d2); /* +0 == -0 */
14665             } else {
14666                 u1.d = d1;
14667                 u2.d = d2;
14668                 res = (u1.u64 == u2.u64); /* +0 != -0 */
14669             }
14670         } else {
14671             res = (d1 == d2); /* if NaN return false and +0 == -0 */
14672         }
14673         goto done_no_free;
14674 #ifdef CONFIG_BIGNUM
14675     case JS_TAG_BIG_INT:
14676         {
14677             bf_t a_s, *a, b_s, *b;
14678             if (tag1 != tag2) {
14679                 res = FALSE;
14680                 break;
14681             }
14682             a = JS_ToBigFloat(ctx, &a_s, op1);
14683             b = JS_ToBigFloat(ctx, &b_s, op2);
14684             res = bf_cmp_eq(a, b);
14685             if (a == &a_s)
14686                 bf_delete(a);
14687             if (b == &b_s)
14688                 bf_delete(b);
14689         }
14690         break;
14691     case JS_TAG_BIG_FLOAT:
14692         {
14693             JSBigFloat *p1, *p2;
14694             const bf_t *a, *b;
14695             if (tag1 != tag2) {
14696                 res = FALSE;
14697                 break;
14698             }
14699             p1 = JS_VALUE_GET_PTR(op1);
14700             p2 = JS_VALUE_GET_PTR(op2);
14701             a = &p1->num;
14702             b = &p2->num;
14703             if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
14704                 if (eq_mode == JS_EQ_SAME_VALUE_ZERO &&
14705                            a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) {
14706                     res = TRUE;
14707                 } else {
14708                     res = (bf_cmp_full(a, b) == 0);
14709                 }
14710             } else {
14711                 res = bf_cmp_eq(a, b);
14712             }
14713         }
14714         break;
14715     case JS_TAG_BIG_DECIMAL:
14716         {
14717             JSBigDecimal *p1, *p2;
14718             const bfdec_t *a, *b;
14719             if (tag1 != tag2) {
14720                 res = FALSE;
14721                 break;
14722             }
14723             p1 = JS_VALUE_GET_PTR(op1);
14724             p2 = JS_VALUE_GET_PTR(op2);
14725             a = &p1->num;
14726             b = &p2->num;
14727             res = bfdec_cmp_eq(a, b);
14728         }
14729         break;
14730 #endif
14731     default:
14732         res = FALSE;
14733         break;
14734     }
14735     JS_FreeValue(ctx, op1);
14736     JS_FreeValue(ctx, op2);
14737  done_no_free:
14738     return res;
14739 }
14740 
js_strict_eq(JSContext * ctx,JSValue op1,JSValue op2)14741 static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2)
14742 {
14743     return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
14744 }
14745 
js_same_value(JSContext * ctx,JSValueConst op1,JSValueConst op2)14746 static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2)
14747 {
14748     return js_strict_eq2(ctx,
14749                          JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
14750                          JS_EQ_SAME_VALUE);
14751 }
14752 
js_same_value_zero(JSContext * ctx,JSValueConst op1,JSValueConst op2)14753 static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2)
14754 {
14755     return js_strict_eq2(ctx,
14756                          JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
14757                          JS_EQ_SAME_VALUE_ZERO);
14758 }
14759 
js_strict_eq_slow(JSContext * ctx,JSValue * sp,BOOL is_neq)14760 static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp,
14761                                        BOOL is_neq)
14762 {
14763     BOOL res;
14764     res = js_strict_eq(ctx, sp[-2], sp[-1]);
14765     sp[-2] = JS_NewBool(ctx, res ^ is_neq);
14766     return 0;
14767 }
14768 
js_operator_in(JSContext * ctx,JSValue * sp)14769 static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
14770 {
14771     JSValue op1, op2;
14772     JSAtom atom;
14773     int ret;
14774 
14775     op1 = sp[-2];
14776     op2 = sp[-1];
14777 
14778     if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) {
14779         JS_ThrowTypeError(ctx, "invalid 'in' operand");
14780         return -1;
14781     }
14782     atom = JS_ValueToAtom(ctx, op1);
14783     if (unlikely(atom == JS_ATOM_NULL))
14784         return -1;
14785     ret = JS_HasProperty(ctx, op2, atom);
14786     JS_FreeAtom(ctx, atom);
14787     if (ret < 0)
14788         return -1;
14789     JS_FreeValue(ctx, op1);
14790     JS_FreeValue(ctx, op2);
14791     sp[-2] = JS_NewBool(ctx, ret);
14792     return 0;
14793 }
14794 
js_has_unscopable(JSContext * ctx,JSValueConst obj,JSAtom atom)14795 static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,
14796                                          JSAtom atom)
14797 {
14798     JSValue arr, val;
14799     int ret;
14800 
14801     arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables);
14802     if (JS_IsException(arr))
14803         return -1;
14804     ret = 0;
14805     if (JS_IsObject(arr)) {
14806         val = JS_GetProperty(ctx, arr, atom);
14807         ret = JS_ToBoolFree(ctx, val);
14808     }
14809     JS_FreeValue(ctx, arr);
14810     return ret;
14811 }
14812 
js_operator_instanceof(JSContext * ctx,JSValue * sp)14813 static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp)
14814 {
14815     JSValue op1, op2;
14816     BOOL ret;
14817 
14818     op1 = sp[-2];
14819     op2 = sp[-1];
14820     ret = JS_IsInstanceOf(ctx, op1, op2);
14821     if (ret < 0)
14822         return ret;
14823     JS_FreeValue(ctx, op1);
14824     JS_FreeValue(ctx, op2);
14825     sp[-2] = JS_NewBool(ctx, ret);
14826     return 0;
14827 }
14828 
js_operator_typeof(JSContext * ctx,JSValueConst op1)14829 static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1)
14830 {
14831     JSAtom atom;
14832     uint32_t tag;
14833 
14834     tag = JS_VALUE_GET_NORM_TAG(op1);
14835     switch(tag) {
14836 #ifdef CONFIG_BIGNUM
14837     case JS_TAG_BIG_INT:
14838         atom = JS_ATOM_bigint;
14839         break;
14840     case JS_TAG_BIG_FLOAT:
14841         atom = JS_ATOM_bigfloat;
14842         break;
14843     case JS_TAG_BIG_DECIMAL:
14844         atom = JS_ATOM_bigdecimal;
14845         break;
14846 #endif
14847     case JS_TAG_INT:
14848     case JS_TAG_FLOAT64:
14849         atom = JS_ATOM_number;
14850         break;
14851     case JS_TAG_UNDEFINED:
14852         atom = JS_ATOM_undefined;
14853         break;
14854     case JS_TAG_BOOL:
14855         atom = JS_ATOM_boolean;
14856         break;
14857     case JS_TAG_STRING:
14858         atom = JS_ATOM_string;
14859         break;
14860     case JS_TAG_OBJECT:
14861         {
14862             JSObject *p;
14863             p = JS_VALUE_GET_OBJ(op1);
14864             if (unlikely(p->is_HTMLDDA))
14865                 atom = JS_ATOM_undefined;
14866             else if (JS_IsFunction(ctx, op1))
14867                 atom = JS_ATOM_function;
14868             else
14869                 goto obj_type;
14870         }
14871         break;
14872     case JS_TAG_NULL:
14873     obj_type:
14874         atom = JS_ATOM_object;
14875         break;
14876     case JS_TAG_SYMBOL:
14877         atom = JS_ATOM_symbol;
14878         break;
14879     default:
14880         atom = JS_ATOM_unknown;
14881         break;
14882     }
14883     return atom;
14884 }
14885 
js_operator_delete(JSContext * ctx,JSValue * sp)14886 static __exception int js_operator_delete(JSContext *ctx, JSValue *sp)
14887 {
14888     JSValue op1, op2;
14889     JSAtom atom;
14890     int ret;
14891 
14892     op1 = sp[-2];
14893     op2 = sp[-1];
14894     atom = JS_ValueToAtom(ctx, op2);
14895     if (unlikely(atom == JS_ATOM_NULL))
14896         return -1;
14897     ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT);
14898     JS_FreeAtom(ctx, atom);
14899     if (unlikely(ret < 0))
14900         return -1;
14901     JS_FreeValue(ctx, op1);
14902     JS_FreeValue(ctx, op2);
14903     sp[-2] = JS_NewBool(ctx, ret);
14904     return 0;
14905 }
14906 
js_throw_type_error(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)14907 static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val,
14908                                    int argc, JSValueConst *argv)
14909 {
14910     return JS_ThrowTypeError(ctx, "invalid property access");
14911 }
14912 
14913 /* XXX: not 100% compatible, but mozilla seems to use a similar
14914    implementation to ensure that caller in non strict mode does not
14915    throw (ES5 compatibility) */
js_function_proto_caller(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)14916 static JSValue js_function_proto_caller(JSContext *ctx, JSValueConst this_val,
14917                                         int argc, JSValueConst *argv)
14918 {
14919     JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
14920     if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) {
14921         return js_throw_type_error(ctx, this_val, 0, NULL);
14922     }
14923     return JS_UNDEFINED;
14924 }
14925 
js_function_proto_fileName(JSContext * ctx,JSValueConst this_val)14926 static JSValue js_function_proto_fileName(JSContext *ctx,
14927                                           JSValueConst this_val)
14928 {
14929     JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
14930     if (b && b->has_debug) {
14931         return JS_AtomToString(ctx, b->debug.filename);
14932     }
14933     return JS_UNDEFINED;
14934 }
14935 
js_function_proto_lineNumber(JSContext * ctx,JSValueConst this_val)14936 static JSValue js_function_proto_lineNumber(JSContext *ctx,
14937                                             JSValueConst this_val)
14938 {
14939     JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
14940     if (b && b->has_debug) {
14941         return JS_NewInt32(ctx, b->debug.line_num);
14942     }
14943     return JS_UNDEFINED;
14944 }
14945 
js_arguments_define_own_property(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSValueConst val,JSValueConst getter,JSValueConst setter,int flags)14946 static int js_arguments_define_own_property(JSContext *ctx,
14947                                             JSValueConst this_obj,
14948                                             JSAtom prop, JSValueConst val,
14949                                             JSValueConst getter, JSValueConst setter, int flags)
14950 {
14951     JSObject *p;
14952     uint32_t idx;
14953     p = JS_VALUE_GET_OBJ(this_obj);
14954     /* convert to normal array when redefining an existing numeric field */
14955     if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) &&
14956         idx < p->u.array.count) {
14957         if (convert_fast_array_to_array(ctx, p))
14958             return -1;
14959     }
14960     /* run the default define own property */
14961     return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
14962                              flags | JS_PROP_NO_EXOTIC);
14963 }
14964 
14965 static const JSClassExoticMethods js_arguments_exotic_methods = {
14966     .define_own_property = js_arguments_define_own_property,
14967 };
14968 
js_build_arguments(JSContext * ctx,int argc,JSValueConst * argv)14969 static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv)
14970 {
14971     JSValue val, *tab;
14972     JSProperty *pr;
14973     JSObject *p;
14974     int i;
14975 
14976     val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
14977                                  JS_CLASS_ARGUMENTS);
14978     if (JS_IsException(val))
14979         return val;
14980     p = JS_VALUE_GET_OBJ(val);
14981 
14982     /* add the length field (cannot fail) */
14983     pr = add_property(ctx, p, JS_ATOM_length,
14984                       JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
14985     pr->u.value = JS_NewInt32(ctx, argc);
14986 
14987     /* initialize the fast array part */
14988     tab = NULL;
14989     if (argc > 0) {
14990         tab = js_malloc(ctx, sizeof(tab[0]) * argc);
14991         if (!tab) {
14992             JS_FreeValue(ctx, val);
14993             return JS_EXCEPTION;
14994         }
14995         for(i = 0; i < argc; i++) {
14996             tab[i] = JS_DupValue(ctx, argv[i]);
14997         }
14998     }
14999     p->u.array.u.values = tab;
15000     p->u.array.count = argc;
15001 
15002     JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
15003                            JS_DupValue(ctx, ctx->array_proto_values),
15004                            JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
15005     /* add callee property to throw a TypeError in strict mode */
15006     JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED,
15007                       ctx->throw_type_error, ctx->throw_type_error,
15008                       JS_PROP_HAS_GET | JS_PROP_HAS_SET);
15009     return val;
15010 }
15011 
15012 #define GLOBAL_VAR_OFFSET 0x40000000
15013 #define ARGUMENT_VAR_OFFSET 0x20000000
15014 
15015 /* legacy arguments object: add references to the function arguments */
js_build_mapped_arguments(JSContext * ctx,int argc,JSValueConst * argv,JSStackFrame * sf,int arg_count)15016 static JSValue js_build_mapped_arguments(JSContext *ctx, int argc,
15017                                          JSValueConst *argv,
15018                                          JSStackFrame *sf, int arg_count)
15019 {
15020     JSValue val;
15021     JSProperty *pr;
15022     JSObject *p;
15023     int i;
15024 
15025     val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
15026                                  JS_CLASS_MAPPED_ARGUMENTS);
15027     if (JS_IsException(val))
15028         return val;
15029     p = JS_VALUE_GET_OBJ(val);
15030 
15031     /* add the length field (cannot fail) */
15032     pr = add_property(ctx, p, JS_ATOM_length,
15033                       JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
15034     pr->u.value = JS_NewInt32(ctx, argc);
15035 
15036     for(i = 0; i < arg_count; i++) {
15037         JSVarRef *var_ref;
15038         var_ref = get_var_ref(ctx, sf, i, TRUE);
15039         if (!var_ref)
15040             goto fail;
15041         pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);
15042         if (!pr) {
15043             free_var_ref(ctx->rt, var_ref);
15044             goto fail;
15045         }
15046         pr->u.var_ref = var_ref;
15047     }
15048 
15049     /* the arguments not mapped to the arguments of the function can
15050        be normal properties */
15051     for(i = arg_count; i < argc; i++) {
15052         if (JS_DefinePropertyValueUint32(ctx, val, i,
15053                                          JS_DupValue(ctx, argv[i]),
15054                                          JS_PROP_C_W_E) < 0)
15055             goto fail;
15056     }
15057 
15058     JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
15059                            JS_DupValue(ctx, ctx->array_proto_values),
15060                            JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
15061     /* callee returns this function in non strict mode */
15062     JS_DefinePropertyValue(ctx, val, JS_ATOM_callee,
15063                            JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func),
15064                            JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
15065     return val;
15066  fail:
15067     JS_FreeValue(ctx, val);
15068     return JS_EXCEPTION;
15069 }
15070 
js_build_rest(JSContext * ctx,int first,int argc,JSValueConst * argv)15071 static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv)
15072 {
15073     JSValue val;
15074     int i, ret;
15075 
15076     val = JS_NewArray(ctx);
15077     if (JS_IsException(val))
15078         return val;
15079     for (i = first; i < argc; i++) {
15080         ret = JS_DefinePropertyValueUint32(ctx, val, i - first,
15081                                            JS_DupValue(ctx, argv[i]),
15082                                            JS_PROP_C_W_E);
15083         if (ret < 0) {
15084             JS_FreeValue(ctx, val);
15085             return JS_EXCEPTION;
15086         }
15087     }
15088     return val;
15089 }
15090 
build_for_in_iterator(JSContext * ctx,JSValue obj)15091 static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
15092 {
15093     JSObject *p, *p1;
15094     JSPropertyEnum *tab_atom;
15095     int i;
15096     JSValue enum_obj;
15097     JSForInIterator *it;
15098     uint32_t tag, tab_atom_count;
15099 
15100     tag = JS_VALUE_GET_TAG(obj);
15101     if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) {
15102         obj = JS_ToObjectFree(ctx, obj);
15103     }
15104 
15105     it = js_malloc(ctx, sizeof(*it));
15106     if (!it) {
15107         JS_FreeValue(ctx, obj);
15108         return JS_EXCEPTION;
15109     }
15110     enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR);
15111     if (JS_IsException(enum_obj)) {
15112         js_free(ctx, it);
15113         JS_FreeValue(ctx, obj);
15114         return JS_EXCEPTION;
15115     }
15116     it->is_array = FALSE;
15117     it->obj = obj;
15118     it->idx = 0;
15119     p = JS_VALUE_GET_OBJ(enum_obj);
15120     p->u.for_in_iterator = it;
15121 
15122     if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
15123         return enum_obj;
15124 
15125     p = JS_VALUE_GET_OBJ(obj);
15126 
15127     /* fast path: assume no enumerable properties in the prototype chain */
15128     p1 = p->shape->proto;
15129     while (p1 != NULL) {
15130         if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p1,
15131                                    JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))
15132             goto fail;
15133         js_free_prop_enum(ctx, tab_atom, tab_atom_count);
15134         if (tab_atom_count != 0) {
15135             goto slow_path;
15136         }
15137         p1 = p1->shape->proto;
15138     }
15139     if (p->fast_array) {
15140         JSShape *sh;
15141         JSShapeProperty *prs;
15142         /* check that there are no enumerable normal fields */
15143         sh = p->shape;
15144         for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
15145             if (prs->flags & JS_PROP_ENUMERABLE)
15146                 goto normal_case;
15147         }
15148         /* for fast arrays, we only store the number of elements */
15149         it->is_array = TRUE;
15150         it->array_length = p->u.array.count;
15151     } else {
15152     normal_case:
15153         if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
15154                                    JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))
15155             goto fail;
15156         for(i = 0; i < tab_atom_count; i++) {
15157             JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0);
15158         }
15159         js_free_prop_enum(ctx, tab_atom, tab_atom_count);
15160     }
15161     return enum_obj;
15162 
15163  slow_path:
15164     /* non enumerable properties hide the enumerables ones in the
15165        prototype chain */
15166     while (p != NULL) {
15167         if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
15168                                    JS_GPN_STRING_MASK | JS_GPN_SET_ENUM))
15169             goto fail;
15170         for(i = 0; i < tab_atom_count; i++) {
15171             JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL,
15172                                    (tab_atom[i].is_enumerable ?
15173                                     JS_PROP_ENUMERABLE : 0));
15174         }
15175         js_free_prop_enum(ctx, tab_atom, tab_atom_count);
15176         p = p->shape->proto;
15177     }
15178     return enum_obj;
15179 
15180  fail:
15181     JS_FreeValue(ctx, enum_obj);
15182     return JS_EXCEPTION;
15183 }
15184 
15185 /* obj -> enum_obj */
js_for_in_start(JSContext * ctx,JSValue * sp)15186 static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)
15187 {
15188     sp[-1] = build_for_in_iterator(ctx, sp[-1]);
15189     if (JS_IsException(sp[-1]))
15190         return -1;
15191     return 0;
15192 }
15193 
15194 /* enum_obj -> enum_obj value done */
js_for_in_next(JSContext * ctx,JSValue * sp)15195 static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)
15196 {
15197     JSValueConst enum_obj;
15198     JSObject *p;
15199     JSAtom prop;
15200     JSForInIterator *it;
15201     int ret;
15202 
15203     enum_obj = sp[-1];
15204     /* fail safe */
15205     if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT)
15206         goto done;
15207     p = JS_VALUE_GET_OBJ(enum_obj);
15208     if (p->class_id != JS_CLASS_FOR_IN_ITERATOR)
15209         goto done;
15210     it = p->u.for_in_iterator;
15211 
15212     for(;;) {
15213         if (it->is_array) {
15214             if (it->idx >= it->array_length)
15215                 goto done;
15216             prop = __JS_AtomFromUInt32(it->idx);
15217             it->idx++;
15218         } else {
15219             JSShape *sh = p->shape;
15220             JSShapeProperty *prs;
15221             if (it->idx >= sh->prop_count)
15222                 goto done;
15223             prs = get_shape_prop(sh) + it->idx;
15224             prop = prs->atom;
15225             it->idx++;
15226             if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE))
15227                 continue;
15228         }
15229         /* check if the property was deleted */
15230         ret = JS_HasProperty(ctx, it->obj, prop);
15231         if (ret < 0)
15232             return ret;
15233         if (ret)
15234             break;
15235     }
15236     /* return the property */
15237     sp[0] = JS_AtomToValue(ctx, prop);
15238     sp[1] = JS_FALSE;
15239     return 0;
15240  done:
15241     /* return the end */
15242     sp[0] = JS_UNDEFINED;
15243     sp[1] = JS_TRUE;
15244     return 0;
15245 }
15246 
JS_GetIterator2(JSContext * ctx,JSValueConst obj,JSValueConst method)15247 static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj,
15248                                JSValueConst method)
15249 {
15250     JSValue enum_obj;
15251 
15252     enum_obj = JS_Call(ctx, method, obj, 0, NULL);
15253     if (JS_IsException(enum_obj))
15254         return enum_obj;
15255     if (!JS_IsObject(enum_obj)) {
15256         JS_FreeValue(ctx, enum_obj);
15257         return JS_ThrowTypeErrorNotAnObject(ctx);
15258     }
15259     return enum_obj;
15260 }
15261 
JS_GetIterator(JSContext * ctx,JSValueConst obj,BOOL is_async)15262 static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async)
15263 {
15264     JSValue method, ret, sync_iter;
15265 
15266     if (is_async) {
15267         method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator);
15268         if (JS_IsException(method))
15269             return method;
15270         if (JS_IsUndefined(method) || JS_IsNull(method)) {
15271             method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
15272             if (JS_IsException(method))
15273                 return method;
15274             sync_iter = JS_GetIterator2(ctx, obj, method);
15275             JS_FreeValue(ctx, method);
15276             if (JS_IsException(sync_iter))
15277                 return sync_iter;
15278             ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter);
15279             JS_FreeValue(ctx, sync_iter);
15280             return ret;
15281         }
15282     } else {
15283         method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
15284         if (JS_IsException(method))
15285             return method;
15286     }
15287     if (!JS_IsFunction(ctx, method)) {
15288         JS_FreeValue(ctx, method);
15289         return JS_ThrowTypeError(ctx, "value is not iterable");
15290     }
15291     ret = JS_GetIterator2(ctx, obj, method);
15292     JS_FreeValue(ctx, method);
15293     return ret;
15294 }
15295 
15296 /* 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)15297 static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj,
15298                                 JSValueConst method,
15299                                 int argc, JSValueConst *argv, int *pdone)
15300 {
15301     JSValue obj;
15302 
15303     /* fast path for the built-in iterators (avoid creating the
15304        intermediate result object) */
15305     if (JS_IsObject(method)) {
15306         JSObject *p = JS_VALUE_GET_OBJ(method);
15307         if (p->class_id == JS_CLASS_C_FUNCTION &&
15308             p->u.cfunc.cproto == JS_CFUNC_iterator_next) {
15309             JSCFunctionType func;
15310             JSValueConst args[1];
15311 
15312             /* in case the function expects one argument */
15313             if (argc == 0) {
15314                 args[0] = JS_UNDEFINED;
15315                 argv = args;
15316             }
15317             func = p->u.cfunc.c_function;
15318             return func.iterator_next(ctx, enum_obj, argc, argv,
15319                                       pdone, p->u.cfunc.magic);
15320         }
15321     }
15322     obj = JS_Call(ctx, method, enum_obj, argc, argv);
15323     if (JS_IsException(obj))
15324         goto fail;
15325     if (!JS_IsObject(obj)) {
15326         JS_FreeValue(ctx, obj);
15327         JS_ThrowTypeError(ctx, "iterator must return an object");
15328         goto fail;
15329     }
15330     *pdone = 2;
15331     return obj;
15332  fail:
15333     *pdone = FALSE;
15334     return JS_EXCEPTION;
15335 }
15336 
JS_IteratorNext(JSContext * ctx,JSValueConst enum_obj,JSValueConst method,int argc,JSValueConst * argv,BOOL * pdone)15337 static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj,
15338                                JSValueConst method,
15339                                int argc, JSValueConst *argv, BOOL *pdone)
15340 {
15341     JSValue obj, value, done_val;
15342     int done;
15343 
15344     obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done);
15345     if (JS_IsException(obj))
15346         goto fail;
15347     if (done != 2) {
15348         *pdone = done;
15349         return obj;
15350     } else {
15351         done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
15352         if (JS_IsException(done_val))
15353             goto fail;
15354         *pdone = JS_ToBoolFree(ctx, done_val);
15355         value = JS_UNDEFINED;
15356         if (!*pdone) {
15357             value = JS_GetProperty(ctx, obj, JS_ATOM_value);
15358         }
15359         JS_FreeValue(ctx, obj);
15360         return value;
15361     }
15362  fail:
15363     JS_FreeValue(ctx, obj);
15364     *pdone = FALSE;
15365     return JS_EXCEPTION;
15366 }
15367 
15368 /* return < 0 in case of exception */
JS_IteratorClose(JSContext * ctx,JSValueConst enum_obj,BOOL is_exception_pending)15369 static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj,
15370                             BOOL is_exception_pending)
15371 {
15372     JSValue method, ret, ex_obj;
15373     int res;
15374 
15375     if (is_exception_pending) {
15376         ex_obj = ctx->rt->current_exception;
15377         ctx->rt->current_exception = JS_NULL;
15378         res = -1;
15379     } else {
15380         ex_obj = JS_UNDEFINED;
15381         res = 0;
15382     }
15383     method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return);
15384     if (JS_IsException(method)) {
15385         res = -1;
15386         goto done;
15387     }
15388     if (JS_IsUndefined(method) || JS_IsNull(method)) {
15389         goto done;
15390     }
15391     ret = JS_CallFree(ctx, method, enum_obj, 0, NULL);
15392     if (!is_exception_pending) {
15393         if (JS_IsException(ret)) {
15394             res = -1;
15395         } else if (!JS_IsObject(ret)) {
15396             JS_ThrowTypeErrorNotAnObject(ctx);
15397             res = -1;
15398         }
15399     }
15400     JS_FreeValue(ctx, ret);
15401  done:
15402     if (is_exception_pending) {
15403         JS_Throw(ctx, ex_obj);
15404     }
15405     return res;
15406 }
15407 
15408 /* obj -> enum_rec (3 slots) */
js_for_of_start(JSContext * ctx,JSValue * sp,BOOL is_async)15409 static __exception int js_for_of_start(JSContext *ctx, JSValue *sp,
15410                                        BOOL is_async)
15411 {
15412     JSValue op1, obj, method;
15413     op1 = sp[-1];
15414     obj = JS_GetIterator(ctx, op1, is_async);
15415     if (JS_IsException(obj))
15416         return -1;
15417     JS_FreeValue(ctx, op1);
15418     sp[-1] = obj;
15419     method = JS_GetProperty(ctx, obj, JS_ATOM_next);
15420     if (JS_IsException(method))
15421         return -1;
15422     sp[0] = method;
15423     return 0;
15424 }
15425 
15426 /* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset'
15427    objs. If 'done' is true or in case of exception, 'enum_rec' is set
15428    to undefined. If 'done' is true, 'value' is always set to
15429    undefined. */
js_for_of_next(JSContext * ctx,JSValue * sp,int offset)15430 static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
15431 {
15432     JSValue value = JS_UNDEFINED;
15433     int done = 1;
15434 
15435     if (likely(!JS_IsUndefined(sp[offset]))) {
15436         value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done);
15437         if (JS_IsException(value))
15438             done = -1;
15439         if (done) {
15440             /* value is JS_UNDEFINED or JS_EXCEPTION */
15441             /* replace the iteration object with undefined */
15442             JS_FreeValue(ctx, sp[offset]);
15443             sp[offset] = JS_UNDEFINED;
15444             if (done < 0) {
15445                 return -1;
15446             } else {
15447                 JS_FreeValue(ctx, value);
15448                 value = JS_UNDEFINED;
15449             }
15450         }
15451     }
15452     sp[0] = value;
15453     sp[1] = JS_NewBool(ctx, done);
15454     return 0;
15455 }
15456 
JS_IteratorGetCompleteValue(JSContext * ctx,JSValueConst obj,BOOL * pdone)15457 static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj,
15458                                            BOOL *pdone)
15459 {
15460     JSValue done_val, value;
15461     BOOL done;
15462     done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
15463     if (JS_IsException(done_val))
15464         goto fail;
15465     done = JS_ToBoolFree(ctx, done_val);
15466     value = JS_GetProperty(ctx, obj, JS_ATOM_value);
15467     if (JS_IsException(value))
15468         goto fail;
15469     *pdone = done;
15470     return value;
15471  fail:
15472     *pdone = FALSE;
15473     return JS_EXCEPTION;
15474 }
15475 
js_iterator_get_value_done(JSContext * ctx,JSValue * sp)15476 static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
15477 {
15478     JSValue obj, value;
15479     BOOL done;
15480     obj = sp[-1];
15481     if (!JS_IsObject(obj)) {
15482         JS_ThrowTypeError(ctx, "iterator must return an object");
15483         return -1;
15484     }
15485     value = JS_IteratorGetCompleteValue(ctx, obj, &done);
15486     if (JS_IsException(value))
15487         return -1;
15488     JS_FreeValue(ctx, obj);
15489     sp[-1] = value;
15490     sp[0] = JS_NewBool(ctx, done);
15491     return 0;
15492 }
15493 
js_create_iterator_result(JSContext * ctx,JSValue val,BOOL done)15494 static JSValue js_create_iterator_result(JSContext *ctx,
15495                                          JSValue val,
15496                                          BOOL done)
15497 {
15498     JSValue obj;
15499     obj = JS_NewObject(ctx);
15500     if (JS_IsException(obj)) {
15501         JS_FreeValue(ctx, val);
15502         return obj;
15503     }
15504     if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value,
15505                                val, JS_PROP_C_W_E) < 0) {
15506         goto fail;
15507     }
15508     if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done,
15509                                JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) {
15510     fail:
15511         JS_FreeValue(ctx, obj);
15512         return JS_EXCEPTION;
15513     }
15514     return obj;
15515 }
15516 
15517 static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
15518                                       int argc, JSValueConst *argv,
15519                                       BOOL *pdone, int magic);
15520 
15521 static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
15522                                         int argc, JSValueConst *argv, int magic);
15523 
js_is_fast_array(JSContext * ctx,JSValueConst obj)15524 static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj)
15525 {
15526     /* Try and handle fast arrays explicitly */
15527     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
15528         JSObject *p = JS_VALUE_GET_OBJ(obj);
15529         if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {
15530             return TRUE;
15531         }
15532     }
15533     return FALSE;
15534 }
15535 
15536 /* Access an Array's internal JSValue array if available */
js_get_fast_array(JSContext * ctx,JSValueConst obj,JSValue ** arrpp,uint32_t * countp)15537 static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
15538                               JSValue **arrpp, uint32_t *countp)
15539 {
15540     /* Try and handle fast arrays explicitly */
15541     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
15542         JSObject *p = JS_VALUE_GET_OBJ(obj);
15543         if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {
15544             *countp = p->u.array.count;
15545             *arrpp = p->u.array.u.values;
15546             return TRUE;
15547         }
15548     }
15549     return FALSE;
15550 }
15551 
js_append_enumerate(JSContext * ctx,JSValue * sp)15552 static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)
15553 {
15554     JSValue iterator, enumobj, method, value;
15555     int is_array_iterator;
15556     JSValue *arrp;
15557     uint32_t i, count32, pos;
15558 
15559     if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) {
15560         JS_ThrowInternalError(ctx, "invalid index for append");
15561         return -1;
15562     }
15563 
15564     pos = JS_VALUE_GET_INT(sp[-2]);
15565 
15566     /* XXX: further optimisations:
15567        - use ctx->array_proto_values?
15568        - check if array_iterator_prototype next method is built-in and
15569          avoid constructing actual iterator object?
15570        - build this into js_for_of_start and use in all `for (x of o)` loops
15571      */
15572     iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator);
15573     if (JS_IsException(iterator))
15574         return -1;
15575     is_array_iterator = JS_IsCFunction(ctx, iterator,
15576                                        (JSCFunction *)js_create_array_iterator,
15577                                        JS_ITERATOR_KIND_VALUE);
15578     JS_FreeValue(ctx, iterator);
15579 
15580     enumobj = JS_GetIterator(ctx, sp[-1], FALSE);
15581     if (JS_IsException(enumobj))
15582         return -1;
15583     method = JS_GetProperty(ctx, enumobj, JS_ATOM_next);
15584     if (JS_IsException(method)) {
15585         JS_FreeValue(ctx, enumobj);
15586         return -1;
15587     }
15588     if (is_array_iterator
15589     &&  JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0)
15590     &&  js_get_fast_array(ctx, sp[-1], &arrp, &count32)) {
15591         uint32_t len;
15592         if (js_get_length32(ctx, &len, sp[-1]))
15593             goto exception;
15594         /* if len > count32, the elements >= count32 might be read in
15595            the prototypes and might have side effects */
15596         if (len != count32)
15597             goto general_case;
15598         /* Handle fast arrays explicitly */
15599         for (i = 0; i < count32; i++) {
15600             if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++,
15601                                              JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0)
15602                 goto exception;
15603         }
15604     } else {
15605     general_case:
15606         for (;;) {
15607             BOOL done;
15608             value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done);
15609             if (JS_IsException(value))
15610                 goto exception;
15611             if (done) {
15612                 /* value is JS_UNDEFINED */
15613                 break;
15614             }
15615             if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0)
15616                 goto exception;
15617         }
15618     }
15619     /* Note: could raise an error if too many elements */
15620     sp[-2] = JS_NewInt32(ctx, pos);
15621     JS_FreeValue(ctx, enumobj);
15622     JS_FreeValue(ctx, method);
15623     return 0;
15624 
15625 exception:
15626     JS_IteratorClose(ctx, enumobj, TRUE);
15627     JS_FreeValue(ctx, enumobj);
15628     JS_FreeValue(ctx, method);
15629     return -1;
15630 }
15631 
JS_CopyDataProperties(JSContext * ctx,JSValueConst target,JSValueConst source,JSValueConst excluded,BOOL setprop)15632 static __exception int JS_CopyDataProperties(JSContext *ctx,
15633                                              JSValueConst target,
15634                                              JSValueConst source,
15635                                              JSValueConst excluded,
15636                                              BOOL setprop)
15637 {
15638     JSPropertyEnum *tab_atom;
15639     JSValue val;
15640     uint32_t i, tab_atom_count;
15641     JSObject *p;
15642     JSObject *pexcl = NULL;
15643     int ret = 0, flags;
15644 
15645     if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT)
15646         return 0;
15647 
15648     if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT)
15649         pexcl = JS_VALUE_GET_OBJ(excluded);
15650 
15651     p = JS_VALUE_GET_OBJ(source);
15652     if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
15653                                JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK |
15654                                JS_GPN_ENUM_ONLY))
15655         return -1;
15656 
15657     flags = JS_PROP_C_W_E;
15658 
15659     for (i = 0; i < tab_atom_count; i++) {
15660         if (pexcl) {
15661             ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom);
15662             if (ret) {
15663                 if (ret < 0)
15664                     break;
15665                 ret = 0;
15666                 continue;
15667             }
15668         }
15669         ret = -1;
15670         val = JS_GetProperty(ctx, source, tab_atom[i].atom);
15671         if (JS_IsException(val))
15672             break;
15673         if (setprop)
15674             ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val);
15675         else
15676             ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, flags);
15677         if (ret < 0)
15678             break;
15679         ret = 0;
15680     }
15681     js_free_prop_enum(ctx, tab_atom, tab_atom_count);
15682     return ret;
15683 }
15684 
15685 /* only valid inside C functions */
JS_GetActiveFunction(JSContext * ctx)15686 static JSValueConst JS_GetActiveFunction(JSContext *ctx)
15687 {
15688     return ctx->rt->current_stack_frame->cur_func;
15689 }
15690 
get_var_ref(JSContext * ctx,JSStackFrame * sf,int var_idx,BOOL is_arg)15691 static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
15692                              int var_idx, BOOL is_arg)
15693 {
15694     JSVarRef *var_ref;
15695     struct list_head *el;
15696 
15697     list_for_each(el, &sf->var_ref_list) {
15698         var_ref = list_entry(el, JSVarRef, header.link);
15699         if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) {
15700             var_ref->header.ref_count++;
15701             return var_ref;
15702         }
15703     }
15704     /* create a new one */
15705     var_ref = js_malloc(ctx, sizeof(JSVarRef));
15706     if (!var_ref)
15707         return NULL;
15708     var_ref->header.ref_count = 1;
15709     var_ref->is_detached = FALSE;
15710     var_ref->is_arg = is_arg;
15711     var_ref->var_idx = var_idx;
15712     list_add_tail(&var_ref->header.link, &sf->var_ref_list);
15713     if (is_arg)
15714         var_ref->pvalue = &sf->arg_buf[var_idx];
15715     else
15716         var_ref->pvalue = &sf->var_buf[var_idx];
15717     var_ref->value = JS_UNDEFINED;
15718     return var_ref;
15719 }
15720 
js_closure2(JSContext * ctx,JSValue func_obj,JSFunctionBytecode * b,JSVarRef ** cur_var_refs,JSStackFrame * sf)15721 static JSValue js_closure2(JSContext *ctx, JSValue func_obj,
15722                            JSFunctionBytecode *b,
15723                            JSVarRef **cur_var_refs,
15724                            JSStackFrame *sf)
15725 {
15726     JSObject *p;
15727     JSVarRef **var_refs;
15728     int i;
15729 
15730     p = JS_VALUE_GET_OBJ(func_obj);
15731     p->u.func.function_bytecode = b;
15732     p->u.func.home_object = NULL;
15733     p->u.func.var_refs = NULL;
15734     if (b->closure_var_count) {
15735         var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
15736         if (!var_refs)
15737             goto fail;
15738         p->u.func.var_refs = var_refs;
15739         for(i = 0; i < b->closure_var_count; i++) {
15740             JSClosureVar *cv = &b->closure_var[i];
15741             JSVarRef *var_ref;
15742             if (cv->is_local) {
15743                 /* reuse the existing variable reference if it already exists */
15744                 var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg);
15745                 if (!var_ref)
15746                     goto fail;
15747             } else {
15748                 var_ref = cur_var_refs[cv->var_idx];
15749                 var_ref->header.ref_count++;
15750             }
15751             var_refs[i] = var_ref;
15752         }
15753     }
15754     return func_obj;
15755  fail:
15756     /* bfunc is freed when func_obj is freed */
15757     JS_FreeValue(ctx, func_obj);
15758     return JS_EXCEPTION;
15759 }
15760 
js_instantiate_prototype(JSContext * ctx,JSObject * p,JSAtom atom,void * opaque)15761 static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque)
15762 {
15763     JSValue obj, this_val;
15764     int ret;
15765 
15766     this_val = JS_MKPTR(JS_TAG_OBJECT, p);
15767     obj = JS_NewObject(ctx);
15768     if (JS_IsException(obj))
15769         return JS_EXCEPTION;
15770     set_cycle_flag(ctx, obj);
15771     set_cycle_flag(ctx, this_val);
15772     ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor,
15773                                  JS_DupValue(ctx, this_val),
15774                                  JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
15775     if (ret < 0) {
15776         JS_FreeValue(ctx, obj);
15777         return JS_EXCEPTION;
15778     }
15779     return obj;
15780 }
15781 
15782 static const uint16_t func_kind_to_class_id[] = {
15783     [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION,
15784     [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION,
15785     [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION,
15786     [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION,
15787 };
15788 
js_closure(JSContext * ctx,JSValue bfunc,JSVarRef ** cur_var_refs,JSStackFrame * sf)15789 static JSValue js_closure(JSContext *ctx, JSValue bfunc,
15790                           JSVarRef **cur_var_refs,
15791                           JSStackFrame *sf)
15792 {
15793     JSFunctionBytecode *b;
15794     JSValue func_obj;
15795     JSAtom name_atom;
15796 
15797     b = JS_VALUE_GET_PTR(bfunc);
15798     func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]);
15799     if (JS_IsException(func_obj)) {
15800         JS_FreeValue(ctx, bfunc);
15801         return JS_EXCEPTION;
15802     }
15803     func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf);
15804     if (JS_IsException(func_obj)) {
15805         /* bfunc has been freed */
15806         goto fail;
15807     }
15808     name_atom = b->func_name;
15809     if (name_atom == JS_ATOM_NULL)
15810         name_atom = JS_ATOM_empty_string;
15811     js_function_set_properties(ctx, func_obj, name_atom,
15812                                b->defined_arg_count);
15813 
15814     if (b->func_kind & JS_FUNC_GENERATOR) {
15815         JSValue proto;
15816         int proto_class_id;
15817         /* generators have a prototype field which is used as
15818            prototype for the generator object */
15819         if (b->func_kind == JS_FUNC_ASYNC_GENERATOR)
15820             proto_class_id = JS_CLASS_ASYNC_GENERATOR;
15821         else
15822             proto_class_id = JS_CLASS_GENERATOR;
15823         proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]);
15824         if (JS_IsException(proto))
15825             goto fail;
15826         JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto,
15827                                JS_PROP_WRITABLE);
15828     } else if (b->has_prototype) {
15829         /* add the 'prototype' property: delay instantiation to avoid
15830            creating cycles for every javascript function. The prototype
15831            object is created on the fly when first accessed */
15832         JS_SetConstructorBit(ctx, func_obj, TRUE);
15833         JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype,
15834                                   JS_AUTOINIT_ID_PROTOTYPE, NULL,
15835                                   JS_PROP_WRITABLE);
15836     }
15837     return func_obj;
15838  fail:
15839     /* bfunc is freed when func_obj is freed */
15840     JS_FreeValue(ctx, func_obj);
15841     return JS_EXCEPTION;
15842 }
15843 
15844 #define JS_DEFINE_CLASS_HAS_HERITAGE     (1 << 0)
15845 
js_op_define_class(JSContext * ctx,JSValue * sp,JSAtom class_name,int class_flags,JSVarRef ** cur_var_refs,JSStackFrame * sf,BOOL is_computed_name)15846 static int js_op_define_class(JSContext *ctx, JSValue *sp,
15847                               JSAtom class_name, int class_flags,
15848                               JSVarRef **cur_var_refs,
15849                               JSStackFrame *sf, BOOL is_computed_name)
15850 {
15851     JSValue bfunc, parent_class, proto = JS_UNDEFINED;
15852     JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED;
15853     JSFunctionBytecode *b;
15854 
15855     parent_class = sp[-2];
15856     bfunc = sp[-1];
15857 
15858     if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) {
15859         if (JS_IsNull(parent_class)) {
15860             parent_proto = JS_NULL;
15861             parent_class = JS_DupValue(ctx, ctx->function_proto);
15862         } else {
15863             if (!JS_IsConstructor(ctx, parent_class)) {
15864                 JS_ThrowTypeError(ctx, "parent class must be constructor");
15865                 goto fail;
15866             }
15867             parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype);
15868             if (JS_IsException(parent_proto))
15869                 goto fail;
15870             if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) {
15871                 JS_ThrowTypeError(ctx, "parent prototype must be an object or null");
15872                 goto fail;
15873             }
15874         }
15875     } else {
15876         /* parent_class is JS_UNDEFINED in this case */
15877         parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]);
15878         parent_class = JS_DupValue(ctx, ctx->function_proto);
15879     }
15880     proto = JS_NewObjectProto(ctx, parent_proto);
15881     if (JS_IsException(proto))
15882         goto fail;
15883 
15884     b = JS_VALUE_GET_PTR(bfunc);
15885     assert(b->func_kind == JS_FUNC_NORMAL);
15886     ctor = JS_NewObjectProtoClass(ctx, parent_class,
15887                                   JS_CLASS_BYTECODE_FUNCTION);
15888     if (JS_IsException(ctor))
15889         goto fail;
15890     ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf);
15891     bfunc = JS_UNDEFINED;
15892     if (JS_IsException(ctor))
15893         goto fail;
15894     js_method_set_home_object(ctx, ctor, proto);
15895     JS_SetConstructorBit(ctx, ctor, TRUE);
15896 
15897     JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length,
15898                            JS_NewInt32(ctx, b->defined_arg_count),
15899                            JS_PROP_CONFIGURABLE);
15900 
15901     if (is_computed_name) {
15902         if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3],
15903                                         JS_PROP_CONFIGURABLE) < 0)
15904             goto fail;
15905     } else {
15906         if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0)
15907             goto fail;
15908     }
15909 
15910     /* the constructor property must be first. It can be overriden by
15911        computed property names */
15912     if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
15913                                JS_DupValue(ctx, ctor),
15914                                JS_PROP_CONFIGURABLE |
15915                                JS_PROP_WRITABLE | JS_PROP_THROW) < 0)
15916         goto fail;
15917     /* set the prototype property */
15918     if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype,
15919                                JS_DupValue(ctx, proto), JS_PROP_THROW) < 0)
15920         goto fail;
15921     set_cycle_flag(ctx, ctor);
15922     set_cycle_flag(ctx, proto);
15923 
15924     JS_FreeValue(ctx, parent_proto);
15925     JS_FreeValue(ctx, parent_class);
15926 
15927     sp[-2] = ctor;
15928     sp[-1] = proto;
15929     return 0;
15930  fail:
15931     JS_FreeValue(ctx, parent_class);
15932     JS_FreeValue(ctx, parent_proto);
15933     JS_FreeValue(ctx, bfunc);
15934     JS_FreeValue(ctx, proto);
15935     JS_FreeValue(ctx, ctor);
15936     sp[-2] = JS_UNDEFINED;
15937     sp[-1] = JS_UNDEFINED;
15938     return -1;
15939 }
15940 
close_var_refs(JSRuntime * rt,JSStackFrame * sf)15941 static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
15942 {
15943     struct list_head *el, *el1;
15944     JSVarRef *var_ref;
15945     int var_idx;
15946 
15947     list_for_each_safe(el, el1, &sf->var_ref_list) {
15948         var_ref = list_entry(el, JSVarRef, header.link);
15949         var_idx = var_ref->var_idx;
15950         if (var_ref->is_arg)
15951             var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]);
15952         else
15953             var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]);
15954         var_ref->pvalue = &var_ref->value;
15955         /* the reference is no longer to a local variable */
15956         var_ref->is_detached = TRUE;
15957         add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
15958     }
15959 }
15960 
close_lexical_var(JSContext * ctx,JSStackFrame * sf,int idx,int is_arg)15961 static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg)
15962 {
15963     struct list_head *el, *el1;
15964     JSVarRef *var_ref;
15965     int var_idx = idx;
15966 
15967     list_for_each_safe(el, el1, &sf->var_ref_list) {
15968         var_ref = list_entry(el, JSVarRef, header.link);
15969         if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) {
15970             var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]);
15971             var_ref->pvalue = &var_ref->value;
15972             list_del(&var_ref->header.link);
15973             /* the reference is no longer to a local variable */
15974             var_ref->is_detached = TRUE;
15975             add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
15976         }
15977     }
15978 }
15979 
15980 #define JS_CALL_FLAG_COPY_ARGV   (1 << 1)
15981 #define JS_CALL_FLAG_GENERATOR   (1 << 2)
15982 
js_call_c_function(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)15983 static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
15984                                   JSValueConst this_obj,
15985                                   int argc, JSValueConst *argv, int flags)
15986 {
15987     JSRuntime *rt = ctx->rt;
15988     JSCFunctionType func;
15989     JSObject *p;
15990     JSStackFrame sf_s, *sf = &sf_s, *prev_sf;
15991     JSValue ret_val;
15992     JSValueConst *arg_buf;
15993     int arg_count, i;
15994     JSCFunctionEnum cproto;
15995 
15996     p = JS_VALUE_GET_OBJ(func_obj);
15997     cproto = p->u.cfunc.cproto;
15998     arg_count = p->u.cfunc.length;
15999 
16000     /* better to always check stack overflow */
16001     if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count))
16002         return JS_ThrowStackOverflow(ctx);
16003 
16004     prev_sf = rt->current_stack_frame;
16005     sf->prev_frame = prev_sf;
16006     rt->current_stack_frame = sf;
16007     ctx = p->u.cfunc.realm; /* change the current realm */
16008 
16009 #ifdef CONFIG_BIGNUM
16010     /* we only propagate the bignum mode as some runtime functions
16011        test it */
16012     if (prev_sf)
16013         sf->js_mode = prev_sf->js_mode & JS_MODE_MATH;
16014     else
16015         sf->js_mode = 0;
16016 #else
16017     sf->js_mode = 0;
16018 #endif
16019     sf->cur_func = (JSValue)func_obj;
16020     sf->arg_count = argc;
16021     arg_buf = argv;
16022 
16023     if (unlikely(argc < arg_count)) {
16024         /* ensure that at least argc_count arguments are readable */
16025         arg_buf = alloca(sizeof(arg_buf[0]) * arg_count);
16026         for(i = 0; i < argc; i++)
16027             arg_buf[i] = argv[i];
16028         for(i = argc; i < arg_count; i++)
16029             arg_buf[i] = JS_UNDEFINED;
16030         sf->arg_count = arg_count;
16031     }
16032     sf->arg_buf = (JSValue*)arg_buf;
16033 
16034     func = p->u.cfunc.c_function;
16035     switch(cproto) {
16036     case JS_CFUNC_constructor:
16037     case JS_CFUNC_constructor_or_func:
16038         if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
16039             if (cproto == JS_CFUNC_constructor) {
16040             not_a_constructor:
16041                 ret_val = JS_ThrowTypeError(ctx, "must be called with new");
16042                 break;
16043             } else {
16044                 this_obj = JS_UNDEFINED;
16045             }
16046         }
16047         /* here this_obj is new_target */
16048         /* fall thru */
16049     case JS_CFUNC_generic:
16050         ret_val = func.generic(ctx, this_obj, argc, arg_buf);
16051         break;
16052     case JS_CFUNC_constructor_magic:
16053     case JS_CFUNC_constructor_or_func_magic:
16054         if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
16055             if (cproto == JS_CFUNC_constructor_magic) {
16056                 goto not_a_constructor;
16057             } else {
16058                 this_obj = JS_UNDEFINED;
16059             }
16060         }
16061         /* fall thru */
16062     case JS_CFUNC_generic_magic:
16063         ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf,
16064                                      p->u.cfunc.magic);
16065         break;
16066     case JS_CFUNC_getter:
16067         ret_val = func.getter(ctx, this_obj);
16068         break;
16069     case JS_CFUNC_setter:
16070         ret_val = func.setter(ctx, this_obj, arg_buf[0]);
16071         break;
16072     case JS_CFUNC_getter_magic:
16073         ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic);
16074         break;
16075     case JS_CFUNC_setter_magic:
16076         ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic);
16077         break;
16078     case JS_CFUNC_f_f:
16079         {
16080             double d1;
16081 
16082             if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
16083                 ret_val = JS_EXCEPTION;
16084                 break;
16085             }
16086             ret_val = JS_NewFloat64(ctx, func.f_f(d1));
16087         }
16088         break;
16089     case JS_CFUNC_f_f_f:
16090         {
16091             double d1, d2;
16092 
16093             if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
16094                 ret_val = JS_EXCEPTION;
16095                 break;
16096             }
16097             if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) {
16098                 ret_val = JS_EXCEPTION;
16099                 break;
16100             }
16101             ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2));
16102         }
16103         break;
16104     case JS_CFUNC_iterator_next:
16105         {
16106             int done;
16107             ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf,
16108                                          &done, p->u.cfunc.magic);
16109             if (!JS_IsException(ret_val) && done != 2) {
16110                 ret_val = js_create_iterator_result(ctx, ret_val, done);
16111             }
16112         }
16113         break;
16114     default:
16115         abort();
16116     }
16117 
16118     rt->current_stack_frame = sf->prev_frame;
16119     return ret_val;
16120 }
16121 
js_call_bound_function(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)16122 static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
16123                                       JSValueConst this_obj,
16124                                       int argc, JSValueConst *argv, int flags)
16125 {
16126     JSObject *p;
16127     JSBoundFunction *bf;
16128     JSValueConst *arg_buf, new_target;
16129     int arg_count, i;
16130 
16131     p = JS_VALUE_GET_OBJ(func_obj);
16132     bf = p->u.bound_function;
16133     arg_count = bf->argc + argc;
16134     if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count))
16135         return JS_ThrowStackOverflow(ctx);
16136     arg_buf = alloca(sizeof(JSValue) * arg_count);
16137     for(i = 0; i < bf->argc; i++) {
16138         arg_buf[i] = bf->argv[i];
16139     }
16140     for(i = 0; i < argc; i++) {
16141         arg_buf[bf->argc + i] = argv[i];
16142     }
16143     if (flags & JS_CALL_FLAG_CONSTRUCTOR) {
16144         new_target = this_obj;
16145         if (js_same_value(ctx, func_obj, new_target))
16146             new_target = bf->func_obj;
16147         return JS_CallConstructor2(ctx, bf->func_obj, new_target,
16148                                    arg_count, arg_buf);
16149     } else {
16150         return JS_Call(ctx, bf->func_obj, bf->this_val,
16151                        arg_count, arg_buf);
16152     }
16153 }
16154 
16155 /* argument of OP_special_object */
16156 typedef enum {
16157     OP_SPECIAL_OBJECT_ARGUMENTS,
16158     OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS,
16159     OP_SPECIAL_OBJECT_THIS_FUNC,
16160     OP_SPECIAL_OBJECT_NEW_TARGET,
16161     OP_SPECIAL_OBJECT_HOME_OBJECT,
16162     OP_SPECIAL_OBJECT_VAR_OBJECT,
16163     OP_SPECIAL_OBJECT_IMPORT_META,
16164 } OPSpecialObjectEnum;
16165 
16166 #define FUNC_RET_AWAIT      0
16167 #define FUNC_RET_YIELD      1
16168 #define FUNC_RET_YIELD_STAR 2
16169 
16170 /* 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)16171 static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
16172                                JSValueConst this_obj, JSValueConst new_target,
16173                                int argc, JSValue *argv, int flags)
16174 {
16175     JSRuntime *rt = caller_ctx->rt;
16176     JSContext *ctx;
16177     JSObject *p;
16178     JSFunctionBytecode *b;
16179     JSStackFrame sf_s, *sf = &sf_s;
16180     const uint8_t *pc;
16181     int opcode, arg_allocated_size, i;
16182     JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval;
16183     JSVarRef **var_refs;
16184     size_t alloca_size;
16185 
16186 #if !DIRECT_DISPATCH
16187 #define SWITCH(pc)      switch (opcode = *pc++)
16188 #define CASE(op)        case op
16189 #define DEFAULT         default
16190 #define BREAK           break
16191 #else
16192     static const void * const dispatch_table[256] = {
16193 #define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id,
16194 #if SHORT_OPCODES
16195 #define def(id, size, n_pop, n_push, f)
16196 #else
16197 #define def(id, size, n_pop, n_push, f) && case_default,
16198 #endif
16199 #include "quickjs-opcode.h"
16200         [ OP_COUNT ... 255 ] = &&case_default
16201     };
16202 
16203 #ifdef ENABLE_JS_DEBUG
16204     static const void * const debugger_dispatch_table[256] = {
16205 #define DEF(id, size, n_pop, n_push, f) && case_debugger_OP_ ## id,
16206 #if SHORT_OPCODES
16207 #define def(id, size, n_pop, n_push, f)
16208 #else
16209 #define def(id, size, n_pop, n_push, f) && case_default,
16210 #endif
16211 #include "quickjs-opcode.h"
16212         [ OP_COUNT ... 255 ] = &&case_default
16213     };
16214 #define SWITCH(pc)      goto *active_dispatch_table[opcode = *pc++];
16215 #define CASE(op)        case_debugger_ ## op: DBG_CallDebugger(caller_ctx, pc); case_ ## op
16216     const void * const * active_dispatch_table = caller_ctx->debugger_info->isConnected
16217         ? debugger_dispatch_table : dispatch_table;
16218 #else
16219 #define SWITCH(pc)      goto *dispatch_table[opcode = *pc++];
16220 #define CASE(op)        case_ ## op
16221 #endif
16222 
16223 #define DEFAULT         case_default
16224 #define BREAK           SWITCH(pc)
16225 #endif
16226 
16227     if (js_poll_interrupts(caller_ctx))
16228         return JS_EXCEPTION;
16229     if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) {
16230         if (flags & JS_CALL_FLAG_GENERATOR) {
16231             JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj);
16232             /* func_obj get contains a pointer to JSFuncAsyncState */
16233             /* the stack frame is already allocated */
16234             sf = &s->frame;
16235             p = JS_VALUE_GET_OBJ(sf->cur_func);
16236             b = p->u.func.function_bytecode;
16237             ctx = b->realm;
16238             var_refs = p->u.func.var_refs;
16239             local_buf = arg_buf = sf->arg_buf;
16240             var_buf = sf->var_buf;
16241             stack_buf = sf->var_buf + b->var_count;
16242             sp = sf->cur_sp;
16243             sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */
16244             pc = sf->cur_pc;
16245             sf->prev_frame = rt->current_stack_frame;
16246             rt->current_stack_frame = sf;
16247             if (s->throw_flag)
16248                 goto exception;
16249             else
16250                 goto restart;
16251         } else {
16252             goto not_a_function;
16253         }
16254     }
16255     p = JS_VALUE_GET_OBJ(func_obj);
16256     if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
16257         JSClassCall *call_func;
16258         call_func = rt->class_array[p->class_id].call;
16259         if (!call_func) {
16260         not_a_function:
16261             return JS_ThrowTypeError(caller_ctx, "not a function");
16262         }
16263         return call_func(caller_ctx, func_obj, this_obj, argc,
16264                          (JSValueConst *)argv, flags);
16265     }
16266     b = p->u.func.function_bytecode;
16267 
16268     if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) {
16269         arg_allocated_size = b->arg_count;
16270     } else {
16271         arg_allocated_size = 0;
16272     }
16273 
16274     alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count +
16275                                      b->stack_size);
16276     if (js_check_stack_overflow(rt, alloca_size))
16277         return JS_ThrowStackOverflow(caller_ctx);
16278 
16279     sf->js_mode = b->js_mode;
16280     arg_buf = argv;
16281     sf->arg_count = argc;
16282     sf->cur_func = (JSValue)func_obj;
16283     init_list_head(&sf->var_ref_list);
16284     var_refs = p->u.func.var_refs;
16285 
16286     local_buf = alloca(alloca_size);
16287     if (unlikely(arg_allocated_size)) {
16288         int n = min_int(argc, b->arg_count);
16289         arg_buf = local_buf;
16290         for(i = 0; i < n; i++)
16291             arg_buf[i] = JS_DupValue(caller_ctx, argv[i]);
16292         for(; i < b->arg_count; i++)
16293             arg_buf[i] = JS_UNDEFINED;
16294         sf->arg_count = b->arg_count;
16295     }
16296     var_buf = local_buf + arg_allocated_size;
16297     sf->var_buf = var_buf;
16298     sf->arg_buf = arg_buf;
16299 
16300     for(i = 0; i < b->var_count; i++)
16301         var_buf[i] = JS_UNDEFINED;
16302 
16303     stack_buf = var_buf + b->var_count;
16304     sp = stack_buf;
16305     pc = b->byte_code_buf;
16306     sf->prev_frame = rt->current_stack_frame;
16307     rt->current_stack_frame = sf;
16308     ctx = b->realm; /* set the current realm */
16309 
16310  restart:
16311     for(;;) {
16312         int call_argc;
16313         JSValue *call_argv;
16314 #ifdef ENABLE_JS_DEBUG
16315         DBG_CallDebugger(ctx, NULL);
16316 #endif
16317         SWITCH(pc) {
16318         CASE(OP_push_i32):
16319             *sp++ = JS_NewInt32(ctx, get_u32(pc));
16320             pc += 4;
16321             BREAK;
16322         CASE(OP_push_const):
16323             *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
16324             pc += 4;
16325             BREAK;
16326 #if SHORT_OPCODES
16327         CASE(OP_push_minus1):
16328         CASE(OP_push_0):
16329         CASE(OP_push_1):
16330         CASE(OP_push_2):
16331         CASE(OP_push_3):
16332         CASE(OP_push_4):
16333         CASE(OP_push_5):
16334         CASE(OP_push_6):
16335         CASE(OP_push_7):
16336             *sp++ = JS_NewInt32(ctx, opcode - OP_push_0);
16337             BREAK;
16338         CASE(OP_push_i8):
16339             *sp++ = JS_NewInt32(ctx, get_i8(pc));
16340             pc += 1;
16341             BREAK;
16342         CASE(OP_push_i16):
16343             *sp++ = JS_NewInt32(ctx, get_i16(pc));
16344             pc += 2;
16345             BREAK;
16346         CASE(OP_push_const8):
16347             *sp++ = JS_DupValue(ctx, b->cpool[*pc++]);
16348             BREAK;
16349         CASE(OP_fclosure8):
16350             *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf);
16351             if (unlikely(JS_IsException(sp[-1])))
16352                 goto exception;
16353             BREAK;
16354         CASE(OP_push_empty_string):
16355             *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string);
16356             BREAK;
16357         CASE(OP_get_length):
16358             {
16359                 JSValue val;
16360 
16361                 val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length);
16362                 if (unlikely(JS_IsException(val)))
16363                     goto exception;
16364                 JS_FreeValue(ctx, sp[-1]);
16365                 sp[-1] = val;
16366             }
16367             BREAK;
16368 #endif
16369         CASE(OP_push_atom_value):
16370             *sp++ = JS_AtomToValue(ctx, get_u32(pc));
16371             pc += 4;
16372             BREAK;
16373         CASE(OP_undefined):
16374             *sp++ = JS_UNDEFINED;
16375             BREAK;
16376         CASE(OP_null):
16377             *sp++ = JS_NULL;
16378             BREAK;
16379         CASE(OP_push_this):
16380             /* OP_push_this is only called at the start of a function */
16381             {
16382                 JSValue val;
16383                 if (!(b->js_mode & JS_MODE_STRICT)) {
16384                     uint32_t tag = JS_VALUE_GET_TAG(this_obj);
16385                     if (likely(tag == JS_TAG_OBJECT))
16386                         goto normal_this;
16387                     if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) {
16388                         val = JS_DupValue(ctx, ctx->global_obj);
16389                     } else {
16390                         val = JS_ToObject(ctx, this_obj);
16391                         if (JS_IsException(val))
16392                             goto exception;
16393                     }
16394                 } else {
16395                 normal_this:
16396                     val = JS_DupValue(ctx, this_obj);
16397                 }
16398                 *sp++ = val;
16399             }
16400             BREAK;
16401         CASE(OP_push_false):
16402             *sp++ = JS_FALSE;
16403             BREAK;
16404         CASE(OP_push_true):
16405             *sp++ = JS_TRUE;
16406             BREAK;
16407         CASE(OP_object):
16408             *sp++ = JS_NewObject(ctx);
16409             if (unlikely(JS_IsException(sp[-1])))
16410                 goto exception;
16411             BREAK;
16412         CASE(OP_special_object):
16413             {
16414                 int arg = *pc++;
16415                 switch(arg) {
16416                 case OP_SPECIAL_OBJECT_ARGUMENTS:
16417                     *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv);
16418                     if (unlikely(JS_IsException(sp[-1])))
16419                         goto exception;
16420                     break;
16421                 case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS:
16422                     *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv,
16423                                                       sf, min_int(argc, b->arg_count));
16424                     if (unlikely(JS_IsException(sp[-1])))
16425                         goto exception;
16426                     break;
16427                 case OP_SPECIAL_OBJECT_THIS_FUNC:
16428                     *sp++ = JS_DupValue(ctx, sf->cur_func);
16429                     break;
16430                 case OP_SPECIAL_OBJECT_NEW_TARGET:
16431                     *sp++ = JS_DupValue(ctx, new_target);
16432                     break;
16433                 case OP_SPECIAL_OBJECT_HOME_OBJECT:
16434                     {
16435                         JSObject *p1;
16436                         p1 = p->u.func.home_object;
16437                         if (unlikely(!p1))
16438                             *sp++ = JS_UNDEFINED;
16439                         else
16440                             *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
16441                     }
16442                     break;
16443                 case OP_SPECIAL_OBJECT_VAR_OBJECT:
16444                     *sp++ = JS_NewObjectProto(ctx, JS_NULL);
16445                     if (unlikely(JS_IsException(sp[-1])))
16446                         goto exception;
16447                     break;
16448                 case OP_SPECIAL_OBJECT_IMPORT_META:
16449                     *sp++ = js_import_meta(ctx);
16450                     if (unlikely(JS_IsException(sp[-1])))
16451                         goto exception;
16452                     break;
16453                 default:
16454                     abort();
16455                 }
16456             }
16457             BREAK;
16458         CASE(OP_rest):
16459             {
16460                 int first = get_u16(pc);
16461                 pc += 2;
16462                 *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv);
16463                 if (unlikely(JS_IsException(sp[-1])))
16464                     goto exception;
16465             }
16466             BREAK;
16467 
16468         CASE(OP_drop):
16469             JS_FreeValue(ctx, sp[-1]);
16470             sp--;
16471             BREAK;
16472         CASE(OP_nip):
16473             JS_FreeValue(ctx, sp[-2]);
16474             sp[-2] = sp[-1];
16475             sp--;
16476             BREAK;
16477         CASE(OP_nip1): /* a b c -> b c */
16478             JS_FreeValue(ctx, sp[-3]);
16479             sp[-3] = sp[-2];
16480             sp[-2] = sp[-1];
16481             sp--;
16482             BREAK;
16483         CASE(OP_dup):
16484             sp[0] = JS_DupValue(ctx, sp[-1]);
16485             sp++;
16486             BREAK;
16487         CASE(OP_dup2): /* a b -> a b a b */
16488             sp[0] = JS_DupValue(ctx, sp[-2]);
16489             sp[1] = JS_DupValue(ctx, sp[-1]);
16490             sp += 2;
16491             BREAK;
16492         CASE(OP_dup3): /* a b c -> a b c a b c */
16493             sp[0] = JS_DupValue(ctx, sp[-3]);
16494             sp[1] = JS_DupValue(ctx, sp[-2]);
16495             sp[2] = JS_DupValue(ctx, sp[-1]);
16496             sp += 3;
16497             BREAK;
16498         CASE(OP_dup1): /* a b -> a a b */
16499             sp[0] = sp[-1];
16500             sp[-1] = JS_DupValue(ctx, sp[-2]);
16501             sp++;
16502             BREAK;
16503         CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */
16504             sp[0] = sp[-1];
16505             sp[-1] = sp[-2];
16506             sp[-2] = JS_DupValue(ctx, sp[0]);
16507             sp++;
16508             BREAK;
16509         CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */
16510             sp[0] = sp[-1];
16511             sp[-1] = sp[-2];
16512             sp[-2] = sp[-3];
16513             sp[-3] = JS_DupValue(ctx, sp[0]);
16514             sp++;
16515             BREAK;
16516         CASE(OP_insert4): /* this obj prop a -> a this obj prop a */
16517             sp[0] = sp[-1];
16518             sp[-1] = sp[-2];
16519             sp[-2] = sp[-3];
16520             sp[-3] = sp[-4];
16521             sp[-4] = JS_DupValue(ctx, sp[0]);
16522             sp++;
16523             BREAK;
16524         CASE(OP_perm3): /* obj a b -> a obj b (213) */
16525             {
16526                 JSValue tmp;
16527                 tmp = sp[-2];
16528                 sp[-2] = sp[-3];
16529                 sp[-3] = tmp;
16530             }
16531             BREAK;
16532         CASE(OP_rot3l): /* x a b -> a b x (231) */
16533             {
16534                 JSValue tmp;
16535                 tmp = sp[-3];
16536                 sp[-3] = sp[-2];
16537                 sp[-2] = sp[-1];
16538                 sp[-1] = tmp;
16539             }
16540             BREAK;
16541         CASE(OP_rot4l): /* x a b c -> a b c x */
16542             {
16543                 JSValue tmp;
16544                 tmp = sp[-4];
16545                 sp[-4] = sp[-3];
16546                 sp[-3] = sp[-2];
16547                 sp[-2] = sp[-1];
16548                 sp[-1] = tmp;
16549             }
16550             BREAK;
16551         CASE(OP_rot5l): /* x a b c d -> a b c d x */
16552             {
16553                 JSValue tmp;
16554                 tmp = sp[-5];
16555                 sp[-5] = sp[-4];
16556                 sp[-4] = sp[-3];
16557                 sp[-3] = sp[-2];
16558                 sp[-2] = sp[-1];
16559                 sp[-1] = tmp;
16560             }
16561             BREAK;
16562         CASE(OP_rot3r): /* a b x -> x a b (312) */
16563             {
16564                 JSValue tmp;
16565                 tmp = sp[-1];
16566                 sp[-1] = sp[-2];
16567                 sp[-2] = sp[-3];
16568                 sp[-3] = tmp;
16569             }
16570             BREAK;
16571         CASE(OP_perm4): /* obj prop a b -> a obj prop b */
16572             {
16573                 JSValue tmp;
16574                 tmp = sp[-2];
16575                 sp[-2] = sp[-3];
16576                 sp[-3] = sp[-4];
16577                 sp[-4] = tmp;
16578             }
16579             BREAK;
16580         CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */
16581             {
16582                 JSValue tmp;
16583                 tmp = sp[-2];
16584                 sp[-2] = sp[-3];
16585                 sp[-3] = sp[-4];
16586                 sp[-4] = sp[-5];
16587                 sp[-5] = tmp;
16588             }
16589             BREAK;
16590         CASE(OP_swap): /* a b -> b a */
16591             {
16592                 JSValue tmp;
16593                 tmp = sp[-2];
16594                 sp[-2] = sp[-1];
16595                 sp[-1] = tmp;
16596             }
16597             BREAK;
16598         CASE(OP_swap2): /* a b c d -> c d a b */
16599             {
16600                 JSValue tmp1, tmp2;
16601                 tmp1 = sp[-4];
16602                 tmp2 = sp[-3];
16603                 sp[-4] = sp[-2];
16604                 sp[-3] = sp[-1];
16605                 sp[-2] = tmp1;
16606                 sp[-1] = tmp2;
16607             }
16608             BREAK;
16609 
16610         CASE(OP_fclosure):
16611             {
16612                 JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
16613                 pc += 4;
16614                 *sp++ = js_closure(ctx, bfunc, var_refs, sf);
16615                 if (unlikely(JS_IsException(sp[-1])))
16616                     goto exception;
16617             }
16618             BREAK;
16619 #if SHORT_OPCODES
16620         CASE(OP_call0):
16621         CASE(OP_call1):
16622         CASE(OP_call2):
16623         CASE(OP_call3):
16624             call_argc = opcode - OP_call0;
16625             goto has_call_argc;
16626 #endif
16627         CASE(OP_call):
16628         CASE(OP_tail_call):
16629             {
16630                 call_argc = get_u16(pc);
16631                 pc += 2;
16632                 goto has_call_argc;
16633             has_call_argc:
16634                 call_argv = sp - call_argc;
16635                 sf->cur_pc = pc;
16636                 ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
16637                                           JS_UNDEFINED, call_argc, call_argv, 0);
16638                 if (unlikely(JS_IsException(ret_val)))
16639                     goto exception;
16640                 if (opcode == OP_tail_call)
16641                     goto done;
16642                 for(i = -1; i < call_argc; i++)
16643                     JS_FreeValue(ctx, call_argv[i]);
16644                 sp -= call_argc + 1;
16645                 *sp++ = ret_val;
16646             }
16647             BREAK;
16648         CASE(OP_call_constructor):
16649             {
16650                 call_argc = get_u16(pc);
16651                 pc += 2;
16652                 call_argv = sp - call_argc;
16653                 sf->cur_pc = pc;
16654                 ret_val = JS_CallConstructorInternal(ctx, call_argv[-2],
16655                                                      call_argv[-1],
16656                                                      call_argc, call_argv, 0);
16657                 if (unlikely(JS_IsException(ret_val)))
16658                     goto exception;
16659                 for(i = -2; i < call_argc; i++)
16660                     JS_FreeValue(ctx, call_argv[i]);
16661                 sp -= call_argc + 2;
16662                 *sp++ = ret_val;
16663             }
16664             BREAK;
16665         CASE(OP_call_method):
16666         CASE(OP_tail_call_method):
16667             {
16668                 call_argc = get_u16(pc);
16669                 pc += 2;
16670                 call_argv = sp - call_argc;
16671                 sf->cur_pc = pc;
16672                 ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2],
16673                                           JS_UNDEFINED, call_argc, call_argv, 0);
16674                 if (unlikely(JS_IsException(ret_val)))
16675                     goto exception;
16676                 if (opcode == OP_tail_call_method)
16677                     goto done;
16678                 for(i = -2; i < call_argc; i++)
16679                     JS_FreeValue(ctx, call_argv[i]);
16680                 sp -= call_argc + 2;
16681                 *sp++ = ret_val;
16682             }
16683             BREAK;
16684         CASE(OP_array_from):
16685             {
16686                 int i, ret;
16687 
16688                 call_argc = get_u16(pc);
16689                 pc += 2;
16690                 ret_val = JS_NewArray(ctx);
16691                 if (unlikely(JS_IsException(ret_val)))
16692                     goto exception;
16693                 call_argv = sp - call_argc;
16694                 for(i = 0; i < call_argc; i++) {
16695                     ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i],
16696                                                  JS_PROP_C_W_E | JS_PROP_THROW);
16697                     call_argv[i] = JS_UNDEFINED;
16698                     if (ret < 0) {
16699                         JS_FreeValue(ctx, ret_val);
16700                         goto exception;
16701                     }
16702                 }
16703                 sp -= call_argc;
16704                 *sp++ = ret_val;
16705             }
16706             BREAK;
16707 
16708         CASE(OP_apply):
16709             {
16710                 int magic;
16711                 magic = get_u16(pc);
16712                 pc += 2;
16713 
16714                 ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic);
16715                 if (unlikely(JS_IsException(ret_val)))
16716                     goto exception;
16717                 JS_FreeValue(ctx, sp[-3]);
16718                 JS_FreeValue(ctx, sp[-2]);
16719                 JS_FreeValue(ctx, sp[-1]);
16720                 sp -= 3;
16721                 *sp++ = ret_val;
16722             }
16723             BREAK;
16724         CASE(OP_return):
16725             ret_val = *--sp;
16726             goto done;
16727         CASE(OP_return_undef):
16728             ret_val = JS_UNDEFINED;
16729             goto done;
16730 
16731         CASE(OP_check_ctor_return):
16732             /* return TRUE if 'this' should be returned */
16733             if (!JS_IsObject(sp[-1])) {
16734                 if (!JS_IsUndefined(sp[-1])) {
16735                     JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined");
16736                     goto exception;
16737                 }
16738                 sp[0] = JS_TRUE;
16739             } else {
16740                 sp[0] = JS_FALSE;
16741             }
16742             sp++;
16743             BREAK;
16744         CASE(OP_check_ctor):
16745             if (JS_IsUndefined(new_target)) {
16746                 JS_ThrowTypeError(caller_ctx, "class constructors must be invoked with 'new'");
16747                 goto exception;
16748             }
16749             BREAK;
16750         CASE(OP_check_brand):
16751             if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0)
16752                 goto exception;
16753             BREAK;
16754         CASE(OP_add_brand):
16755             if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0)
16756                 goto exception;
16757             JS_FreeValue(ctx, sp[-2]);
16758             JS_FreeValue(ctx, sp[-1]);
16759             sp -= 2;
16760             BREAK;
16761 
16762         CASE(OP_throw):
16763             JS_Throw(ctx, *--sp);
16764             goto exception;
16765 
16766         CASE(OP_throw_error):
16767 #define JS_THROW_VAR_RO             0
16768 #define JS_THROW_VAR_REDECL         1
16769 #define JS_THROW_VAR_UNINITIALIZED  2
16770 #define JS_THROW_ERROR_DELETE_SUPER   3
16771 #define JS_THROW_ERROR_ITERATOR_THROW 4
16772             {
16773                 JSAtom atom;
16774                 int type;
16775                 atom = get_u32(pc);
16776                 type = pc[4];
16777                 pc += 5;
16778                 if (type == JS_THROW_VAR_RO)
16779                     JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom);
16780                 else
16781                 if (type == JS_THROW_VAR_REDECL)
16782                     JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom);
16783                 else
16784                 if (type == JS_THROW_VAR_UNINITIALIZED)
16785                     JS_ThrowReferenceErrorUninitialized(ctx, atom);
16786                 else
16787                 if (type == JS_THROW_ERROR_DELETE_SUPER)
16788                     JS_ThrowReferenceError(ctx, "unsupported reference to 'super'");
16789                 else
16790                 if (type == JS_THROW_ERROR_ITERATOR_THROW)
16791                     JS_ThrowTypeError(ctx, "iterator does not have a throw method");
16792                 else
16793                     JS_ThrowInternalError(ctx, "invalid throw var type %d", type);
16794             }
16795             goto exception;
16796 
16797         CASE(OP_eval):
16798             {
16799                 JSValueConst obj;
16800                 int scope_idx;
16801                 call_argc = get_u16(pc);
16802                 scope_idx = get_u16(pc + 2) - 1;
16803                 pc += 4;
16804                 call_argv = sp - call_argc;
16805                 sf->cur_pc = pc;
16806                 if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) {
16807                     if (call_argc >= 1)
16808                         obj = call_argv[0];
16809                     else
16810                         obj = JS_UNDEFINED;
16811                     ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
16812                                             JS_EVAL_TYPE_DIRECT, scope_idx);
16813                 } else {
16814                     ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
16815                                               JS_UNDEFINED, call_argc, call_argv, 0);
16816                 }
16817                 if (unlikely(JS_IsException(ret_val)))
16818                     goto exception;
16819                 for(i = -1; i < call_argc; i++)
16820                     JS_FreeValue(ctx, call_argv[i]);
16821                 sp -= call_argc + 1;
16822                 *sp++ = ret_val;
16823             }
16824             BREAK;
16825             /* could merge with OP_apply */
16826         CASE(OP_apply_eval):
16827             {
16828                 int scope_idx;
16829                 uint32_t len;
16830                 JSValue *tab;
16831                 JSValueConst obj;
16832 
16833                 scope_idx = get_u16(pc) - 1;
16834                 pc += 2;
16835                 tab = build_arg_list(ctx, &len, sp[-1]);
16836                 if (!tab)
16837                     goto exception;
16838                 if (js_same_value(ctx, sp[-2], ctx->eval_obj)) {
16839                     if (len >= 1)
16840                         obj = tab[0];
16841                     else
16842                         obj = JS_UNDEFINED;
16843                     ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
16844                                             JS_EVAL_TYPE_DIRECT, scope_idx);
16845                 } else {
16846                     ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len,
16847                                       (JSValueConst *)tab);
16848                 }
16849                 free_arg_list(ctx, tab, len);
16850                 if (unlikely(JS_IsException(ret_val)))
16851                     goto exception;
16852                 JS_FreeValue(ctx, sp[-2]);
16853                 JS_FreeValue(ctx, sp[-1]);
16854                 sp -= 2;
16855                 *sp++ = ret_val;
16856             }
16857             BREAK;
16858 
16859         CASE(OP_regexp):
16860             {
16861                 sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED,
16862                                                         sp[-2], sp[-1]);
16863                 sp--;
16864             }
16865             BREAK;
16866 
16867         CASE(OP_get_super):
16868             {
16869                 JSValue proto;
16870                 proto = JS_GetPrototype(ctx, sp[-1]);
16871                 if (JS_IsException(proto))
16872                     goto exception;
16873                 JS_FreeValue(ctx, sp[-1]);
16874                 sp[-1] = proto;
16875             }
16876             BREAK;
16877 
16878         CASE(OP_import):
16879             {
16880                 JSValue val;
16881                 val = js_dynamic_import(ctx, sp[-1]);
16882                 if (JS_IsException(val))
16883                     goto exception;
16884                 JS_FreeValue(ctx, sp[-1]);
16885                 sp[-1] = val;
16886             }
16887             BREAK;
16888 
16889         CASE(OP_check_var):
16890             {
16891                 int ret;
16892                 JSAtom atom;
16893                 atom = get_u32(pc);
16894                 pc += 4;
16895 
16896                 ret = JS_CheckGlobalVar(ctx, atom);
16897                 if (ret < 0)
16898                     goto exception;
16899                 *sp++ = JS_NewBool(ctx, ret);
16900             }
16901             BREAK;
16902 
16903         CASE(OP_get_var_undef):
16904         CASE(OP_get_var):
16905             {
16906                 JSValue val;
16907                 JSAtom atom;
16908                 atom = get_u32(pc);
16909                 pc += 4;
16910 
16911                 val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef);
16912                 if (unlikely(JS_IsException(val)))
16913                     goto exception;
16914                 *sp++ = val;
16915             }
16916             BREAK;
16917 
16918         CASE(OP_put_var):
16919         CASE(OP_put_var_init):
16920             {
16921                 int ret;
16922                 JSAtom atom;
16923                 atom = get_u32(pc);
16924                 pc += 4;
16925 
16926                 ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var);
16927                 sp--;
16928                 if (unlikely(ret < 0))
16929                     goto exception;
16930             }
16931             BREAK;
16932 
16933         CASE(OP_put_var_strict):
16934             {
16935                 int ret;
16936                 JSAtom atom;
16937                 atom = get_u32(pc);
16938                 pc += 4;
16939 
16940                 /* sp[-2] is JS_TRUE or JS_FALSE */
16941                 if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) {
16942                     JS_ThrowReferenceErrorNotDefined(ctx, atom);
16943                     goto exception;
16944                 }
16945                 ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2);
16946                 sp -= 2;
16947                 if (unlikely(ret < 0))
16948                     goto exception;
16949             }
16950             BREAK;
16951 
16952         CASE(OP_check_define_var):
16953             {
16954                 JSAtom atom;
16955                 int flags;
16956                 atom = get_u32(pc);
16957                 flags = pc[4];
16958                 pc += 5;
16959                 if (JS_CheckDefineGlobalVar(ctx, atom, flags))
16960                     goto exception;
16961             }
16962             BREAK;
16963         CASE(OP_define_var):
16964             {
16965                 JSAtom atom;
16966                 int flags;
16967                 atom = get_u32(pc);
16968                 flags = pc[4];
16969                 pc += 5;
16970                 if (JS_DefineGlobalVar(ctx, atom, flags))
16971                     goto exception;
16972             }
16973             BREAK;
16974         CASE(OP_define_func):
16975             {
16976                 JSAtom atom;
16977                 int flags;
16978                 atom = get_u32(pc);
16979                 flags = pc[4];
16980                 pc += 5;
16981                 if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags))
16982                     goto exception;
16983                 JS_FreeValue(ctx, sp[-1]);
16984                 sp--;
16985             }
16986             BREAK;
16987 
16988         CASE(OP_get_loc):
16989             {
16990                 int idx;
16991                 idx = get_u16(pc);
16992                 pc += 2;
16993                 sp[0] = JS_DupValue(ctx, var_buf[idx]);
16994                 sp++;
16995             }
16996             BREAK;
16997         CASE(OP_put_loc):
16998             {
16999                 int idx;
17000                 idx = get_u16(pc);
17001                 pc += 2;
17002                 set_value(ctx, &var_buf[idx], sp[-1]);
17003                 sp--;
17004             }
17005             BREAK;
17006         CASE(OP_set_loc):
17007             {
17008                 int idx;
17009                 idx = get_u16(pc);
17010                 pc += 2;
17011                 set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1]));
17012             }
17013             BREAK;
17014         CASE(OP_get_arg):
17015             {
17016                 int idx;
17017                 idx = get_u16(pc);
17018                 pc += 2;
17019                 sp[0] = JS_DupValue(ctx, arg_buf[idx]);
17020                 sp++;
17021             }
17022             BREAK;
17023         CASE(OP_put_arg):
17024             {
17025                 int idx;
17026                 idx = get_u16(pc);
17027                 pc += 2;
17028                 set_value(ctx, &arg_buf[idx], sp[-1]);
17029                 sp--;
17030             }
17031             BREAK;
17032         CASE(OP_set_arg):
17033             {
17034                 int idx;
17035                 idx = get_u16(pc);
17036                 pc += 2;
17037                 set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1]));
17038             }
17039             BREAK;
17040 
17041 #if SHORT_OPCODES
17042         CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK;
17043         CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK;
17044         CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK;
17045 
17046         CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK;
17047         CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK;
17048         CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK;
17049         CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK;
17050         CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK;
17051         CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK;
17052         CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK;
17053         CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK;
17054         CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
17055         CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
17056         CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
17057         CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
17058         CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK;
17059         CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK;
17060         CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK;
17061         CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK;
17062         CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK;
17063         CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK;
17064         CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK;
17065         CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK;
17066         CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
17067         CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
17068         CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
17069         CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
17070         CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK;
17071         CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK;
17072         CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK;
17073         CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK;
17074         CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK;
17075         CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK;
17076         CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK;
17077         CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK;
17078         CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
17079         CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
17080         CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
17081         CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
17082 #endif
17083 
17084         CASE(OP_get_var_ref):
17085             {
17086                 int idx;
17087                 JSValue val;
17088                 idx = get_u16(pc);
17089                 pc += 2;
17090                 val = *var_refs[idx]->pvalue;
17091                 sp[0] = JS_DupValue(ctx, val);
17092                 sp++;
17093             }
17094             BREAK;
17095         CASE(OP_put_var_ref):
17096             {
17097                 int idx;
17098                 idx = get_u16(pc);
17099                 pc += 2;
17100                 set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
17101                 sp--;
17102             }
17103             BREAK;
17104         CASE(OP_set_var_ref):
17105             {
17106                 int idx;
17107                 idx = get_u16(pc);
17108                 pc += 2;
17109                 set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1]));
17110             }
17111             BREAK;
17112         CASE(OP_get_var_ref_check):
17113             {
17114                 int idx;
17115                 JSValue val;
17116                 idx = get_u16(pc);
17117                 pc += 2;
17118                 val = *var_refs[idx]->pvalue;
17119                 if (unlikely(JS_IsUninitialized(val))) {
17120                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
17121                     goto exception;
17122                 }
17123                 sp[0] = JS_DupValue(ctx, val);
17124                 sp++;
17125             }
17126             BREAK;
17127         CASE(OP_put_var_ref_check):
17128             {
17129                 int idx;
17130                 idx = get_u16(pc);
17131                 pc += 2;
17132                 if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) {
17133                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
17134                     goto exception;
17135                 }
17136                 set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
17137                 sp--;
17138             }
17139             BREAK;
17140         CASE(OP_put_var_ref_check_init):
17141             {
17142                 int idx;
17143                 idx = get_u16(pc);
17144                 pc += 2;
17145                 if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) {
17146                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
17147                     goto exception;
17148                 }
17149                 set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
17150                 sp--;
17151             }
17152             BREAK;
17153         CASE(OP_set_loc_uninitialized):
17154             {
17155                 int idx;
17156                 idx = get_u16(pc);
17157                 pc += 2;
17158                 set_value(ctx, &var_buf[idx], JS_UNINITIALIZED);
17159             }
17160             BREAK;
17161         CASE(OP_get_loc_check):
17162             {
17163                 int idx;
17164                 idx = get_u16(pc);
17165                 pc += 2;
17166                 if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
17167                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
17168                     goto exception;
17169                 }
17170                 sp[0] = JS_DupValue(ctx, var_buf[idx]);
17171                 sp++;
17172             }
17173             BREAK;
17174         CASE(OP_put_loc_check):
17175             {
17176                 int idx;
17177                 idx = get_u16(pc);
17178                 pc += 2;
17179                 if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
17180                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
17181                     goto exception;
17182                 }
17183                 set_value(ctx, &var_buf[idx], sp[-1]);
17184                 sp--;
17185             }
17186             BREAK;
17187         CASE(OP_put_loc_check_init):
17188             {
17189                 int idx;
17190                 idx = get_u16(pc);
17191                 pc += 2;
17192                 if (unlikely(!JS_IsUninitialized(var_buf[idx]))) {
17193                     JS_ThrowReferenceError(ctx, "'this' can be initialized only once");
17194                     goto exception;
17195                 }
17196                 set_value(ctx, &var_buf[idx], sp[-1]);
17197                 sp--;
17198             }
17199             BREAK;
17200         CASE(OP_close_loc):
17201             {
17202                 int idx;
17203                 idx = get_u16(pc);
17204                 pc += 2;
17205                 close_lexical_var(ctx, sf, idx, FALSE);
17206             }
17207             BREAK;
17208 
17209         CASE(OP_make_loc_ref):
17210         CASE(OP_make_arg_ref):
17211         CASE(OP_make_var_ref_ref):
17212             {
17213                 JSVarRef *var_ref;
17214                 JSProperty *pr;
17215                 JSAtom atom;
17216                 int idx;
17217                 atom = get_u32(pc);
17218                 idx = get_u16(pc + 4);
17219                 pc += 6;
17220                 *sp++ = JS_NewObjectProto(ctx, JS_NULL);
17221                 if (unlikely(JS_IsException(sp[-1])))
17222                     goto exception;
17223                 if (opcode == OP_make_var_ref_ref) {
17224                     var_ref = var_refs[idx];
17225                     var_ref->header.ref_count++;
17226                 } else {
17227                     var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref);
17228                     if (!var_ref)
17229                         goto exception;
17230                 }
17231                 pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom,
17232                                   JS_PROP_WRITABLE | JS_PROP_VARREF);
17233                 if (!pr) {
17234                     free_var_ref(rt, var_ref);
17235                     goto exception;
17236                 }
17237                 pr->u.var_ref = var_ref;
17238                 *sp++ = JS_AtomToValue(ctx, atom);
17239             }
17240             BREAK;
17241         CASE(OP_make_var_ref):
17242             {
17243                 JSAtom atom;
17244                 atom = get_u32(pc);
17245                 pc += 4;
17246 
17247                 if (JS_GetGlobalVarRef(ctx, atom, sp))
17248                     goto exception;
17249                 sp += 2;
17250             }
17251             BREAK;
17252 
17253         CASE(OP_goto):
17254             pc += (int32_t)get_u32(pc);
17255             if (unlikely(js_poll_interrupts(ctx)))
17256                 goto exception;
17257             BREAK;
17258 #if SHORT_OPCODES
17259         CASE(OP_goto16):
17260             pc += (int16_t)get_u16(pc);
17261             if (unlikely(js_poll_interrupts(ctx)))
17262                 goto exception;
17263             BREAK;
17264         CASE(OP_goto8):
17265             pc += (int8_t)pc[0];
17266             if (unlikely(js_poll_interrupts(ctx)))
17267                 goto exception;
17268             BREAK;
17269 #endif
17270         CASE(OP_if_true):
17271             {
17272                 int res;
17273                 JSValue op1;
17274 
17275                 op1 = sp[-1];
17276                 pc += 4;
17277                 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
17278                     res = JS_VALUE_GET_INT(op1);
17279                 } else {
17280                     res = JS_ToBoolFree(ctx, op1);
17281                 }
17282                 sp--;
17283                 if (res) {
17284                     pc += (int32_t)get_u32(pc - 4) - 4;
17285                 }
17286                 if (unlikely(js_poll_interrupts(ctx)))
17287                     goto exception;
17288             }
17289             BREAK;
17290         CASE(OP_if_false):
17291             {
17292                 int res;
17293                 JSValue op1;
17294 
17295                 op1 = sp[-1];
17296                 pc += 4;
17297                 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
17298                     res = JS_VALUE_GET_INT(op1);
17299                 } else {
17300                     res = JS_ToBoolFree(ctx, op1);
17301                 }
17302                 sp--;
17303                 if (!res) {
17304                     pc += (int32_t)get_u32(pc - 4) - 4;
17305                 }
17306                 if (unlikely(js_poll_interrupts(ctx)))
17307                     goto exception;
17308             }
17309             BREAK;
17310 #if SHORT_OPCODES
17311         CASE(OP_if_true8):
17312             {
17313                 int res;
17314                 JSValue op1;
17315 
17316                 op1 = sp[-1];
17317                 pc += 1;
17318                 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
17319                     res = JS_VALUE_GET_INT(op1);
17320                 } else {
17321                     res = JS_ToBoolFree(ctx, op1);
17322                 }
17323                 sp--;
17324                 if (res) {
17325                     pc += (int8_t)pc[-1] - 1;
17326                 }
17327                 if (unlikely(js_poll_interrupts(ctx)))
17328                     goto exception;
17329             }
17330             BREAK;
17331         CASE(OP_if_false8):
17332             {
17333                 int res;
17334                 JSValue op1;
17335 
17336                 op1 = sp[-1];
17337                 pc += 1;
17338                 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
17339                     res = JS_VALUE_GET_INT(op1);
17340                 } else {
17341                     res = JS_ToBoolFree(ctx, op1);
17342                 }
17343                 sp--;
17344                 if (!res) {
17345                     pc += (int8_t)pc[-1] - 1;
17346                 }
17347                 if (unlikely(js_poll_interrupts(ctx)))
17348                     goto exception;
17349             }
17350             BREAK;
17351 #endif
17352         CASE(OP_catch):
17353             {
17354                 int32_t diff;
17355                 diff = get_u32(pc);
17356                 sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf);
17357                 sp++;
17358                 pc += 4;
17359             }
17360             BREAK;
17361         CASE(OP_gosub):
17362             {
17363                 int32_t diff;
17364                 diff = get_u32(pc);
17365                 /* XXX: should have a different tag to avoid security flaw */
17366                 sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf);
17367                 sp++;
17368                 pc += diff;
17369             }
17370             BREAK;
17371         CASE(OP_ret):
17372             {
17373                 JSValue op1;
17374                 uint32_t pos;
17375                 op1 = sp[-1];
17376                 if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT))
17377                     goto ret_fail;
17378                 pos = JS_VALUE_GET_INT(op1);
17379                 if (unlikely(pos >= b->byte_code_len)) {
17380                 ret_fail:
17381                     JS_ThrowInternalError(ctx, "invalid ret value");
17382                     goto exception;
17383                 }
17384                 sp--;
17385                 pc = b->byte_code_buf + pos;
17386             }
17387             BREAK;
17388 
17389         CASE(OP_for_in_start):
17390             if (js_for_in_start(ctx, sp))
17391                 goto exception;
17392             BREAK;
17393         CASE(OP_for_in_next):
17394             if (js_for_in_next(ctx, sp))
17395                 goto exception;
17396             sp += 2;
17397             BREAK;
17398         CASE(OP_for_of_start):
17399             if (js_for_of_start(ctx, sp, FALSE))
17400                 goto exception;
17401             sp += 1;
17402             *sp++ = JS_NewCatchOffset(ctx, 0);
17403             BREAK;
17404         CASE(OP_for_of_next):
17405             {
17406                 int offset = -3 - pc[0];
17407                 pc += 1;
17408                 if (js_for_of_next(ctx, sp, offset))
17409                     goto exception;
17410                 sp += 2;
17411             }
17412             BREAK;
17413         CASE(OP_for_await_of_start):
17414             if (js_for_of_start(ctx, sp, TRUE))
17415                 goto exception;
17416             sp += 1;
17417             *sp++ = JS_NewCatchOffset(ctx, 0);
17418             BREAK;
17419         CASE(OP_iterator_get_value_done):
17420             if (js_iterator_get_value_done(ctx, sp))
17421                 goto exception;
17422             sp += 1;
17423             BREAK;
17424         CASE(OP_iterator_check_object):
17425             if (unlikely(!JS_IsObject(sp[-1]))) {
17426                 JS_ThrowTypeError(ctx, "iterator must return an object");
17427                 goto exception;
17428             }
17429             BREAK;
17430 
17431         CASE(OP_iterator_close):
17432             /* iter_obj next catch_offset -> */
17433             sp--; /* drop the catch offset to avoid getting caught by exception */
17434             JS_FreeValue(ctx, sp[-1]); /* drop the next method */
17435             sp--;
17436             if (!JS_IsUndefined(sp[-1])) {
17437                 if (JS_IteratorClose(ctx, sp[-1], FALSE))
17438                     goto exception;
17439                 JS_FreeValue(ctx, sp[-1]);
17440             }
17441             sp--;
17442             BREAK;
17443         CASE(OP_iterator_close_return):
17444             {
17445                 JSValue ret_val;
17446                 /* iter_obj next catch_offset ... ret_val ->
17447                    ret_eval iter_obj next catch_offset */
17448                 ret_val = *--sp;
17449                 while (sp > stack_buf &&
17450                        JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {
17451                     JS_FreeValue(ctx, *--sp);
17452                 }
17453                 if (unlikely(sp < stack_buf + 3)) {
17454                     JS_ThrowInternalError(ctx, "iterator_close_return");
17455                     JS_FreeValue(ctx, ret_val);
17456                     goto exception;
17457                 }
17458                 sp[0] = sp[-1];
17459                 sp[-1] = sp[-2];
17460                 sp[-2] = sp[-3];
17461                 sp[-3] = ret_val;
17462                 sp++;
17463             }
17464             BREAK;
17465 
17466         CASE(OP_iterator_next):
17467             /* stack: iter_obj next catch_offset val */
17468             {
17469                 JSValue ret;
17470                 ret = JS_Call(ctx, sp[-3], sp[-4],
17471                               1, (JSValueConst *)(sp - 1));
17472                 if (JS_IsException(ret))
17473                     goto exception;
17474                 JS_FreeValue(ctx, sp[-1]);
17475                 sp[-1] = ret;
17476             }
17477             BREAK;
17478 
17479         CASE(OP_iterator_call):
17480             /* stack: iter_obj next catch_offset val */
17481             {
17482                 JSValue method, ret;
17483                 BOOL ret_flag;
17484                 int flags;
17485                 flags = *pc++;
17486                 method = JS_GetProperty(ctx, sp[-4], (flags & 1) ?
17487                                         JS_ATOM_throw : JS_ATOM_return);
17488                 if (JS_IsException(method))
17489                     goto exception;
17490                 if (JS_IsUndefined(method) || JS_IsNull(method)) {
17491                     ret_flag = TRUE;
17492                 } else {
17493                     if (flags & 2) {
17494                         /* no argument */
17495                         ret = JS_CallFree(ctx, method, sp[-4],
17496                                           0, NULL);
17497                     } else {
17498                         ret = JS_CallFree(ctx, method, sp[-4],
17499                                           1, (JSValueConst *)(sp - 1));
17500                     }
17501                     if (JS_IsException(ret))
17502                         goto exception;
17503                     JS_FreeValue(ctx, sp[-1]);
17504                     sp[-1] = ret;
17505                     ret_flag = FALSE;
17506                 }
17507                 sp[0] = JS_NewBool(ctx, ret_flag);
17508                 sp += 1;
17509             }
17510             BREAK;
17511 
17512         CASE(OP_lnot):
17513             {
17514                 int res;
17515                 JSValue op1;
17516 
17517                 op1 = sp[-1];
17518                 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
17519                     res = JS_VALUE_GET_INT(op1) != 0;
17520                 } else {
17521                     res = JS_ToBoolFree(ctx, op1);
17522                 }
17523                 sp[-1] = JS_NewBool(ctx, !res);
17524             }
17525             BREAK;
17526 
17527         CASE(OP_get_field):
17528             {
17529                 JSValue val;
17530                 JSAtom atom;
17531                 atom = get_u32(pc);
17532                 pc += 4;
17533 
17534                 val = JS_GetProperty(ctx, sp[-1], atom);
17535                 if (unlikely(JS_IsException(val)))
17536                     goto exception;
17537                 JS_FreeValue(ctx, sp[-1]);
17538                 sp[-1] = val;
17539             }
17540             BREAK;
17541 
17542         CASE(OP_get_field2):
17543             {
17544                 JSValue val;
17545                 JSAtom atom;
17546                 atom = get_u32(pc);
17547                 pc += 4;
17548 
17549                 val = JS_GetProperty(ctx, sp[-1], atom);
17550                 if (unlikely(JS_IsException(val)))
17551                     goto exception;
17552                 *sp++ = val;
17553             }
17554             BREAK;
17555 
17556         CASE(OP_put_field):
17557             {
17558                 int ret;
17559                 JSAtom atom;
17560                 atom = get_u32(pc);
17561                 pc += 4;
17562 
17563                 ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1],
17564                                              JS_PROP_THROW_STRICT);
17565                 JS_FreeValue(ctx, sp[-2]);
17566                 sp -= 2;
17567                 if (unlikely(ret < 0))
17568                     goto exception;
17569             }
17570             BREAK;
17571 
17572         CASE(OP_private_symbol):
17573             {
17574                 JSAtom atom;
17575                 JSValue val;
17576 
17577                 atom = get_u32(pc);
17578                 pc += 4;
17579                 val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE);
17580                 if (JS_IsException(val))
17581                     goto exception;
17582                 *sp++ = val;
17583             }
17584             BREAK;
17585 
17586         CASE(OP_get_private_field):
17587             {
17588                 JSValue val;
17589 
17590                 val = JS_GetPrivateField(ctx, sp[-2], sp[-1]);
17591                 JS_FreeValue(ctx, sp[-1]);
17592                 JS_FreeValue(ctx, sp[-2]);
17593                 sp[-2] = val;
17594                 sp--;
17595                 if (unlikely(JS_IsException(val)))
17596                     goto exception;
17597             }
17598             BREAK;
17599 
17600         CASE(OP_put_private_field):
17601             {
17602                 int ret;
17603                 ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]);
17604                 JS_FreeValue(ctx, sp[-3]);
17605                 JS_FreeValue(ctx, sp[-1]);
17606                 sp -= 3;
17607                 if (unlikely(ret < 0))
17608                     goto exception;
17609             }
17610             BREAK;
17611 
17612         CASE(OP_define_private_field):
17613             {
17614                 int ret;
17615                 ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]);
17616                 JS_FreeValue(ctx, sp[-2]);
17617                 sp -= 2;
17618                 if (unlikely(ret < 0))
17619                     goto exception;
17620             }
17621             BREAK;
17622 
17623         CASE(OP_define_field):
17624             {
17625                 int ret;
17626                 JSAtom atom;
17627                 atom = get_u32(pc);
17628                 pc += 4;
17629 
17630                 ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1],
17631                                              JS_PROP_C_W_E | JS_PROP_THROW);
17632                 sp--;
17633                 if (unlikely(ret < 0))
17634                     goto exception;
17635             }
17636             BREAK;
17637 
17638         CASE(OP_set_name):
17639             {
17640                 int ret;
17641                 JSAtom atom;
17642                 atom = get_u32(pc);
17643                 pc += 4;
17644 
17645                 ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE);
17646                 if (unlikely(ret < 0))
17647                     goto exception;
17648             }
17649             BREAK;
17650         CASE(OP_set_name_computed):
17651             {
17652                 int ret;
17653                 ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE);
17654                 if (unlikely(ret < 0))
17655                     goto exception;
17656             }
17657             BREAK;
17658         CASE(OP_set_proto):
17659             {
17660                 JSValue proto;
17661                 proto = sp[-1];
17662                 if (JS_IsObject(proto) || JS_IsNull(proto)) {
17663                     if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0)
17664                         goto exception;
17665                 }
17666                 JS_FreeValue(ctx, proto);
17667                 sp--;
17668             }
17669             BREAK;
17670         CASE(OP_set_home_object):
17671             js_method_set_home_object(ctx, sp[-1], sp[-2]);
17672             BREAK;
17673         CASE(OP_define_method):
17674         CASE(OP_define_method_computed):
17675             {
17676                 JSValue getter, setter, value;
17677                 JSValueConst obj;
17678                 JSAtom atom;
17679                 int flags, ret, op_flags;
17680                 BOOL is_computed;
17681 #define OP_DEFINE_METHOD_METHOD 0
17682 #define OP_DEFINE_METHOD_GETTER 1
17683 #define OP_DEFINE_METHOD_SETTER 2
17684 #define OP_DEFINE_METHOD_ENUMERABLE 4
17685 
17686                 is_computed = (opcode == OP_define_method_computed);
17687                 if (is_computed) {
17688                     atom = JS_ValueToAtom(ctx, sp[-2]);
17689                     if (unlikely(atom == JS_ATOM_NULL))
17690                         goto exception;
17691                     opcode += OP_define_method - OP_define_method_computed;
17692                 } else {
17693                     atom = get_u32(pc);
17694                     pc += 4;
17695                 }
17696                 op_flags = *pc++;
17697 
17698                 obj = sp[-2 - is_computed];
17699                 flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE |
17700                     JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW;
17701                 if (op_flags & OP_DEFINE_METHOD_ENUMERABLE)
17702                     flags |= JS_PROP_ENUMERABLE;
17703                 op_flags &= 3;
17704                 value = JS_UNDEFINED;
17705                 getter = JS_UNDEFINED;
17706                 setter = JS_UNDEFINED;
17707                 if (op_flags == OP_DEFINE_METHOD_METHOD) {
17708                     value = sp[-1];
17709                     flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE;
17710                 } else if (op_flags == OP_DEFINE_METHOD_GETTER) {
17711                     getter = sp[-1];
17712                     flags |= JS_PROP_HAS_GET;
17713                 } else {
17714                     setter = sp[-1];
17715                     flags |= JS_PROP_HAS_SET;
17716                 }
17717                 ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj);
17718                 if (ret >= 0) {
17719                     ret = JS_DefineProperty(ctx, obj, atom, value,
17720                                             getter, setter, flags);
17721                 }
17722                 JS_FreeValue(ctx, sp[-1]);
17723                 if (is_computed) {
17724                     JS_FreeAtom(ctx, atom);
17725                     JS_FreeValue(ctx, sp[-2]);
17726                 }
17727                 sp -= 1 + is_computed;
17728                 if (unlikely(ret < 0))
17729                     goto exception;
17730             }
17731             BREAK;
17732 
17733         CASE(OP_define_class):
17734         CASE(OP_define_class_computed):
17735             {
17736                 int class_flags;
17737                 JSAtom atom;
17738 
17739                 atom = get_u32(pc);
17740                 class_flags = pc[4];
17741                 pc += 5;
17742                 if (js_op_define_class(ctx, sp, atom, class_flags,
17743                                        var_refs, sf,
17744                                        (opcode == OP_define_class_computed)) < 0)
17745                     goto exception;
17746             }
17747             BREAK;
17748 
17749         CASE(OP_get_array_el):
17750             {
17751                 JSValue val;
17752 
17753                 val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
17754                 JS_FreeValue(ctx, sp[-2]);
17755                 sp[-2] = val;
17756                 sp--;
17757                 if (unlikely(JS_IsException(val)))
17758                     goto exception;
17759             }
17760             BREAK;
17761 
17762         CASE(OP_get_array_el2):
17763             {
17764                 JSValue val;
17765 
17766                 val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
17767                 sp[-1] = val;
17768                 if (unlikely(JS_IsException(val)))
17769                     goto exception;
17770             }
17771             BREAK;
17772 
17773         CASE(OP_get_ref_value):
17774             {
17775                 JSValue val;
17776                 if (unlikely(JS_IsUndefined(sp[-2]))) {
17777                     JSAtom atom = JS_ValueToAtom(ctx, sp[-1]);
17778                     if (atom != JS_ATOM_NULL) {
17779                         JS_ThrowReferenceErrorNotDefined(ctx, atom);
17780                         JS_FreeAtom(ctx, atom);
17781                     }
17782                     goto exception;
17783                 }
17784                 val = JS_GetPropertyValue(ctx, sp[-2],
17785                                           JS_DupValue(ctx, sp[-1]));
17786                 if (unlikely(JS_IsException(val)))
17787                     goto exception;
17788                 sp[0] = val;
17789                 sp++;
17790             }
17791             BREAK;
17792 
17793         CASE(OP_get_super_value):
17794             {
17795                 JSValue val;
17796                 JSAtom atom;
17797                 atom = JS_ValueToAtom(ctx, sp[-1]);
17798                 if (unlikely(atom == JS_ATOM_NULL))
17799                     goto exception;
17800                 val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE);
17801                 JS_FreeAtom(ctx, atom);
17802                 if (unlikely(JS_IsException(val)))
17803                     goto exception;
17804                 JS_FreeValue(ctx, sp[-1]);
17805                 JS_FreeValue(ctx, sp[-2]);
17806                 JS_FreeValue(ctx, sp[-3]);
17807                 sp[-3] = val;
17808                 sp -= 2;
17809             }
17810             BREAK;
17811 
17812         CASE(OP_put_array_el):
17813             {
17814                 int ret;
17815 
17816                 ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);
17817                 JS_FreeValue(ctx, sp[-3]);
17818                 sp -= 3;
17819                 if (unlikely(ret < 0))
17820                     goto exception;
17821             }
17822             BREAK;
17823 
17824         CASE(OP_put_ref_value):
17825             {
17826                 int ret, flags;
17827                 flags = JS_PROP_THROW_STRICT;
17828                 if (unlikely(JS_IsUndefined(sp[-3]))) {
17829                     if (is_strict_mode(ctx)) {
17830                         JSAtom atom = JS_ValueToAtom(ctx, sp[-2]);
17831                         if (atom != JS_ATOM_NULL) {
17832                             JS_ThrowReferenceErrorNotDefined(ctx, atom);
17833                             JS_FreeAtom(ctx, atom);
17834                         }
17835                         goto exception;
17836                     } else {
17837                         sp[-3] = JS_DupValue(ctx, ctx->global_obj);
17838                     }
17839                 } else {
17840                     if (is_strict_mode(ctx))
17841                         flags |= JS_PROP_NO_ADD;
17842                 }
17843                 ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags);
17844                 JS_FreeValue(ctx, sp[-3]);
17845                 sp -= 3;
17846                 if (unlikely(ret < 0))
17847                     goto exception;
17848             }
17849             BREAK;
17850 
17851         CASE(OP_put_super_value):
17852             {
17853                 int ret;
17854                 JSAtom atom;
17855                 if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) {
17856                     JS_ThrowTypeErrorNotAnObject(ctx);
17857                     goto exception;
17858                 }
17859                 atom = JS_ValueToAtom(ctx, sp[-2]);
17860                 if (unlikely(atom == JS_ATOM_NULL))
17861                     goto exception;
17862                 ret = JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(sp[-3]),
17863                                             atom, sp[-1], sp[-4],
17864                                             JS_PROP_THROW_STRICT);
17865                 JS_FreeAtom(ctx, atom);
17866                 JS_FreeValue(ctx, sp[-4]);
17867                 JS_FreeValue(ctx, sp[-3]);
17868                 JS_FreeValue(ctx, sp[-2]);
17869                 sp -= 4;
17870                 if (ret < 0)
17871                     goto exception;
17872             }
17873             BREAK;
17874 
17875         CASE(OP_define_array_el):
17876             {
17877                 int ret;
17878                 ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1],
17879                                                   JS_PROP_C_W_E | JS_PROP_THROW);
17880                 sp -= 1;
17881                 if (unlikely(ret < 0))
17882                     goto exception;
17883             }
17884             BREAK;
17885 
17886         CASE(OP_append):    /* array pos enumobj -- array pos */
17887             {
17888                 if (js_append_enumerate(ctx, sp))
17889                     goto exception;
17890                 JS_FreeValue(ctx, *--sp);
17891             }
17892             BREAK;
17893 
17894         CASE(OP_copy_data_properties):    /* target source excludeList */
17895             {
17896                 /* stack offsets (-1 based):
17897                    2 bits for target,
17898                    3 bits for source,
17899                    2 bits for exclusionList */
17900                 int mask;
17901 
17902                 mask = *pc++;
17903                 if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)],
17904                                           sp[-1 - ((mask >> 2) & 7)],
17905                                           sp[-1 - ((mask >> 5) & 7)], 0))
17906                     goto exception;
17907             }
17908             BREAK;
17909 
17910         CASE(OP_add):
17911             {
17912                 JSValue op1, op2;
17913                 op1 = sp[-2];
17914                 op2 = sp[-1];
17915                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
17916                     int64_t r;
17917                     r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2);
17918                     if (unlikely((int)r != r))
17919                         goto add_slow;
17920                     sp[-2] = JS_NewInt32(ctx, r);
17921                     sp--;
17922                 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
17923                     sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) +
17924                                              JS_VALUE_GET_FLOAT64(op2));
17925                     sp--;
17926                 } else {
17927                 add_slow:
17928                     if (js_add_slow(ctx, sp))
17929                         goto exception;
17930                     sp--;
17931                 }
17932             }
17933             BREAK;
17934         CASE(OP_add_loc):
17935             {
17936                 JSValue *pv;
17937                 int idx;
17938                 idx = *pc;
17939                 pc += 1;
17940 
17941                 pv = &var_buf[idx];
17942                 if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) {
17943                     int64_t r;
17944                     r = (int64_t)JS_VALUE_GET_INT(*pv) +
17945                         JS_VALUE_GET_INT(sp[-1]);
17946                     if (unlikely((int)r != r))
17947                         goto add_loc_slow;
17948                     *pv = JS_NewInt32(ctx, r);
17949                     sp--;
17950                 } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) {
17951                     JSValue op1;
17952                     op1 = sp[-1];
17953                     sp--;
17954                     op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
17955                     if (JS_IsException(op1))
17956                         goto exception;
17957                     op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1);
17958                     if (JS_IsException(op1))
17959                         goto exception;
17960                     set_value(ctx, pv, op1);
17961                 } else {
17962                     JSValue ops[2];
17963                 add_loc_slow:
17964                     /* In case of exception, js_add_slow frees ops[0]
17965                        and ops[1], so we must duplicate *pv */
17966                     ops[0] = JS_DupValue(ctx, *pv);
17967                     ops[1] = sp[-1];
17968                     sp--;
17969                     if (js_add_slow(ctx, ops + 2))
17970                         goto exception;
17971                     set_value(ctx, pv, ops[0]);
17972                 }
17973             }
17974             BREAK;
17975         CASE(OP_sub):
17976             {
17977                 JSValue op1, op2;
17978                 op1 = sp[-2];
17979                 op2 = sp[-1];
17980                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
17981                     int64_t r;
17982                     r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2);
17983                     if (unlikely((int)r != r))
17984                         goto binary_arith_slow;
17985                     sp[-2] = JS_NewInt32(ctx, r);
17986                     sp--;
17987                 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
17988                     sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) -
17989                                              JS_VALUE_GET_FLOAT64(op2));
17990                     sp--;
17991                 } else {
17992                     goto binary_arith_slow;
17993                 }
17994             }
17995             BREAK;
17996         CASE(OP_mul):
17997             {
17998                 JSValue op1, op2;
17999                 double d;
18000                 op1 = sp[-2];
18001                 op2 = sp[-1];
18002                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18003                     int32_t v1, v2;
18004                     int64_t r;
18005                     v1 = JS_VALUE_GET_INT(op1);
18006                     v2 = JS_VALUE_GET_INT(op2);
18007                     r = (int64_t)v1 * v2;
18008                     if (unlikely((int)r != r)) {
18009 #ifdef CONFIG_BIGNUM
18010                         if (unlikely(sf->js_mode & JS_MODE_MATH) &&
18011                             (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER))
18012                             goto binary_arith_slow;
18013 #endif
18014                         d = (double)r;
18015                         goto mul_fp_res;
18016                     }
18017                     /* need to test zero case for -0 result */
18018                     if (unlikely(r == 0 && (v1 | v2) < 0)) {
18019                         d = -0.0;
18020                         goto mul_fp_res;
18021                     }
18022                     sp[-2] = JS_NewInt32(ctx, r);
18023                     sp--;
18024                 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
18025 #ifdef CONFIG_BIGNUM
18026                     if (unlikely(sf->js_mode & JS_MODE_MATH))
18027                         goto binary_arith_slow;
18028 #endif
18029                     d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);
18030                 mul_fp_res:
18031                     sp[-2] = __JS_NewFloat64(ctx, d);
18032                     sp--;
18033                 } else {
18034                     goto binary_arith_slow;
18035                 }
18036             }
18037             BREAK;
18038         CASE(OP_div):
18039             {
18040                 JSValue op1, op2;
18041                 op1 = sp[-2];
18042                 op2 = sp[-1];
18043                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18044                     int v1, v2;
18045                     if (unlikely(sf->js_mode & JS_MODE_MATH))
18046                         goto binary_arith_slow;
18047                     v1 = JS_VALUE_GET_INT(op1);
18048                     v2 = JS_VALUE_GET_INT(op2);
18049                     sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
18050                     sp--;
18051                 } else {
18052                     goto binary_arith_slow;
18053                 }
18054             }
18055             BREAK;
18056         CASE(OP_mod):
18057 #ifdef CONFIG_BIGNUM
18058         CASE(OP_math_mod):
18059 #endif
18060             {
18061                 JSValue op1, op2;
18062                 op1 = sp[-2];
18063                 op2 = sp[-1];
18064                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18065                     int v1, v2, r;
18066                     v1 = JS_VALUE_GET_INT(op1);
18067                     v2 = JS_VALUE_GET_INT(op2);
18068                     /* We must avoid v2 = 0, v1 = INT32_MIN and v2 =
18069                        -1 and the cases where the result is -0. */
18070                     if (unlikely(v1 < 0 || v2 <= 0))
18071                         goto binary_arith_slow;
18072                     r = v1 % v2;
18073                     sp[-2] = JS_NewInt32(ctx, r);
18074                     sp--;
18075                 } else {
18076                     goto binary_arith_slow;
18077                 }
18078             }
18079             BREAK;
18080         CASE(OP_pow):
18081         binary_arith_slow:
18082             if (js_binary_arith_slow(ctx, sp, opcode))
18083                 goto exception;
18084             sp--;
18085             BREAK;
18086 
18087         CASE(OP_plus):
18088             {
18089                 JSValue op1;
18090                 uint32_t tag;
18091                 op1 = sp[-1];
18092                 tag = JS_VALUE_GET_TAG(op1);
18093                 if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) {
18094                 } else {
18095                     if (js_unary_arith_slow(ctx, sp, opcode))
18096                         goto exception;
18097                 }
18098             }
18099             BREAK;
18100         CASE(OP_neg):
18101             {
18102                 JSValue op1;
18103                 uint32_t tag;
18104                 int val;
18105                 double d;
18106                 op1 = sp[-1];
18107                 tag = JS_VALUE_GET_TAG(op1);
18108                 if (tag == JS_TAG_INT) {
18109                     val = JS_VALUE_GET_INT(op1);
18110                     /* Note: -0 cannot be expressed as integer */
18111                     if (unlikely(val == 0)) {
18112                         d = -0.0;
18113                         goto neg_fp_res;
18114                     }
18115                     if (unlikely(val == INT32_MIN)) {
18116                         d = -(double)val;
18117                         goto neg_fp_res;
18118                     }
18119                     sp[-1] = JS_NewInt32(ctx, -val);
18120                 } else if (JS_TAG_IS_FLOAT64(tag)) {
18121                     d = -JS_VALUE_GET_FLOAT64(op1);
18122                 neg_fp_res:
18123                     sp[-1] = __JS_NewFloat64(ctx, d);
18124                 } else {
18125                     if (js_unary_arith_slow(ctx, sp, opcode))
18126                         goto exception;
18127                 }
18128             }
18129             BREAK;
18130         CASE(OP_inc):
18131             {
18132                 JSValue op1;
18133                 int val;
18134                 op1 = sp[-1];
18135                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18136                     val = JS_VALUE_GET_INT(op1);
18137                     if (unlikely(val == INT32_MAX))
18138                         goto inc_slow;
18139                     sp[-1] = JS_NewInt32(ctx, val + 1);
18140                 } else {
18141                 inc_slow:
18142                     if (js_unary_arith_slow(ctx, sp, opcode))
18143                         goto exception;
18144                 }
18145             }
18146             BREAK;
18147         CASE(OP_dec):
18148             {
18149                 JSValue op1;
18150                 int val;
18151                 op1 = sp[-1];
18152                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18153                     val = JS_VALUE_GET_INT(op1);
18154                     if (unlikely(val == INT32_MIN))
18155                         goto dec_slow;
18156                     sp[-1] = JS_NewInt32(ctx, val - 1);
18157                 } else {
18158                 dec_slow:
18159                     if (js_unary_arith_slow(ctx, sp, opcode))
18160                         goto exception;
18161                 }
18162             }
18163             BREAK;
18164         CASE(OP_post_inc):
18165         CASE(OP_post_dec):
18166             if (js_post_inc_slow(ctx, sp, opcode))
18167                 goto exception;
18168             sp++;
18169             BREAK;
18170         CASE(OP_inc_loc):
18171             {
18172                 JSValue op1;
18173                 int val;
18174                 int idx;
18175                 idx = *pc;
18176                 pc += 1;
18177 
18178                 op1 = var_buf[idx];
18179                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18180                     val = JS_VALUE_GET_INT(op1);
18181                     if (unlikely(val == INT32_MAX))
18182                         goto inc_loc_slow;
18183                     var_buf[idx] = JS_NewInt32(ctx, val + 1);
18184                 } else {
18185                 inc_loc_slow:
18186                     /* must duplicate otherwise the variable value may
18187                        be destroyed before JS code accesses it */
18188                     op1 = JS_DupValue(ctx, op1);
18189                     if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc))
18190                         goto exception;
18191                     set_value(ctx, &var_buf[idx], op1);
18192                 }
18193             }
18194             BREAK;
18195         CASE(OP_dec_loc):
18196             {
18197                 JSValue op1;
18198                 int val;
18199                 int idx;
18200                 idx = *pc;
18201                 pc += 1;
18202 
18203                 op1 = var_buf[idx];
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_loc_slow;
18208                     var_buf[idx] = JS_NewInt32(ctx, val - 1);
18209                 } else {
18210                 dec_loc_slow:
18211                     /* must duplicate otherwise the variable value may
18212                        be destroyed before JS code accesses it */
18213                     op1 = JS_DupValue(ctx, op1);
18214                     if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec))
18215                         goto exception;
18216                     set_value(ctx, &var_buf[idx], op1);
18217                 }
18218             }
18219             BREAK;
18220         CASE(OP_not):
18221             {
18222                 JSValue op1;
18223                 op1 = sp[-1];
18224                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18225                     sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1));
18226                 } else {
18227                     if (js_not_slow(ctx, sp))
18228                         goto exception;
18229                 }
18230             }
18231             BREAK;
18232 
18233         CASE(OP_shl):
18234             {
18235                 JSValue op1, op2;
18236                 op1 = sp[-2];
18237                 op2 = sp[-1];
18238                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18239                     uint32_t v1, v2;
18240                     v1 = JS_VALUE_GET_INT(op1);
18241                     v2 = JS_VALUE_GET_INT(op2);
18242 #ifdef CONFIG_BIGNUM
18243                     {
18244                         int64_t r;
18245                         if (unlikely(sf->js_mode & JS_MODE_MATH)) {
18246                             if (v2 > 0x1f)
18247                                 goto shl_slow;
18248                             r = (int64_t)v1 << v2;
18249                             if ((int)r != r)
18250                                 goto shl_slow;
18251                         } else {
18252                             v2 &= 0x1f;
18253                         }
18254                     }
18255 #else
18256                     v2 &= 0x1f;
18257 #endif
18258                     sp[-2] = JS_NewInt32(ctx, v1 << v2);
18259                     sp--;
18260                 } else {
18261 #ifdef CONFIG_BIGNUM
18262                 shl_slow:
18263 #endif
18264                     if (js_binary_logic_slow(ctx, sp, opcode))
18265                         goto exception;
18266                     sp--;
18267                 }
18268             }
18269             BREAK;
18270         CASE(OP_shr):
18271             {
18272                 JSValue op1, op2;
18273                 op1 = sp[-2];
18274                 op2 = sp[-1];
18275                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18276                     uint32_t v2;
18277                     v2 = JS_VALUE_GET_INT(op2);
18278                     /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */
18279                     v2 &= 0x1f;
18280                     sp[-2] = JS_NewUint32(ctx,
18281                                           (uint32_t)JS_VALUE_GET_INT(op1) >>
18282                                           v2);
18283                     sp--;
18284                 } else {
18285                     if (js_shr_slow(ctx, sp))
18286                         goto exception;
18287                     sp--;
18288                 }
18289             }
18290             BREAK;
18291         CASE(OP_sar):
18292             {
18293                 JSValue op1, op2;
18294                 op1 = sp[-2];
18295                 op2 = sp[-1];
18296                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18297                     uint32_t v2;
18298                     v2 = JS_VALUE_GET_INT(op2);
18299 #ifdef CONFIG_BIGNUM
18300                     if (unlikely(v2 > 0x1f)) {
18301                         if (unlikely(sf->js_mode & JS_MODE_MATH))
18302                             goto sar_slow;
18303                         else
18304                             v2 &= 0x1f;
18305                     }
18306 #else
18307                     v2 &= 0x1f;
18308 #endif
18309                     sp[-2] = JS_NewInt32(ctx,
18310                                           (int)JS_VALUE_GET_INT(op1) >> v2);
18311                     sp--;
18312                 } else {
18313 #ifdef CONFIG_BIGNUM
18314                 sar_slow:
18315 #endif
18316                     if (js_binary_logic_slow(ctx, sp, opcode))
18317                         goto exception;
18318                     sp--;
18319                 }
18320             }
18321             BREAK;
18322         CASE(OP_and):
18323             {
18324                 JSValue op1, op2;
18325                 op1 = sp[-2];
18326                 op2 = sp[-1];
18327                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18328                     sp[-2] = JS_NewInt32(ctx,
18329                                          JS_VALUE_GET_INT(op1) &
18330                                          JS_VALUE_GET_INT(op2));
18331                     sp--;
18332                 } else {
18333                     if (js_binary_logic_slow(ctx, sp, opcode))
18334                         goto exception;
18335                     sp--;
18336                 }
18337             }
18338             BREAK;
18339         CASE(OP_or):
18340             {
18341                 JSValue op1, op2;
18342                 op1 = sp[-2];
18343                 op2 = sp[-1];
18344                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18345                     sp[-2] = JS_NewInt32(ctx,
18346                                          JS_VALUE_GET_INT(op1) |
18347                                          JS_VALUE_GET_INT(op2));
18348                     sp--;
18349                 } else {
18350                     if (js_binary_logic_slow(ctx, sp, opcode))
18351                         goto exception;
18352                     sp--;
18353                 }
18354             }
18355             BREAK;
18356         CASE(OP_xor):
18357             {
18358                 JSValue op1, op2;
18359                 op1 = sp[-2];
18360                 op2 = sp[-1];
18361                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18362                     sp[-2] = JS_NewInt32(ctx,
18363                                          JS_VALUE_GET_INT(op1) ^
18364                                          JS_VALUE_GET_INT(op2));
18365                     sp--;
18366                 } else {
18367                     if (js_binary_logic_slow(ctx, sp, opcode))
18368                         goto exception;
18369                     sp--;
18370                 }
18371             }
18372             BREAK;
18373 
18374 
18375 #define OP_CMP(opcode, binary_op, slow_call)              \
18376             CASE(opcode):                                 \
18377                 {                                         \
18378                 JSValue op1, op2;                         \
18379                 op1 = sp[-2];                             \
18380                 op2 = sp[-1];                                   \
18381                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {           \
18382                     sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \
18383                     sp--;                                               \
18384                 } else {                                                \
18385                     if (slow_call)                                      \
18386                         goto exception;                                 \
18387                     sp--;                                               \
18388                 }                                                       \
18389                 }                                                       \
18390             BREAK
18391 
18392             OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode));
18393             OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode));
18394             OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode));
18395             OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode));
18396             OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0));
18397             OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1));
18398             OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0));
18399             OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1));
18400 
18401 #ifdef CONFIG_BIGNUM
18402         CASE(OP_mul_pow10):
18403             if (rt->bigfloat_ops.mul_pow10(ctx, sp))
18404                 goto exception;
18405             sp--;
18406             BREAK;
18407 #endif
18408         CASE(OP_in):
18409             if (js_operator_in(ctx, sp))
18410                 goto exception;
18411             sp--;
18412             BREAK;
18413         CASE(OP_instanceof):
18414             if (js_operator_instanceof(ctx, sp))
18415                 goto exception;
18416             sp--;
18417             BREAK;
18418         CASE(OP_typeof):
18419             {
18420                 JSValue op1;
18421                 JSAtom atom;
18422 
18423                 op1 = sp[-1];
18424                 atom = js_operator_typeof(ctx, op1);
18425                 JS_FreeValue(ctx, op1);
18426                 sp[-1] = JS_AtomToString(ctx, atom);
18427             }
18428             BREAK;
18429         CASE(OP_delete):
18430             if (js_operator_delete(ctx, sp))
18431                 goto exception;
18432             sp--;
18433             BREAK;
18434         CASE(OP_delete_var):
18435             {
18436                 JSAtom atom;
18437                 int ret;
18438 
18439                 atom = get_u32(pc);
18440                 pc += 4;
18441 
18442                 ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0);
18443                 if (unlikely(ret < 0))
18444                     goto exception;
18445                 *sp++ = JS_NewBool(ctx, ret);
18446             }
18447             BREAK;
18448 
18449         CASE(OP_to_object):
18450             if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) {
18451                 ret_val = JS_ToObject(ctx, sp[-1]);
18452                 if (JS_IsException(ret_val))
18453                     goto exception;
18454                 JS_FreeValue(ctx, sp[-1]);
18455                 sp[-1] = ret_val;
18456             }
18457             BREAK;
18458 
18459         CASE(OP_to_propkey):
18460             switch (JS_VALUE_GET_TAG(sp[-1])) {
18461             case JS_TAG_INT:
18462             case JS_TAG_STRING:
18463             case JS_TAG_SYMBOL:
18464                 break;
18465             default:
18466                 ret_val = JS_ToPropertyKey(ctx, sp[-1]);
18467                 if (JS_IsException(ret_val))
18468                     goto exception;
18469                 JS_FreeValue(ctx, sp[-1]);
18470                 sp[-1] = ret_val;
18471                 break;
18472             }
18473             BREAK;
18474 
18475         CASE(OP_to_propkey2):
18476             /* must be tested first */
18477             if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) {
18478                 JS_ThrowTypeError(ctx, "value has no property");
18479                 goto exception;
18480             }
18481             switch (JS_VALUE_GET_TAG(sp[-1])) {
18482             case JS_TAG_INT:
18483             case JS_TAG_STRING:
18484             case JS_TAG_SYMBOL:
18485                 break;
18486             default:
18487                 ret_val = JS_ToPropertyKey(ctx, sp[-1]);
18488                 if (JS_IsException(ret_val))
18489                     goto exception;
18490                 JS_FreeValue(ctx, sp[-1]);
18491                 sp[-1] = ret_val;
18492                 break;
18493             }
18494             BREAK;
18495 #if 0
18496         CASE(OP_to_string):
18497             if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) {
18498                 ret_val = JS_ToString(ctx, sp[-1]);
18499                 if (JS_IsException(ret_val))
18500                     goto exception;
18501                 JS_FreeValue(ctx, sp[-1]);
18502                 sp[-1] = ret_val;
18503             }
18504             BREAK;
18505 #endif
18506         CASE(OP_with_get_var):
18507         CASE(OP_with_put_var):
18508         CASE(OP_with_delete_var):
18509         CASE(OP_with_make_ref):
18510         CASE(OP_with_get_ref):
18511         CASE(OP_with_get_ref_undef):
18512             {
18513                 JSAtom atom;
18514                 int32_t diff;
18515                 JSValue obj, val;
18516                 int ret, is_with;
18517                 atom = get_u32(pc);
18518                 diff = get_u32(pc + 4);
18519                 is_with = pc[8];
18520                 pc += 9;
18521 
18522                 obj = sp[-1];
18523                 ret = JS_HasProperty(ctx, obj, atom);
18524                 if (unlikely(ret < 0))
18525                     goto exception;
18526                 if (ret) {
18527                     if (is_with) {
18528                         ret = js_has_unscopable(ctx, obj, atom);
18529                         if (unlikely(ret < 0))
18530                             goto exception;
18531                         if (ret)
18532                             goto no_with;
18533                     }
18534                     switch (opcode) {
18535                     case OP_with_get_var:
18536                         val = JS_GetProperty(ctx, obj, atom);
18537                         if (unlikely(JS_IsException(val)))
18538                             goto exception;
18539                         set_value(ctx, &sp[-1], val);
18540                         break;
18541                     case OP_with_put_var:
18542                         /* XXX: check if strict mode */
18543                         ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2],
18544                                                      JS_PROP_THROW_STRICT);
18545                         JS_FreeValue(ctx, sp[-1]);
18546                         sp -= 2;
18547                         if (unlikely(ret < 0))
18548                             goto exception;
18549                         break;
18550                     case OP_with_delete_var:
18551                         ret = JS_DeleteProperty(ctx, obj, atom, 0);
18552                         if (unlikely(ret < 0))
18553                             goto exception;
18554                         JS_FreeValue(ctx, sp[-1]);
18555                         sp[-1] = JS_NewBool(ctx, ret);
18556                         break;
18557                     case OP_with_make_ref:
18558                         /* produce a pair object/propname on the stack */
18559                         *sp++ = JS_AtomToValue(ctx, atom);
18560                         break;
18561                     case OP_with_get_ref:
18562                         /* produce a pair object/method on the stack */
18563                         val = JS_GetProperty(ctx, obj, atom);
18564                         if (unlikely(JS_IsException(val)))
18565                             goto exception;
18566                         *sp++ = val;
18567                         break;
18568                     case OP_with_get_ref_undef:
18569                         /* produce a pair undefined/function on the stack */
18570                         val = JS_GetProperty(ctx, obj, atom);
18571                         if (unlikely(JS_IsException(val)))
18572                             goto exception;
18573                         JS_FreeValue(ctx, sp[-1]);
18574                         sp[-1] = JS_UNDEFINED;
18575                         *sp++ = val;
18576                         break;
18577                     }
18578                     pc += diff - 5;
18579                 } else {
18580                 no_with:
18581                     /* if not jumping, drop the object argument */
18582                     JS_FreeValue(ctx, sp[-1]);
18583                     sp--;
18584                 }
18585             }
18586             BREAK;
18587 
18588         CASE(OP_await):
18589             ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT);
18590             goto done_generator;
18591         CASE(OP_yield):
18592             ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD);
18593             goto done_generator;
18594         CASE(OP_yield_star):
18595         CASE(OP_async_yield_star):
18596             ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);
18597             goto done_generator;
18598         CASE(OP_return_async):
18599         CASE(OP_initial_yield):
18600             ret_val = JS_UNDEFINED;
18601             goto done_generator;
18602 
18603         CASE(OP_nop):
18604             BREAK;
18605         CASE(OP_is_undefined_or_null):
18606             if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED ||
18607                 JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
18608                 goto set_true;
18609             } else {
18610                 goto free_and_set_false;
18611             }
18612 #if SHORT_OPCODES
18613         CASE(OP_is_undefined):
18614             if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) {
18615                 goto set_true;
18616             } else {
18617                 goto free_and_set_false;
18618             }
18619         CASE(OP_is_null):
18620             if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
18621                 goto set_true;
18622             } else {
18623                 goto free_and_set_false;
18624             }
18625             /* XXX: could merge to a single opcode */
18626         CASE(OP_typeof_is_undefined):
18627             /* different from OP_is_undefined because of isHTMLDDA */
18628             if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) {
18629                 goto free_and_set_true;
18630             } else {
18631                 goto free_and_set_false;
18632             }
18633         CASE(OP_typeof_is_function):
18634             if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) {
18635                 goto free_and_set_true;
18636             } else {
18637                 goto free_and_set_false;
18638             }
18639         free_and_set_true:
18640             JS_FreeValue(ctx, sp[-1]);
18641 #endif
18642         set_true:
18643             sp[-1] = JS_TRUE;
18644             BREAK;
18645         free_and_set_false:
18646             JS_FreeValue(ctx, sp[-1]);
18647             sp[-1] = JS_FALSE;
18648             BREAK;
18649         CASE(OP_invalid):
18650         DEFAULT:
18651             JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x",
18652                                   (int)(pc - b->byte_code_buf - 1), opcode);
18653             goto exception;
18654         }
18655     }
18656  exception:
18657     if (is_backtrace_needed(ctx, rt->current_exception)) {
18658         /* add the backtrace information now (it is not done
18659            before if the exception happens in a bytecode
18660            operation */
18661         sf->cur_pc = pc;
18662         build_backtrace(ctx, rt->current_exception, NULL, 0, 0);
18663     }
18664     if (!JS_IsUncatchableError(ctx, rt->current_exception)) {
18665         while (sp > stack_buf) {
18666             JSValue val = *--sp;
18667             JS_FreeValue(ctx, val);
18668             if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) {
18669                 int pos = JS_VALUE_GET_INT(val);
18670                 if (pos == 0) {
18671                     /* enumerator: close it with a throw */
18672                     JS_FreeValue(ctx, sp[-1]); /* drop the next method */
18673                     sp--;
18674                     JS_IteratorClose(ctx, sp[-1], TRUE);
18675                 } else {
18676                     *sp++ = rt->current_exception;
18677                     rt->current_exception = JS_NULL;
18678                     pc = b->byte_code_buf + pos;
18679                     goto restart;
18680                 }
18681             }
18682         }
18683     }
18684     ret_val = JS_EXCEPTION;
18685     /* the local variables are freed by the caller in the generator
18686        case. Hence the label 'done' should never be reached in a
18687        generator function. */
18688     if (b->func_kind != JS_FUNC_NORMAL) {
18689     done_generator:
18690         sf->cur_pc = pc;
18691         sf->cur_sp = sp;
18692     } else {
18693     done:
18694         if (unlikely(!list_empty(&sf->var_ref_list))) {
18695             /* variable references reference the stack: must close them */
18696             close_var_refs(rt, sf);
18697         }
18698         /* free the local variables and stack */
18699         for(pval = local_buf; pval < sp; pval++) {
18700             JS_FreeValue(ctx, *pval);
18701         }
18702     }
18703     rt->current_stack_frame = sf->prev_frame;
18704     return ret_val;
18705 }
18706 
JS_Call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv)18707 JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,
18708                 int argc, JSValueConst *argv)
18709 {
18710 #ifdef ENABLE_JS_DEBUG
18711     DebuggerInfo *debugger_info = JS_GetDebuggerInfo(ctx);
18712     if (debugger_info != NULL) {
18713         debugger_info->stepOperation = NO_STEP_OPERATION;
18714     }
18715 #endif
18716     return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
18717                            argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
18718 }
18719 
JS_CallFree(JSContext * ctx,JSValue func_obj,JSValueConst this_obj,int argc,JSValueConst * argv)18720 static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
18721                            int argc, JSValueConst *argv)
18722 {
18723     JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
18724                                   argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
18725     JS_FreeValue(ctx, func_obj);
18726     return res;
18727 }
18728 
18729 /* warning: the refcount of the context is not incremented. Return
18730    NULL in case of exception (case of revoked proxy only) */
JS_GetFunctionRealm(JSContext * ctx,JSValueConst func_obj)18731 static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj)
18732 {
18733     JSObject *p;
18734     JSContext *realm;
18735 
18736     if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
18737         return ctx;
18738     p = JS_VALUE_GET_OBJ(func_obj);
18739     switch(p->class_id) {
18740     case JS_CLASS_C_FUNCTION:
18741         realm = p->u.cfunc.realm;
18742         break;
18743     case JS_CLASS_BYTECODE_FUNCTION:
18744     case JS_CLASS_GENERATOR_FUNCTION:
18745     case JS_CLASS_ASYNC_FUNCTION:
18746     case JS_CLASS_ASYNC_GENERATOR_FUNCTION:
18747         {
18748             JSFunctionBytecode *b;
18749             b = p->u.func.function_bytecode;
18750             realm = b->realm;
18751         }
18752         break;
18753     case JS_CLASS_PROXY:
18754         {
18755             JSProxyData *s = p->u.opaque;
18756             if (!s)
18757                 return ctx;
18758             if (s->is_revoked) {
18759                 JS_ThrowTypeErrorRevokedProxy(ctx);
18760                 return NULL;
18761             } else {
18762                 realm = JS_GetFunctionRealm(ctx, s->target);
18763             }
18764         }
18765         break;
18766     case JS_CLASS_BOUND_FUNCTION:
18767         {
18768             JSBoundFunction *bf = p->u.bound_function;
18769             realm = JS_GetFunctionRealm(ctx, bf->func_obj);
18770         }
18771         break;
18772     default:
18773         realm = ctx;
18774         break;
18775     }
18776     return realm;
18777 }
18778 
js_create_from_ctor(JSContext * ctx,JSValueConst ctor,int class_id)18779 static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor,
18780                                    int class_id)
18781 {
18782     JSValue proto, obj;
18783     JSContext *realm;
18784 
18785     if (JS_IsUndefined(ctor)) {
18786         proto = JS_DupValue(ctx, ctx->class_proto[class_id]);
18787     } else {
18788         proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
18789         if (JS_IsException(proto))
18790             return proto;
18791         if (!JS_IsObject(proto)) {
18792             JS_FreeValue(ctx, proto);
18793             realm = JS_GetFunctionRealm(ctx, ctor);
18794             if (!realm)
18795                 return JS_EXCEPTION;
18796             proto = JS_DupValue(ctx, realm->class_proto[class_id]);
18797         }
18798     }
18799     obj = JS_NewObjectProtoClass(ctx, proto, class_id);
18800     JS_FreeValue(ctx, proto);
18801     return obj;
18802 }
18803 
18804 /* 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)18805 static JSValue JS_CallConstructorInternal(JSContext *ctx,
18806                                           JSValueConst func_obj,
18807                                           JSValueConst new_target,
18808                                           int argc, JSValue *argv, int flags)
18809 {
18810     JSObject *p;
18811     JSFunctionBytecode *b;
18812 
18813     if (js_poll_interrupts(ctx))
18814         return JS_EXCEPTION;
18815     flags |= JS_CALL_FLAG_CONSTRUCTOR;
18816     if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT))
18817         goto not_a_function;
18818     p = JS_VALUE_GET_OBJ(func_obj);
18819     if (unlikely(!p->is_constructor))
18820         return JS_ThrowTypeError(ctx, "not a constructor");
18821     if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
18822         JSClassCall *call_func;
18823         call_func = ctx->rt->class_array[p->class_id].call;
18824         if (!call_func) {
18825         not_a_function:
18826             return JS_ThrowTypeError(ctx, "not a function");
18827         }
18828         return call_func(ctx, func_obj, new_target, argc,
18829                          (JSValueConst *)argv, flags);
18830     }
18831 
18832     b = p->u.func.function_bytecode;
18833     if (b->is_derived_class_constructor) {
18834         return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags);
18835     } else {
18836         JSValue obj, ret;
18837         /* legacy constructor behavior */
18838         obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
18839         if (JS_IsException(obj))
18840             return JS_EXCEPTION;
18841         ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags);
18842         if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT ||
18843             JS_IsException(ret)) {
18844             JS_FreeValue(ctx, obj);
18845             return ret;
18846         } else {
18847             JS_FreeValue(ctx, ret);
18848             return obj;
18849         }
18850     }
18851 }
18852 
JS_CallConstructor2(JSContext * ctx,JSValueConst func_obj,JSValueConst new_target,int argc,JSValueConst * argv)18853 JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
18854                             JSValueConst new_target,
18855                             int argc, JSValueConst *argv)
18856 {
18857     return JS_CallConstructorInternal(ctx, func_obj, new_target,
18858                                       argc, (JSValue *)argv,
18859                                       JS_CALL_FLAG_COPY_ARGV);
18860 }
18861 
JS_CallConstructor(JSContext * ctx,JSValueConst func_obj,int argc,JSValueConst * argv)18862 JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,
18863                            int argc, JSValueConst *argv)
18864 {
18865     return JS_CallConstructorInternal(ctx, func_obj, func_obj,
18866                                       argc, (JSValue *)argv,
18867                                       JS_CALL_FLAG_COPY_ARGV);
18868 }
18869 
JS_Invoke(JSContext * ctx,JSValueConst this_val,JSAtom atom,int argc,JSValueConst * argv)18870 JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,
18871                   int argc, JSValueConst *argv)
18872 {
18873     JSValue func_obj;
18874     func_obj = JS_GetProperty(ctx, this_val, atom);
18875     if (JS_IsException(func_obj))
18876         return func_obj;
18877     return JS_CallFree(ctx, func_obj, this_val, argc, argv);
18878 }
18879 
JS_InvokeFree(JSContext * ctx,JSValue this_val,JSAtom atom,int argc,JSValueConst * argv)18880 static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
18881                              int argc, JSValueConst *argv)
18882 {
18883     JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv);
18884     JS_FreeValue(ctx, this_val);
18885     return res;
18886 }
18887 
18888 /* JSAsyncFunctionState (used by generator and async functions) */
async_func_init(JSContext * ctx,JSAsyncFunctionState * s,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv)18889 static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
18890                                        JSValueConst func_obj, JSValueConst this_obj,
18891                                        int argc, JSValueConst *argv)
18892 {
18893     JSObject *p;
18894     JSFunctionBytecode *b;
18895     JSStackFrame *sf;
18896     int local_count, i, arg_buf_len, n;
18897 
18898     sf = &s->frame;
18899     init_list_head(&sf->var_ref_list);
18900     p = JS_VALUE_GET_OBJ(func_obj);
18901     b = p->u.func.function_bytecode;
18902     sf->js_mode = b->js_mode;
18903     sf->cur_pc = b->byte_code_buf;
18904     arg_buf_len = max_int(b->arg_count, argc);
18905     local_count = arg_buf_len + b->var_count + b->stack_size;
18906     sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));
18907     if (!sf->arg_buf)
18908         return -1;
18909     sf->cur_func = JS_DupValue(ctx, func_obj);
18910     s->this_val = JS_DupValue(ctx, this_obj);
18911     s->argc = argc;
18912     sf->arg_count = arg_buf_len;
18913     sf->var_buf = sf->arg_buf + arg_buf_len;
18914     sf->cur_sp = sf->var_buf + b->var_count;
18915     for(i = 0; i < argc; i++)
18916         sf->arg_buf[i] = JS_DupValue(ctx, argv[i]);
18917     n = arg_buf_len + b->var_count;
18918     for(i = argc; i < n; i++)
18919         sf->arg_buf[i] = JS_UNDEFINED;
18920     return 0;
18921 }
18922 
async_func_mark(JSRuntime * rt,JSAsyncFunctionState * s,JS_MarkFunc * mark_func)18923 static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,
18924                             JS_MarkFunc *mark_func)
18925 {
18926     JSStackFrame *sf;
18927     JSValue *sp;
18928 
18929     sf = &s->frame;
18930     JS_MarkValue(rt, sf->cur_func, mark_func);
18931     JS_MarkValue(rt, s->this_val, mark_func);
18932     if (sf->cur_sp) {
18933         /* if the function is running, cur_sp is not known so we
18934            cannot mark the stack. Marking the variables is not needed
18935            because a running function cannot be part of a removable
18936            cycle */
18937         for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)
18938             JS_MarkValue(rt, *sp, mark_func);
18939     }
18940 }
18941 
async_func_free(JSRuntime * rt,JSAsyncFunctionState * s)18942 static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
18943 {
18944     JSStackFrame *sf;
18945     JSValue *sp;
18946 
18947     sf = &s->frame;
18948 
18949     /* close the closure variables. */
18950     close_var_refs(rt, sf);
18951 
18952     if (sf->arg_buf) {
18953         /* cannot free the function if it is running */
18954         assert(sf->cur_sp != NULL);
18955         for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {
18956             JS_FreeValueRT(rt, *sp);
18957         }
18958         js_free_rt(rt, sf->arg_buf);
18959     }
18960     JS_FreeValueRT(rt, sf->cur_func);
18961     JS_FreeValueRT(rt, s->this_val);
18962 }
18963 
async_func_resume(JSContext * ctx,JSAsyncFunctionState * s)18964 static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)
18965 {
18966     JSValue func_obj;
18967 
18968     if (js_check_stack_overflow(ctx->rt, 0))
18969         return JS_ThrowStackOverflow(ctx);
18970 
18971     /* the tag does not matter provided it is not an object */
18972     func_obj = JS_MKPTR(JS_TAG_INT, s);
18973     return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
18974                            s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR);
18975 }
18976 
18977 
18978 /* Generators */
18979 
18980 typedef enum JSGeneratorStateEnum {
18981     JS_GENERATOR_STATE_SUSPENDED_START,
18982     JS_GENERATOR_STATE_SUSPENDED_YIELD,
18983     JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
18984     JS_GENERATOR_STATE_EXECUTING,
18985     JS_GENERATOR_STATE_COMPLETED,
18986 } JSGeneratorStateEnum;
18987 
18988 typedef struct JSGeneratorData {
18989     JSGeneratorStateEnum state;
18990     JSAsyncFunctionState func_state;
18991 } JSGeneratorData;
18992 
free_generator_stack_rt(JSRuntime * rt,JSGeneratorData * s)18993 static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)
18994 {
18995     if (s->state == JS_GENERATOR_STATE_COMPLETED)
18996         return;
18997     async_func_free(rt, &s->func_state);
18998     s->state = JS_GENERATOR_STATE_COMPLETED;
18999 }
19000 
js_generator_finalizer(JSRuntime * rt,JSValue obj)19001 static void js_generator_finalizer(JSRuntime *rt, JSValue obj)
19002 {
19003     JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR);
19004 
19005     if (s) {
19006         free_generator_stack_rt(rt, s);
19007         js_free_rt(rt, s);
19008     }
19009 }
19010 
free_generator_stack(JSContext * ctx,JSGeneratorData * s)19011 static void free_generator_stack(JSContext *ctx, JSGeneratorData *s)
19012 {
19013     free_generator_stack_rt(ctx->rt, s);
19014 }
19015 
js_generator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)19016 static void js_generator_mark(JSRuntime *rt, JSValueConst val,
19017                               JS_MarkFunc *mark_func)
19018 {
19019     JSObject *p = JS_VALUE_GET_OBJ(val);
19020     JSGeneratorData *s = p->u.generator_data;
19021 
19022     if (!s || s->state == JS_GENERATOR_STATE_COMPLETED)
19023         return;
19024     async_func_mark(rt, &s->func_state, mark_func);
19025 }
19026 
19027 /* XXX: use enum */
19028 #define GEN_MAGIC_NEXT   0
19029 #define GEN_MAGIC_RETURN 1
19030 #define GEN_MAGIC_THROW  2
19031 
js_generator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,BOOL * pdone,int magic)19032 static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
19033                                  int argc, JSValueConst *argv,
19034                                  BOOL *pdone, int magic)
19035 {
19036     JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR);
19037     JSStackFrame *sf;
19038     JSValue ret, func_ret;
19039 
19040     *pdone = TRUE;
19041     if (!s)
19042         return JS_ThrowTypeError(ctx, "not a generator");
19043     sf = &s->func_state.frame;
19044     switch(s->state) {
19045     default:
19046     case JS_GENERATOR_STATE_SUSPENDED_START:
19047         if (magic == GEN_MAGIC_NEXT) {
19048             goto exec_no_arg;
19049         } else {
19050             free_generator_stack(ctx, s);
19051             goto done;
19052         }
19053         break;
19054     case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
19055     case JS_GENERATOR_STATE_SUSPENDED_YIELD:
19056         /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */
19057         ret = JS_DupValue(ctx, argv[0]);
19058         if (magic == GEN_MAGIC_THROW &&
19059             s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {
19060             JS_Throw(ctx, ret);
19061             s->func_state.throw_flag = TRUE;
19062         } else {
19063             sf->cur_sp[-1] = ret;
19064             sf->cur_sp[0] = JS_NewInt32(ctx, magic);
19065             sf->cur_sp++;
19066         exec_no_arg:
19067             s->func_state.throw_flag = FALSE;
19068         }
19069         s->state = JS_GENERATOR_STATE_EXECUTING;
19070         func_ret = async_func_resume(ctx, &s->func_state);
19071         s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;
19072         if (JS_IsException(func_ret)) {
19073             /* finalize the execution in case of exception */
19074             free_generator_stack(ctx, s);
19075             return func_ret;
19076         }
19077         if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
19078             /* get the returned yield value at the top of the stack */
19079             ret = sf->cur_sp[-1];
19080             sf->cur_sp[-1] = JS_UNDEFINED;
19081             if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) {
19082                 s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
19083                 /* return (value, done) object */
19084                 *pdone = 2;
19085             } else {
19086                 *pdone = FALSE;
19087             }
19088         } else {
19089             /* end of iterator */
19090             ret = sf->cur_sp[-1];
19091             sf->cur_sp[-1] = JS_UNDEFINED;
19092             JS_FreeValue(ctx, func_ret);
19093             free_generator_stack(ctx, s);
19094         }
19095         break;
19096     case JS_GENERATOR_STATE_COMPLETED:
19097     done:
19098         /* execution is finished */
19099         switch(magic) {
19100         default:
19101         case GEN_MAGIC_NEXT:
19102             ret = JS_UNDEFINED;
19103             break;
19104         case GEN_MAGIC_RETURN:
19105             ret = JS_DupValue(ctx, argv[0]);
19106             break;
19107         case GEN_MAGIC_THROW:
19108             ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0]));
19109             break;
19110         }
19111         break;
19112     case JS_GENERATOR_STATE_EXECUTING:
19113         ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator");
19114         break;
19115     }
19116     return ret;
19117 }
19118 
js_generator_function_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)19119 static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
19120                                           JSValueConst this_obj,
19121                                           int argc, JSValueConst *argv,
19122                                           int flags)
19123 {
19124     JSValue obj, func_ret;
19125     JSGeneratorData *s;
19126 
19127     s = js_mallocz(ctx, sizeof(*s));
19128     if (!s)
19129         return JS_EXCEPTION;
19130     s->state = JS_GENERATOR_STATE_SUSPENDED_START;
19131     if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
19132         s->state = JS_GENERATOR_STATE_COMPLETED;
19133         goto fail;
19134     }
19135 
19136     /* execute the function up to 'OP_initial_yield' */
19137     func_ret = async_func_resume(ctx, &s->func_state);
19138     if (JS_IsException(func_ret))
19139         goto fail;
19140     JS_FreeValue(ctx, func_ret);
19141 
19142     obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR);
19143     if (JS_IsException(obj))
19144         goto fail;
19145     JS_SetOpaque(obj, s);
19146     return obj;
19147  fail:
19148     free_generator_stack_rt(ctx->rt, s);
19149     js_free(ctx, s);
19150     return JS_EXCEPTION;
19151 }
19152 
19153 /* AsyncFunction */
19154 
js_async_function_terminate(JSRuntime * rt,JSAsyncFunctionData * s)19155 static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s)
19156 {
19157     if (s->is_active) {
19158         async_func_free(rt, &s->func_state);
19159         s->is_active = FALSE;
19160     }
19161 }
19162 
js_async_function_free0(JSRuntime * rt,JSAsyncFunctionData * s)19163 static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s)
19164 {
19165     js_async_function_terminate(rt, s);
19166     JS_FreeValueRT(rt, s->resolving_funcs[0]);
19167     JS_FreeValueRT(rt, s->resolving_funcs[1]);
19168     remove_gc_object(&s->header);
19169     js_free_rt(rt, s);
19170 }
19171 
js_async_function_free(JSRuntime * rt,JSAsyncFunctionData * s)19172 static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s)
19173 {
19174     if (--s->header.ref_count == 0) {
19175         js_async_function_free0(rt, s);
19176     }
19177 }
19178 
js_async_function_resolve_finalizer(JSRuntime * rt,JSValue val)19179 static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val)
19180 {
19181     JSObject *p = JS_VALUE_GET_OBJ(val);
19182     JSAsyncFunctionData *s = p->u.async_function_data;
19183     if (s) {
19184         js_async_function_free(rt, s);
19185     }
19186 }
19187 
js_async_function_resolve_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)19188 static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
19189                                            JS_MarkFunc *mark_func)
19190 {
19191     JSObject *p = JS_VALUE_GET_OBJ(val);
19192     JSAsyncFunctionData *s = p->u.async_function_data;
19193     if (s) {
19194         mark_func(rt, &s->header);
19195     }
19196 }
19197 
js_async_function_resolve_create(JSContext * ctx,JSAsyncFunctionData * s,JSValue * resolving_funcs)19198 static int js_async_function_resolve_create(JSContext *ctx,
19199                                             JSAsyncFunctionData *s,
19200                                             JSValue *resolving_funcs)
19201 {
19202     int i;
19203     JSObject *p;
19204 
19205     for(i = 0; i < 2; i++) {
19206         resolving_funcs[i] =
19207             JS_NewObjectProtoClass(ctx, ctx->function_proto,
19208                                    JS_CLASS_ASYNC_FUNCTION_RESOLVE + i);
19209         if (JS_IsException(resolving_funcs[i])) {
19210             if (i == 1)
19211                 JS_FreeValue(ctx, resolving_funcs[0]);
19212             return -1;
19213         }
19214         p = JS_VALUE_GET_OBJ(resolving_funcs[i]);
19215         s->header.ref_count++;
19216         p->u.async_function_data = s;
19217     }
19218     return 0;
19219 }
19220 
js_async_function_resume(JSContext * ctx,JSAsyncFunctionData * s)19221 static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)
19222 {
19223     JSValue func_ret, ret2;
19224 
19225     func_ret = async_func_resume(ctx, &s->func_state);
19226     if (JS_IsException(func_ret)) {
19227         JSValue error;
19228     fail:
19229         error = JS_GetException(ctx);
19230         ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
19231                        1, (JSValueConst *)&error);
19232         JS_FreeValue(ctx, error);
19233         js_async_function_terminate(ctx->rt, s);
19234         JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
19235     } else {
19236         JSValue value;
19237         value = s->func_state.frame.cur_sp[-1];
19238         s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
19239         if (JS_IsUndefined(func_ret)) {
19240             /* function returned */
19241             ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
19242                            1, (JSValueConst *)&value);
19243             JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
19244             JS_FreeValue(ctx, value);
19245             js_async_function_terminate(ctx->rt, s);
19246         } else {
19247             JSValue promise, resolving_funcs[2], resolving_funcs1[2];
19248             int i, res;
19249 
19250             /* await */
19251             JS_FreeValue(ctx, func_ret); /* not used */
19252             promise = js_promise_resolve(ctx, ctx->promise_ctor,
19253                                          1, (JSValueConst *)&value, 0);
19254             JS_FreeValue(ctx, value);
19255             if (JS_IsException(promise))
19256                 goto fail;
19257             if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {
19258                 JS_FreeValue(ctx, promise);
19259                 goto fail;
19260             }
19261 
19262             /* Note: no need to create 'thrownawayCapability' as in
19263                the spec */
19264             for(i = 0; i < 2; i++)
19265                 resolving_funcs1[i] = JS_UNDEFINED;
19266             res = perform_promise_then(ctx, promise,
19267                                        (JSValueConst *)resolving_funcs,
19268                                        (JSValueConst *)resolving_funcs1);
19269             JS_FreeValue(ctx, promise);
19270             for(i = 0; i < 2; i++)
19271                 JS_FreeValue(ctx, resolving_funcs[i]);
19272             if (res)
19273                 goto fail;
19274         }
19275     }
19276 }
19277 
js_async_function_resolve_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)19278 static JSValue js_async_function_resolve_call(JSContext *ctx,
19279                                               JSValueConst func_obj,
19280                                               JSValueConst this_obj,
19281                                               int argc, JSValueConst *argv,
19282                                               int flags)
19283 {
19284     JSObject *p = JS_VALUE_GET_OBJ(func_obj);
19285     JSAsyncFunctionData *s = p->u.async_function_data;
19286     BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;
19287     JSValueConst arg;
19288 
19289     if (argc > 0)
19290         arg = argv[0];
19291     else
19292         arg = JS_UNDEFINED;
19293     s->func_state.throw_flag = is_reject;
19294     if (is_reject) {
19295         JS_Throw(ctx, JS_DupValue(ctx, arg));
19296     } else {
19297         /* return value of await */
19298         s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);
19299     }
19300     js_async_function_resume(ctx, s);
19301     return JS_UNDEFINED;
19302 }
19303 
js_async_function_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)19304 static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,
19305                                       JSValueConst this_obj,
19306                                       int argc, JSValueConst *argv, int flags)
19307 {
19308     JSValue promise;
19309     JSAsyncFunctionData *s;
19310 
19311     s = js_mallocz(ctx, sizeof(*s));
19312     if (!s)
19313         return JS_EXCEPTION;
19314     s->header.ref_count = 1;
19315     add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
19316     s->is_active = FALSE;
19317     s->resolving_funcs[0] = JS_UNDEFINED;
19318     s->resolving_funcs[1] = JS_UNDEFINED;
19319 
19320     promise = JS_NewPromiseCapability(ctx, s->resolving_funcs);
19321     if (JS_IsException(promise))
19322         goto fail;
19323 
19324     if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
19325     fail:
19326         JS_FreeValue(ctx, promise);
19327         js_async_function_free(ctx->rt, s);
19328         return JS_EXCEPTION;
19329     }
19330     s->is_active = TRUE;
19331 
19332     js_async_function_resume(ctx, s);
19333 
19334     js_async_function_free(ctx->rt, s);
19335 
19336     return promise;
19337 }
19338 
19339 /* AsyncGenerator */
19340 
19341 typedef enum JSAsyncGeneratorStateEnum {
19342     JS_ASYNC_GENERATOR_STATE_SUSPENDED_START,
19343     JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD,
19344     JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
19345     JS_ASYNC_GENERATOR_STATE_EXECUTING,
19346     JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN,
19347     JS_ASYNC_GENERATOR_STATE_COMPLETED,
19348 } JSAsyncGeneratorStateEnum;
19349 
19350 typedef struct JSAsyncGeneratorRequest {
19351     struct list_head link;
19352     /* completion */
19353     int completion_type; /* GEN_MAGIC_x */
19354     JSValue result;
19355     /* promise capability */
19356     JSValue promise;
19357     JSValue resolving_funcs[2];
19358 } JSAsyncGeneratorRequest;
19359 
19360 typedef struct JSAsyncGeneratorData {
19361     JSObject *generator; /* back pointer to the object (const) */
19362     JSAsyncGeneratorStateEnum state;
19363     JSAsyncFunctionState func_state;
19364     struct list_head queue; /* list of JSAsyncGeneratorRequest.link */
19365 } JSAsyncGeneratorData;
19366 
js_async_generator_free(JSRuntime * rt,JSAsyncGeneratorData * s)19367 static void js_async_generator_free(JSRuntime *rt,
19368                                     JSAsyncGeneratorData *s)
19369 {
19370     struct list_head *el, *el1;
19371     JSAsyncGeneratorRequest *req;
19372 
19373     list_for_each_safe(el, el1, &s->queue) {
19374         req = list_entry(el, JSAsyncGeneratorRequest, link);
19375         JS_FreeValueRT(rt, req->result);
19376         JS_FreeValueRT(rt, req->promise);
19377         JS_FreeValueRT(rt, req->resolving_funcs[0]);
19378         JS_FreeValueRT(rt, req->resolving_funcs[1]);
19379         js_free_rt(rt, req);
19380     }
19381     if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
19382         s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
19383         async_func_free(rt, &s->func_state);
19384     }
19385     js_free_rt(rt, s);
19386 }
19387 
js_async_generator_finalizer(JSRuntime * rt,JSValue obj)19388 static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj)
19389 {
19390     JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR);
19391 
19392     if (s) {
19393         js_async_generator_free(rt, s);
19394     }
19395 }
19396 
js_async_generator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)19397 static void js_async_generator_mark(JSRuntime *rt, JSValueConst val,
19398                                     JS_MarkFunc *mark_func)
19399 {
19400     JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR);
19401     struct list_head *el;
19402     JSAsyncGeneratorRequest *req;
19403     if (s) {
19404         list_for_each(el, &s->queue) {
19405             req = list_entry(el, JSAsyncGeneratorRequest, link);
19406             JS_MarkValue(rt, req->result, mark_func);
19407             JS_MarkValue(rt, req->promise, mark_func);
19408             JS_MarkValue(rt, req->resolving_funcs[0], mark_func);
19409             JS_MarkValue(rt, req->resolving_funcs[1], mark_func);
19410         }
19411         if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
19412             s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
19413             async_func_mark(rt, &s->func_state, mark_func);
19414         }
19415     }
19416 }
19417 
19418 static JSValue js_async_generator_resolve_function(JSContext *ctx,
19419                                           JSValueConst this_obj,
19420                                           int argc, JSValueConst *argv,
19421                                           int magic, JSValue *func_data);
19422 
js_async_generator_resolve_function_create(JSContext * ctx,JSValueConst generator,JSValue * resolving_funcs,BOOL is_resume_next)19423 static int js_async_generator_resolve_function_create(JSContext *ctx,
19424                                                       JSValueConst generator,
19425                                                       JSValue *resolving_funcs,
19426                                                       BOOL is_resume_next)
19427 {
19428     int i;
19429     JSValue func;
19430 
19431     for(i = 0; i < 2; i++) {
19432         func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1,
19433                                    i + is_resume_next * 2, 1, &generator);
19434         if (JS_IsException(func)) {
19435             if (i == 1)
19436                 JS_FreeValue(ctx, resolving_funcs[0]);
19437             return -1;
19438         }
19439         resolving_funcs[i] = func;
19440     }
19441     return 0;
19442 }
19443 
js_async_generator_await(JSContext * ctx,JSAsyncGeneratorData * s,JSValueConst value)19444 static int js_async_generator_await(JSContext *ctx,
19445                                     JSAsyncGeneratorData *s,
19446                                     JSValueConst value)
19447 {
19448     JSValue promise, resolving_funcs[2], resolving_funcs1[2];
19449     int i, res;
19450 
19451     promise = js_promise_resolve(ctx, ctx->promise_ctor,
19452                                  1, &value, 0);
19453     if (JS_IsException(promise))
19454         goto fail;
19455 
19456     if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator),
19457                                                    resolving_funcs, FALSE)) {
19458         JS_FreeValue(ctx, promise);
19459         goto fail;
19460     }
19461 
19462     /* Note: no need to create 'thrownawayCapability' as in
19463        the spec */
19464     for(i = 0; i < 2; i++)
19465         resolving_funcs1[i] = JS_UNDEFINED;
19466     res = perform_promise_then(ctx, promise,
19467                                (JSValueConst *)resolving_funcs,
19468                                (JSValueConst *)resolving_funcs1);
19469     JS_FreeValue(ctx, promise);
19470     for(i = 0; i < 2; i++)
19471         JS_FreeValue(ctx, resolving_funcs[i]);
19472     if (res)
19473         goto fail;
19474     return 0;
19475  fail:
19476     return -1;
19477 }
19478 
js_async_generator_resolve_or_reject(JSContext * ctx,JSAsyncGeneratorData * s,JSValueConst result,int is_reject)19479 static void js_async_generator_resolve_or_reject(JSContext *ctx,
19480                                                  JSAsyncGeneratorData *s,
19481                                                  JSValueConst result,
19482                                                  int is_reject)
19483 {
19484     JSAsyncGeneratorRequest *next;
19485     JSValue ret;
19486 
19487     next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
19488     list_del(&next->link);
19489     ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1,
19490                   &result);
19491     JS_FreeValue(ctx, ret);
19492     JS_FreeValue(ctx, next->result);
19493     JS_FreeValue(ctx, next->promise);
19494     JS_FreeValue(ctx, next->resolving_funcs[0]);
19495     JS_FreeValue(ctx, next->resolving_funcs[1]);
19496     js_free(ctx, next);
19497 }
19498 
js_async_generator_resolve(JSContext * ctx,JSAsyncGeneratorData * s,JSValueConst value,BOOL done)19499 static void js_async_generator_resolve(JSContext *ctx,
19500                                        JSAsyncGeneratorData *s,
19501                                        JSValueConst value,
19502                                        BOOL done)
19503 {
19504     JSValue result;
19505     result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done);
19506     /* XXX: better exception handling ? */
19507     js_async_generator_resolve_or_reject(ctx, s, result, 0);
19508     JS_FreeValue(ctx, result);
19509  }
19510 
js_async_generator_reject(JSContext * ctx,JSAsyncGeneratorData * s,JSValueConst exception)19511 static void js_async_generator_reject(JSContext *ctx,
19512                                        JSAsyncGeneratorData *s,
19513                                        JSValueConst exception)
19514 {
19515     js_async_generator_resolve_or_reject(ctx, s, exception, 1);
19516 }
19517 
js_async_generator_complete(JSContext * ctx,JSAsyncGeneratorData * s)19518 static void js_async_generator_complete(JSContext *ctx,
19519                                         JSAsyncGeneratorData *s)
19520 {
19521     if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {
19522         s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
19523         async_func_free(ctx->rt, &s->func_state);
19524     }
19525 }
19526 
js_async_generator_completed_return(JSContext * ctx,JSAsyncGeneratorData * s,JSValueConst value)19527 static int js_async_generator_completed_return(JSContext *ctx,
19528                                                JSAsyncGeneratorData *s,
19529                                                JSValueConst value)
19530 {
19531     JSValue promise, resolving_funcs[2], resolving_funcs1[2];
19532     int res;
19533 
19534     promise = js_promise_resolve(ctx, ctx->promise_ctor,
19535                                  1, (JSValueConst *)&value, 0);
19536     if (JS_IsException(promise))
19537         return -1;
19538     if (js_async_generator_resolve_function_create(ctx,
19539                                                    JS_MKPTR(JS_TAG_OBJECT, s->generator),
19540                                                    resolving_funcs1,
19541                                                    TRUE)) {
19542         JS_FreeValue(ctx, promise);
19543         return -1;
19544     }
19545     resolving_funcs[0] = JS_UNDEFINED;
19546     resolving_funcs[1] = JS_UNDEFINED;
19547     res = perform_promise_then(ctx, promise,
19548                                (JSValueConst *)resolving_funcs1,
19549                                (JSValueConst *)resolving_funcs);
19550     JS_FreeValue(ctx, resolving_funcs1[0]);
19551     JS_FreeValue(ctx, resolving_funcs1[1]);
19552     JS_FreeValue(ctx, promise);
19553     return res;
19554 }
19555 
js_async_generator_resume_next(JSContext * ctx,JSAsyncGeneratorData * s)19556 static void js_async_generator_resume_next(JSContext *ctx,
19557                                            JSAsyncGeneratorData *s)
19558 {
19559     JSAsyncGeneratorRequest *next;
19560     JSValue func_ret, value;
19561 
19562     for(;;) {
19563         if (list_empty(&s->queue))
19564             break;
19565         next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
19566         switch(s->state) {
19567         case JS_ASYNC_GENERATOR_STATE_EXECUTING:
19568             /* only happens when restarting execution after await() */
19569             goto resume_exec;
19570         case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN:
19571             goto done;
19572         case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START:
19573             if (next->completion_type == GEN_MAGIC_NEXT) {
19574                 goto exec_no_arg;
19575             } else {
19576                 js_async_generator_complete(ctx, s);
19577             }
19578             break;
19579         case JS_ASYNC_GENERATOR_STATE_COMPLETED:
19580             if (next->completion_type == GEN_MAGIC_NEXT) {
19581                 js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE);
19582             } else if (next->completion_type == GEN_MAGIC_RETURN) {
19583                 s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;
19584                 js_async_generator_completed_return(ctx, s, next->result);
19585                 goto done;
19586             } else {
19587                 js_async_generator_reject(ctx, s, next->result);
19588             }
19589             goto done;
19590         case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD:
19591         case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
19592             value = JS_DupValue(ctx, next->result);
19593             if (next->completion_type == GEN_MAGIC_THROW &&
19594                 s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {
19595                 JS_Throw(ctx, value);
19596                 s->func_state.throw_flag = TRUE;
19597             } else {
19598                 /* 'yield' returns a value. 'yield *' also returns a value
19599                    in case the 'throw' method is called */
19600                 s->func_state.frame.cur_sp[-1] = value;
19601                 s->func_state.frame.cur_sp[0] =
19602                     JS_NewInt32(ctx, next->completion_type);
19603                 s->func_state.frame.cur_sp++;
19604             exec_no_arg:
19605                 s->func_state.throw_flag = FALSE;
19606             }
19607             s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;
19608         resume_exec:
19609             func_ret = async_func_resume(ctx, &s->func_state);
19610             if (JS_IsException(func_ret)) {
19611                 value = JS_GetException(ctx);
19612                 js_async_generator_complete(ctx, s);
19613                 js_async_generator_reject(ctx, s, value);
19614                 JS_FreeValue(ctx, value);
19615             } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
19616                 int func_ret_code;
19617                 value = s->func_state.frame.cur_sp[-1];
19618                 s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
19619                 func_ret_code = JS_VALUE_GET_INT(func_ret);
19620                 switch(func_ret_code) {
19621                 case FUNC_RET_YIELD:
19622                 case FUNC_RET_YIELD_STAR:
19623                     if (func_ret_code == FUNC_RET_YIELD_STAR)
19624                         s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
19625                     else
19626                         s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD;
19627                     js_async_generator_resolve(ctx, s, value, FALSE);
19628                     JS_FreeValue(ctx, value);
19629                     break;
19630                 case FUNC_RET_AWAIT:
19631                     js_async_generator_await(ctx, s, value);
19632                     JS_FreeValue(ctx, value);
19633                     goto done;
19634                 default:
19635                     abort();
19636                 }
19637             } else {
19638                 assert(JS_IsUndefined(func_ret));
19639                 /* end of function */
19640                 value = s->func_state.frame.cur_sp[-1];
19641                 s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
19642                 js_async_generator_complete(ctx, s);
19643                 js_async_generator_resolve(ctx, s, value, TRUE);
19644                 JS_FreeValue(ctx, value);
19645             }
19646             break;
19647         default:
19648             abort();
19649         }
19650     }
19651  done: ;
19652 }
19653 
js_async_generator_resolve_function(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv,int magic,JSValue * func_data)19654 static JSValue js_async_generator_resolve_function(JSContext *ctx,
19655                                                    JSValueConst this_obj,
19656                                                    int argc, JSValueConst *argv,
19657                                                    int magic, JSValue *func_data)
19658 {
19659     BOOL is_reject = magic & 1;
19660     JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR);
19661     JSValueConst arg = argv[0];
19662 
19663     /* XXX: what if s == NULL */
19664 
19665     if (magic >= 2) {
19666         /* resume next case in AWAITING_RETURN state */
19667         assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN ||
19668                s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED);
19669         s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
19670         if (is_reject) {
19671             js_async_generator_reject(ctx, s, arg);
19672         } else {
19673             js_async_generator_resolve(ctx, s, arg, TRUE);
19674         }
19675     } else {
19676         /* restart function execution after await() */
19677         assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);
19678         s->func_state.throw_flag = is_reject;
19679         if (is_reject) {
19680             JS_Throw(ctx, JS_DupValue(ctx, arg));
19681         } else {
19682             /* return value of await */
19683             s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);
19684         }
19685         js_async_generator_resume_next(ctx, s);
19686     }
19687     return JS_UNDEFINED;
19688 }
19689 
19690 /* magic = GEN_MAGIC_x */
js_async_generator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)19691 static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val,
19692                                        int argc, JSValueConst *argv,
19693                                        int magic)
19694 {
19695     JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR);
19696     JSValue promise, resolving_funcs[2];
19697     JSAsyncGeneratorRequest *req;
19698 
19699     promise = JS_NewPromiseCapability(ctx, resolving_funcs);
19700     if (JS_IsException(promise))
19701         return JS_EXCEPTION;
19702     if (!s) {
19703         JSValue err, res2;
19704         JS_ThrowTypeError(ctx, "not an AsyncGenerator object");
19705         err = JS_GetException(ctx);
19706         res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
19707                        1, (JSValueConst *)&err);
19708         JS_FreeValue(ctx, err);
19709         JS_FreeValue(ctx, res2);
19710         JS_FreeValue(ctx, resolving_funcs[0]);
19711         JS_FreeValue(ctx, resolving_funcs[1]);
19712         return promise;
19713     }
19714     req = js_mallocz(ctx, sizeof(*req));
19715     if (!req)
19716         goto fail;
19717     req->completion_type = magic;
19718     req->result = JS_DupValue(ctx, argv[0]);
19719     req->promise = JS_DupValue(ctx, promise);
19720     req->resolving_funcs[0] = resolving_funcs[0];
19721     req->resolving_funcs[1] = resolving_funcs[1];
19722     list_add_tail(&req->link, &s->queue);
19723     if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) {
19724         js_async_generator_resume_next(ctx, s);
19725     }
19726     return promise;
19727  fail:
19728     JS_FreeValue(ctx, resolving_funcs[0]);
19729     JS_FreeValue(ctx, resolving_funcs[1]);
19730     JS_FreeValue(ctx, promise);
19731     return JS_EXCEPTION;
19732 }
19733 
js_async_generator_function_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)19734 static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj,
19735                                                 JSValueConst this_obj,
19736                                                 int argc, JSValueConst *argv,
19737                                                 int flags)
19738 {
19739     JSValue obj, func_ret;
19740     JSAsyncGeneratorData *s;
19741 
19742     s = js_mallocz(ctx, sizeof(*s));
19743     if (!s)
19744         return JS_EXCEPTION;
19745     s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;
19746     init_list_head(&s->queue);
19747     if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
19748         s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
19749         goto fail;
19750     }
19751 
19752     /* execute the function up to 'OP_initial_yield' (no yield nor
19753        await are possible) */
19754     func_ret = async_func_resume(ctx, &s->func_state);
19755     if (JS_IsException(func_ret))
19756         goto fail;
19757     JS_FreeValue(ctx, func_ret);
19758 
19759     obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR);
19760     if (JS_IsException(obj))
19761         goto fail;
19762     s->generator = JS_VALUE_GET_OBJ(obj);
19763     JS_SetOpaque(obj, s);
19764     return obj;
19765  fail:
19766     js_async_generator_free(ctx->rt, s);
19767     return JS_EXCEPTION;
19768 }
19769 
19770 /* JS parser */
19771 
19772 enum {
19773     TOK_NUMBER = -128,
19774     TOK_STRING,
19775     TOK_TEMPLATE,
19776     TOK_IDENT,
19777     TOK_REGEXP,
19778     /* warning: order matters (see js_parse_assign_expr) */
19779     TOK_MUL_ASSIGN,
19780     TOK_DIV_ASSIGN,
19781     TOK_MOD_ASSIGN,
19782     TOK_PLUS_ASSIGN,
19783     TOK_MINUS_ASSIGN,
19784     TOK_SHL_ASSIGN,
19785     TOK_SAR_ASSIGN,
19786     TOK_SHR_ASSIGN,
19787     TOK_AND_ASSIGN,
19788     TOK_XOR_ASSIGN,
19789     TOK_OR_ASSIGN,
19790 #ifdef CONFIG_BIGNUM
19791     TOK_MATH_POW_ASSIGN,
19792 #endif
19793     TOK_POW_ASSIGN,
19794     TOK_LAND_ASSIGN,
19795     TOK_LOR_ASSIGN,
19796     TOK_DOUBLE_QUESTION_MARK_ASSIGN,
19797     TOK_DEC,
19798     TOK_INC,
19799     TOK_SHL,
19800     TOK_SAR,
19801     TOK_SHR,
19802     TOK_LT,
19803     TOK_LTE,
19804     TOK_GT,
19805     TOK_GTE,
19806     TOK_EQ,
19807     TOK_STRICT_EQ,
19808     TOK_NEQ,
19809     TOK_STRICT_NEQ,
19810     TOK_LAND,
19811     TOK_LOR,
19812 #ifdef CONFIG_BIGNUM
19813     TOK_MATH_POW,
19814 #endif
19815     TOK_POW,
19816     TOK_ARROW,
19817     TOK_ELLIPSIS,
19818     TOK_DOUBLE_QUESTION_MARK,
19819     TOK_QUESTION_MARK_DOT,
19820     TOK_ERROR,
19821     TOK_PRIVATE_NAME,
19822     TOK_EOF,
19823     /* keywords: WARNING: same order as atoms */
19824     TOK_NULL, /* must be first */
19825     TOK_FALSE,
19826     TOK_TRUE,
19827     TOK_IF,
19828     TOK_ELSE,
19829     TOK_RETURN,
19830     TOK_VAR,
19831     TOK_THIS,
19832     TOK_DELETE,
19833     TOK_VOID,
19834     TOK_TYPEOF,
19835     TOK_NEW,
19836     TOK_IN,
19837     TOK_INSTANCEOF,
19838     TOK_DO,
19839     TOK_WHILE,
19840     TOK_FOR,
19841     TOK_BREAK,
19842     TOK_CONTINUE,
19843     TOK_SWITCH,
19844     TOK_CASE,
19845     TOK_DEFAULT,
19846     TOK_THROW,
19847     TOK_TRY,
19848     TOK_CATCH,
19849     TOK_FINALLY,
19850     TOK_FUNCTION,
19851     TOK_DEBUGGER,
19852     TOK_WITH,
19853     /* FutureReservedWord */
19854     TOK_CLASS,
19855     TOK_CONST,
19856     TOK_ENUM,
19857     TOK_EXPORT,
19858     TOK_EXTENDS,
19859     TOK_IMPORT,
19860     TOK_SUPER,
19861     /* FutureReservedWords when parsing strict mode code */
19862     TOK_IMPLEMENTS,
19863     TOK_INTERFACE,
19864     TOK_LET,
19865     TOK_PACKAGE,
19866     TOK_PRIVATE,
19867     TOK_PROTECTED,
19868     TOK_PUBLIC,
19869     TOK_STATIC,
19870     TOK_YIELD,
19871     TOK_AWAIT, /* must be last */
19872     TOK_OF,     /* only used for js_parse_skip_parens_token() */
19873 };
19874 
19875 #define TOK_FIRST_KEYWORD   TOK_NULL
19876 #define TOK_LAST_KEYWORD    TOK_AWAIT
19877 
19878 /* unicode code points */
19879 #define CP_NBSP 0x00a0
19880 #define CP_BOM  0xfeff
19881 
19882 #define CP_LS   0x2028
19883 #define CP_PS   0x2029
19884 
19885 typedef struct BlockEnv {
19886     struct BlockEnv *prev;
19887     JSAtom label_name; /* JS_ATOM_NULL if none */
19888     int label_break; /* -1 if none */
19889     int label_cont; /* -1 if none */
19890     int drop_count; /* number of stack elements to drop */
19891     int label_finally; /* -1 if none */
19892     int scope_level;
19893     int has_iterator;
19894 } BlockEnv;
19895 
19896 typedef struct JSGlobalVar {
19897     int cpool_idx; /* if >= 0, index in the constant pool for hoisted
19898                       function defintion*/
19899     uint8_t force_init : 1; /* force initialization to undefined */
19900     uint8_t is_lexical : 1; /* global let/const definition */
19901     uint8_t is_const   : 1; /* const definition */
19902     int scope_level;    /* scope of definition */
19903     JSAtom var_name;  /* variable name */
19904 } JSGlobalVar;
19905 
19906 typedef struct RelocEntry {
19907     struct RelocEntry *next;
19908     uint32_t addr; /* address to patch */
19909     int size;   /* address size: 1, 2 or 4 bytes */
19910 } RelocEntry;
19911 
19912 typedef struct JumpSlot {
19913     int op;
19914     int size;
19915     int pos;
19916     int label;
19917 } JumpSlot;
19918 
19919 typedef struct LabelSlot {
19920     int ref_count;
19921     int pos;    /* phase 1 address, -1 means not resolved yet */
19922     int pos2;   /* phase 2 address, -1 means not resolved yet */
19923     int addr;   /* phase 3 address, -1 means not resolved yet */
19924     RelocEntry *first_reloc;
19925 } LabelSlot;
19926 
19927 typedef struct LineNumberSlot {
19928     uint32_t pc;
19929     int line_num;
19930 } LineNumberSlot;
19931 
19932 typedef enum JSParseFunctionEnum {
19933     JS_PARSE_FUNC_STATEMENT,
19934     JS_PARSE_FUNC_VAR,
19935     JS_PARSE_FUNC_EXPR,
19936     JS_PARSE_FUNC_ARROW,
19937     JS_PARSE_FUNC_GETTER,
19938     JS_PARSE_FUNC_SETTER,
19939     JS_PARSE_FUNC_METHOD,
19940     JS_PARSE_FUNC_CLASS_CONSTRUCTOR,
19941     JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR,
19942 } JSParseFunctionEnum;
19943 
19944 typedef enum JSParseExportEnum {
19945     JS_PARSE_EXPORT_NONE,
19946     JS_PARSE_EXPORT_NAMED,
19947     JS_PARSE_EXPORT_DEFAULT,
19948 } JSParseExportEnum;
19949 
19950 typedef struct JSFunctionDef {
19951     JSContext *ctx;
19952     struct JSFunctionDef *parent;
19953     int parent_cpool_idx; /* index in the constant pool of the parent
19954                              or -1 if none */
19955     int parent_scope_level; /* scope level in parent at point of definition */
19956     struct list_head child_list; /* list of JSFunctionDef.link */
19957     struct list_head link;
19958 
19959     BOOL is_eval; /* TRUE if eval code */
19960     int eval_type; /* only valid if is_eval = TRUE */
19961     BOOL is_global_var; /* TRUE if variables are not defined locally:
19962                            eval global, eval module or non strict eval */
19963     BOOL is_func_expr; /* TRUE if function expression */
19964     BOOL has_home_object; /* TRUE if the home object is available */
19965     BOOL has_prototype; /* true if a prototype field is necessary */
19966     BOOL has_simple_parameter_list;
19967     BOOL has_parameter_expressions; /* if true, an argument scope is created */
19968     BOOL has_use_strict; /* to reject directive in special cases */
19969     BOOL has_eval_call; /* true if the function contains a call to eval() */
19970     BOOL has_arguments_binding; /* true if the 'arguments' binding is
19971                                    available in the function */
19972     BOOL has_this_binding; /* true if the 'this' and new.target binding are
19973                               available in the function */
19974     BOOL new_target_allowed; /* true if the 'new.target' does not
19975                                 throw a syntax error */
19976     BOOL super_call_allowed; /* true if super() is allowed */
19977     BOOL super_allowed; /* true if super. or super[] is allowed */
19978     BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */
19979     BOOL is_derived_class_constructor;
19980     BOOL in_function_body;
19981     BOOL backtrace_barrier;
19982     JSFunctionKindEnum func_kind : 8;
19983     JSParseFunctionEnum func_type : 8;
19984     uint8_t js_mode; /* bitmap of JS_MODE_x */
19985     JSAtom func_name; /* JS_ATOM_NULL if no name */
19986 
19987     JSVarDef *vars;
19988     int var_size; /* allocated size for vars[] */
19989     int var_count;
19990     JSVarDef *args;
19991     int arg_size; /* allocated size for args[] */
19992     int arg_count; /* number of arguments */
19993     int defined_arg_count;
19994     int var_object_idx; /* -1 if none */
19995     int arg_var_object_idx; /* -1 if none (var object for the argument scope) */
19996     int arguments_var_idx; /* -1 if none */
19997     int arguments_arg_idx; /* argument variable definition in argument scope,
19998                               -1 if none */
19999     int func_var_idx; /* variable containing the current function (-1
20000                          if none, only used if is_func_expr is true) */
20001     int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */
20002     int this_var_idx; /* variable containg the 'this' value, -1 if none */
20003     int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */
20004     int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */
20005     int home_object_var_idx;
20006     BOOL need_home_object;
20007 
20008     int scope_level;    /* index into fd->scopes if the current lexical scope */
20009     int scope_first;    /* index into vd->vars of first lexically scoped variable */
20010     int scope_size;     /* allocated size of fd->scopes array */
20011     int scope_count;    /* number of entries used in the fd->scopes array */
20012     JSVarScope *scopes;
20013     JSVarScope def_scope_array[4];
20014     int body_scope; /* scope of the body of the function or eval */
20015 
20016     int global_var_count;
20017     int global_var_size;
20018     JSGlobalVar *global_vars;
20019 
20020     DynBuf byte_code;
20021     int last_opcode_pos; /* -1 if no last opcode */
20022     int last_opcode_line_num;
20023     BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */
20024 
20025     LabelSlot *label_slots;
20026     int label_size; /* allocated size for label_slots[] */
20027     int label_count;
20028     BlockEnv *top_break; /* break/continue label stack */
20029 
20030     /* constant pool (strings, functions, numbers) */
20031     JSValue *cpool;
20032     int cpool_count;
20033     int cpool_size;
20034 
20035     /* list of variables in the closure */
20036     int closure_var_count;
20037     int closure_var_size;
20038     JSClosureVar *closure_var;
20039 
20040     JumpSlot *jump_slots;
20041     int jump_size;
20042     int jump_count;
20043 
20044     LineNumberSlot *line_number_slots;
20045     int line_number_size;
20046     int line_number_count;
20047     int line_number_last;
20048     int line_number_last_pc;
20049 
20050     /* pc2line table */
20051     JSAtom filename;
20052     int line_num;
20053     DynBuf pc2line;
20054 
20055     char *source;  /* raw source, utf-8 encoded */
20056     int source_len;
20057 
20058     JSModuleDef *module; /* != NULL when parsing a module */
20059 } JSFunctionDef;
20060 
20061 typedef struct JSToken {
20062     int val;
20063     int line_num;   /* line number of token start */
20064     const uint8_t *ptr;
20065     union {
20066         struct {
20067             JSValue str;
20068             int sep;
20069         } str;
20070         struct {
20071             JSValue val;
20072 #ifdef CONFIG_BIGNUM
20073             slimb_t exponent; /* may be != 0 only if val is a float */
20074 #endif
20075         } num;
20076         struct {
20077             JSAtom atom;
20078             BOOL has_escape;
20079             BOOL is_reserved;
20080         } ident;
20081         struct {
20082             JSValue body;
20083             JSValue flags;
20084         } regexp;
20085     } u;
20086 } JSToken;
20087 
20088 typedef struct JSParseState {
20089     JSContext *ctx;
20090     int last_line_num;  /* line number of last token */
20091     int line_num;       /* line number of current offset */
20092     const char *filename;
20093     JSToken token;
20094     BOOL got_lf; /* true if got line feed before the current token */
20095     const uint8_t *last_ptr;
20096     const uint8_t *buf_ptr;
20097     const uint8_t *buf_end;
20098 
20099     /* current function code */
20100     JSFunctionDef *cur_func;
20101     BOOL is_module; /* parsing a module */
20102     BOOL allow_html_comments;
20103     BOOL ext_json; /* true if accepting JSON superset */
20104 } JSParseState;
20105 
20106 typedef struct JSOpCode {
20107 #ifdef DUMP_BYTECODE
20108     const char *name;
20109 #endif
20110     uint8_t size; /* in bytes */
20111     /* the opcodes remove n_pop items from the top of the stack, then
20112        pushes n_push items */
20113     uint8_t n_pop;
20114     uint8_t n_push;
20115     uint8_t fmt;
20116 } JSOpCode;
20117 
20118 static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
20119 #define FMT(f)
20120 #ifdef DUMP_BYTECODE
20121 #define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f },
20122 #else
20123 #define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f },
20124 #endif
20125 #include "quickjs-opcode.h"
20126 #undef DEF
20127 #undef FMT
20128 };
20129 
20130 #if SHORT_OPCODES
20131 /* After the final compilation pass, short opcodes are used. Their
20132    opcodes overlap with the temporary opcodes which cannot appear in
20133    the final bytecode. Their description is after the temporary
20134    opcodes in opcode_info[]. */
20135 #define short_opcode_info(op)           \
20136     opcode_info[(op) >= OP_TEMP_START ? \
20137                 (op) + (OP_TEMP_END - OP_TEMP_START) : (op)]
20138 #else
20139 #define short_opcode_info(op) opcode_info[op]
20140 #endif
20141 
20142 static __exception int next_token(JSParseState *s);
20143 
free_token(JSParseState * s,JSToken * token)20144 static void free_token(JSParseState *s, JSToken *token)
20145 {
20146     switch(token->val) {
20147 #ifdef CONFIG_BIGNUM
20148     case TOK_NUMBER:
20149         JS_FreeValue(s->ctx, token->u.num.val);
20150         break;
20151 #endif
20152     case TOK_STRING:
20153     case TOK_TEMPLATE:
20154         JS_FreeValue(s->ctx, token->u.str.str);
20155         break;
20156     case TOK_REGEXP:
20157         JS_FreeValue(s->ctx, token->u.regexp.body);
20158         JS_FreeValue(s->ctx, token->u.regexp.flags);
20159         break;
20160     case TOK_IDENT:
20161     case TOK_PRIVATE_NAME:
20162         JS_FreeAtom(s->ctx, token->u.ident.atom);
20163         break;
20164     default:
20165         if (token->val >= TOK_FIRST_KEYWORD &&
20166             token->val <= TOK_LAST_KEYWORD) {
20167             JS_FreeAtom(s->ctx, token->u.ident.atom);
20168         }
20169         break;
20170     }
20171 }
20172 
dump_token(JSParseState * s,const JSToken * token)20173 static void __attribute((unused)) dump_token(JSParseState *s,
20174                                              const JSToken *token)
20175 {
20176     switch(token->val) {
20177     case TOK_NUMBER:
20178         {
20179             double d;
20180             JS_ToFloat64(s->ctx, &d, token->u.num.val);  /* no exception possible */
20181             printf("number: %.14g\n", d);
20182         }
20183         break;
20184     case TOK_IDENT:
20185     dump_atom:
20186         {
20187             char buf[ATOM_GET_STR_BUF_SIZE];
20188             printf("ident: '%s'\n",
20189                    JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom));
20190         }
20191         break;
20192     case TOK_STRING:
20193         {
20194             const char *str;
20195             /* XXX: quote the string */
20196             str = JS_ToCString(s->ctx, token->u.str.str);
20197             printf("string: '%s'\n", str);
20198             JS_FreeCString(s->ctx, str);
20199         }
20200         break;
20201     case TOK_TEMPLATE:
20202         {
20203             const char *str;
20204             str = JS_ToCString(s->ctx, token->u.str.str);
20205             printf("template: `%s`\n", str);
20206             JS_FreeCString(s->ctx, str);
20207         }
20208         break;
20209     case TOK_REGEXP:
20210         {
20211             const char *str, *str2;
20212             str = JS_ToCString(s->ctx, token->u.regexp.body);
20213             str2 = JS_ToCString(s->ctx, token->u.regexp.flags);
20214             printf("regexp: '%s' '%s'\n", str, str2);
20215             JS_FreeCString(s->ctx, str);
20216             JS_FreeCString(s->ctx, str2);
20217         }
20218         break;
20219     case TOK_EOF:
20220         printf("eof\n");
20221         break;
20222     default:
20223         if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) {
20224             goto dump_atom;
20225         } else if (s->token.val >= 256) {
20226             printf("token: %d\n", token->val);
20227         } else {
20228             printf("token: '%c'\n", token->val);
20229         }
20230         break;
20231     }
20232 }
20233 
js_parse_error(JSParseState * s,const char * fmt,...)20234 int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...)
20235 {
20236     JSContext *ctx = s->ctx;
20237     va_list ap;
20238     int backtrace_flags;
20239 
20240     va_start(ap, fmt);
20241     JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE);
20242     va_end(ap);
20243     backtrace_flags = 0;
20244     if (s->cur_func && s->cur_func->backtrace_barrier)
20245         backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
20246     build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num,
20247                     backtrace_flags);
20248     return -1;
20249 }
20250 
js_parse_expect(JSParseState * s,int tok)20251 static int js_parse_expect(JSParseState *s, int tok)
20252 {
20253     if (s->token.val != tok) {
20254         /* XXX: dump token correctly in all cases */
20255         return js_parse_error(s, "expecting '%c'", tok);
20256     }
20257     return next_token(s);
20258 }
20259 
js_parse_expect_semi(JSParseState * s)20260 static int js_parse_expect_semi(JSParseState *s)
20261 {
20262     if (s->token.val != ';') {
20263         /* automatic insertion of ';' */
20264         if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) {
20265             return 0;
20266         }
20267         return js_parse_error(s, "expecting '%c'", ';');
20268     }
20269     return next_token(s);
20270 }
20271 
js_parse_error_reserved_identifier(JSParseState * s)20272 static int js_parse_error_reserved_identifier(JSParseState *s)
20273 {
20274     char buf1[ATOM_GET_STR_BUF_SIZE];
20275     return js_parse_error(s, "'%s' is a reserved identifier",
20276                           JS_AtomGetStr(s->ctx, buf1, sizeof(buf1),
20277                                         s->token.u.ident.atom));
20278 }
20279 
js_parse_template_part(JSParseState * s,const uint8_t * p)20280 static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)
20281 {
20282     uint32_t c;
20283     StringBuffer b_s, *b = &b_s;
20284 
20285     /* p points to the first byte of the template part */
20286     if (string_buffer_init(s->ctx, b, 32))
20287         goto fail;
20288     for(;;) {
20289         if (p >= s->buf_end)
20290             goto unexpected_eof;
20291         c = *p++;
20292         if (c == '`') {
20293             /* template end part */
20294             break;
20295         }
20296         if (c == '$' && *p == '{') {
20297             /* template start or middle part */
20298             p++;
20299             break;
20300         }
20301         if (c == '\\') {
20302             if (string_buffer_putc8(b, c))
20303                 goto fail;
20304             if (p >= s->buf_end)
20305                 goto unexpected_eof;
20306             c = *p++;
20307         }
20308         /* newline sequences are normalized as single '\n' bytes */
20309         if (c == '\r') {
20310             if (*p == '\n')
20311                 p++;
20312             c = '\n';
20313         }
20314         if (c == '\n') {
20315             s->line_num++;
20316         } else if (c >= 0x80) {
20317             const uint8_t *p_next;
20318             c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
20319             if (c > 0x10FFFF) {
20320                 js_parse_error(s, "invalid UTF-8 sequence");
20321                 goto fail;
20322             }
20323             p = p_next;
20324         }
20325         if (string_buffer_putc(b, c))
20326             goto fail;
20327     }
20328     s->token.val = TOK_TEMPLATE;
20329     s->token.u.str.sep = c;
20330     s->token.u.str.str = string_buffer_end(b);
20331     s->buf_ptr = p;
20332     return 0;
20333 
20334  unexpected_eof:
20335     js_parse_error(s, "unexpected end of string");
20336  fail:
20337     string_buffer_free(b);
20338     return -1;
20339 }
20340 
js_parse_string(JSParseState * s,int sep,BOOL do_throw,const uint8_t * p,JSToken * token,const uint8_t ** pp)20341 static __exception int js_parse_string(JSParseState *s, int sep,
20342                                        BOOL do_throw, const uint8_t *p,
20343                                        JSToken *token, const uint8_t **pp)
20344 {
20345     int ret;
20346     uint32_t c;
20347     StringBuffer b_s, *b = &b_s;
20348 
20349     /* string */
20350     if (string_buffer_init(s->ctx, b, 32))
20351         goto fail;
20352     for(;;) {
20353         if (p >= s->buf_end)
20354             goto invalid_char;
20355         c = *p;
20356         if (c < 0x20) {
20357             if (!s->cur_func) {
20358                 if (do_throw)
20359                     js_parse_error(s, "invalid character in a JSON string");
20360                 goto fail;
20361             }
20362             if (sep == '`') {
20363                 if (c == '\r') {
20364                     if (p[1] == '\n')
20365                         p++;
20366                     c = '\n';
20367                 }
20368                 /* do not update s->line_num */
20369             } else if (c == '\n' || c == '\r')
20370                 goto invalid_char;
20371         }
20372         p++;
20373         if (c == sep)
20374             break;
20375         if (c == '$' && *p == '{' && sep == '`') {
20376             /* template start or middle part */
20377             p++;
20378             break;
20379         }
20380         if (c == '\\') {
20381             c = *p;
20382             /* XXX: need a specific JSON case to avoid
20383                accepting invalid escapes */
20384             switch(c) {
20385             case '\0':
20386                 if (p >= s->buf_end)
20387                     goto invalid_char;
20388                 p++;
20389                 break;
20390             case '\'':
20391             case '\"':
20392             case '\\':
20393                 p++;
20394                 break;
20395             case '\r':  /* accept DOS and MAC newline sequences */
20396                 if (p[1] == '\n') {
20397                     p++;
20398                 }
20399                 /* fall thru */
20400             case '\n':
20401                 /* ignore escaped newline sequence */
20402                 p++;
20403                 if (sep != '`')
20404                     s->line_num++;
20405                 continue;
20406             default:
20407                 if (c >= '0' && c <= '9') {
20408                     if (!s->cur_func)
20409                         goto invalid_escape; /* JSON case */
20410                     if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`')
20411                         goto parse_escape;
20412                     if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) {
20413                         p++;
20414                         c = '\0';
20415                     } else {
20416                         if (c >= '8' || sep == '`') {
20417                             /* Note: according to ES2021, \8 and \9 are not
20418                                accepted in strict mode or in templates. */
20419                             goto invalid_escape;
20420                         } else {
20421                             if (do_throw)
20422                                 js_parse_error(s, "octal escape sequences are not allowed in strict mode");
20423                         }
20424                         goto fail;
20425                     }
20426                 } else if (c >= 0x80) {
20427                     const uint8_t *p_next;
20428                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
20429                     if (c > 0x10FFFF) {
20430                         goto invalid_utf8;
20431                     }
20432                     p = p_next;
20433                     /* LS or PS are skipped */
20434                     if (c == CP_LS || c == CP_PS)
20435                         continue;
20436                 } else {
20437                 parse_escape:
20438                     ret = lre_parse_escape(&p, TRUE);
20439                     if (ret == -1) {
20440                     invalid_escape:
20441                         if (do_throw)
20442                             js_parse_error(s, "malformed escape sequence in string literal");
20443                         goto fail;
20444                     } else if (ret < 0) {
20445                         /* ignore the '\' (could output a warning) */
20446                         p++;
20447                     } else {
20448                         c = ret;
20449                     }
20450                 }
20451                 break;
20452             }
20453         } else if (c >= 0x80) {
20454             const uint8_t *p_next;
20455             c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
20456             if (c > 0x10FFFF)
20457                 goto invalid_utf8;
20458             p = p_next;
20459         }
20460         if (string_buffer_putc(b, c))
20461             goto fail;
20462     }
20463     token->val = TOK_STRING;
20464     token->u.str.sep = c;
20465     token->u.str.str = string_buffer_end(b);
20466     *pp = p;
20467     return 0;
20468 
20469  invalid_utf8:
20470     if (do_throw)
20471         js_parse_error(s, "invalid UTF-8 sequence");
20472     goto fail;
20473  invalid_char:
20474     if (do_throw)
20475         js_parse_error(s, "unexpected end of string");
20476  fail:
20477     string_buffer_free(b);
20478     return -1;
20479 }
20480 
token_is_pseudo_keyword(JSParseState * s,JSAtom atom)20481 static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) {
20482     return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom &&
20483         !s->token.u.ident.has_escape;
20484 }
20485 
js_parse_regexp(JSParseState * s)20486 static __exception int js_parse_regexp(JSParseState *s)
20487 {
20488     const uint8_t *p;
20489     BOOL in_class;
20490     StringBuffer b_s, *b = &b_s;
20491     StringBuffer b2_s, *b2 = &b2_s;
20492     uint32_t c;
20493 
20494     p = s->buf_ptr;
20495     p++;
20496     in_class = FALSE;
20497     if (string_buffer_init(s->ctx, b, 32))
20498         return -1;
20499     if (string_buffer_init(s->ctx, b2, 1))
20500         goto fail;
20501     for(;;) {
20502         if (p >= s->buf_end) {
20503         eof_error:
20504             js_parse_error(s, "unexpected end of regexp");
20505             goto fail;
20506         }
20507         c = *p++;
20508         if (c == '\n' || c == '\r') {
20509             goto eol_error;
20510         } else if (c == '/') {
20511             if (!in_class)
20512                 break;
20513         } else if (c == '[') {
20514             in_class = TRUE;
20515         } else if (c == ']') {
20516             /* XXX: incorrect as the first character in a class */
20517             in_class = FALSE;
20518         } else if (c == '\\') {
20519             if (string_buffer_putc8(b, c))
20520                 goto fail;
20521             c = *p++;
20522             if (c == '\n' || c == '\r')
20523                 goto eol_error;
20524             else if (c == '\0' && p >= s->buf_end)
20525                 goto eof_error;
20526             else if (c >= 0x80) {
20527                 const uint8_t *p_next;
20528                 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
20529                 if (c > 0x10FFFF) {
20530                     goto invalid_utf8;
20531                 }
20532                 p = p_next;
20533                 if (c == CP_LS || c == CP_PS)
20534                     goto eol_error;
20535             }
20536         } else if (c >= 0x80) {
20537             const uint8_t *p_next;
20538             c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
20539             if (c > 0x10FFFF) {
20540             invalid_utf8:
20541                 js_parse_error(s, "invalid UTF-8 sequence");
20542                 goto fail;
20543             }
20544             p = p_next;
20545             /* LS or PS are considered as line terminator */
20546             if (c == CP_LS || c == CP_PS) {
20547             eol_error:
20548                 js_parse_error(s, "unexpected line terminator in regexp");
20549                 goto fail;
20550             }
20551         }
20552         if (string_buffer_putc(b, c))
20553             goto fail;
20554     }
20555 
20556     /* flags */
20557     for(;;) {
20558         const uint8_t *p_next = p;
20559         c = *p_next++;
20560         if (c >= 0x80) {
20561             c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
20562             if (c > 0x10FFFF) {
20563                 goto invalid_utf8;
20564             }
20565         }
20566         if (!lre_js_is_ident_next(c))
20567             break;
20568         if (string_buffer_putc(b2, c))
20569             goto fail;
20570         p = p_next;
20571     }
20572 
20573     s->token.val = TOK_REGEXP;
20574     s->token.u.regexp.body = string_buffer_end(b);
20575     s->token.u.regexp.flags = string_buffer_end(b2);
20576     s->buf_ptr = p;
20577     return 0;
20578  fail:
20579     string_buffer_free(b);
20580     string_buffer_free(b2);
20581     return -1;
20582 }
20583 
ident_realloc(JSContext * ctx,char ** pbuf,size_t * psize,char * static_buf)20584 static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
20585                                      char *static_buf)
20586 {
20587     char *buf, *new_buf;
20588     size_t size, new_size;
20589 
20590     buf = *pbuf;
20591     size = *psize;
20592     if (size >= (SIZE_MAX / 3) * 2)
20593         new_size = SIZE_MAX;
20594     else
20595         new_size = size + (size >> 1);
20596     if (buf == static_buf) {
20597         new_buf = js_malloc(ctx, new_size);
20598         if (!new_buf)
20599             return -1;
20600         memcpy(new_buf, buf, size);
20601     } else {
20602         new_buf = js_realloc(ctx, buf, new_size);
20603         if (!new_buf)
20604             return -1;
20605     }
20606     *pbuf = new_buf;
20607     *psize = new_size;
20608     return 0;
20609 }
20610 
20611 /* '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)20612 static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,
20613                           BOOL *pident_has_escape, int c, BOOL is_private)
20614 {
20615     const uint8_t *p, *p1;
20616     char ident_buf[128], *buf;
20617     size_t ident_size, ident_pos;
20618     JSAtom atom;
20619 
20620     p = *pp;
20621     buf = ident_buf;
20622     ident_size = sizeof(ident_buf);
20623     ident_pos = 0;
20624     if (is_private)
20625         buf[ident_pos++] = '#';
20626     for(;;) {
20627         p1 = p;
20628 
20629         if (c < 128) {
20630             buf[ident_pos++] = c;
20631         } else {
20632             ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c);
20633         }
20634         c = *p1++;
20635         if (c == '\\' && *p1 == 'u') {
20636             c = lre_parse_escape(&p1, TRUE);
20637             *pident_has_escape = TRUE;
20638         } else if (c >= 128) {
20639             c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
20640         }
20641         if (!lre_js_is_ident_next(c))
20642             break;
20643         p = p1;
20644         if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
20645             if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
20646                 atom = JS_ATOM_NULL;
20647                 goto done;
20648             }
20649         }
20650     }
20651     atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
20652  done:
20653     if (unlikely(buf != ident_buf))
20654         js_free(s->ctx, buf);
20655     *pp = p;
20656     return atom;
20657 }
20658 
20659 
next_token(JSParseState * s)20660 static __exception int next_token(JSParseState *s)
20661 {
20662     const uint8_t *p;
20663     int c;
20664     BOOL ident_has_escape;
20665     JSAtom atom;
20666 
20667     if (js_check_stack_overflow(s->ctx->rt, 0)) {
20668         return js_parse_error(s, "stack overflow");
20669     }
20670 
20671     free_token(s, &s->token);
20672 
20673     p = s->last_ptr = s->buf_ptr;
20674     s->got_lf = FALSE;
20675     s->last_line_num = s->token.line_num;
20676  redo:
20677     s->token.line_num = s->line_num;
20678     s->token.ptr = p;
20679     c = *p;
20680     switch(c) {
20681     case 0:
20682         if (p >= s->buf_end) {
20683             s->token.val = TOK_EOF;
20684         } else {
20685             goto def_token;
20686         }
20687         break;
20688     case '`':
20689         if (js_parse_template_part(s, p + 1))
20690             goto fail;
20691         p = s->buf_ptr;
20692         break;
20693     case '\'':
20694     case '\"':
20695         if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
20696             goto fail;
20697         break;
20698     case '\r':  /* accept DOS and MAC newline sequences */
20699         if (p[1] == '\n') {
20700             p++;
20701         }
20702         /* fall thru */
20703     case '\n':
20704         p++;
20705     line_terminator:
20706         s->got_lf = TRUE;
20707         s->line_num++;
20708         goto redo;
20709     case '\f':
20710     case '\v':
20711     case ' ':
20712     case '\t':
20713         p++;
20714         goto redo;
20715     case '/':
20716         if (p[1] == '*') {
20717             /* comment */
20718             p += 2;
20719             for(;;) {
20720                 if (*p == '\0' && p >= s->buf_end) {
20721                     js_parse_error(s, "unexpected end of comment");
20722                     goto fail;
20723                 }
20724                 if (p[0] == '*' && p[1] == '/') {
20725                     p += 2;
20726                     break;
20727                 }
20728                 if (*p == '\n') {
20729                     s->line_num++;
20730                     s->got_lf = TRUE; /* considered as LF for ASI */
20731                     p++;
20732                 } else if (*p == '\r') {
20733                     s->got_lf = TRUE; /* considered as LF for ASI */
20734                     p++;
20735                 } else if (*p >= 0x80) {
20736                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
20737                     if (c == CP_LS || c == CP_PS) {
20738                         s->got_lf = TRUE; /* considered as LF for ASI */
20739                     } else if (c == -1) {
20740                         p++; /* skip invalid UTF-8 */
20741                     }
20742                 } else {
20743                     p++;
20744                 }
20745             }
20746             goto redo;
20747         } else if (p[1] == '/') {
20748             /* line comment */
20749             p += 2;
20750         skip_line_comment:
20751             for(;;) {
20752                 if (*p == '\0' && p >= s->buf_end)
20753                     break;
20754                 if (*p == '\r' || *p == '\n')
20755                     break;
20756                 if (*p >= 0x80) {
20757                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
20758                     /* LS or PS are considered as line terminator */
20759                     if (c == CP_LS || c == CP_PS) {
20760                         break;
20761                     } else if (c == -1) {
20762                         p++; /* skip invalid UTF-8 */
20763                     }
20764                 } else {
20765                     p++;
20766                 }
20767             }
20768             goto redo;
20769         } else if (p[1] == '=') {
20770             p += 2;
20771             s->token.val = TOK_DIV_ASSIGN;
20772         } else {
20773             p++;
20774             s->token.val = c;
20775         }
20776         break;
20777     case '\\':
20778         if (p[1] == 'u') {
20779             const uint8_t *p1 = p + 1;
20780             int c1 = lre_parse_escape(&p1, TRUE);
20781             if (c1 >= 0 && lre_js_is_ident_first(c1)) {
20782                 c = c1;
20783                 p = p1;
20784                 ident_has_escape = TRUE;
20785                 goto has_ident;
20786             } else {
20787                 /* XXX: syntax error? */
20788             }
20789         }
20790         goto def_token;
20791     case 'a': case 'b': case 'c': case 'd':
20792     case 'e': case 'f': case 'g': case 'h':
20793     case 'i': case 'j': case 'k': case 'l':
20794     case 'm': case 'n': case 'o': case 'p':
20795     case 'q': case 'r': case 's': case 't':
20796     case 'u': case 'v': case 'w': case 'x':
20797     case 'y': case 'z':
20798     case 'A': case 'B': case 'C': case 'D':
20799     case 'E': case 'F': case 'G': case 'H':
20800     case 'I': case 'J': case 'K': case 'L':
20801     case 'M': case 'N': case 'O': case 'P':
20802     case 'Q': case 'R': case 'S': case 'T':
20803     case 'U': case 'V': case 'W': case 'X':
20804     case 'Y': case 'Z':
20805     case '_':
20806     case '$':
20807         /* identifier */
20808         p++;
20809         ident_has_escape = FALSE;
20810     has_ident:
20811         atom = parse_ident(s, &p, &ident_has_escape, c, FALSE);
20812         if (atom == JS_ATOM_NULL)
20813             goto fail;
20814         s->token.u.ident.atom = atom;
20815         s->token.u.ident.has_escape = ident_has_escape;
20816         s->token.u.ident.is_reserved = FALSE;
20817         if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
20818             (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
20819              (s->cur_func->js_mode & JS_MODE_STRICT)) ||
20820             (s->token.u.ident.atom == JS_ATOM_yield &&
20821              ((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||
20822               (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
20823                !s->cur_func->in_function_body && s->cur_func->parent &&
20824                (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||
20825             (s->token.u.ident.atom == JS_ATOM_await &&
20826              (s->is_module ||
20827               (((s->cur_func->func_kind & JS_FUNC_ASYNC) ||
20828                 (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
20829                  !s->cur_func->in_function_body && s->cur_func->parent &&
20830                  (s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) {
20831                   if (ident_has_escape) {
20832                       s->token.u.ident.is_reserved = TRUE;
20833                       s->token.val = TOK_IDENT;
20834                   } else {
20835                       /* The keywords atoms are pre allocated */
20836                       s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;
20837                   }
20838         } else {
20839             s->token.val = TOK_IDENT;
20840         }
20841         break;
20842     case '#':
20843         /* private name */
20844         {
20845             const uint8_t *p1;
20846             p++;
20847             p1 = p;
20848             c = *p1++;
20849             if (c == '\\' && *p1 == 'u') {
20850                 c = lre_parse_escape(&p1, TRUE);
20851             } else if (c >= 128) {
20852                 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
20853             }
20854             if (!lre_js_is_ident_first(c)) {
20855                 js_parse_error(s, "invalid first character of private name");
20856                 goto fail;
20857             }
20858             p = p1;
20859             ident_has_escape = FALSE; /* not used */
20860             atom = parse_ident(s, &p, &ident_has_escape, c, TRUE);
20861             if (atom == JS_ATOM_NULL)
20862                 goto fail;
20863             s->token.u.ident.atom = atom;
20864             s->token.val = TOK_PRIVATE_NAME;
20865         }
20866         break;
20867     case '.':
20868         if (p[1] == '.' && p[2] == '.') {
20869             p += 3;
20870             s->token.val = TOK_ELLIPSIS;
20871             break;
20872         }
20873         if (p[1] >= '0' && p[1] <= '9') {
20874             goto parse_number;
20875         } else {
20876             goto def_token;
20877         }
20878         break;
20879     case '0':
20880         /* in strict mode, octal literals are not accepted */
20881         if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) {
20882             js_parse_error(s, "octal literals are deprecated in strict mode");
20883             goto fail;
20884         }
20885         goto parse_number;
20886     case '1': case '2': case '3': case '4':
20887     case '5': case '6': case '7': case '8':
20888     case '9':
20889         /* number */
20890     parse_number:
20891         {
20892             JSValue ret;
20893             const uint8_t *p1;
20894             int flags, radix;
20895             flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |
20896                 ATOD_ACCEPT_UNDERSCORES;
20897 #ifdef CONFIG_BIGNUM
20898             flags |= ATOD_ACCEPT_SUFFIX;
20899             if (s->cur_func->js_mode & JS_MODE_MATH) {
20900                 flags |= ATOD_MODE_BIGINT;
20901                 if (s->cur_func->js_mode & JS_MODE_MATH)
20902                     flags |= ATOD_TYPE_BIG_FLOAT;
20903             }
20904 #endif
20905             radix = 0;
20906 #ifdef CONFIG_BIGNUM
20907             s->token.u.num.exponent = 0;
20908             ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix,
20909                            flags, &s->token.u.num.exponent);
20910 #else
20911             ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
20912                           flags);
20913 #endif
20914             if (JS_IsException(ret))
20915                 goto fail;
20916             /* reject `10instanceof Number` */
20917             if (JS_VALUE_IS_NAN(ret) ||
20918                 lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) {
20919                 JS_FreeValue(s->ctx, ret);
20920                 js_parse_error(s, "invalid number literal");
20921                 goto fail;
20922             }
20923             s->token.val = TOK_NUMBER;
20924             s->token.u.num.val = ret;
20925         }
20926         break;
20927     case '*':
20928         if (p[1] == '=') {
20929             p += 2;
20930             s->token.val = TOK_MUL_ASSIGN;
20931         } else if (p[1] == '*') {
20932             if (p[2] == '=') {
20933                 p += 3;
20934                 s->token.val = TOK_POW_ASSIGN;
20935             } else {
20936                 p += 2;
20937                 s->token.val = TOK_POW;
20938             }
20939         } else {
20940             goto def_token;
20941         }
20942         break;
20943     case '%':
20944         if (p[1] == '=') {
20945             p += 2;
20946             s->token.val = TOK_MOD_ASSIGN;
20947         } else {
20948             goto def_token;
20949         }
20950         break;
20951     case '+':
20952         if (p[1] == '=') {
20953             p += 2;
20954             s->token.val = TOK_PLUS_ASSIGN;
20955         } else if (p[1] == '+') {
20956             p += 2;
20957             s->token.val = TOK_INC;
20958         } else {
20959             goto def_token;
20960         }
20961         break;
20962     case '-':
20963         if (p[1] == '=') {
20964             p += 2;
20965             s->token.val = TOK_MINUS_ASSIGN;
20966         } else if (p[1] == '-') {
20967             if (s->allow_html_comments &&
20968                 p[2] == '>' && s->last_line_num != s->line_num) {
20969                 /* Annex B: `-->` at beginning of line is an html comment end.
20970                    It extends to the end of the line.
20971                  */
20972                 goto skip_line_comment;
20973             }
20974             p += 2;
20975             s->token.val = TOK_DEC;
20976         } else {
20977             goto def_token;
20978         }
20979         break;
20980     case '<':
20981         if (p[1] == '=') {
20982             p += 2;
20983             s->token.val = TOK_LTE;
20984         } else if (p[1] == '<') {
20985             if (p[2] == '=') {
20986                 p += 3;
20987                 s->token.val = TOK_SHL_ASSIGN;
20988             } else {
20989                 p += 2;
20990                 s->token.val = TOK_SHL;
20991             }
20992         } else if (s->allow_html_comments &&
20993                    p[1] == '!' && p[2] == '-' && p[3] == '-') {
20994             /* Annex B: handle `<!--` single line html comments */
20995             goto skip_line_comment;
20996         } else {
20997             goto def_token;
20998         }
20999         break;
21000     case '>':
21001         if (p[1] == '=') {
21002             p += 2;
21003             s->token.val = TOK_GTE;
21004         } else if (p[1] == '>') {
21005             if (p[2] == '>') {
21006                 if (p[3] == '=') {
21007                     p += 4;
21008                     s->token.val = TOK_SHR_ASSIGN;
21009                 } else {
21010                     p += 3;
21011                     s->token.val = TOK_SHR;
21012                 }
21013             } else if (p[2] == '=') {
21014                 p += 3;
21015                 s->token.val = TOK_SAR_ASSIGN;
21016             } else {
21017                 p += 2;
21018                 s->token.val = TOK_SAR;
21019             }
21020         } else {
21021             goto def_token;
21022         }
21023         break;
21024     case '=':
21025         if (p[1] == '=') {
21026             if (p[2] == '=') {
21027                 p += 3;
21028                 s->token.val = TOK_STRICT_EQ;
21029             } else {
21030                 p += 2;
21031                 s->token.val = TOK_EQ;
21032             }
21033         } else if (p[1] == '>') {
21034             p += 2;
21035             s->token.val = TOK_ARROW;
21036         } else {
21037             goto def_token;
21038         }
21039         break;
21040     case '!':
21041         if (p[1] == '=') {
21042             if (p[2] == '=') {
21043                 p += 3;
21044                 s->token.val = TOK_STRICT_NEQ;
21045             } else {
21046                 p += 2;
21047                 s->token.val = TOK_NEQ;
21048             }
21049         } else {
21050             goto def_token;
21051         }
21052         break;
21053     case '&':
21054         if (p[1] == '=') {
21055             p += 2;
21056             s->token.val = TOK_AND_ASSIGN;
21057         } else if (p[1] == '&') {
21058             if (p[2] == '=') {
21059                 p += 3;
21060                 s->token.val = TOK_LAND_ASSIGN;
21061             } else {
21062                 p += 2;
21063                 s->token.val = TOK_LAND;
21064             }
21065         } else {
21066             goto def_token;
21067         }
21068         break;
21069 #ifdef CONFIG_BIGNUM
21070         /* in math mode, '^' is the power operator. '^^' is always the
21071            xor operator and '**' is always the power operator */
21072     case '^':
21073         if (p[1] == '=') {
21074             p += 2;
21075             if (s->cur_func->js_mode & JS_MODE_MATH)
21076                 s->token.val = TOK_MATH_POW_ASSIGN;
21077             else
21078                 s->token.val = TOK_XOR_ASSIGN;
21079         } else if (p[1] == '^') {
21080             if (p[2] == '=') {
21081                 p += 3;
21082                 s->token.val = TOK_XOR_ASSIGN;
21083             } else {
21084                 p += 2;
21085                 s->token.val = '^';
21086             }
21087         } else {
21088             p++;
21089             if (s->cur_func->js_mode & JS_MODE_MATH)
21090                 s->token.val = TOK_MATH_POW;
21091             else
21092                 s->token.val = '^';
21093         }
21094         break;
21095 #else
21096     case '^':
21097         if (p[1] == '=') {
21098             p += 2;
21099             s->token.val = TOK_XOR_ASSIGN;
21100         } else {
21101             goto def_token;
21102         }
21103         break;
21104 #endif
21105     case '|':
21106         if (p[1] == '=') {
21107             p += 2;
21108             s->token.val = TOK_OR_ASSIGN;
21109         } else if (p[1] == '|') {
21110             if (p[2] == '=') {
21111                 p += 3;
21112                 s->token.val = TOK_LOR_ASSIGN;
21113             } else {
21114                 p += 2;
21115                 s->token.val = TOK_LOR;
21116             }
21117         } else {
21118             goto def_token;
21119         }
21120         break;
21121     case '?':
21122         if (p[1] == '?') {
21123             if (p[2] == '=') {
21124                 p += 3;
21125                 s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN;
21126             } else {
21127                 p += 2;
21128                 s->token.val = TOK_DOUBLE_QUESTION_MARK;
21129             }
21130         } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) {
21131             p += 2;
21132             s->token.val = TOK_QUESTION_MARK_DOT;
21133         } else {
21134             goto def_token;
21135         }
21136         break;
21137     default:
21138         if (c >= 128) {
21139             /* unicode value */
21140             c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
21141             switch(c) {
21142             case CP_PS:
21143             case CP_LS:
21144                 /* XXX: should avoid incrementing line_number, but
21145                    needed to handle HTML comments */
21146                 goto line_terminator;
21147             default:
21148                 if (lre_is_space(c)) {
21149                     goto redo;
21150                 } else if (lre_js_is_ident_first(c)) {
21151                     ident_has_escape = FALSE;
21152                     goto has_ident;
21153                 } else {
21154                     js_parse_error(s, "unexpected character");
21155                     goto fail;
21156                 }
21157             }
21158         }
21159     def_token:
21160         s->token.val = c;
21161         p++;
21162         break;
21163     }
21164     s->buf_ptr = p;
21165 
21166     //    dump_token(s, &s->token);
21167     return 0;
21168 
21169  fail:
21170     s->token.val = TOK_ERROR;
21171     return -1;
21172 }
21173 
21174 /* 'c' is the first character. Return JS_ATOM_NULL in case of error */
json_parse_ident(JSParseState * s,const uint8_t ** pp,int c)21175 static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)
21176 {
21177     const uint8_t *p;
21178     char ident_buf[128], *buf;
21179     size_t ident_size, ident_pos;
21180     JSAtom atom;
21181 
21182     p = *pp;
21183     buf = ident_buf;
21184     ident_size = sizeof(ident_buf);
21185     ident_pos = 0;
21186     for(;;) {
21187         buf[ident_pos++] = c;
21188         c = *p;
21189         if (c >= 128 ||
21190             !((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1))
21191             break;
21192         p++;
21193         if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
21194             if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
21195                 atom = JS_ATOM_NULL;
21196                 goto done;
21197             }
21198         }
21199     }
21200     atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
21201  done:
21202     if (unlikely(buf != ident_buf))
21203         js_free(s->ctx, buf);
21204     *pp = p;
21205     return atom;
21206 }
21207 
json_next_token(JSParseState * s)21208 static __exception int json_next_token(JSParseState *s)
21209 {
21210     const uint8_t *p;
21211     int c;
21212     JSAtom atom;
21213 
21214     if (js_check_stack_overflow(s->ctx->rt, 0)) {
21215         return js_parse_error(s, "stack overflow");
21216     }
21217 
21218     free_token(s, &s->token);
21219 
21220     p = s->last_ptr = s->buf_ptr;
21221     s->last_line_num = s->token.line_num;
21222  redo:
21223     s->token.line_num = s->line_num;
21224     s->token.ptr = p;
21225     c = *p;
21226     switch(c) {
21227     case 0:
21228         if (p >= s->buf_end) {
21229             s->token.val = TOK_EOF;
21230         } else {
21231             goto def_token;
21232         }
21233         break;
21234     case '\'':
21235         if (!s->ext_json) {
21236             /* JSON does not accept single quoted strings */
21237             goto def_token;
21238         }
21239         /* fall through */
21240     case '\"':
21241         if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
21242             goto fail;
21243         break;
21244     case '\r':  /* accept DOS and MAC newline sequences */
21245         if (p[1] == '\n') {
21246             p++;
21247         }
21248         /* fall thru */
21249     case '\n':
21250         p++;
21251         s->line_num++;
21252         goto redo;
21253     case '\f':
21254     case '\v':
21255         if (!s->ext_json) {
21256             /* JSONWhitespace does not match <VT>, nor <FF> */
21257             goto def_token;
21258         }
21259         /* fall through */
21260     case ' ':
21261     case '\t':
21262         p++;
21263         goto redo;
21264     case '/':
21265         if (!s->ext_json) {
21266             /* JSON does not accept comments */
21267             goto def_token;
21268         }
21269         if (p[1] == '*') {
21270             /* comment */
21271             p += 2;
21272             for(;;) {
21273                 if (*p == '\0' && p >= s->buf_end) {
21274                     js_parse_error(s, "unexpected end of comment");
21275                     goto fail;
21276                 }
21277                 if (p[0] == '*' && p[1] == '/') {
21278                     p += 2;
21279                     break;
21280                 }
21281                 if (*p == '\n') {
21282                     s->line_num++;
21283                     p++;
21284                 } else if (*p == '\r') {
21285                     p++;
21286                 } else if (*p >= 0x80) {
21287                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
21288                     if (c == -1) {
21289                         p++; /* skip invalid UTF-8 */
21290                     }
21291                 } else {
21292                     p++;
21293                 }
21294             }
21295             goto redo;
21296         } else if (p[1] == '/') {
21297             /* line comment */
21298             p += 2;
21299             for(;;) {
21300                 if (*p == '\0' && p >= s->buf_end)
21301                     break;
21302                 if (*p == '\r' || *p == '\n')
21303                     break;
21304                 if (*p >= 0x80) {
21305                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
21306                     /* LS or PS are considered as line terminator */
21307                     if (c == CP_LS || c == CP_PS) {
21308                         break;
21309                     } else if (c == -1) {
21310                         p++; /* skip invalid UTF-8 */
21311                     }
21312                 } else {
21313                     p++;
21314                 }
21315             }
21316             goto redo;
21317         } else {
21318             goto def_token;
21319         }
21320         break;
21321     case 'a': case 'b': case 'c': case 'd':
21322     case 'e': case 'f': case 'g': case 'h':
21323     case 'i': case 'j': case 'k': case 'l':
21324     case 'm': case 'n': case 'o': case 'p':
21325     case 'q': case 'r': case 's': case 't':
21326     case 'u': case 'v': case 'w': case 'x':
21327     case 'y': case 'z':
21328     case 'A': case 'B': case 'C': case 'D':
21329     case 'E': case 'F': case 'G': case 'H':
21330     case 'I': case 'J': case 'K': case 'L':
21331     case 'M': case 'N': case 'O': case 'P':
21332     case 'Q': case 'R': case 'S': case 'T':
21333     case 'U': case 'V': case 'W': case 'X':
21334     case 'Y': case 'Z':
21335     case '_':
21336     case '$':
21337         /* identifier : only pure ascii characters are accepted */
21338         p++;
21339         atom = json_parse_ident(s, &p, c);
21340         if (atom == JS_ATOM_NULL)
21341             goto fail;
21342         s->token.u.ident.atom = atom;
21343         s->token.u.ident.has_escape = FALSE;
21344         s->token.u.ident.is_reserved = FALSE;
21345         s->token.val = TOK_IDENT;
21346         break;
21347     case '+':
21348         if (!s->ext_json || !is_digit(p[1]))
21349             goto def_token;
21350         goto parse_number;
21351     case '0':
21352         if (is_digit(p[1]))
21353             goto def_token;
21354         goto parse_number;
21355     case '-':
21356         if (!is_digit(p[1]))
21357             goto def_token;
21358         goto parse_number;
21359     case '1': case '2': case '3': case '4':
21360     case '5': case '6': case '7': case '8':
21361     case '9':
21362         /* number */
21363     parse_number:
21364         {
21365             JSValue ret;
21366             int flags, radix;
21367             if (!s->ext_json) {
21368                 flags = 0;
21369                 radix = 10;
21370             } else {
21371                 flags = ATOD_ACCEPT_BIN_OCT;
21372                 radix = 0;
21373             }
21374             ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
21375                           flags);
21376             if (JS_IsException(ret))
21377                 goto fail;
21378             s->token.val = TOK_NUMBER;
21379             s->token.u.num.val = ret;
21380         }
21381         break;
21382     default:
21383         if (c >= 128) {
21384             js_parse_error(s, "unexpected character");
21385             goto fail;
21386         }
21387     def_token:
21388         s->token.val = c;
21389         p++;
21390         break;
21391     }
21392     s->buf_ptr = p;
21393 
21394     //    dump_token(s, &s->token);
21395     return 0;
21396 
21397  fail:
21398     s->token.val = TOK_ERROR;
21399     return -1;
21400 }
21401 
21402 /* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is
21403    only set if TOK_IMPORT is returned */
21404 /* XXX: handle all unicode cases */
simple_next_token(const uint8_t ** pp,BOOL no_line_terminator)21405 static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator)
21406 {
21407     const uint8_t *p;
21408     uint32_t c;
21409 
21410     /* skip spaces and comments */
21411     p = *pp;
21412     for (;;) {
21413         switch(c = *p++) {
21414         case '\r':
21415         case '\n':
21416             if (no_line_terminator)
21417                 return '\n';
21418             continue;
21419         case ' ':
21420         case '\t':
21421         case '\v':
21422         case '\f':
21423             continue;
21424         case '/':
21425             if (*p == '/') {
21426                 if (no_line_terminator)
21427                     return '\n';
21428                 while (*p && *p != '\r' && *p != '\n')
21429                     p++;
21430                 continue;
21431             }
21432             if (*p == '*') {
21433                 while (*++p) {
21434                     if ((*p == '\r' || *p == '\n') && no_line_terminator)
21435                         return '\n';
21436                     if (*p == '*' && p[1] == '/') {
21437                         p += 2;
21438                         break;
21439                     }
21440                 }
21441                 continue;
21442             }
21443             break;
21444         case '=':
21445             if (*p == '>')
21446                 return TOK_ARROW;
21447             break;
21448         default:
21449             if (lre_js_is_ident_first(c)) {
21450                 if (c == 'i') {
21451                     if (p[0] == 'n' && !lre_js_is_ident_next(p[1])) {
21452                         return TOK_IN;
21453                     }
21454                     if (p[0] == 'm' && p[1] == 'p' && p[2] == 'o' &&
21455                         p[3] == 'r' && p[4] == 't' &&
21456                         !lre_js_is_ident_next(p[5])) {
21457                         *pp = p + 5;
21458                         return TOK_IMPORT;
21459                     }
21460                 } else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) {
21461                     return TOK_OF;
21462                 } else if (c == 'e' &&
21463                            p[0] == 'x' && p[1] == 'p' && p[2] == 'o' &&
21464                            p[3] == 'r' && p[4] == 't' &&
21465                            !lre_js_is_ident_next(p[5])) {
21466                     *pp = p + 5;
21467                     return TOK_EXPORT;
21468                 } else if (c == 'f' && p[0] == 'u' && p[1] == 'n' &&
21469                          p[2] == 'c' && p[3] == 't' && p[4] == 'i' &&
21470                          p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) {
21471                     return TOK_FUNCTION;
21472                 }
21473                 return TOK_IDENT;
21474             }
21475             break;
21476         }
21477         return c;
21478     }
21479 }
21480 
peek_token(JSParseState * s,BOOL no_line_terminator)21481 static int peek_token(JSParseState *s, BOOL no_line_terminator)
21482 {
21483     const uint8_t *p = s->buf_ptr;
21484     return simple_next_token(&p, no_line_terminator);
21485 }
21486 
21487 /* return true if 'input' contains the source of a module
21488    (heuristic). 'input' must be a zero terminated.
21489 
21490    Heuristic: skip comments and expect 'import' keyword not followed
21491    by '(' or '.' or export keyword.
21492 */
JS_DetectModule(const char * input,size_t input_len)21493 BOOL JS_DetectModule(const char *input, size_t input_len)
21494 {
21495     const uint8_t *p = (const uint8_t *)input;
21496     int tok;
21497     switch(simple_next_token(&p, FALSE)) {
21498     case TOK_IMPORT:
21499         tok = simple_next_token(&p, FALSE);
21500         return (tok != '.' && tok != '(');
21501     case TOK_EXPORT:
21502         return TRUE;
21503     default:
21504         return FALSE;
21505     }
21506 }
21507 
get_prev_opcode(JSFunctionDef * fd)21508 static inline int get_prev_opcode(JSFunctionDef *fd) {
21509     if (fd->last_opcode_pos < 0)
21510         return OP_invalid;
21511     else
21512         return fd->byte_code.buf[fd->last_opcode_pos];
21513 }
21514 
js_is_live_code(JSParseState * s)21515 static BOOL js_is_live_code(JSParseState *s) {
21516     switch (get_prev_opcode(s->cur_func)) {
21517     case OP_tail_call:
21518     case OP_tail_call_method:
21519     case OP_return:
21520     case OP_return_undef:
21521     case OP_return_async:
21522     case OP_throw:
21523     case OP_throw_error:
21524     case OP_goto:
21525 #if SHORT_OPCODES
21526     case OP_goto8:
21527     case OP_goto16:
21528 #endif
21529     case OP_ret:
21530         return FALSE;
21531     default:
21532         return TRUE;
21533     }
21534 }
21535 
emit_u8(JSParseState * s,uint8_t val)21536 static void emit_u8(JSParseState *s, uint8_t val)
21537 {
21538     dbuf_putc(&s->cur_func->byte_code, val);
21539 }
21540 
emit_u16(JSParseState * s,uint16_t val)21541 static void emit_u16(JSParseState *s, uint16_t val)
21542 {
21543     dbuf_put_u16(&s->cur_func->byte_code, val);
21544 }
21545 
emit_u32(JSParseState * s,uint32_t val)21546 static void emit_u32(JSParseState *s, uint32_t val)
21547 {
21548     dbuf_put_u32(&s->cur_func->byte_code, val);
21549 }
21550 
emit_op(JSParseState * s,uint8_t val)21551 static void emit_op(JSParseState *s, uint8_t val)
21552 {
21553     JSFunctionDef *fd = s->cur_func;
21554     DynBuf *bc = &fd->byte_code;
21555 
21556     /* Use the line number of the last token used, not the next token,
21557        nor the current offset in the source file.
21558      */
21559     if (unlikely(fd->last_opcode_line_num != s->last_line_num)) {
21560         dbuf_putc(bc, OP_line_num);
21561         dbuf_put_u32(bc, s->last_line_num);
21562         fd->last_opcode_line_num = s->last_line_num;
21563     }
21564     fd->last_opcode_pos = bc->size;
21565     dbuf_putc(bc, val);
21566 }
21567 
emit_atom(JSParseState * s,JSAtom name)21568 static void emit_atom(JSParseState *s, JSAtom name)
21569 {
21570     emit_u32(s, JS_DupAtom(s->ctx, name));
21571 }
21572 
update_label(JSFunctionDef * s,int label,int delta)21573 static int update_label(JSFunctionDef *s, int label, int delta)
21574 {
21575     LabelSlot *ls;
21576 
21577     assert(label >= 0 && label < s->label_count);
21578     ls = &s->label_slots[label];
21579     ls->ref_count += delta;
21580     assert(ls->ref_count >= 0);
21581     return ls->ref_count;
21582 }
21583 
new_label_fd(JSFunctionDef * fd,int label)21584 static int new_label_fd(JSFunctionDef *fd, int label)
21585 {
21586     LabelSlot *ls;
21587 
21588     if (label < 0) {
21589         if (js_resize_array(fd->ctx, (void *)&fd->label_slots,
21590                             sizeof(fd->label_slots[0]),
21591                             &fd->label_size, fd->label_count + 1))
21592             return -1;
21593         label = fd->label_count++;
21594         ls = &fd->label_slots[label];
21595         ls->ref_count = 0;
21596         ls->pos = -1;
21597         ls->pos2 = -1;
21598         ls->addr = -1;
21599         ls->first_reloc = NULL;
21600     }
21601     return label;
21602 }
21603 
new_label(JSParseState * s)21604 static int new_label(JSParseState *s)
21605 {
21606     return new_label_fd(s->cur_func, -1);
21607 }
21608 
21609 /* return the label ID offset */
emit_label(JSParseState * s,int label)21610 static int emit_label(JSParseState *s, int label)
21611 {
21612     if (label >= 0) {
21613         emit_op(s, OP_label);
21614         emit_u32(s, label);
21615         s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;
21616         return s->cur_func->byte_code.size - 4;
21617     } else {
21618         return -1;
21619     }
21620 }
21621 
21622 /* return label or -1 if dead code */
emit_goto(JSParseState * s,int opcode,int label)21623 static int emit_goto(JSParseState *s, int opcode, int label)
21624 {
21625     if (js_is_live_code(s)) {
21626         if (label < 0)
21627             label = new_label(s);
21628         emit_op(s, opcode);
21629         emit_u32(s, label);
21630         s->cur_func->label_slots[label].ref_count++;
21631         return label;
21632     }
21633     return -1;
21634 }
21635 
21636 /* return the constant pool index. 'val' is not duplicated. */
cpool_add(JSParseState * s,JSValue val)21637 static int cpool_add(JSParseState *s, JSValue val)
21638 {
21639     JSFunctionDef *fd = s->cur_func;
21640 
21641     if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]),
21642                         &fd->cpool_size, fd->cpool_count + 1))
21643         return -1;
21644     fd->cpool[fd->cpool_count++] = val;
21645     return fd->cpool_count - 1;
21646 }
21647 
emit_push_const(JSParseState * s,JSValueConst val,BOOL as_atom)21648 static __exception int emit_push_const(JSParseState *s, JSValueConst val,
21649                                        BOOL as_atom)
21650 {
21651     int idx;
21652 
21653     if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) {
21654         JSAtom atom;
21655         /* warning: JS_NewAtomStr frees the string value */
21656         JS_DupValue(s->ctx, val);
21657         atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val));
21658         if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) {
21659             emit_op(s, OP_push_atom_value);
21660             emit_u32(s, atom);
21661             return 0;
21662         }
21663     }
21664 
21665     idx = cpool_add(s, JS_DupValue(s->ctx, val));
21666     if (idx < 0)
21667         return -1;
21668     emit_op(s, OP_push_const);
21669     emit_u32(s, idx);
21670     return 0;
21671 }
21672 
21673 /* return the variable index or -1 if not found,
21674    add ARGUMENT_VAR_OFFSET for argument variables */
find_arg(JSContext * ctx,JSFunctionDef * fd,JSAtom name)21675 static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
21676 {
21677     int i;
21678     for(i = fd->arg_count; i-- > 0;) {
21679         if (fd->args[i].var_name == name)
21680             return i | ARGUMENT_VAR_OFFSET;
21681     }
21682     return -1;
21683 }
21684 
find_var(JSContext * ctx,JSFunctionDef * fd,JSAtom name)21685 static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
21686 {
21687     int i;
21688     for(i = fd->var_count; i-- > 0;) {
21689         if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0)
21690             return i;
21691     }
21692     return find_arg(ctx, fd, name);
21693 }
21694 
21695 /* find a variable declaration in a given scope */
find_var_in_scope(JSContext * ctx,JSFunctionDef * fd,JSAtom name,int scope_level)21696 static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd,
21697                              JSAtom name, int scope_level)
21698 {
21699     int scope_idx;
21700     for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0;
21701         scope_idx = fd->vars[scope_idx].scope_next) {
21702         if (fd->vars[scope_idx].scope_level != scope_level)
21703             break;
21704         if (fd->vars[scope_idx].var_name == name)
21705             return scope_idx;
21706     }
21707     return -1;
21708 }
21709 
21710 /* return true if scope == parent_scope or if scope is a child of
21711    parent_scope */
is_child_scope(JSContext * ctx,JSFunctionDef * fd,int scope,int parent_scope)21712 static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd,
21713                            int scope, int parent_scope)
21714 {
21715     while (scope >= 0) {
21716         if (scope == parent_scope)
21717             return TRUE;
21718         scope = fd->scopes[scope].parent;
21719     }
21720     return FALSE;
21721 }
21722 
21723 /* 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)21724 static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd,
21725                                    JSAtom name, int scope_level)
21726 {
21727     int i;
21728     for(i = 0; i < fd->var_count; i++) {
21729         JSVarDef *vd = &fd->vars[i];
21730         if (vd->var_name == name && vd->scope_level == 0) {
21731             if (is_child_scope(ctx, fd, vd->scope_next,
21732                                scope_level))
21733                 return i;
21734         }
21735     }
21736     return -1;
21737 }
21738 
21739 
find_global_var(JSFunctionDef * fd,JSAtom name)21740 static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name)
21741 {
21742     int i;
21743     for(i = 0; i < fd->global_var_count; i++) {
21744         JSGlobalVar *hf = &fd->global_vars[i];
21745         if (hf->var_name == name)
21746             return hf;
21747     }
21748     return NULL;
21749 
21750 }
21751 
find_lexical_global_var(JSFunctionDef * fd,JSAtom name)21752 static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name)
21753 {
21754     JSGlobalVar *hf = find_global_var(fd, name);
21755     if (hf && hf->is_lexical)
21756         return hf;
21757     else
21758         return NULL;
21759 }
21760 
find_lexical_decl(JSContext * ctx,JSFunctionDef * fd,JSAtom name,int scope_idx,BOOL check_catch_var)21761 static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
21762                              int scope_idx, BOOL check_catch_var)
21763 {
21764     while (scope_idx >= 0) {
21765         JSVarDef *vd = &fd->vars[scope_idx];
21766         if (vd->var_name == name &&
21767             (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH &&
21768                                 check_catch_var)))
21769             return scope_idx;
21770         scope_idx = vd->scope_next;
21771     }
21772 
21773     if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) {
21774         if (find_lexical_global_var(fd, name))
21775             return GLOBAL_VAR_OFFSET;
21776     }
21777     return -1;
21778 }
21779 
push_scope(JSParseState * s)21780 static int push_scope(JSParseState *s) {
21781     if (s->cur_func) {
21782         JSFunctionDef *fd = s->cur_func;
21783         int scope = fd->scope_count;
21784         /* XXX: should check for scope overflow */
21785         if ((fd->scope_count + 1) > fd->scope_size) {
21786             int new_size;
21787             size_t slack;
21788             JSVarScope *new_buf;
21789             /* XXX: potential arithmetic overflow */
21790             new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2);
21791             if (fd->scopes == fd->def_scope_array) {
21792                 new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack);
21793                 if (!new_buf)
21794                     return -1;
21795                 memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes));
21796             } else {
21797                 new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack);
21798                 if (!new_buf)
21799                     return -1;
21800             }
21801             new_size += slack / sizeof(*new_buf);
21802             fd->scopes = new_buf;
21803             fd->scope_size = new_size;
21804         }
21805         fd->scope_count++;
21806         fd->scopes[scope].parent = fd->scope_level;
21807         fd->scopes[scope].first = fd->scope_first;
21808         emit_op(s, OP_enter_scope);
21809         emit_u16(s, scope);
21810         return fd->scope_level = scope;
21811     }
21812     return 0;
21813 }
21814 
get_first_lexical_var(JSFunctionDef * fd,int scope)21815 static int get_first_lexical_var(JSFunctionDef *fd, int scope)
21816 {
21817     while (scope >= 0) {
21818         int scope_idx = fd->scopes[scope].first;
21819         if (scope_idx >= 0)
21820             return scope_idx;
21821         scope = fd->scopes[scope].parent;
21822     }
21823     return -1;
21824 }
21825 
pop_scope(JSParseState * s)21826 static void pop_scope(JSParseState *s) {
21827     if (s->cur_func) {
21828         /* disable scoped variables */
21829         JSFunctionDef *fd = s->cur_func;
21830         int scope = fd->scope_level;
21831         emit_op(s, OP_leave_scope);
21832         emit_u16(s, scope);
21833         fd->scope_level = fd->scopes[scope].parent;
21834         fd->scope_first = get_first_lexical_var(fd, fd->scope_level);
21835     }
21836 }
21837 
close_scopes(JSParseState * s,int scope,int scope_stop)21838 static void close_scopes(JSParseState *s, int scope, int scope_stop)
21839 {
21840     while (scope > scope_stop) {
21841         emit_op(s, OP_leave_scope);
21842         emit_u16(s, scope);
21843         scope = s->cur_func->scopes[scope].parent;
21844     }
21845 }
21846 
21847 /* return the variable index or -1 if error */
add_var(JSContext * ctx,JSFunctionDef * fd,JSAtom name)21848 static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
21849 {
21850     JSVarDef *vd;
21851 
21852     /* the local variable indexes are currently stored on 16 bits */
21853     if (fd->var_count >= JS_MAX_LOCAL_VARS) {
21854         JS_ThrowInternalError(ctx, "too many local variables");
21855         return -1;
21856     }
21857     if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]),
21858                         &fd->var_size, fd->var_count + 1))
21859         return -1;
21860     vd = &fd->vars[fd->var_count++];
21861     memset(vd, 0, sizeof(*vd));
21862     vd->var_name = JS_DupAtom(ctx, name);
21863     vd->func_pool_idx = -1;
21864     return fd->var_count - 1;
21865 }
21866 
add_scope_var(JSContext * ctx,JSFunctionDef * fd,JSAtom name,JSVarKindEnum var_kind)21867 static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
21868                          JSVarKindEnum var_kind)
21869 {
21870     int idx = add_var(ctx, fd, name);
21871     if (idx >= 0) {
21872         JSVarDef *vd = &fd->vars[idx];
21873         vd->var_kind = var_kind;
21874         vd->scope_level = fd->scope_level;
21875         vd->scope_next = fd->scope_first;
21876         fd->scopes[fd->scope_level].first = idx;
21877         fd->scope_first = idx;
21878     }
21879     return idx;
21880 }
21881 
add_func_var(JSContext * ctx,JSFunctionDef * fd,JSAtom name)21882 static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
21883 {
21884     int idx = fd->func_var_idx;
21885     if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) {
21886         fd->func_var_idx = idx;
21887         fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME;
21888         if (fd->js_mode & JS_MODE_STRICT)
21889             fd->vars[idx].is_const = TRUE;
21890     }
21891     return idx;
21892 }
21893 
add_arguments_var(JSContext * ctx,JSFunctionDef * fd)21894 static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd)
21895 {
21896     int idx = fd->arguments_var_idx;
21897     if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) {
21898         fd->arguments_var_idx = idx;
21899     }
21900     return idx;
21901 }
21902 
21903 /* add an argument definition in the argument scope. Only needed when
21904    "eval()" may be called in the argument scope. Return 0 if OK. */
add_arguments_arg(JSContext * ctx,JSFunctionDef * fd)21905 static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd)
21906 {
21907     int idx;
21908     if (fd->arguments_arg_idx < 0) {
21909         idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX);
21910         if (idx < 0) {
21911             /* XXX: the scope links are not fully updated. May be an
21912                issue if there are child scopes of the argument
21913                scope */
21914             idx = add_var(ctx, fd, JS_ATOM_arguments);
21915             if (idx < 0)
21916                 return -1;
21917             fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first;
21918             fd->scopes[ARG_SCOPE_INDEX].first = idx;
21919             fd->vars[idx].scope_level = ARG_SCOPE_INDEX;
21920             fd->vars[idx].is_lexical = TRUE;
21921 
21922             fd->arguments_arg_idx = idx;
21923         }
21924     }
21925     return 0;
21926 }
21927 
add_arg(JSContext * ctx,JSFunctionDef * fd,JSAtom name)21928 static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
21929 {
21930     JSVarDef *vd;
21931 
21932     /* the local variable indexes are currently stored on 16 bits */
21933     if (fd->arg_count >= JS_MAX_LOCAL_VARS) {
21934         JS_ThrowInternalError(ctx, "too many arguments");
21935         return -1;
21936     }
21937     if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]),
21938                         &fd->arg_size, fd->arg_count + 1))
21939         return -1;
21940     vd = &fd->args[fd->arg_count++];
21941     memset(vd, 0, sizeof(*vd));
21942     vd->var_name = JS_DupAtom(ctx, name);
21943     vd->func_pool_idx = -1;
21944     return fd->arg_count - 1;
21945 }
21946 
21947 /* add a global variable definition */
add_global_var(JSContext * ctx,JSFunctionDef * s,JSAtom name)21948 static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s,
21949                                      JSAtom name)
21950 {
21951     JSGlobalVar *hf;
21952 
21953     if (js_resize_array(ctx, (void **)&s->global_vars,
21954                         sizeof(s->global_vars[0]),
21955                         &s->global_var_size, s->global_var_count + 1))
21956         return NULL;
21957     hf = &s->global_vars[s->global_var_count++];
21958     hf->cpool_idx = -1;
21959     hf->force_init = FALSE;
21960     hf->is_lexical = FALSE;
21961     hf->is_const = FALSE;
21962     hf->scope_level = s->scope_level;
21963     hf->var_name = JS_DupAtom(ctx, name);
21964     return hf;
21965 }
21966 
21967 typedef enum {
21968     JS_VAR_DEF_WITH,
21969     JS_VAR_DEF_LET,
21970     JS_VAR_DEF_CONST,
21971     JS_VAR_DEF_FUNCTION_DECL, /* function declaration */
21972     JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */
21973     JS_VAR_DEF_CATCH,
21974     JS_VAR_DEF_VAR,
21975 } JSVarDefEnum;
21976 
define_var(JSParseState * s,JSFunctionDef * fd,JSAtom name,JSVarDefEnum var_def_type)21977 static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,
21978                       JSVarDefEnum var_def_type)
21979 {
21980     JSContext *ctx = s->ctx;
21981     JSVarDef *vd;
21982     int idx;
21983 
21984     switch (var_def_type) {
21985     case JS_VAR_DEF_WITH:
21986         idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL);
21987         break;
21988 
21989     case JS_VAR_DEF_LET:
21990     case JS_VAR_DEF_CONST:
21991     case JS_VAR_DEF_FUNCTION_DECL:
21992     case JS_VAR_DEF_NEW_FUNCTION_DECL:
21993         idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE);
21994         if (idx >= 0) {
21995             if (idx < GLOBAL_VAR_OFFSET) {
21996                 if (fd->vars[idx].scope_level == fd->scope_level) {
21997                     /* same scope: in non strict mode, functions
21998                        can be redefined (annex B.3.3.4). */
21999                     if (!(!(fd->js_mode & JS_MODE_STRICT) &&
22000                           var_def_type == JS_VAR_DEF_FUNCTION_DECL &&
22001                           fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) {
22002                         goto redef_lex_error;
22003                     }
22004                 } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) {
22005                     goto redef_lex_error;
22006                 }
22007             } else {
22008                 if (fd->scope_level == fd->body_scope) {
22009                 redef_lex_error:
22010                     /* redefining a scoped var in the same scope: error */
22011                     return js_parse_error(s, "invalid redefinition of lexical identifier");
22012                 }
22013             }
22014         }
22015         if (var_def_type != JS_VAR_DEF_FUNCTION_DECL &&
22016             var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL &&
22017             fd->scope_level == fd->body_scope &&
22018             find_arg(ctx, fd, name) >= 0) {
22019             /* lexical variable redefines a parameter name */
22020             return js_parse_error(s, "invalid redefinition of parameter name");
22021         }
22022 
22023         if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) {
22024             return js_parse_error(s, "invalid redefinition of a variable");
22025         }
22026 
22027         if (fd->is_global_var) {
22028             JSGlobalVar *hf;
22029             hf = find_global_var(fd, name);
22030             if (hf && is_child_scope(ctx, fd, hf->scope_level,
22031                                      fd->scope_level)) {
22032                 return js_parse_error(s, "invalid redefinition of global identifier");
22033             }
22034         }
22035 
22036         if (fd->is_eval &&
22037             (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
22038              fd->eval_type == JS_EVAL_TYPE_MODULE) &&
22039             fd->scope_level == fd->body_scope) {
22040             JSGlobalVar *hf;
22041             hf = add_global_var(s->ctx, fd, name);
22042             if (!hf)
22043                 return -1;
22044             hf->is_lexical = TRUE;
22045             hf->is_const = (var_def_type == JS_VAR_DEF_CONST);
22046             idx = GLOBAL_VAR_OFFSET;
22047         } else {
22048             JSVarKindEnum var_kind;
22049             if (var_def_type == JS_VAR_DEF_FUNCTION_DECL)
22050                 var_kind = JS_VAR_FUNCTION_DECL;
22051             else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL)
22052                 var_kind = JS_VAR_NEW_FUNCTION_DECL;
22053             else
22054                 var_kind = JS_VAR_NORMAL;
22055             idx = add_scope_var(ctx, fd, name, var_kind);
22056             if (idx >= 0) {
22057                 vd = &fd->vars[idx];
22058                 vd->is_lexical = 1;
22059                 vd->is_const = (var_def_type == JS_VAR_DEF_CONST);
22060             }
22061         }
22062         break;
22063 
22064     case JS_VAR_DEF_CATCH:
22065         idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH);
22066         break;
22067 
22068     case JS_VAR_DEF_VAR:
22069         if (find_lexical_decl(ctx, fd, name, fd->scope_first,
22070                               FALSE) >= 0) {
22071        invalid_lexical_redefinition:
22072             /* error to redefine a var that inside a lexical scope */
22073             return js_parse_error(s, "invalid redefinition of lexical identifier");
22074         }
22075         if (fd->is_global_var) {
22076             JSGlobalVar *hf;
22077             hf = find_global_var(fd, name);
22078             if (hf && hf->is_lexical && hf->scope_level == fd->scope_level &&
22079                 fd->eval_type == JS_EVAL_TYPE_MODULE) {
22080                 goto invalid_lexical_redefinition;
22081             }
22082             hf = add_global_var(s->ctx, fd, name);
22083             if (!hf)
22084                 return -1;
22085             idx = GLOBAL_VAR_OFFSET;
22086         } else {
22087             /* if the variable already exists, don't add it again  */
22088             idx = find_var(ctx, fd, name);
22089             if (idx >= 0)
22090                 break;
22091             idx = add_var(ctx, fd, name);
22092             if (idx >= 0) {
22093                 if (name == JS_ATOM_arguments && fd->has_arguments_binding)
22094                     fd->arguments_var_idx = idx;
22095                 fd->vars[idx].scope_next = fd->scope_level;
22096             }
22097         }
22098         break;
22099     default:
22100         abort();
22101     }
22102     return idx;
22103 }
22104 
22105 /* add a private field variable in the current scope */
add_private_class_field(JSParseState * s,JSFunctionDef * fd,JSAtom name,JSVarKindEnum var_kind)22106 static int add_private_class_field(JSParseState *s, JSFunctionDef *fd,
22107                                    JSAtom name, JSVarKindEnum var_kind)
22108 {
22109     JSContext *ctx = s->ctx;
22110     JSVarDef *vd;
22111     int idx;
22112 
22113     idx = add_scope_var(ctx, fd, name, var_kind);
22114     if (idx < 0)
22115         return idx;
22116     vd = &fd->vars[idx];
22117     vd->is_lexical = 1;
22118     vd->is_const = 1;
22119     return idx;
22120 }
22121 
22122 static __exception int js_parse_expr(JSParseState *s);
22123 static __exception int js_parse_function_decl(JSParseState *s,
22124                                               JSParseFunctionEnum func_type,
22125                                               JSFunctionKindEnum func_kind,
22126                                               JSAtom func_name, const uint8_t *ptr,
22127                                               int start_line);
22128 static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s);
22129 static __exception int js_parse_function_decl2(JSParseState *s,
22130                                                JSParseFunctionEnum func_type,
22131                                                JSFunctionKindEnum func_kind,
22132                                                JSAtom func_name,
22133                                                const uint8_t *ptr,
22134                                                int function_line_num,
22135                                                JSParseExportEnum export_flag,
22136                                                JSFunctionDef **pfd);
22137 static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags);
22138 static __exception int js_parse_assign_expr(JSParseState *s);
22139 static __exception int js_parse_unary(JSParseState *s, int parse_flags);
22140 static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
22141                              JSAtom label_name,
22142                              int label_break, int label_cont,
22143                              int drop_count);
22144 static void pop_break_entry(JSFunctionDef *fd);
22145 static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
22146                                        JSAtom local_name, JSAtom export_name,
22147                                        JSExportTypeEnum export_type);
22148 
22149 /* Note: all the fields are already sealed except length */
seal_template_obj(JSContext * ctx,JSValueConst obj)22150 static int seal_template_obj(JSContext *ctx, JSValueConst obj)
22151 {
22152     JSObject *p;
22153     JSShapeProperty *prs;
22154 
22155     p = JS_VALUE_GET_OBJ(obj);
22156     prs = find_own_property1(p, JS_ATOM_length);
22157     if (prs) {
22158         if (js_update_property_flags(ctx, p, &prs,
22159                                      prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)))
22160             return -1;
22161     }
22162     p->extensible = FALSE;
22163     return 0;
22164 }
22165 
js_parse_template(JSParseState * s,int call,int * argc)22166 static __exception int js_parse_template(JSParseState *s, int call, int *argc)
22167 {
22168     JSContext *ctx = s->ctx;
22169     JSValue raw_array, template_object;
22170     JSToken cooked;
22171     int depth, ret;
22172 
22173     raw_array = JS_UNDEFINED; /* avoid warning */
22174     template_object = JS_UNDEFINED; /* avoid warning */
22175     if (call) {
22176         /* Create a template object: an array of cooked strings */
22177         /* Create an array of raw strings and store it to the raw property */
22178         template_object = JS_NewArray(ctx);
22179         if (JS_IsException(template_object))
22180             return -1;
22181         //        pool_idx = s->cur_func->cpool_count;
22182         ret = emit_push_const(s, template_object, 0);
22183         JS_FreeValue(ctx, template_object);
22184         if (ret)
22185             return -1;
22186         raw_array = JS_NewArray(ctx);
22187         if (JS_IsException(raw_array))
22188             return -1;
22189         if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw,
22190                                    raw_array, JS_PROP_THROW) < 0) {
22191             return -1;
22192         }
22193     }
22194 
22195     depth = 0;
22196     while (s->token.val == TOK_TEMPLATE) {
22197         const uint8_t *p = s->token.ptr + 1;
22198         cooked = s->token;
22199         if (call) {
22200             if (JS_DefinePropertyValueUint32(ctx, raw_array, depth,
22201                                              JS_DupValue(ctx, s->token.u.str.str),
22202                                              JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
22203                 return -1;
22204             }
22205             /* re-parse the string with escape sequences but do not throw a
22206                syntax error if it contains invalid sequences
22207              */
22208             if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) {
22209                 cooked.u.str.str = JS_UNDEFINED;
22210             }
22211             if (JS_DefinePropertyValueUint32(ctx, template_object, depth,
22212                                              cooked.u.str.str,
22213                                              JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
22214                 return -1;
22215             }
22216         } else {
22217             JSString *str;
22218             /* re-parse the string with escape sequences and throw a
22219                syntax error if it contains invalid sequences
22220              */
22221             JS_FreeValue(ctx, s->token.u.str.str);
22222             s->token.u.str.str = JS_UNDEFINED;
22223             if (js_parse_string(s, '`', TRUE, p, &cooked, &p))
22224                 return -1;
22225             str = JS_VALUE_GET_STRING(cooked.u.str.str);
22226             if (str->len != 0 || depth == 0) {
22227                 ret = emit_push_const(s, cooked.u.str.str, 1);
22228                 JS_FreeValue(s->ctx, cooked.u.str.str);
22229                 if (ret)
22230                     return -1;
22231                 if (depth == 0) {
22232                     if (s->token.u.str.sep == '`')
22233                         goto done1;
22234                     emit_op(s, OP_get_field2);
22235                     emit_atom(s, JS_ATOM_concat);
22236                 }
22237                 depth++;
22238             } else {
22239                 JS_FreeValue(s->ctx, cooked.u.str.str);
22240             }
22241         }
22242         if (s->token.u.str.sep == '`')
22243             goto done;
22244         if (next_token(s))
22245             return -1;
22246         if (js_parse_expr(s))
22247             return -1;
22248         depth++;
22249         if (s->token.val != '}') {
22250             return js_parse_error(s, "expected '}' after template expression");
22251         }
22252         /* XXX: should convert to string at this stage? */
22253         free_token(s, &s->token);
22254         /* Resume TOK_TEMPLATE parsing (s->token.line_num and
22255          * s->token.ptr are OK) */
22256         s->got_lf = FALSE;
22257         s->last_line_num = s->token.line_num;
22258         if (js_parse_template_part(s, s->buf_ptr))
22259             return -1;
22260     }
22261     return js_parse_expect(s, TOK_TEMPLATE);
22262 
22263  done:
22264     if (call) {
22265         /* Seal the objects */
22266         seal_template_obj(ctx, raw_array);
22267         seal_template_obj(ctx, template_object);
22268         *argc = depth + 1;
22269     } else {
22270         emit_op(s, OP_call_method);
22271         emit_u16(s, depth - 1);
22272     }
22273  done1:
22274     return next_token(s);
22275 }
22276 
22277 
22278 #define PROP_TYPE_IDENT 0
22279 #define PROP_TYPE_VAR   1
22280 #define PROP_TYPE_GET   2
22281 #define PROP_TYPE_SET   3
22282 #define PROP_TYPE_STAR  4
22283 #define PROP_TYPE_ASYNC 5
22284 #define PROP_TYPE_ASYNC_STAR 6
22285 
22286 #define PROP_TYPE_PRIVATE (1 << 4)
22287 
token_is_ident(int tok)22288 static BOOL token_is_ident(int tok)
22289 {
22290     /* Accept keywords and reserved words as property names */
22291     return (tok == TOK_IDENT ||
22292             (tok >= TOK_FIRST_KEYWORD &&
22293              tok <= TOK_LAST_KEYWORD));
22294 }
22295 
22296 /* 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)22297 static int __exception js_parse_property_name(JSParseState *s,
22298                                               JSAtom *pname,
22299                                               BOOL allow_method, BOOL allow_var,
22300                                               BOOL allow_private)
22301 {
22302     int is_private = 0;
22303     BOOL is_non_reserved_ident;
22304     JSAtom name;
22305     int prop_type;
22306 
22307     prop_type = PROP_TYPE_IDENT;
22308     if (allow_method) {
22309         if (token_is_pseudo_keyword(s, JS_ATOM_get)
22310         ||  token_is_pseudo_keyword(s, JS_ATOM_set)) {
22311             /* get x(), set x() */
22312             name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
22313             if (next_token(s))
22314                 goto fail1;
22315             if (s->token.val == ':' || s->token.val == ',' ||
22316                 s->token.val == '}' || s->token.val == '(') {
22317                 is_non_reserved_ident = TRUE;
22318                 goto ident_found;
22319             }
22320             prop_type = PROP_TYPE_GET + (name == JS_ATOM_set);
22321             JS_FreeAtom(s->ctx, name);
22322         } else if (s->token.val == '*') {
22323             if (next_token(s))
22324                 goto fail;
22325             prop_type = PROP_TYPE_STAR;
22326         } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
22327                    peek_token(s, TRUE) != '\n') {
22328             name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
22329             if (next_token(s))
22330                 goto fail1;
22331             if (s->token.val == ':' || s->token.val == ',' ||
22332                 s->token.val == '}' || s->token.val == '(') {
22333                 is_non_reserved_ident = TRUE;
22334                 goto ident_found;
22335             }
22336             JS_FreeAtom(s->ctx, name);
22337             if (s->token.val == '*') {
22338                 if (next_token(s))
22339                     goto fail;
22340                 prop_type = PROP_TYPE_ASYNC_STAR;
22341             } else {
22342                 prop_type = PROP_TYPE_ASYNC;
22343             }
22344         }
22345     }
22346 
22347     if (token_is_ident(s->token.val)) {
22348         /* variable can only be a non-reserved identifier */
22349         is_non_reserved_ident =
22350             (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved);
22351         /* keywords and reserved words have a valid atom */
22352         name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
22353         if (next_token(s))
22354             goto fail1;
22355     ident_found:
22356         if (is_non_reserved_ident &&
22357             prop_type == PROP_TYPE_IDENT && allow_var) {
22358             if (!(s->token.val == ':' ||
22359                   (s->token.val == '(' && allow_method))) {
22360                 prop_type = PROP_TYPE_VAR;
22361             }
22362         }
22363     } else if (s->token.val == TOK_STRING) {
22364         name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
22365         if (name == JS_ATOM_NULL)
22366             goto fail;
22367         if (next_token(s))
22368             goto fail1;
22369     } else if (s->token.val == TOK_NUMBER) {
22370         JSValue val;
22371         val = s->token.u.num.val;
22372 #ifdef CONFIG_BIGNUM
22373         if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
22374             JSBigFloat *p = JS_VALUE_GET_PTR(val);
22375             val = s->ctx->rt->bigfloat_ops.
22376                 mul_pow10_to_float64(s->ctx, &p->num,
22377                                      s->token.u.num.exponent);
22378             if (JS_IsException(val))
22379                 goto fail;
22380             name = JS_ValueToAtom(s->ctx, val);
22381             JS_FreeValue(s->ctx, val);
22382         } else
22383 #endif
22384         {
22385             name = JS_ValueToAtom(s->ctx, val);
22386         }
22387         if (name == JS_ATOM_NULL)
22388             goto fail;
22389         if (next_token(s))
22390             goto fail1;
22391     } else if (s->token.val == '[') {
22392         if (next_token(s))
22393             goto fail;
22394         if (js_parse_expr(s))
22395             goto fail;
22396         if (js_parse_expect(s, ']'))
22397             goto fail;
22398         name = JS_ATOM_NULL;
22399     } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) {
22400         name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
22401         if (next_token(s))
22402             goto fail1;
22403         is_private = PROP_TYPE_PRIVATE;
22404     } else {
22405         goto invalid_prop;
22406     }
22407     if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR &&
22408         s->token.val != '(') {
22409         JS_FreeAtom(s->ctx, name);
22410     invalid_prop:
22411         js_parse_error(s, "invalid property name");
22412         goto fail;
22413     }
22414     *pname = name;
22415     return prop_type | is_private;
22416  fail1:
22417     JS_FreeAtom(s->ctx, name);
22418  fail:
22419     *pname = JS_ATOM_NULL;
22420     return -1;
22421 }
22422 
22423 typedef struct JSParsePos {
22424     int last_line_num;
22425     int line_num;
22426     BOOL got_lf;
22427     const uint8_t *ptr;
22428 } JSParsePos;
22429 
js_parse_get_pos(JSParseState * s,JSParsePos * sp)22430 static int js_parse_get_pos(JSParseState *s, JSParsePos *sp)
22431 {
22432     sp->last_line_num = s->last_line_num;
22433     sp->line_num = s->token.line_num;
22434     sp->ptr = s->token.ptr;
22435     sp->got_lf = s->got_lf;
22436     return 0;
22437 }
22438 
js_parse_seek_token(JSParseState * s,const JSParsePos * sp)22439 static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)
22440 {
22441     s->token.line_num = sp->last_line_num;
22442     s->line_num = sp->line_num;
22443     s->buf_ptr = sp->ptr;
22444     s->got_lf = sp->got_lf;
22445     return next_token(s);
22446 }
22447 
22448 /* return TRUE if a regexp literal is allowed after this token */
is_regexp_allowed(int tok)22449 static BOOL is_regexp_allowed(int tok)
22450 {
22451     switch (tok) {
22452     case TOK_NUMBER:
22453     case TOK_STRING:
22454     case TOK_REGEXP:
22455     case TOK_DEC:
22456     case TOK_INC:
22457     case TOK_NULL:
22458     case TOK_FALSE:
22459     case TOK_TRUE:
22460     case TOK_THIS:
22461     case ')':
22462     case ']':
22463     case '}': /* XXX: regexp may occur after */
22464     case TOK_IDENT:
22465         return FALSE;
22466     default:
22467         return TRUE;
22468     }
22469 }
22470 
22471 #define SKIP_HAS_SEMI       (1 << 0)
22472 #define SKIP_HAS_ELLIPSIS   (1 << 1)
22473 #define SKIP_HAS_ASSIGNMENT (1 << 2)
22474 
22475 /* XXX: improve speed with early bailout */
22476 /* XXX: no longer works if regexps are present. Could use previous
22477    regexp parsing heuristics to handle most cases */
js_parse_skip_parens_token(JSParseState * s,int * pbits,BOOL no_line_terminator)22478 static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator)
22479 {
22480     char state[256];
22481     size_t level = 0;
22482     JSParsePos pos;
22483     int last_tok, tok = TOK_EOF;
22484     int c, tok_len, bits = 0;
22485 
22486     /* protect from underflow */
22487     state[level++] = 0;
22488 
22489     js_parse_get_pos(s, &pos);
22490     last_tok = 0;
22491     for (;;) {
22492         switch(s->token.val) {
22493         case '(':
22494         case '[':
22495         case '{':
22496             if (level >= sizeof(state))
22497                 goto done;
22498             state[level++] = s->token.val;
22499             break;
22500         case ')':
22501             if (state[--level] != '(')
22502                 goto done;
22503             break;
22504         case ']':
22505             if (state[--level] != '[')
22506                 goto done;
22507             break;
22508         case '}':
22509             c = state[--level];
22510             if (c == '`') {
22511                 /* continue the parsing of the template */
22512                 free_token(s, &s->token);
22513                 /* Resume TOK_TEMPLATE parsing (s->token.line_num and
22514                  * s->token.ptr are OK) */
22515                 s->got_lf = FALSE;
22516                 s->last_line_num = s->token.line_num;
22517                 if (js_parse_template_part(s, s->buf_ptr))
22518                     goto done;
22519                 goto handle_template;
22520             } else if (c != '{') {
22521                 goto done;
22522             }
22523             break;
22524         case TOK_TEMPLATE:
22525         handle_template:
22526             if (s->token.u.str.sep != '`') {
22527                 /* '${' inside the template : closing '}' and continue
22528                    parsing the template */
22529                 if (level >= sizeof(state))
22530                     goto done;
22531                 state[level++] = '`';
22532             }
22533             break;
22534         case TOK_EOF:
22535             goto done;
22536         case ';':
22537             if (level == 2) {
22538                 bits |= SKIP_HAS_SEMI;
22539             }
22540             break;
22541         case TOK_ELLIPSIS:
22542             if (level == 2) {
22543                 bits |= SKIP_HAS_ELLIPSIS;
22544             }
22545             break;
22546         case '=':
22547             bits |= SKIP_HAS_ASSIGNMENT;
22548             break;
22549 
22550         case TOK_DIV_ASSIGN:
22551             tok_len = 2;
22552             goto parse_regexp;
22553         case '/':
22554             tok_len = 1;
22555         parse_regexp:
22556             if (is_regexp_allowed(last_tok)) {
22557                 s->buf_ptr -= tok_len;
22558                 if (js_parse_regexp(s)) {
22559                     /* XXX: should clear the exception */
22560                     goto done;
22561                 }
22562             }
22563             break;
22564         }
22565         /* last_tok is only used to recognize regexps */
22566         if (s->token.val == TOK_IDENT &&
22567             (token_is_pseudo_keyword(s, JS_ATOM_of) ||
22568              token_is_pseudo_keyword(s, JS_ATOM_yield))) {
22569             last_tok = TOK_OF;
22570         } else {
22571             last_tok = s->token.val;
22572         }
22573         if (next_token(s)) {
22574             /* XXX: should clear the exception generated by next_token() */
22575             break;
22576         }
22577         if (level <= 1) {
22578             tok = s->token.val;
22579             if (token_is_pseudo_keyword(s, JS_ATOM_of))
22580                 tok = TOK_OF;
22581             if (no_line_terminator && s->last_line_num != s->token.line_num)
22582                 tok = '\n';
22583             break;
22584         }
22585     }
22586  done:
22587     if (pbits) {
22588         *pbits = bits;
22589     }
22590     if (js_parse_seek_token(s, &pos))
22591         return -1;
22592     return tok;
22593 }
22594 
set_object_name(JSParseState * s,JSAtom name)22595 static void set_object_name(JSParseState *s, JSAtom name)
22596 {
22597     JSFunctionDef *fd = s->cur_func;
22598     int opcode;
22599 
22600     opcode = get_prev_opcode(fd);
22601     if (opcode == OP_set_name) {
22602         /* XXX: should free atom after OP_set_name? */
22603         fd->byte_code.size = fd->last_opcode_pos;
22604         fd->last_opcode_pos = -1;
22605         emit_op(s, OP_set_name);
22606         emit_atom(s, name);
22607     } else if (opcode == OP_set_class_name) {
22608         int define_class_pos;
22609         JSAtom atom;
22610         define_class_pos = fd->last_opcode_pos + 1 -
22611             get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
22612         assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
22613         /* for consistency we free the previous atom which is
22614            JS_ATOM_empty_string */
22615         atom = get_u32(fd->byte_code.buf + define_class_pos + 1);
22616         JS_FreeAtom(s->ctx, atom);
22617         put_u32(fd->byte_code.buf + define_class_pos + 1,
22618                 JS_DupAtom(s->ctx, name));
22619         fd->last_opcode_pos = -1;
22620     }
22621 }
22622 
set_object_name_computed(JSParseState * s)22623 static void set_object_name_computed(JSParseState *s)
22624 {
22625     JSFunctionDef *fd = s->cur_func;
22626     int opcode;
22627 
22628     opcode = get_prev_opcode(fd);
22629     if (opcode == OP_set_name) {
22630         /* XXX: should free atom after OP_set_name? */
22631         fd->byte_code.size = fd->last_opcode_pos;
22632         fd->last_opcode_pos = -1;
22633         emit_op(s, OP_set_name_computed);
22634     } else if (opcode == OP_set_class_name) {
22635         int define_class_pos;
22636         define_class_pos = fd->last_opcode_pos + 1 -
22637             get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
22638         assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
22639         fd->byte_code.buf[define_class_pos] = OP_define_class_computed;
22640         fd->last_opcode_pos = -1;
22641     }
22642 }
22643 
js_parse_object_literal(JSParseState * s)22644 static __exception int js_parse_object_literal(JSParseState *s)
22645 {
22646     JSAtom name = JS_ATOM_NULL;
22647     const uint8_t *start_ptr;
22648     int start_line, prop_type;
22649     BOOL has_proto;
22650 
22651     if (next_token(s))
22652         goto fail;
22653     /* XXX: add an initial length that will be patched back */
22654     emit_op(s, OP_object);
22655     has_proto = FALSE;
22656     while (s->token.val != '}') {
22657         /* specific case for getter/setter */
22658         start_ptr = s->token.ptr;
22659         start_line = s->token.line_num;
22660 
22661         if (s->token.val == TOK_ELLIPSIS) {
22662             if (next_token(s))
22663                 return -1;
22664             if (js_parse_assign_expr(s))
22665                 return -1;
22666             emit_op(s, OP_null);  /* dummy excludeList */
22667             emit_op(s, OP_copy_data_properties);
22668             emit_u8(s, 2 | (1 << 2) | (0 << 5));
22669             emit_op(s, OP_drop); /* pop excludeList */
22670             emit_op(s, OP_drop); /* pop src object */
22671             goto next;
22672         }
22673 
22674         prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE);
22675         if (prop_type < 0)
22676             goto fail;
22677 
22678         if (prop_type == PROP_TYPE_VAR) {
22679             /* shortcut for x: x */
22680             emit_op(s, OP_scope_get_var);
22681             emit_atom(s, name);
22682             emit_u16(s, s->cur_func->scope_level);
22683             emit_op(s, OP_define_field);
22684             emit_atom(s, name);
22685         } else if (s->token.val == '(') {
22686             BOOL is_getset = (prop_type == PROP_TYPE_GET ||
22687                               prop_type == PROP_TYPE_SET);
22688             JSParseFunctionEnum func_type;
22689             JSFunctionKindEnum func_kind;
22690             int op_flags;
22691 
22692             func_kind = JS_FUNC_NORMAL;
22693             if (is_getset) {
22694                 func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET;
22695             } else {
22696                 func_type = JS_PARSE_FUNC_METHOD;
22697                 if (prop_type == PROP_TYPE_STAR)
22698                     func_kind = JS_FUNC_GENERATOR;
22699                 else if (prop_type == PROP_TYPE_ASYNC)
22700                     func_kind = JS_FUNC_ASYNC;
22701                 else if (prop_type == PROP_TYPE_ASYNC_STAR)
22702                     func_kind = JS_FUNC_ASYNC_GENERATOR;
22703             }
22704             if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL,
22705                                        start_ptr, start_line))
22706                 goto fail;
22707             if (name == JS_ATOM_NULL) {
22708                 emit_op(s, OP_define_method_computed);
22709             } else {
22710                 emit_op(s, OP_define_method);
22711                 emit_atom(s, name);
22712             }
22713             if (is_getset) {
22714                 op_flags = OP_DEFINE_METHOD_GETTER +
22715                     prop_type - PROP_TYPE_GET;
22716             } else {
22717                 op_flags = OP_DEFINE_METHOD_METHOD;
22718             }
22719             emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE);
22720         } else {
22721             if (js_parse_expect(s, ':'))
22722                 goto fail;
22723             if (js_parse_assign_expr(s))
22724                 goto fail;
22725             if (name == JS_ATOM_NULL) {
22726                 set_object_name_computed(s);
22727                 emit_op(s, OP_define_array_el);
22728                 emit_op(s, OP_drop);
22729             } else if (name == JS_ATOM___proto__) {
22730                 if (has_proto) {
22731                     js_parse_error(s, "duplicate __proto__ property name");
22732                     goto fail;
22733                 }
22734                 emit_op(s, OP_set_proto);
22735                 has_proto = TRUE;
22736             } else {
22737                 set_object_name(s, name);
22738                 emit_op(s, OP_define_field);
22739                 emit_atom(s, name);
22740             }
22741         }
22742         JS_FreeAtom(s->ctx, name);
22743     next:
22744         name = JS_ATOM_NULL;
22745         if (s->token.val != ',')
22746             break;
22747         if (next_token(s))
22748             goto fail;
22749     }
22750     if (js_parse_expect(s, '}'))
22751         goto fail;
22752     return 0;
22753  fail:
22754     JS_FreeAtom(s->ctx, name);
22755     return -1;
22756 }
22757 
22758 /* allow the 'in' binary operator */
22759 #define PF_IN_ACCEPTED  (1 << 0)
22760 /* allow function calls parsing in js_parse_postfix_expr() */
22761 #define PF_POSTFIX_CALL (1 << 1)
22762 /* allow arrow functions parsing in js_parse_postfix_expr() */
22763 #define PF_ARROW_FUNC   (1 << 2)
22764 /* allow the exponentiation operator in js_parse_unary() */
22765 #define PF_POW_ALLOWED  (1 << 3)
22766 /* forbid the exponentiation operator in js_parse_unary() */
22767 #define PF_POW_FORBIDDEN (1 << 4)
22768 
22769 static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags);
22770 
js_parse_left_hand_side_expr(JSParseState * s)22771 static __exception int js_parse_left_hand_side_expr(JSParseState *s)
22772 {
22773     return js_parse_postfix_expr(s, PF_POSTFIX_CALL);
22774 }
22775 
22776 /* XXX: is there is nicer solution ? */
js_parse_class_default_ctor(JSParseState * s,BOOL has_super,JSFunctionDef ** pfd)22777 static __exception int js_parse_class_default_ctor(JSParseState *s,
22778                                                    BOOL has_super,
22779                                                    JSFunctionDef **pfd)
22780 {
22781     JSParsePos pos;
22782     const char *str;
22783     int ret, line_num;
22784     JSParseFunctionEnum func_type;
22785     const uint8_t *saved_buf_end;
22786 
22787     js_parse_get_pos(s, &pos);
22788     if (has_super) {
22789         str = "(...a){super(...a);}";
22790         func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
22791     } else {
22792         str = "(){}";
22793         func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
22794     }
22795     line_num = s->token.line_num;
22796     saved_buf_end = s->buf_end;
22797     s->buf_ptr = (uint8_t *)str;
22798     s->buf_end = (uint8_t *)(str + strlen(str));
22799     ret = next_token(s);
22800     if (!ret) {
22801         ret = js_parse_function_decl2(s, func_type, JS_FUNC_NORMAL,
22802                                       JS_ATOM_NULL, (uint8_t *)str,
22803                                       line_num, JS_PARSE_EXPORT_NONE, pfd);
22804     }
22805     s->buf_end = saved_buf_end;
22806     ret |= js_parse_seek_token(s, &pos);
22807     return ret;
22808 }
22809 
22810 /* find field in the current scope */
find_private_class_field(JSContext * ctx,JSFunctionDef * fd,JSAtom name,int scope_level)22811 static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd,
22812                                     JSAtom name, int scope_level)
22813 {
22814     int idx;
22815     idx = fd->scopes[scope_level].first;
22816     while (idx != -1) {
22817         if (fd->vars[idx].scope_level != scope_level)
22818             break;
22819         if (fd->vars[idx].var_name == name)
22820             return idx;
22821         idx = fd->vars[idx].scope_next;
22822     }
22823     return -1;
22824 }
22825 
22826 /* initialize the class fields, called by the constructor. Note:
22827    super() can be called in an arrow function, so <this> and
22828    <class_fields_init> can be variable references */
emit_class_field_init(JSParseState * s)22829 static void emit_class_field_init(JSParseState *s)
22830 {
22831     int label_next;
22832 
22833     emit_op(s, OP_scope_get_var);
22834     emit_atom(s, JS_ATOM_class_fields_init);
22835     emit_u16(s, s->cur_func->scope_level);
22836 
22837     /* no need to call the class field initializer if not defined */
22838     emit_op(s, OP_dup);
22839     label_next = emit_goto(s, OP_if_false, -1);
22840 
22841     emit_op(s, OP_scope_get_var);
22842     emit_atom(s, JS_ATOM_this);
22843     emit_u16(s, 0);
22844 
22845     emit_op(s, OP_swap);
22846 
22847     emit_op(s, OP_call_method);
22848     emit_u16(s, 0);
22849 
22850     emit_label(s, label_next);
22851     emit_op(s, OP_drop);
22852 }
22853 
22854 /* build a private setter function name from the private getter name */
get_private_setter_name(JSContext * ctx,JSAtom name)22855 static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name)
22856 {
22857     return js_atom_concat_str(ctx, name, "<set>");
22858 }
22859 
22860 typedef struct {
22861     JSFunctionDef *fields_init_fd;
22862     int computed_fields_count;
22863     BOOL has_brand;
22864     int brand_push_pos;
22865 } ClassFieldsDef;
22866 
emit_class_init_start(JSParseState * s,ClassFieldsDef * cf)22867 static __exception int emit_class_init_start(JSParseState *s,
22868                                              ClassFieldsDef *cf)
22869 {
22870     int label_add_brand;
22871 
22872     cf->fields_init_fd = js_parse_function_class_fields_init(s);
22873     if (!cf->fields_init_fd)
22874         return -1;
22875 
22876     s->cur_func = cf->fields_init_fd;
22877 
22878     /* XXX: would be better to add the code only if needed, maybe in a
22879        later pass */
22880     emit_op(s, OP_push_false); /* will be patched later */
22881     cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;
22882     label_add_brand = emit_goto(s, OP_if_false, -1);
22883 
22884     emit_op(s, OP_scope_get_var);
22885     emit_atom(s, JS_ATOM_this);
22886     emit_u16(s, 0);
22887 
22888     emit_op(s, OP_scope_get_var);
22889     emit_atom(s, JS_ATOM_home_object);
22890     emit_u16(s, 0);
22891 
22892     emit_op(s, OP_add_brand);
22893 
22894     emit_label(s, label_add_brand);
22895 
22896     s->cur_func = s->cur_func->parent;
22897     return 0;
22898 }
22899 
add_brand(JSParseState * s,ClassFieldsDef * cf)22900 static __exception int add_brand(JSParseState *s, ClassFieldsDef *cf)
22901 {
22902     if (!cf->has_brand) {
22903         /* define the brand field in 'this' of the initializer */
22904         if (!cf->fields_init_fd) {
22905             if (emit_class_init_start(s, cf))
22906                 return -1;
22907         }
22908         /* patch the start of the function to enable the OP_add_brand code */
22909         cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;
22910 
22911         cf->has_brand = TRUE;
22912     }
22913     return 0;
22914 }
22915 
emit_class_init_end(JSParseState * s,ClassFieldsDef * cf)22916 static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf)
22917 {
22918     int cpool_idx;
22919 
22920     s->cur_func = cf->fields_init_fd;
22921     emit_op(s, OP_return_undef);
22922     s->cur_func = s->cur_func->parent;
22923 
22924     cpool_idx = cpool_add(s, JS_NULL);
22925     cf->fields_init_fd->parent_cpool_idx = cpool_idx;
22926     emit_op(s, OP_fclosure);
22927     emit_u32(s, cpool_idx);
22928     emit_op(s, OP_set_home_object);
22929 }
22930 
22931 
js_parse_class(JSParseState * s,BOOL is_class_expr,JSParseExportEnum export_flag)22932 static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
22933                                       JSParseExportEnum export_flag)
22934 {
22935     JSContext *ctx = s->ctx;
22936     JSFunctionDef *fd = s->cur_func;
22937     JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1;
22938     JSAtom class_var_name = JS_ATOM_NULL;
22939     JSFunctionDef *method_fd, *ctor_fd;
22940     int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset;
22941     int class_flags = 0, i, define_class_offset;
22942     BOOL is_static, is_private;
22943     const uint8_t *class_start_ptr = s->token.ptr;
22944     const uint8_t *start_ptr;
22945     ClassFieldsDef class_fields[2];
22946 
22947     /* classes are parsed and executed in strict mode */
22948     saved_js_mode = fd->js_mode;
22949     fd->js_mode |= JS_MODE_STRICT;
22950     if (next_token(s))
22951         goto fail;
22952     if (s->token.val == TOK_IDENT) {
22953         if (s->token.u.ident.is_reserved) {
22954             js_parse_error_reserved_identifier(s);
22955             goto fail;
22956         }
22957         class_name = JS_DupAtom(ctx, s->token.u.ident.atom);
22958         if (next_token(s))
22959             goto fail;
22960     } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) {
22961         js_parse_error(s, "class statement requires a name");
22962         goto fail;
22963     }
22964     if (!is_class_expr) {
22965         if (class_name == JS_ATOM_NULL)
22966             class_var_name = JS_ATOM__default_; /* export default */
22967         else
22968             class_var_name = class_name;
22969         class_var_name = JS_DupAtom(ctx, class_var_name);
22970     }
22971 
22972     push_scope(s);
22973 
22974     if (s->token.val == TOK_EXTENDS) {
22975         class_flags = JS_DEFINE_CLASS_HAS_HERITAGE;
22976         if (next_token(s))
22977             goto fail;
22978         if (js_parse_left_hand_side_expr(s))
22979             goto fail;
22980     } else {
22981         emit_op(s, OP_undefined);
22982     }
22983 
22984     /* add a 'const' definition for the class name */
22985     if (class_name != JS_ATOM_NULL) {
22986         class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST);
22987         if (class_name_var_idx < 0)
22988             goto fail;
22989     }
22990 
22991     if (js_parse_expect(s, '{'))
22992         goto fail;
22993 
22994     /* this scope contains the private fields */
22995     push_scope(s);
22996 
22997     emit_op(s, OP_push_const);
22998     ctor_cpool_offset = fd->byte_code.size;
22999     emit_u32(s, 0); /* will be patched at the end of the class parsing */
23000 
23001     if (class_name == JS_ATOM_NULL) {
23002         if (class_var_name != JS_ATOM_NULL)
23003             class_name1 = JS_ATOM_default;
23004         else
23005             class_name1 = JS_ATOM_empty_string;
23006     } else {
23007         class_name1 = class_name;
23008     }
23009 
23010     emit_op(s, OP_define_class);
23011     emit_atom(s, class_name1);
23012     emit_u8(s, class_flags);
23013     define_class_offset = fd->last_opcode_pos;
23014 
23015     for(i = 0; i < 2; i++) {
23016         ClassFieldsDef *cf = &class_fields[i];
23017         cf->fields_init_fd = NULL;
23018         cf->computed_fields_count = 0;
23019         cf->has_brand = FALSE;
23020     }
23021 
23022     ctor_fd = NULL;
23023     while (s->token.val != '}') {
23024         if (s->token.val == ';') {
23025             if (next_token(s))
23026                 goto fail;
23027             continue;
23028         }
23029         is_static = (s->token.val == TOK_STATIC);
23030         prop_type = -1;
23031         if (is_static) {
23032             if (next_token(s))
23033                 goto fail;
23034             /* allow "static" field name */
23035             if (s->token.val == ';' || s->token.val == '=') {
23036                 is_static = FALSE;
23037                 name = JS_DupAtom(ctx, JS_ATOM_static);
23038                 prop_type = PROP_TYPE_IDENT;
23039             }
23040         }
23041         if (is_static)
23042             emit_op(s, OP_swap);
23043         start_ptr = s->token.ptr;
23044         if (prop_type < 0) {
23045             prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE);
23046             if (prop_type < 0)
23047                 goto fail;
23048         }
23049         is_private = prop_type & PROP_TYPE_PRIVATE;
23050         prop_type &= ~PROP_TYPE_PRIVATE;
23051 
23052         if ((name == JS_ATOM_constructor && !is_static &&
23053              prop_type != PROP_TYPE_IDENT) ||
23054             (name == JS_ATOM_prototype && is_static) ||
23055             name == JS_ATOM_hash_constructor) {
23056             js_parse_error(s, "invalid method name");
23057             goto fail;
23058         }
23059         if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) {
23060             BOOL is_set = prop_type - PROP_TYPE_GET;
23061             JSFunctionDef *method_fd;
23062 
23063             if (is_private) {
23064                 int idx, var_kind;
23065                 idx = find_private_class_field(ctx, fd, name, fd->scope_level);
23066                 if (idx >= 0) {
23067                     var_kind = fd->vars[idx].var_kind;
23068                     if (var_kind == JS_VAR_PRIVATE_FIELD ||
23069                         var_kind == JS_VAR_PRIVATE_METHOD ||
23070                         var_kind == JS_VAR_PRIVATE_GETTER_SETTER ||
23071                         var_kind == (JS_VAR_PRIVATE_GETTER + is_set)) {
23072                         goto private_field_already_defined;
23073                     }
23074                     fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER;
23075                 } else {
23076                     if (add_private_class_field(s, fd, name,
23077                                                 JS_VAR_PRIVATE_GETTER + is_set) < 0)
23078                         goto fail;
23079                 }
23080                 if (add_brand(s, &class_fields[is_static]) < 0)
23081                     goto fail;
23082             }
23083 
23084             if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set,
23085                                         JS_FUNC_NORMAL, JS_ATOM_NULL,
23086                                         start_ptr, s->token.line_num,
23087                                         JS_PARSE_EXPORT_NONE, &method_fd))
23088                 goto fail;
23089             if (is_private) {
23090                 method_fd->need_home_object = TRUE; /* needed for brand check */
23091                 emit_op(s, OP_set_home_object);
23092                 /* XXX: missing function name */
23093                 emit_op(s, OP_scope_put_var_init);
23094                 if (is_set) {
23095                     JSAtom setter_name;
23096                     int ret;
23097 
23098                     setter_name = get_private_setter_name(ctx, name);
23099                     if (setter_name == JS_ATOM_NULL)
23100                         goto fail;
23101                     emit_atom(s, setter_name);
23102                     ret = add_private_class_field(s, fd, setter_name,
23103                                                   JS_VAR_PRIVATE_SETTER);
23104                     JS_FreeAtom(ctx, setter_name);
23105                     if (ret < 0)
23106                         goto fail;
23107                 } else {
23108                     emit_atom(s, name);
23109                 }
23110                 emit_u16(s, s->cur_func->scope_level);
23111             } else {
23112                 if (name == JS_ATOM_NULL) {
23113                     emit_op(s, OP_define_method_computed);
23114                 } else {
23115                     emit_op(s, OP_define_method);
23116                     emit_atom(s, name);
23117                 }
23118                 emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set);
23119             }
23120         } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') {
23121             ClassFieldsDef *cf = &class_fields[is_static];
23122             JSAtom field_var_name = JS_ATOM_NULL;
23123 
23124             /* class field */
23125 
23126             /* XXX: spec: not consistent with method name checks */
23127             if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) {
23128                 js_parse_error(s, "invalid field name");
23129                 goto fail;
23130             }
23131 
23132             if (is_private) {
23133                 if (find_private_class_field(ctx, fd, name,
23134                                              fd->scope_level) >= 0) {
23135                     goto private_field_already_defined;
23136                 }
23137                 if (add_private_class_field(s, fd, name,
23138                                             JS_VAR_PRIVATE_FIELD) < 0)
23139                     goto fail;
23140                 emit_op(s, OP_private_symbol);
23141                 emit_atom(s, name);
23142                 emit_op(s, OP_scope_put_var_init);
23143                 emit_atom(s, name);
23144                 emit_u16(s, s->cur_func->scope_level);
23145             }
23146 
23147             if (!cf->fields_init_fd) {
23148                 if (emit_class_init_start(s, cf))
23149                     goto fail;
23150             }
23151             if (name == JS_ATOM_NULL ) {
23152                 /* save the computed field name into a variable */
23153                 field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count);
23154                 if (field_var_name == JS_ATOM_NULL)
23155                     goto fail;
23156                 if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) {
23157                     JS_FreeAtom(ctx, field_var_name);
23158                     goto fail;
23159                 }
23160                 emit_op(s, OP_to_propkey);
23161                 emit_op(s, OP_scope_put_var_init);
23162                 emit_atom(s, field_var_name);
23163                 emit_u16(s, s->cur_func->scope_level);
23164             }
23165             s->cur_func = cf->fields_init_fd;
23166             emit_op(s, OP_scope_get_var);
23167             emit_atom(s, JS_ATOM_this);
23168             emit_u16(s, 0);
23169 
23170             if (name == JS_ATOM_NULL) {
23171                 emit_op(s, OP_scope_get_var);
23172                 emit_atom(s, field_var_name);
23173                 emit_u16(s, s->cur_func->scope_level);
23174                 cf->computed_fields_count++;
23175                 JS_FreeAtom(ctx, field_var_name);
23176             } else if (is_private) {
23177                 emit_op(s, OP_scope_get_var);
23178                 emit_atom(s, name);
23179                 emit_u16(s, s->cur_func->scope_level);
23180             }
23181 
23182             if (s->token.val == '=') {
23183                 if (next_token(s))
23184                     goto fail;
23185                 if (js_parse_assign_expr(s))
23186                     goto fail;
23187             } else {
23188                 emit_op(s, OP_undefined);
23189             }
23190             if (is_private) {
23191                 set_object_name_computed(s);
23192                 emit_op(s, OP_define_private_field);
23193             } else if (name == JS_ATOM_NULL) {
23194                 set_object_name_computed(s);
23195                 emit_op(s, OP_define_array_el);
23196                 emit_op(s, OP_drop);
23197             } else {
23198                 set_object_name(s, name);
23199                 emit_op(s, OP_define_field);
23200                 emit_atom(s, name);
23201             }
23202             s->cur_func = s->cur_func->parent;
23203             if (js_parse_expect_semi(s))
23204                 goto fail;
23205         } else {
23206             JSParseFunctionEnum func_type;
23207             JSFunctionKindEnum func_kind;
23208 
23209             func_type = JS_PARSE_FUNC_METHOD;
23210             func_kind = JS_FUNC_NORMAL;
23211             if (prop_type == PROP_TYPE_STAR) {
23212                 func_kind = JS_FUNC_GENERATOR;
23213             } else if (prop_type == PROP_TYPE_ASYNC) {
23214                 func_kind = JS_FUNC_ASYNC;
23215             } else if (prop_type == PROP_TYPE_ASYNC_STAR) {
23216                 func_kind = JS_FUNC_ASYNC_GENERATOR;
23217             } else if (name == JS_ATOM_constructor && !is_static) {
23218                 if (ctor_fd) {
23219                     js_parse_error(s, "property constructor appears more than once");
23220                     goto fail;
23221                 }
23222                 if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE)
23223                     func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
23224                 else
23225                     func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
23226             }
23227             if (is_private) {
23228                 if (add_brand(s, &class_fields[is_static]) < 0)
23229                     goto fail;
23230             }
23231             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))
23232                 goto fail;
23233             if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR ||
23234                 func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
23235                 ctor_fd = method_fd;
23236             } else if (is_private) {
23237                 method_fd->need_home_object = TRUE; /* needed for brand check */
23238                 if (find_private_class_field(ctx, fd, name,
23239                                              fd->scope_level) >= 0) {
23240                 private_field_already_defined:
23241                     js_parse_error(s, "private class field is already defined");
23242                     goto fail;
23243                 }
23244                 if (add_private_class_field(s, fd, name,
23245                                             JS_VAR_PRIVATE_METHOD) < 0)
23246                     goto fail;
23247                 emit_op(s, OP_set_home_object);
23248                 emit_op(s, OP_set_name);
23249                 emit_atom(s, name);
23250                 emit_op(s, OP_scope_put_var_init);
23251                 emit_atom(s, name);
23252                 emit_u16(s, s->cur_func->scope_level);
23253             } else {
23254                 if (name == JS_ATOM_NULL) {
23255                     emit_op(s, OP_define_method_computed);
23256                 } else {
23257                     emit_op(s, OP_define_method);
23258                     emit_atom(s, name);
23259                 }
23260                 emit_u8(s, OP_DEFINE_METHOD_METHOD);
23261             }
23262         }
23263         if (is_static)
23264             emit_op(s, OP_swap);
23265         JS_FreeAtom(ctx, name);
23266         name = JS_ATOM_NULL;
23267     }
23268 
23269     if (s->token.val != '}') {
23270         js_parse_error(s, "expecting '%c'", '}');
23271         goto fail;
23272     }
23273 
23274     if (!ctor_fd) {
23275         if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd))
23276             goto fail;
23277     }
23278     /* patch the constant pool index for the constructor */
23279     put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx);
23280 
23281     /* store the class source code in the constructor. */
23282     if (!(fd->js_mode & JS_MODE_STRIP)) {
23283         js_free(ctx, ctor_fd->source);
23284         ctor_fd->source_len = s->buf_ptr - class_start_ptr;
23285         ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr,
23286                                      ctor_fd->source_len);
23287         if (!ctor_fd->source)
23288             goto fail;
23289     }
23290 
23291     /* consume the '}' */
23292     if (next_token(s))
23293         goto fail;
23294 
23295     /* store the function to initialize the fields to that it can be
23296        referenced by the constructor */
23297     {
23298         ClassFieldsDef *cf = &class_fields[0];
23299         int var_idx;
23300 
23301         var_idx = define_var(s, fd, JS_ATOM_class_fields_init,
23302                              JS_VAR_DEF_CONST);
23303         if (var_idx < 0)
23304             goto fail;
23305         if (cf->fields_init_fd) {
23306             emit_class_init_end(s, cf);
23307         } else {
23308             emit_op(s, OP_undefined);
23309         }
23310         emit_op(s, OP_scope_put_var_init);
23311         emit_atom(s, JS_ATOM_class_fields_init);
23312         emit_u16(s, s->cur_func->scope_level);
23313     }
23314 
23315     /* drop the prototype */
23316     emit_op(s, OP_drop);
23317 
23318     /* initialize the static fields */
23319     if (class_fields[1].fields_init_fd != NULL) {
23320         ClassFieldsDef *cf = &class_fields[1];
23321         emit_op(s, OP_dup);
23322         emit_class_init_end(s, cf);
23323         emit_op(s, OP_call_method);
23324         emit_u16(s, 0);
23325         emit_op(s, OP_drop);
23326     }
23327 
23328     if (class_name != JS_ATOM_NULL) {
23329         /* store the class name in the scoped class name variable (it
23330            is independent from the class statement variable
23331            definition) */
23332         emit_op(s, OP_dup);
23333         emit_op(s, OP_scope_put_var_init);
23334         emit_atom(s, class_name);
23335         emit_u16(s, fd->scope_level);
23336     }
23337     pop_scope(s);
23338     pop_scope(s);
23339 
23340     /* the class statements have a block level scope */
23341     if (class_var_name != JS_ATOM_NULL) {
23342         if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0)
23343             goto fail;
23344         emit_op(s, OP_scope_put_var_init);
23345         emit_atom(s, class_var_name);
23346         emit_u16(s, fd->scope_level);
23347     } else {
23348         if (class_name == JS_ATOM_NULL) {
23349             /* cannot use OP_set_name because the name of the class
23350                must be defined before the static initializers are
23351                executed */
23352             emit_op(s, OP_set_class_name);
23353             emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset);
23354         }
23355     }
23356 
23357     if (export_flag != JS_PARSE_EXPORT_NONE) {
23358         if (!add_export_entry(s, fd->module,
23359                               class_var_name,
23360                               export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default,
23361                               JS_EXPORT_TYPE_LOCAL))
23362             goto fail;
23363     }
23364 
23365     JS_FreeAtom(ctx, class_name);
23366     JS_FreeAtom(ctx, class_var_name);
23367     fd->js_mode = saved_js_mode;
23368     return 0;
23369  fail:
23370     JS_FreeAtom(ctx, name);
23371     JS_FreeAtom(ctx, class_name);
23372     JS_FreeAtom(ctx, class_var_name);
23373     fd->js_mode = saved_js_mode;
23374     return -1;
23375 }
23376 
js_parse_array_literal(JSParseState * s)23377 static __exception int js_parse_array_literal(JSParseState *s)
23378 {
23379     uint32_t idx;
23380     BOOL need_length;
23381 
23382     if (next_token(s))
23383         return -1;
23384     /* small regular arrays are created on the stack */
23385     idx = 0;
23386     while (s->token.val != ']' && idx < 32) {
23387         if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS)
23388             break;
23389         if (js_parse_assign_expr(s))
23390             return -1;
23391         idx++;
23392         /* accept trailing comma */
23393         if (s->token.val == ',') {
23394             if (next_token(s))
23395                 return -1;
23396         } else
23397         if (s->token.val != ']')
23398             goto done;
23399     }
23400     emit_op(s, OP_array_from);
23401     emit_u16(s, idx);
23402 
23403     /* larger arrays and holes are handled with explicit indices */
23404     need_length = FALSE;
23405     while (s->token.val != ']' && idx < 0x7fffffff) {
23406         if (s->token.val == TOK_ELLIPSIS)
23407             break;
23408         need_length = TRUE;
23409         if (s->token.val != ',') {
23410             if (js_parse_assign_expr(s))
23411                 return -1;
23412             emit_op(s, OP_define_field);
23413             emit_u32(s, __JS_AtomFromUInt32(idx));
23414             need_length = FALSE;
23415         }
23416         idx++;
23417         /* accept trailing comma */
23418         if (s->token.val == ',') {
23419             if (next_token(s))
23420                 return -1;
23421         }
23422     }
23423     if (s->token.val == ']') {
23424         if (need_length) {
23425             /* Set the length: Cannot use OP_define_field because
23426                length is not configurable */
23427             emit_op(s, OP_dup);
23428             emit_op(s, OP_push_i32);
23429             emit_u32(s, idx);
23430             emit_op(s, OP_put_field);
23431             emit_atom(s, JS_ATOM_length);
23432         }
23433         goto done;
23434     }
23435 
23436     /* huge arrays and spread elements require a dynamic index on the stack */
23437     emit_op(s, OP_push_i32);
23438     emit_u32(s, idx);
23439 
23440     /* stack has array, index */
23441     while (s->token.val != ']') {
23442         if (s->token.val == TOK_ELLIPSIS) {
23443             if (next_token(s))
23444                 return -1;
23445             if (js_parse_assign_expr(s))
23446                 return -1;
23447 #if 1
23448             emit_op(s, OP_append);
23449 #else
23450             int label_next, label_done;
23451             label_next = new_label(s);
23452             label_done = new_label(s);
23453             /* enumerate object */
23454             emit_op(s, OP_for_of_start);
23455             emit_op(s, OP_rot5l);
23456             emit_op(s, OP_rot5l);
23457             emit_label(s, label_next);
23458             /* on stack: enum_rec array idx */
23459             emit_op(s, OP_for_of_next);
23460             emit_u8(s, 2);
23461             emit_goto(s, OP_if_true, label_done);
23462             /* append element */
23463             /* enum_rec array idx val -> enum_rec array new_idx */
23464             emit_op(s, OP_define_array_el);
23465             emit_op(s, OP_inc);
23466             emit_goto(s, OP_goto, label_next);
23467             emit_label(s, label_done);
23468             /* close enumeration */
23469             emit_op(s, OP_drop); /* drop undef val */
23470             emit_op(s, OP_nip1); /* drop enum_rec */
23471             emit_op(s, OP_nip1);
23472             emit_op(s, OP_nip1);
23473 #endif
23474         } else {
23475             need_length = TRUE;
23476             if (s->token.val != ',') {
23477                 if (js_parse_assign_expr(s))
23478                     return -1;
23479                 /* a idx val */
23480                 emit_op(s, OP_define_array_el);
23481                 need_length = FALSE;
23482             }
23483             emit_op(s, OP_inc);
23484         }
23485         if (s->token.val != ',')
23486             break;
23487         if (next_token(s))
23488             return -1;
23489     }
23490     if (need_length) {
23491         /* Set the length: cannot use OP_define_field because
23492            length is not configurable */
23493         emit_op(s, OP_dup1);    /* array length - array array length */
23494         emit_op(s, OP_put_field);
23495         emit_atom(s, JS_ATOM_length);
23496     } else {
23497         emit_op(s, OP_drop);    /* array length - array */
23498     }
23499 done:
23500     return js_parse_expect(s, ']');
23501 }
23502 
23503 /* XXX: remove */
has_with_scope(JSFunctionDef * s,int scope_level)23504 static BOOL has_with_scope(JSFunctionDef *s, int scope_level)
23505 {
23506     /* check if scope chain contains a with statement */
23507     while (s) {
23508         int scope_idx = s->scopes[scope_level].first;
23509         while (scope_idx >= 0) {
23510             JSVarDef *vd = &s->vars[scope_idx];
23511 
23512             if (vd->var_name == JS_ATOM__with_)
23513                 return TRUE;
23514             scope_idx = vd->scope_next;
23515         }
23516         /* check parent scopes */
23517         scope_level = s->parent_scope_level;
23518         s = s->parent;
23519     }
23520     return FALSE;
23521 }
23522 
get_lvalue(JSParseState * s,int * popcode,int * pscope,JSAtom * pname,int * plabel,int * pdepth,BOOL keep,int tok)23523 static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
23524                                   JSAtom *pname, int *plabel, int *pdepth, BOOL keep,
23525                                   int tok)
23526 {
23527     JSFunctionDef *fd;
23528     int opcode, scope, label, depth;
23529     JSAtom name;
23530 
23531     /* we check the last opcode to get the lvalue type */
23532     fd = s->cur_func;
23533     scope = 0;
23534     name = JS_ATOM_NULL;
23535     label = -1;
23536     depth = 0;
23537     switch(opcode = get_prev_opcode(fd)) {
23538     case OP_scope_get_var:
23539         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
23540         scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
23541         if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) &&
23542             (fd->js_mode & JS_MODE_STRICT)) {
23543             return js_parse_error(s, "invalid lvalue in strict mode");
23544         }
23545         if (name == JS_ATOM_this || name == JS_ATOM_new_target)
23546             goto invalid_lvalue;
23547         depth = 2;  /* will generate OP_get_ref_value */
23548         break;
23549     case OP_get_field:
23550         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
23551         depth = 1;
23552         break;
23553     case OP_scope_get_private_field:
23554         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
23555         scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
23556         depth = 1;
23557         break;
23558     case OP_get_array_el:
23559         depth = 2;
23560         break;
23561     case OP_get_super_value:
23562         depth = 3;
23563         break;
23564     default:
23565     invalid_lvalue:
23566         if (tok == TOK_FOR) {
23567             return js_parse_error(s, "invalid for in/of left hand-side");
23568         } else if (tok == TOK_INC || tok == TOK_DEC) {
23569             return js_parse_error(s, "invalid increment/decrement operand");
23570         } else if (tok == '[' || tok == '{') {
23571             return js_parse_error(s, "invalid destructuring target");
23572         } else {
23573             return js_parse_error(s, "invalid assignment left-hand side");
23574         }
23575     }
23576     /* remove the last opcode */
23577     fd->byte_code.size = fd->last_opcode_pos;
23578     fd->last_opcode_pos = -1;
23579 
23580     if (keep) {
23581         /* get the value but keep the object/fields on the stack */
23582         switch(opcode) {
23583         case OP_scope_get_var:
23584             label = new_label(s);
23585             emit_op(s, OP_scope_make_ref);
23586             emit_atom(s, name);
23587             emit_u32(s, label);
23588             emit_u16(s, scope);
23589             update_label(fd, label, 1);
23590             emit_op(s, OP_get_ref_value);
23591             opcode = OP_get_ref_value;
23592             break;
23593         case OP_get_field:
23594             emit_op(s, OP_get_field2);
23595             emit_atom(s, name);
23596             break;
23597         case OP_scope_get_private_field:
23598             emit_op(s, OP_scope_get_private_field2);
23599             emit_atom(s, name);
23600             emit_u16(s, scope);
23601             break;
23602         case OP_get_array_el:
23603             /* XXX: replace by a single opcode ? */
23604             emit_op(s, OP_to_propkey2);
23605             emit_op(s, OP_dup2);
23606             emit_op(s, OP_get_array_el);
23607             break;
23608         case OP_get_super_value:
23609             emit_op(s, OP_to_propkey);
23610             emit_op(s, OP_dup3);
23611             emit_op(s, OP_get_super_value);
23612             break;
23613         default:
23614             abort();
23615         }
23616     } else {
23617         switch(opcode) {
23618         case OP_scope_get_var:
23619             label = new_label(s);
23620             emit_op(s, OP_scope_make_ref);
23621             emit_atom(s, name);
23622             emit_u32(s, label);
23623             emit_u16(s, scope);
23624             update_label(fd, label, 1);
23625             opcode = OP_get_ref_value;
23626             break;
23627         case OP_get_array_el:
23628             emit_op(s, OP_to_propkey2);
23629             break;
23630         case OP_get_super_value:
23631             emit_op(s, OP_to_propkey);
23632             break;
23633         }
23634     }
23635 
23636     *popcode = opcode;
23637     *pscope = scope;
23638     /* name has refcount for OP_get_field and OP_get_ref_value,
23639        and JS_ATOM_NULL for other opcodes */
23640     *pname = name;
23641     *plabel = label;
23642     if (pdepth)
23643         *pdepth = depth;
23644     return 0;
23645 }
23646 
23647 typedef enum {
23648     PUT_LVALUE_NOKEEP, /* [depth] v -> */
23649     PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently
23650                                 just disable optimizations) */
23651     PUT_LVALUE_KEEP_TOP,  /* [depth] v -> v */
23652     PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */
23653     PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */
23654 } PutLValueEnum;
23655 
23656 /* name has a live reference. 'is_let' is only used with opcode =
23657    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)23658 static void put_lvalue(JSParseState *s, int opcode, int scope,
23659                        JSAtom name, int label, PutLValueEnum special,
23660                        BOOL is_let)
23661 {
23662     switch(opcode) {
23663     case OP_get_field:
23664     case OP_scope_get_private_field:
23665         /* depth = 1 */
23666         switch(special) {
23667         case PUT_LVALUE_NOKEEP:
23668         case PUT_LVALUE_NOKEEP_DEPTH:
23669             break;
23670         case PUT_LVALUE_KEEP_TOP:
23671             emit_op(s, OP_insert2); /* obj v -> v obj v */
23672             break;
23673         case PUT_LVALUE_KEEP_SECOND:
23674             emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */
23675             break;
23676         case PUT_LVALUE_NOKEEP_BOTTOM:
23677             emit_op(s, OP_swap);
23678             break;
23679         default:
23680             abort();
23681         }
23682         break;
23683     case OP_get_array_el:
23684     case OP_get_ref_value:
23685         /* depth = 2 */
23686         if (opcode == OP_get_ref_value) {
23687             JS_FreeAtom(s->ctx, name);
23688             emit_label(s, label);
23689         }
23690         switch(special) {
23691         case PUT_LVALUE_NOKEEP:
23692             emit_op(s, OP_nop); /* will trigger optimization */
23693             break;
23694         case PUT_LVALUE_NOKEEP_DEPTH:
23695             break;
23696         case PUT_LVALUE_KEEP_TOP:
23697             emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */
23698             break;
23699         case PUT_LVALUE_KEEP_SECOND:
23700             emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */
23701             break;
23702         case PUT_LVALUE_NOKEEP_BOTTOM:
23703             emit_op(s, OP_rot3l);
23704             break;
23705         default:
23706             abort();
23707         }
23708         break;
23709     case OP_get_super_value:
23710         /* depth = 3 */
23711         switch(special) {
23712         case PUT_LVALUE_NOKEEP:
23713         case PUT_LVALUE_NOKEEP_DEPTH:
23714             break;
23715         case PUT_LVALUE_KEEP_TOP:
23716             emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */
23717             break;
23718         case PUT_LVALUE_KEEP_SECOND:
23719             emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */
23720             break;
23721         case PUT_LVALUE_NOKEEP_BOTTOM:
23722             emit_op(s, OP_rot4l);
23723             break;
23724         default:
23725             abort();
23726         }
23727         break;
23728     default:
23729         break;
23730     }
23731 
23732     switch(opcode) {
23733     case OP_scope_get_var:  /* val -- */
23734         assert(special == PUT_LVALUE_NOKEEP ||
23735                special == PUT_LVALUE_NOKEEP_DEPTH);
23736         emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var);
23737         emit_u32(s, name);  /* has refcount */
23738         emit_u16(s, scope);
23739         break;
23740     case OP_get_field:
23741         emit_op(s, OP_put_field);
23742         emit_u32(s, name);  /* name has refcount */
23743         break;
23744     case OP_scope_get_private_field:
23745         emit_op(s, OP_scope_put_private_field);
23746         emit_u32(s, name);  /* name has refcount */
23747         emit_u16(s, scope);
23748         break;
23749     case OP_get_array_el:
23750         emit_op(s, OP_put_array_el);
23751         break;
23752     case OP_get_ref_value:
23753         emit_op(s, OP_put_ref_value);
23754         break;
23755     case OP_get_super_value:
23756         emit_op(s, OP_put_super_value);
23757         break;
23758     default:
23759         abort();
23760     }
23761 }
23762 
js_parse_expr_paren(JSParseState * s)23763 static __exception int js_parse_expr_paren(JSParseState *s)
23764 {
23765     if (js_parse_expect(s, '('))
23766         return -1;
23767     if (js_parse_expr(s))
23768         return -1;
23769     if (js_parse_expect(s, ')'))
23770         return -1;
23771     return 0;
23772 }
23773 
js_unsupported_keyword(JSParseState * s,JSAtom atom)23774 static int js_unsupported_keyword(JSParseState *s, JSAtom atom)
23775 {
23776     char buf[ATOM_GET_STR_BUF_SIZE];
23777     return js_parse_error(s, "unsupported keyword: %s",
23778                           JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom));
23779 }
23780 
js_define_var(JSParseState * s,JSAtom name,int tok)23781 static __exception int js_define_var(JSParseState *s, JSAtom name, int tok)
23782 {
23783     JSFunctionDef *fd = s->cur_func;
23784     JSVarDefEnum var_def_type;
23785 
23786     if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
23787         return js_parse_error(s, "yield is a reserved identifier");
23788     }
23789     if ((name == JS_ATOM_arguments || name == JS_ATOM_eval)
23790     &&  (fd->js_mode & JS_MODE_STRICT)) {
23791         return js_parse_error(s, "invalid variable name in strict mode");
23792     }
23793     if ((name == JS_ATOM_let || name == JS_ATOM_undefined)
23794     &&  (tok == TOK_LET || tok == TOK_CONST)) {
23795         return js_parse_error(s, "invalid lexical variable name");
23796     }
23797     switch(tok) {
23798     case TOK_LET:
23799         var_def_type = JS_VAR_DEF_LET;
23800         break;
23801     case TOK_CONST:
23802         var_def_type = JS_VAR_DEF_CONST;
23803         break;
23804     case TOK_VAR:
23805         var_def_type = JS_VAR_DEF_VAR;
23806         break;
23807     case TOK_CATCH:
23808         var_def_type = JS_VAR_DEF_CATCH;
23809         break;
23810     default:
23811         abort();
23812     }
23813     if (define_var(s, fd, name, var_def_type) < 0)
23814         return -1;
23815     return 0;
23816 }
23817 
js_emit_spread_code(JSParseState * s,int depth)23818 static void js_emit_spread_code(JSParseState *s, int depth)
23819 {
23820     int label_rest_next, label_rest_done;
23821 
23822     /* XXX: could check if enum object is an actual array and optimize
23823        slice extraction. enumeration record and target array are in a
23824        different order from OP_append case. */
23825     /* enum_rec xxx -- enum_rec xxx array 0 */
23826     emit_op(s, OP_array_from);
23827     emit_u16(s, 0);
23828     emit_op(s, OP_push_i32);
23829     emit_u32(s, 0);
23830     emit_label(s, label_rest_next = new_label(s));
23831     emit_op(s, OP_for_of_next);
23832     emit_u8(s, 2 + depth);
23833     label_rest_done = emit_goto(s, OP_if_true, -1);
23834     /* array idx val -- array idx */
23835     emit_op(s, OP_define_array_el);
23836     emit_op(s, OP_inc);
23837     emit_goto(s, OP_goto, label_rest_next);
23838     emit_label(s, label_rest_done);
23839     /* enum_rec xxx array idx undef -- enum_rec xxx array */
23840     emit_op(s, OP_drop);
23841     emit_op(s, OP_drop);
23842 }
23843 
js_parse_check_duplicate_parameter(JSParseState * s,JSAtom name)23844 static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name)
23845 {
23846     /* Check for duplicate parameter names */
23847     JSFunctionDef *fd = s->cur_func;
23848     int i;
23849     for (i = 0; i < fd->arg_count; i++) {
23850         if (fd->args[i].var_name == name)
23851             goto duplicate;
23852     }
23853     for (i = 0; i < fd->var_count; i++) {
23854         if (fd->vars[i].var_name == name)
23855             goto duplicate;
23856     }
23857     return 0;
23858 
23859 duplicate:
23860     return js_parse_error(s, "duplicate parameter names not allowed in this context");
23861 }
23862 
js_parse_destructuring_var(JSParseState * s,int tok,int is_arg)23863 static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg)
23864 {
23865     JSAtom name;
23866 
23867     if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
23868     ||  ((s->cur_func->js_mode & JS_MODE_STRICT) &&
23869          (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) {
23870         js_parse_error(s, "invalid destructuring target");
23871         return JS_ATOM_NULL;
23872     }
23873     name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
23874     if (is_arg && js_parse_check_duplicate_parameter(s, name))
23875         goto fail;
23876     if (next_token(s))
23877         goto fail;
23878 
23879     return name;
23880 fail:
23881     JS_FreeAtom(s->ctx, name);
23882     return JS_ATOM_NULL;
23883 }
23884 
23885 /* Return -1 if error, 0 if no initializer, 1 if an initializer is
23886    present at the top level. */
js_parse_destructuring_element(JSParseState * s,int tok,int is_arg,int hasval,int has_ellipsis,BOOL allow_initializer)23887 static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
23888                                         int hasval, int has_ellipsis,
23889                                         BOOL allow_initializer)
23890 {
23891     int label_parse, label_assign, label_done, label_lvalue, depth_lvalue;
23892     int start_addr, assign_addr;
23893     JSAtom prop_name, var_name;
23894     int opcode, scope, tok1, skip_bits;
23895     BOOL has_initializer;
23896 
23897     if (has_ellipsis < 0) {
23898         /* pre-parse destructuration target for spread detection */
23899         js_parse_skip_parens_token(s, &skip_bits, FALSE);
23900         has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS;
23901     }
23902 
23903     label_parse = new_label(s);
23904     label_assign = new_label(s);
23905 
23906     start_addr = s->cur_func->byte_code.size;
23907     if (hasval) {
23908         /* consume value from the stack */
23909         emit_op(s, OP_dup);
23910         emit_op(s, OP_undefined);
23911         emit_op(s, OP_strict_eq);
23912         emit_goto(s, OP_if_true, label_parse);
23913         emit_label(s, label_assign);
23914     } else {
23915         emit_goto(s, OP_goto, label_parse);
23916         emit_label(s, label_assign);
23917         /* leave value on the stack */
23918         emit_op(s, OP_dup);
23919     }
23920     assign_addr = s->cur_func->byte_code.size;
23921     if (s->token.val == '{') {
23922         if (next_token(s))
23923             return -1;
23924         /* throw an exception if the value cannot be converted to an object */
23925         emit_op(s, OP_to_object);
23926         if (has_ellipsis) {
23927             /* add excludeList on stack just below src object */
23928             emit_op(s, OP_object);
23929             emit_op(s, OP_swap);
23930         }
23931         while (s->token.val != '}') {
23932             int prop_type;
23933             if (s->token.val == TOK_ELLIPSIS) {
23934                 if (!has_ellipsis) {
23935                     JS_ThrowInternalError(s->ctx, "unexpected ellipsis token");
23936                     return -1;
23937                 }
23938                 if (next_token(s))
23939                     return -1;
23940                 if (tok) {
23941                     var_name = js_parse_destructuring_var(s, tok, is_arg);
23942                     if (var_name == JS_ATOM_NULL)
23943                         return -1;
23944                     opcode = OP_scope_get_var;
23945                     scope = s->cur_func->scope_level;
23946                     label_lvalue = -1;
23947                     depth_lvalue = 0;
23948                 } else {
23949                     if (js_parse_left_hand_side_expr(s))
23950                         return -1;
23951 
23952                     if (get_lvalue(s, &opcode, &scope, &var_name,
23953                                    &label_lvalue, &depth_lvalue, FALSE, '{'))
23954                         return -1;
23955                 }
23956                 if (s->token.val != '}') {
23957                     js_parse_error(s, "assignment rest property must be last");
23958                     goto var_error;
23959                 }
23960                 emit_op(s, OP_object);  /* target */
23961                 emit_op(s, OP_copy_data_properties);
23962                 emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5));
23963                 goto set_val;
23964             }
23965             prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE);
23966             if (prop_type < 0)
23967                 return -1;
23968             var_name = JS_ATOM_NULL;
23969             opcode = OP_scope_get_var;
23970             scope = s->cur_func->scope_level;
23971             label_lvalue = -1;
23972             depth_lvalue = 0;
23973             if (prop_type == PROP_TYPE_IDENT) {
23974                 if (next_token(s))
23975                     goto prop_error;
23976                 if ((s->token.val == '[' || s->token.val == '{')
23977                     &&  ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
23978                          tok1 == '=' || tok1 == '}')) {
23979                     if (prop_name == JS_ATOM_NULL) {
23980                         /* computed property name on stack */
23981                         if (has_ellipsis) {
23982                             /* define the property in excludeList */
23983                             emit_op(s, OP_to_propkey); /* avoid calling ToString twice */
23984                             emit_op(s, OP_perm3); /* TOS: src excludeList prop */
23985                             emit_op(s, OP_null); /* TOS: src excludeList prop null */
23986                             emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */
23987                             emit_op(s, OP_perm3); /* TOS: excludeList src prop */
23988                         }
23989                         /* get the computed property from the source object */
23990                         emit_op(s, OP_get_array_el2);
23991                     } else {
23992                         /* named property */
23993                         if (has_ellipsis) {
23994                             /* define the property in excludeList */
23995                             emit_op(s, OP_swap); /* TOS: src excludeList */
23996                             emit_op(s, OP_null); /* TOS: src excludeList null */
23997                             emit_op(s, OP_define_field); /* TOS: src excludeList */
23998                             emit_atom(s, prop_name);
23999                             emit_op(s, OP_swap); /* TOS: excludeList src */
24000                         }
24001                         /* get the named property from the source object */
24002                         emit_op(s, OP_get_field2);
24003                         emit_u32(s, prop_name);
24004                     }
24005                     if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0)
24006                         return -1;
24007                     if (s->token.val == '}')
24008                         break;
24009                     /* accept a trailing comma before the '}' */
24010                     if (js_parse_expect(s, ','))
24011                         return -1;
24012                     continue;
24013                 }
24014                 if (prop_name == JS_ATOM_NULL) {
24015                     emit_op(s, OP_to_propkey2);
24016                     if (has_ellipsis) {
24017                         /* define the property in excludeList */
24018                         emit_op(s, OP_perm3);
24019                         emit_op(s, OP_null);
24020                         emit_op(s, OP_define_array_el);
24021                         emit_op(s, OP_perm3);
24022                     }
24023                     /* source prop -- source source prop */
24024                     emit_op(s, OP_dup1);
24025                 } else {
24026                     if (has_ellipsis) {
24027                         /* define the property in excludeList */
24028                         emit_op(s, OP_swap);
24029                         emit_op(s, OP_null);
24030                         emit_op(s, OP_define_field);
24031                         emit_atom(s, prop_name);
24032                         emit_op(s, OP_swap);
24033                     }
24034                     /* source -- source source */
24035                     emit_op(s, OP_dup);
24036                 }
24037                 if (tok) {
24038                     var_name = js_parse_destructuring_var(s, tok, is_arg);
24039                     if (var_name == JS_ATOM_NULL)
24040                         goto prop_error;
24041                 } else {
24042                     if (js_parse_left_hand_side_expr(s))
24043                         goto prop_error;
24044                 lvalue:
24045                     if (get_lvalue(s, &opcode, &scope, &var_name,
24046                                    &label_lvalue, &depth_lvalue, FALSE, '{'))
24047                         goto prop_error;
24048                     /* swap ref and lvalue object if any */
24049                     if (prop_name == JS_ATOM_NULL) {
24050                         switch(depth_lvalue) {
24051                         case 1:
24052                             /* source prop x -> x source prop */
24053                             emit_op(s, OP_rot3r);
24054                             break;
24055                         case 2:
24056                             /* source prop x y -> x y source prop */
24057                             emit_op(s, OP_swap2);   /* t p2 s p1 */
24058                             break;
24059                         case 3:
24060                             /* source prop x y z -> x y z source prop */
24061                             emit_op(s, OP_rot5l);
24062                             emit_op(s, OP_rot5l);
24063                             break;
24064                         }
24065                     } else {
24066                         switch(depth_lvalue) {
24067                         case 1:
24068                             /* source x -> x source */
24069                             emit_op(s, OP_swap);
24070                             break;
24071                         case 2:
24072                             /* source x y -> x y source */
24073                             emit_op(s, OP_rot3l);
24074                             break;
24075                         case 3:
24076                             /* source x y z -> x y z source */
24077                             emit_op(s, OP_rot4l);
24078                             break;
24079                         }
24080                     }
24081                 }
24082                 if (prop_name == JS_ATOM_NULL) {
24083                     /* computed property name on stack */
24084                     /* XXX: should have OP_get_array_el2x with depth */
24085                     /* source prop -- val */
24086                     emit_op(s, OP_get_array_el);
24087                 } else {
24088                     /* named property */
24089                     /* XXX: should have OP_get_field2x with depth */
24090                     /* source -- val */
24091                     emit_op(s, OP_get_field);
24092                     emit_u32(s, prop_name);
24093                 }
24094             } else {
24095                 /* prop_type = PROP_TYPE_VAR, cannot be a computed property */
24096                 if (is_arg && js_parse_check_duplicate_parameter(s, prop_name))
24097                     goto prop_error;
24098                 if ((s->cur_func->js_mode & JS_MODE_STRICT) &&
24099                     (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) {
24100                     js_parse_error(s, "invalid destructuring target");
24101                     goto prop_error;
24102                 }
24103                 if (has_ellipsis) {
24104                     /* define the property in excludeList */
24105                     emit_op(s, OP_swap);
24106                     emit_op(s, OP_null);
24107                     emit_op(s, OP_define_field);
24108                     emit_atom(s, prop_name);
24109                     emit_op(s, OP_swap);
24110                 }
24111                 if (!tok || tok == TOK_VAR) {
24112                     /* generate reference */
24113                     /* source -- source source */
24114                     emit_op(s, OP_dup);
24115                     emit_op(s, OP_scope_get_var);
24116                     emit_atom(s, prop_name);
24117                     emit_u16(s, s->cur_func->scope_level);
24118                     goto lvalue;
24119                 }
24120                 var_name = JS_DupAtom(s->ctx, prop_name);
24121                 /* source -- source val */
24122                 emit_op(s, OP_get_field2);
24123                 emit_u32(s, prop_name);
24124             }
24125         set_val:
24126             if (tok) {
24127                 if (js_define_var(s, var_name, tok))
24128                     goto var_error;
24129                 scope = s->cur_func->scope_level;
24130             }
24131             if (s->token.val == '=') {  /* handle optional default value */
24132                 int label_hasval;
24133                 emit_op(s, OP_dup);
24134                 emit_op(s, OP_undefined);
24135                 emit_op(s, OP_strict_eq);
24136                 label_hasval = emit_goto(s, OP_if_false, -1);
24137                 if (next_token(s))
24138                     goto var_error;
24139                 emit_op(s, OP_drop);
24140                 if (js_parse_assign_expr(s))
24141                     goto var_error;
24142                 if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
24143                     set_object_name(s, var_name);
24144                 emit_label(s, label_hasval);
24145             }
24146             /* store value into lvalue object */
24147             put_lvalue(s, opcode, scope, var_name, label_lvalue,
24148                        PUT_LVALUE_NOKEEP_DEPTH,
24149                        (tok == TOK_CONST || tok == TOK_LET));
24150             if (s->token.val == '}')
24151                 break;
24152             /* accept a trailing comma before the '}' */
24153             if (js_parse_expect(s, ','))
24154                 return -1;
24155         }
24156         /* drop the source object */
24157         emit_op(s, OP_drop);
24158         if (has_ellipsis) {
24159             emit_op(s, OP_drop); /* pop excludeList */
24160         }
24161         if (next_token(s))
24162             return -1;
24163     } else if (s->token.val == '[') {
24164         BOOL has_spread;
24165         int enum_depth;
24166         BlockEnv block_env;
24167 
24168         if (next_token(s))
24169             return -1;
24170         /* the block environment is only needed in generators in case
24171            'yield' triggers a 'return' */
24172         push_break_entry(s->cur_func, &block_env,
24173                          JS_ATOM_NULL, -1, -1, 2);
24174         block_env.has_iterator = TRUE;
24175         emit_op(s, OP_for_of_start);
24176         has_spread = FALSE;
24177         while (s->token.val != ']') {
24178             /* get the next value */
24179             if (s->token.val == TOK_ELLIPSIS) {
24180                 if (next_token(s))
24181                     return -1;
24182                 if (s->token.val == ',' || s->token.val == ']')
24183                     return js_parse_error(s, "missing binding pattern...");
24184                 has_spread = TRUE;
24185             }
24186             if (s->token.val == ',') {
24187                 /* do nothing, skip the value, has_spread is false */
24188                 emit_op(s, OP_for_of_next);
24189                 emit_u8(s, 0);
24190                 emit_op(s, OP_drop);
24191                 emit_op(s, OP_drop);
24192             } else if ((s->token.val == '[' || s->token.val == '{')
24193                    &&  ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
24194                         tok1 == '=' || tok1 == ']')) {
24195                 if (has_spread) {
24196                     if (tok1 == '=')
24197                         return js_parse_error(s, "rest element cannot have a default value");
24198                     js_emit_spread_code(s, 0);
24199                 } else {
24200                     emit_op(s, OP_for_of_next);
24201                     emit_u8(s, 0);
24202                     emit_op(s, OP_drop);
24203                 }
24204                 if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
24205                     return -1;
24206             } else {
24207                 var_name = JS_ATOM_NULL;
24208                 enum_depth = 0;
24209                 if (tok) {
24210                     var_name = js_parse_destructuring_var(s, tok, is_arg);
24211                     if (var_name == JS_ATOM_NULL)
24212                         goto var_error;
24213                     if (js_define_var(s, var_name, tok))
24214                         goto var_error;
24215                     opcode = OP_scope_get_var;
24216                     scope = s->cur_func->scope_level;
24217                 } else {
24218                     if (js_parse_left_hand_side_expr(s))
24219                         return -1;
24220                     if (get_lvalue(s, &opcode, &scope, &var_name,
24221                                    &label_lvalue, &enum_depth, FALSE, '[')) {
24222                         return -1;
24223                     }
24224                 }
24225                 if (has_spread) {
24226                     js_emit_spread_code(s, enum_depth);
24227                 } else {
24228                     emit_op(s, OP_for_of_next);
24229                     emit_u8(s, enum_depth);
24230                     emit_op(s, OP_drop);
24231                 }
24232                 if (s->token.val == '=' && !has_spread) {
24233                     /* handle optional default value */
24234                     int label_hasval;
24235                     emit_op(s, OP_dup);
24236                     emit_op(s, OP_undefined);
24237                     emit_op(s, OP_strict_eq);
24238                     label_hasval = emit_goto(s, OP_if_false, -1);
24239                     if (next_token(s))
24240                         goto var_error;
24241                     emit_op(s, OP_drop);
24242                     if (js_parse_assign_expr(s))
24243                         goto var_error;
24244                     if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
24245                         set_object_name(s, var_name);
24246                     emit_label(s, label_hasval);
24247                 }
24248                 /* store value into lvalue object */
24249                 put_lvalue(s, opcode, scope, var_name,
24250                            label_lvalue, PUT_LVALUE_NOKEEP_DEPTH,
24251                            (tok == TOK_CONST || tok == TOK_LET));
24252             }
24253             if (s->token.val == ']')
24254                 break;
24255             if (has_spread)
24256                 return js_parse_error(s, "rest element must be the last one");
24257             /* accept a trailing comma before the ']' */
24258             if (js_parse_expect(s, ','))
24259                 return -1;
24260         }
24261         /* close iterator object:
24262            if completed, enum_obj has been replaced by undefined */
24263         emit_op(s, OP_iterator_close);
24264         pop_break_entry(s->cur_func);
24265         if (next_token(s))
24266             return -1;
24267     } else {
24268         return js_parse_error(s, "invalid assignment syntax");
24269     }
24270     if (s->token.val == '=' && allow_initializer) {
24271         label_done = emit_goto(s, OP_goto, -1);
24272         if (next_token(s))
24273             return -1;
24274         emit_label(s, label_parse);
24275         if (hasval)
24276             emit_op(s, OP_drop);
24277         if (js_parse_assign_expr(s))
24278             return -1;
24279         emit_goto(s, OP_goto, label_assign);
24280         emit_label(s, label_done);
24281         has_initializer = TRUE;
24282     } else {
24283         /* normally hasval is true except if
24284            js_parse_skip_parens_token() was wrong in the parsing */
24285         //        assert(hasval);
24286         if (!hasval) {
24287             js_parse_error(s, "too complicated destructuring expression");
24288             return -1;
24289         }
24290         /* remove test and decrement label ref count */
24291         memset(s->cur_func->byte_code.buf + start_addr, OP_nop,
24292                assign_addr - start_addr);
24293         s->cur_func->label_slots[label_parse].ref_count--;
24294         has_initializer = FALSE;
24295     }
24296     return has_initializer;
24297 
24298  prop_error:
24299     JS_FreeAtom(s->ctx, prop_name);
24300  var_error:
24301     JS_FreeAtom(s->ctx, var_name);
24302     return -1;
24303 }
24304 
24305 typedef enum FuncCallType {
24306     FUNC_CALL_NORMAL,
24307     FUNC_CALL_NEW,
24308     FUNC_CALL_SUPER_CTOR,
24309     FUNC_CALL_TEMPLATE,
24310 } FuncCallType;
24311 
optional_chain_test(JSParseState * s,int * poptional_chaining_label,int drop_count)24312 static void optional_chain_test(JSParseState *s, int *poptional_chaining_label,
24313                                 int drop_count)
24314 {
24315     int label_next, i;
24316     if (*poptional_chaining_label < 0)
24317         *poptional_chaining_label = new_label(s);
24318    /* XXX: could be more efficient with a specific opcode */
24319     emit_op(s, OP_dup);
24320     emit_op(s, OP_is_undefined_or_null);
24321     label_next = emit_goto(s, OP_if_false, -1);
24322     for(i = 0; i < drop_count; i++)
24323         emit_op(s, OP_drop);
24324     emit_op(s, OP_undefined);
24325     emit_goto(s, OP_goto, *poptional_chaining_label);
24326     emit_label(s, label_next);
24327 }
24328 
24329 /* allowed parse_flags: PF_POSTFIX_CALL, PF_ARROW_FUNC */
js_parse_postfix_expr(JSParseState * s,int parse_flags)24330 static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
24331 {
24332     FuncCallType call_type;
24333     int optional_chaining_label;
24334     BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0;
24335 
24336     call_type = FUNC_CALL_NORMAL;
24337     switch(s->token.val) {
24338     case TOK_NUMBER:
24339         {
24340             JSValue val;
24341             val = s->token.u.num.val;
24342 
24343             if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
24344                 emit_op(s, OP_push_i32);
24345                 emit_u32(s, JS_VALUE_GET_INT(val));
24346             } else
24347 #ifdef CONFIG_BIGNUM
24348             if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
24349                 slimb_t e;
24350                 int ret;
24351 
24352                 /* need a runtime conversion */
24353                 /* XXX: could add a cache and/or do it once at
24354                    the start of the function */
24355                 if (emit_push_const(s, val, 0) < 0)
24356                     return -1;
24357                 e = s->token.u.num.exponent;
24358                 if (e == (int32_t)e) {
24359                     emit_op(s, OP_push_i32);
24360                     emit_u32(s, e);
24361                 } else {
24362                     val = JS_NewBigInt64_1(s->ctx, e);
24363                     if (JS_IsException(val))
24364                         return -1;
24365                     ret = emit_push_const(s, val, 0);
24366                     JS_FreeValue(s->ctx, val);
24367                     if (ret < 0)
24368                         return -1;
24369                 }
24370                 emit_op(s, OP_mul_pow10);
24371             } else
24372 #endif
24373             {
24374                 if (emit_push_const(s, val, 0) < 0)
24375                     return -1;
24376             }
24377         }
24378         if (next_token(s))
24379             return -1;
24380         break;
24381     case TOK_TEMPLATE:
24382         if (js_parse_template(s, 0, NULL))
24383             return -1;
24384         break;
24385     case TOK_STRING:
24386         if (emit_push_const(s, s->token.u.str.str, 1))
24387             return -1;
24388         if (next_token(s))
24389             return -1;
24390         break;
24391 
24392     case TOK_DIV_ASSIGN:
24393         s->buf_ptr -= 2;
24394         goto parse_regexp;
24395     case '/':
24396         s->buf_ptr--;
24397     parse_regexp:
24398         {
24399             JSValue str;
24400             int ret, backtrace_flags;
24401             if (!s->ctx->compile_regexp)
24402                 return js_parse_error(s, "RegExp are not supported");
24403             /* the previous token is '/' or '/=', so no need to free */
24404             if (js_parse_regexp(s))
24405                 return -1;
24406             ret = emit_push_const(s, s->token.u.regexp.body, 0);
24407             str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body,
24408                                          s->token.u.regexp.flags);
24409             if (JS_IsException(str)) {
24410                 /* add the line number info */
24411                 backtrace_flags = 0;
24412                 if (s->cur_func && s->cur_func->backtrace_barrier)
24413                     backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
24414                 build_backtrace(s->ctx, s->ctx->rt->current_exception,
24415                                 s->filename, s->token.line_num,
24416                                 backtrace_flags);
24417                 return -1;
24418             }
24419             ret = emit_push_const(s, str, 0);
24420             JS_FreeValue(s->ctx, str);
24421             if (ret)
24422                 return -1;
24423             /* we use a specific opcode to be sure the correct
24424                function is called (otherwise the bytecode would have
24425                to be verified by the RegExp constructor) */
24426             emit_op(s, OP_regexp);
24427             if (next_token(s))
24428                 return -1;
24429         }
24430         break;
24431     case '(':
24432         if ((parse_flags & PF_ARROW_FUNC) &&
24433             js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) {
24434             if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
24435                                        JS_FUNC_NORMAL, JS_ATOM_NULL,
24436                                        s->token.ptr, s->token.line_num))
24437                 return -1;
24438         } else {
24439             if (js_parse_expr_paren(s))
24440                 return -1;
24441         }
24442         break;
24443     case TOK_FUNCTION:
24444         if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
24445                                    JS_FUNC_NORMAL, JS_ATOM_NULL,
24446                                    s->token.ptr, s->token.line_num))
24447             return -1;
24448         break;
24449     case TOK_CLASS:
24450         if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE))
24451             return -1;
24452         break;
24453     case TOK_NULL:
24454         if (next_token(s))
24455             return -1;
24456         emit_op(s, OP_null);
24457         break;
24458     case TOK_THIS:
24459         if (next_token(s))
24460             return -1;
24461         emit_op(s, OP_scope_get_var);
24462         emit_atom(s, JS_ATOM_this);
24463         emit_u16(s, 0);
24464         break;
24465     case TOK_FALSE:
24466         if (next_token(s))
24467             return -1;
24468         emit_op(s, OP_push_false);
24469         break;
24470     case TOK_TRUE:
24471         if (next_token(s))
24472             return -1;
24473         emit_op(s, OP_push_true);
24474         break;
24475     case TOK_IDENT:
24476         {
24477             JSAtom name;
24478             if (s->token.u.ident.is_reserved) {
24479                 return js_parse_error_reserved_identifier(s);
24480             }
24481             if ((parse_flags & PF_ARROW_FUNC) &&
24482                 peek_token(s, TRUE) == TOK_ARROW) {
24483                 if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
24484                                            JS_FUNC_NORMAL, JS_ATOM_NULL,
24485                                            s->token.ptr, s->token.line_num))
24486                     return -1;
24487             } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
24488                        peek_token(s, TRUE) != '\n') {
24489                 const uint8_t *source_ptr;
24490                 int source_line_num;
24491 
24492                 source_ptr = s->token.ptr;
24493                 source_line_num = s->token.line_num;
24494                 if (next_token(s))
24495                     return -1;
24496                 if (s->token.val == TOK_FUNCTION) {
24497                     if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
24498                                                JS_FUNC_ASYNC, JS_ATOM_NULL,
24499                                                source_ptr, source_line_num))
24500                         return -1;
24501                 } else if ((parse_flags & PF_ARROW_FUNC) &&
24502                            ((s->token.val == '(' &&
24503                              js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) ||
24504                             (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
24505                              peek_token(s, TRUE) == TOK_ARROW))) {
24506                     if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
24507                                                JS_FUNC_ASYNC, JS_ATOM_NULL,
24508                                                source_ptr, source_line_num))
24509                         return -1;
24510                 } else {
24511                     name = JS_DupAtom(s->ctx, JS_ATOM_async);
24512                     goto do_get_var;
24513                 }
24514             } else {
24515                 if (s->token.u.ident.atom == JS_ATOM_arguments &&
24516                     !s->cur_func->arguments_allowed) {
24517                     js_parse_error(s, "'arguments' identifier is not allowed in class field initializer");
24518                     return -1;
24519                 }
24520                 name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
24521                 if (next_token(s))  /* update line number before emitting code */
24522                     return -1;
24523             do_get_var:
24524                 emit_op(s, OP_scope_get_var);
24525                 emit_u32(s, name);
24526                 emit_u16(s, s->cur_func->scope_level);
24527             }
24528         }
24529         break;
24530     case '{':
24531     case '[':
24532         {
24533             int skip_bits;
24534             if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
24535                 if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
24536                     return -1;
24537             } else {
24538                 if (s->token.val == '{') {
24539                     if (js_parse_object_literal(s))
24540                         return -1;
24541                 } else {
24542                     if (js_parse_array_literal(s))
24543                         return -1;
24544                 }
24545             }
24546         }
24547         break;
24548     case TOK_NEW:
24549         if (next_token(s))
24550             return -1;
24551         if (s->token.val == '.') {
24552             if (next_token(s))
24553                 return -1;
24554             if (!token_is_pseudo_keyword(s, JS_ATOM_target))
24555                 return js_parse_error(s, "expecting target");
24556             if (!s->cur_func->new_target_allowed)
24557                 return js_parse_error(s, "new.target only allowed within functions");
24558             if (next_token(s))
24559                 return -1;
24560             emit_op(s, OP_scope_get_var);
24561             emit_atom(s, JS_ATOM_new_target);
24562             emit_u16(s, 0);
24563         } else {
24564             if (js_parse_postfix_expr(s, 0))
24565                 return -1;
24566             accept_lparen = TRUE;
24567             if (s->token.val != '(') {
24568                 /* new operator on an object */
24569                 emit_op(s, OP_dup);
24570                 emit_op(s, OP_call_constructor);
24571                 emit_u16(s, 0);
24572             } else {
24573                 call_type = FUNC_CALL_NEW;
24574             }
24575         }
24576         break;
24577     case TOK_SUPER:
24578         if (next_token(s))
24579             return -1;
24580         if (s->token.val == '(') {
24581             if (!s->cur_func->super_call_allowed)
24582                 return js_parse_error(s, "super() is only valid in a derived class constructor");
24583             call_type = FUNC_CALL_SUPER_CTOR;
24584         } else if (s->token.val == '.' || s->token.val == '[') {
24585             if (!s->cur_func->super_allowed)
24586                 return js_parse_error(s, "'super' is only valid in a method");
24587             emit_op(s, OP_scope_get_var);
24588             emit_atom(s, JS_ATOM_this);
24589             emit_u16(s, 0);
24590             emit_op(s, OP_scope_get_var);
24591             emit_atom(s, JS_ATOM_home_object);
24592             emit_u16(s, 0);
24593             emit_op(s, OP_get_super);
24594         } else {
24595             return js_parse_error(s, "invalid use of 'super'");
24596         }
24597         break;
24598     case TOK_IMPORT:
24599         if (next_token(s))
24600             return -1;
24601         if (s->token.val == '.') {
24602             if (next_token(s))
24603                 return -1;
24604             if (!token_is_pseudo_keyword(s, JS_ATOM_meta))
24605                 return js_parse_error(s, "meta expected");
24606             if (!s->is_module)
24607                 return js_parse_error(s, "import.meta only valid in module code");
24608             if (next_token(s))
24609                 return -1;
24610             emit_op(s, OP_special_object);
24611             emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META);
24612         } else {
24613             if (js_parse_expect(s, '('))
24614                 return -1;
24615             if (!accept_lparen)
24616                 return js_parse_error(s, "invalid use of 'import()'");
24617             if (js_parse_assign_expr(s))
24618                 return -1;
24619             if (js_parse_expect(s, ')'))
24620                 return -1;
24621             emit_op(s, OP_import);
24622         }
24623         break;
24624     default:
24625         return js_parse_error(s, "unexpected token in expression: '%.*s'",
24626                               (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
24627     }
24628 
24629     optional_chaining_label = -1;
24630     for(;;) {
24631         JSFunctionDef *fd = s->cur_func;
24632         BOOL has_optional_chain = FALSE;
24633 
24634         if (s->token.val == TOK_QUESTION_MARK_DOT) {
24635             /* optional chaining */
24636             if (next_token(s))
24637                 return -1;
24638             has_optional_chain = TRUE;
24639             if (s->token.val == '(' && accept_lparen) {
24640                 goto parse_func_call;
24641             } else if (s->token.val == '[') {
24642                 goto parse_array_access;
24643             } else {
24644                 goto parse_property;
24645             }
24646         } else if (s->token.val == TOK_TEMPLATE &&
24647                    call_type == FUNC_CALL_NORMAL) {
24648             if (optional_chaining_label >= 0) {
24649                 return js_parse_error(s, "template literal cannot appear in an optional chain");
24650             }
24651             call_type = FUNC_CALL_TEMPLATE;
24652             goto parse_func_call2;
24653         } else if (s->token.val == '(' && accept_lparen) {
24654             int opcode, arg_count, drop_count;
24655 
24656             /* function call */
24657         parse_func_call:
24658             if (next_token(s))
24659                 return -1;
24660 
24661             if (call_type == FUNC_CALL_NORMAL) {
24662             parse_func_call2:
24663                 switch(opcode = get_prev_opcode(fd)) {
24664                 case OP_get_field:
24665                     /* keep the object on the stack */
24666                     fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
24667                     drop_count = 2;
24668                     break;
24669                 case OP_scope_get_private_field:
24670                     /* keep the object on the stack */
24671                     fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2;
24672                     drop_count = 2;
24673                     break;
24674                 case OP_get_array_el:
24675                     /* keep the object on the stack */
24676                     fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
24677                     drop_count = 2;
24678                     break;
24679                 case OP_scope_get_var:
24680                     {
24681                         JSAtom name;
24682                         int scope;
24683                         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
24684                         scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
24685                         if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) {
24686                             /* direct 'eval' */
24687                             opcode = OP_eval;
24688                         } else {
24689                             /* verify if function name resolves to a simple
24690                                get_loc/get_arg: a function call inside a `with`
24691                                statement can resolve to a method call of the
24692                                `with` context object
24693                              */
24694                             /* XXX: always generate the OP_scope_get_ref
24695                                and remove it in variable resolution
24696                                pass ? */
24697                             if (has_with_scope(fd, scope)) {
24698                                 opcode = OP_scope_get_ref;
24699                                 fd->byte_code.buf[fd->last_opcode_pos] = opcode;
24700                             }
24701                         }
24702                         drop_count = 1;
24703                     }
24704                     break;
24705                 case OP_get_super_value:
24706                     fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el;
24707                     /* on stack: this func_obj */
24708                     opcode = OP_get_array_el;
24709                     drop_count = 2;
24710                     break;
24711                 default:
24712                     opcode = OP_invalid;
24713                     drop_count = 1;
24714                     break;
24715                 }
24716                 if (has_optional_chain) {
24717                     optional_chain_test(s, &optional_chaining_label,
24718                                         drop_count);
24719                 }
24720             } else {
24721                 opcode = OP_invalid;
24722             }
24723 
24724             if (call_type == FUNC_CALL_TEMPLATE) {
24725                 if (js_parse_template(s, 1, &arg_count))
24726                     return -1;
24727                 goto emit_func_call;
24728             } else if (call_type == FUNC_CALL_SUPER_CTOR) {
24729                 emit_op(s, OP_scope_get_var);
24730                 emit_atom(s, JS_ATOM_this_active_func);
24731                 emit_u16(s, 0);
24732 
24733                 emit_op(s, OP_get_super);
24734 
24735                 emit_op(s, OP_scope_get_var);
24736                 emit_atom(s, JS_ATOM_new_target);
24737                 emit_u16(s, 0);
24738             } else if (call_type == FUNC_CALL_NEW) {
24739                 emit_op(s, OP_dup); /* new.target = function */
24740             }
24741 
24742             /* parse arguments */
24743             arg_count = 0;
24744             while (s->token.val != ')') {
24745                 if (arg_count >= 65535) {
24746                     return js_parse_error(s, "Too many call arguments");
24747                 }
24748                 if (s->token.val == TOK_ELLIPSIS)
24749                     break;
24750                 if (js_parse_assign_expr(s))
24751                     return -1;
24752                 arg_count++;
24753                 if (s->token.val == ')')
24754                     break;
24755                 /* accept a trailing comma before the ')' */
24756                 if (js_parse_expect(s, ','))
24757                     return -1;
24758             }
24759             if (s->token.val == TOK_ELLIPSIS) {
24760                 emit_op(s, OP_array_from);
24761                 emit_u16(s, arg_count);
24762                 emit_op(s, OP_push_i32);
24763                 emit_u32(s, arg_count);
24764 
24765                 /* on stack: array idx */
24766                 while (s->token.val != ')') {
24767                     if (s->token.val == TOK_ELLIPSIS) {
24768                         if (next_token(s))
24769                             return -1;
24770                         if (js_parse_assign_expr(s))
24771                             return -1;
24772 #if 1
24773                         /* XXX: could pass is_last indicator? */
24774                         emit_op(s, OP_append);
24775 #else
24776                         int label_next, label_done;
24777                         label_next = new_label(s);
24778                         label_done = new_label(s);
24779                         /* push enumerate object below array/idx pair */
24780                         emit_op(s, OP_for_of_start);
24781                         emit_op(s, OP_rot5l);
24782                         emit_op(s, OP_rot5l);
24783                         emit_label(s, label_next);
24784                         /* on stack: enum_rec array idx */
24785                         emit_op(s, OP_for_of_next);
24786                         emit_u8(s, 2);
24787                         emit_goto(s, OP_if_true, label_done);
24788                         /* append element */
24789                         /* enum_rec array idx val -> enum_rec array new_idx */
24790                         emit_op(s, OP_define_array_el);
24791                         emit_op(s, OP_inc);
24792                         emit_goto(s, OP_goto, label_next);
24793                         emit_label(s, label_done);
24794                         /* close enumeration, drop enum_rec and idx */
24795                         emit_op(s, OP_drop); /* drop undef */
24796                         emit_op(s, OP_nip1); /* drop enum_rec */
24797                         emit_op(s, OP_nip1);
24798                         emit_op(s, OP_nip1);
24799 #endif
24800                     } else {
24801                         if (js_parse_assign_expr(s))
24802                             return -1;
24803                         /* array idx val */
24804                         emit_op(s, OP_define_array_el);
24805                         emit_op(s, OP_inc);
24806                     }
24807                     if (s->token.val == ')')
24808                         break;
24809                     /* accept a trailing comma before the ')' */
24810                     if (js_parse_expect(s, ','))
24811                         return -1;
24812                 }
24813                 if (next_token(s))
24814                     return -1;
24815                 /* drop the index */
24816                 emit_op(s, OP_drop);
24817 
24818                 /* apply function call */
24819                 switch(opcode) {
24820                 case OP_get_field:
24821                 case OP_scope_get_private_field:
24822                 case OP_get_array_el:
24823                 case OP_scope_get_ref:
24824                     /* obj func array -> func obj array */
24825                     emit_op(s, OP_perm3);
24826                     emit_op(s, OP_apply);
24827                     emit_u16(s, call_type == FUNC_CALL_NEW);
24828                     break;
24829                 case OP_eval:
24830                     emit_op(s, OP_apply_eval);
24831                     emit_u16(s, fd->scope_level);
24832                     fd->has_eval_call = TRUE;
24833                     break;
24834                 default:
24835                     if (call_type == FUNC_CALL_SUPER_CTOR) {
24836                         emit_op(s, OP_apply);
24837                         emit_u16(s, 1);
24838                         /* set the 'this' value */
24839                         emit_op(s, OP_dup);
24840                         emit_op(s, OP_scope_put_var_init);
24841                         emit_atom(s, JS_ATOM_this);
24842                         emit_u16(s, 0);
24843 
24844                         emit_class_field_init(s);
24845                     } else if (call_type == FUNC_CALL_NEW) {
24846                         /* obj func array -> func obj array */
24847                         emit_op(s, OP_perm3);
24848                         emit_op(s, OP_apply);
24849                         emit_u16(s, 1);
24850                     } else {
24851                         /* func array -> func undef array */
24852                         emit_op(s, OP_undefined);
24853                         emit_op(s, OP_swap);
24854                         emit_op(s, OP_apply);
24855                         emit_u16(s, 0);
24856                     }
24857                     break;
24858                 }
24859             } else {
24860                 if (next_token(s))
24861                     return -1;
24862             emit_func_call:
24863                 switch(opcode) {
24864                 case OP_get_field:
24865                 case OP_scope_get_private_field:
24866                 case OP_get_array_el:
24867                 case OP_scope_get_ref:
24868                     emit_op(s, OP_call_method);
24869                     emit_u16(s, arg_count);
24870                     break;
24871                 case OP_eval:
24872                     emit_op(s, OP_eval);
24873                     emit_u16(s, arg_count);
24874                     emit_u16(s, fd->scope_level);
24875                     fd->has_eval_call = TRUE;
24876                     break;
24877                 default:
24878                     if (call_type == FUNC_CALL_SUPER_CTOR) {
24879                         emit_op(s, OP_call_constructor);
24880                         emit_u16(s, arg_count);
24881 
24882                         /* set the 'this' value */
24883                         emit_op(s, OP_dup);
24884                         emit_op(s, OP_scope_put_var_init);
24885                         emit_atom(s, JS_ATOM_this);
24886                         emit_u16(s, 0);
24887 
24888                         emit_class_field_init(s);
24889                     } else if (call_type == FUNC_CALL_NEW) {
24890                         emit_op(s, OP_call_constructor);
24891                         emit_u16(s, arg_count);
24892                     } else {
24893                         emit_op(s, OP_call);
24894                         emit_u16(s, arg_count);
24895                     }
24896                     break;
24897                 }
24898             }
24899             call_type = FUNC_CALL_NORMAL;
24900         } else if (s->token.val == '.') {
24901             if (next_token(s))
24902                 return -1;
24903         parse_property:
24904             if (s->token.val == TOK_PRIVATE_NAME) {
24905                 /* private class field */
24906                 if (get_prev_opcode(fd) == OP_get_super) {
24907                     return js_parse_error(s, "private class field forbidden after super");
24908                 }
24909                 if (has_optional_chain) {
24910                     optional_chain_test(s, &optional_chaining_label, 1);
24911                 }
24912                 emit_op(s, OP_scope_get_private_field);
24913                 emit_atom(s, s->token.u.ident.atom);
24914                 emit_u16(s, s->cur_func->scope_level);
24915             } else {
24916                 if (!token_is_ident(s->token.val)) {
24917                     return js_parse_error(s, "expecting field name");
24918                 }
24919                 if (get_prev_opcode(fd) == OP_get_super) {
24920                     JSValue val;
24921                     int ret;
24922                     val = JS_AtomToValue(s->ctx, s->token.u.ident.atom);
24923                     ret = emit_push_const(s, val, 1);
24924                     JS_FreeValue(s->ctx, val);
24925                     if (ret)
24926                         return -1;
24927                     emit_op(s, OP_get_super_value);
24928                 } else {
24929                     if (has_optional_chain) {
24930                         optional_chain_test(s, &optional_chaining_label, 1);
24931                     }
24932                     emit_op(s, OP_get_field);
24933                     emit_atom(s, s->token.u.ident.atom);
24934                 }
24935             }
24936             if (next_token(s))
24937                 return -1;
24938         } else if (s->token.val == '[') {
24939             int prev_op;
24940 
24941         parse_array_access:
24942             prev_op = get_prev_opcode(fd);
24943             if (has_optional_chain) {
24944                 optional_chain_test(s, &optional_chaining_label, 1);
24945             }
24946             if (next_token(s))
24947                 return -1;
24948             if (js_parse_expr(s))
24949                 return -1;
24950             if (js_parse_expect(s, ']'))
24951                 return -1;
24952             if (prev_op == OP_get_super) {
24953                 emit_op(s, OP_get_super_value);
24954             } else {
24955                 emit_op(s, OP_get_array_el);
24956             }
24957         } else {
24958             break;
24959         }
24960     }
24961     if (optional_chaining_label >= 0)
24962         emit_label(s, optional_chaining_label);
24963     return 0;
24964 }
24965 
js_parse_delete(JSParseState * s)24966 static __exception int js_parse_delete(JSParseState *s)
24967 {
24968     JSFunctionDef *fd = s->cur_func;
24969     JSAtom name;
24970     int opcode;
24971 
24972     if (next_token(s))
24973         return -1;
24974     if (js_parse_unary(s, PF_POW_FORBIDDEN))
24975         return -1;
24976     switch(opcode = get_prev_opcode(fd)) {
24977     case OP_get_field:
24978         {
24979             JSValue val;
24980             int ret;
24981 
24982             name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
24983             fd->byte_code.size = fd->last_opcode_pos;
24984             fd->last_opcode_pos = -1;
24985             val = JS_AtomToValue(s->ctx, name);
24986             ret = emit_push_const(s, val, 1);
24987             JS_FreeValue(s->ctx, val);
24988             JS_FreeAtom(s->ctx, name);
24989             if (ret)
24990                 return ret;
24991         }
24992         goto do_delete;
24993     case OP_get_array_el:
24994         fd->byte_code.size = fd->last_opcode_pos;
24995         fd->last_opcode_pos = -1;
24996     do_delete:
24997         emit_op(s, OP_delete);
24998         break;
24999     case OP_scope_get_var:
25000         /* 'delete this': this is not a reference */
25001         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
25002         if (name == JS_ATOM_this || name == JS_ATOM_new_target)
25003             goto ret_true;
25004         if (fd->js_mode & JS_MODE_STRICT) {
25005             return js_parse_error(s, "cannot delete a direct reference in strict mode");
25006         } else {
25007             fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var;
25008         }
25009         break;
25010     case OP_scope_get_private_field:
25011         return js_parse_error(s, "cannot delete a private class field");
25012     case OP_get_super_value:
25013         emit_op(s, OP_throw_error);
25014         emit_atom(s, JS_ATOM_NULL);
25015         emit_u8(s, JS_THROW_ERROR_DELETE_SUPER);
25016         break;
25017     default:
25018     ret_true:
25019         emit_op(s, OP_drop);
25020         emit_op(s, OP_push_true);
25021         break;
25022     }
25023     return 0;
25024 }
25025 
25026 /* allowed parse_flags: PF_ARROW_FUNC, PF_POW_ALLOWED, PF_POW_FORBIDDEN */
js_parse_unary(JSParseState * s,int parse_flags)25027 static __exception int js_parse_unary(JSParseState *s, int parse_flags)
25028 {
25029     int op;
25030 
25031     switch(s->token.val) {
25032     case '+':
25033     case '-':
25034     case '!':
25035     case '~':
25036     case TOK_VOID:
25037         op = s->token.val;
25038         if (next_token(s))
25039             return -1;
25040         if (js_parse_unary(s, PF_POW_FORBIDDEN))
25041             return -1;
25042         switch(op) {
25043         case '-':
25044             emit_op(s, OP_neg);
25045             break;
25046         case '+':
25047             emit_op(s, OP_plus);
25048             break;
25049         case '!':
25050             emit_op(s, OP_lnot);
25051             break;
25052         case '~':
25053             emit_op(s, OP_not);
25054             break;
25055         case TOK_VOID:
25056             emit_op(s, OP_drop);
25057             emit_op(s, OP_undefined);
25058             break;
25059         default:
25060             abort();
25061         }
25062         parse_flags = 0;
25063         break;
25064     case TOK_DEC:
25065     case TOK_INC:
25066         {
25067             int opcode, op, scope, label;
25068             JSAtom name;
25069             op = s->token.val;
25070             if (next_token(s))
25071                 return -1;
25072             if (js_parse_unary(s, 0))
25073                 return -1;
25074             if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
25075                 return -1;
25076             emit_op(s, OP_dec + op - TOK_DEC);
25077             put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP,
25078                        FALSE);
25079         }
25080         break;
25081     case TOK_TYPEOF:
25082         {
25083             JSFunctionDef *fd;
25084             if (next_token(s))
25085                 return -1;
25086             if (js_parse_unary(s, PF_POW_FORBIDDEN))
25087                 return -1;
25088             /* reference access should not return an exception, so we
25089                patch the get_var */
25090             fd = s->cur_func;
25091             if (get_prev_opcode(fd) == OP_scope_get_var) {
25092                 fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef;
25093             }
25094             emit_op(s, OP_typeof);
25095             parse_flags = 0;
25096         }
25097         break;
25098     case TOK_DELETE:
25099         if (js_parse_delete(s))
25100             return -1;
25101         parse_flags = 0;
25102         break;
25103     case TOK_AWAIT:
25104         if (!(s->cur_func->func_kind & JS_FUNC_ASYNC))
25105             return js_parse_error(s, "unexpected 'await' keyword");
25106         if (!s->cur_func->in_function_body)
25107             return js_parse_error(s, "await in default expression");
25108         if (next_token(s))
25109             return -1;
25110         if (js_parse_unary(s, PF_POW_FORBIDDEN))
25111             return -1;
25112         emit_op(s, OP_await);
25113         parse_flags = 0;
25114         break;
25115     default:
25116         if (js_parse_postfix_expr(s, (parse_flags & PF_ARROW_FUNC) |
25117                                   PF_POSTFIX_CALL))
25118             return -1;
25119         if (!s->got_lf &&
25120             (s->token.val == TOK_DEC || s->token.val == TOK_INC)) {
25121             int opcode, op, scope, label;
25122             JSAtom name;
25123             op = s->token.val;
25124             if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
25125                 return -1;
25126             emit_op(s, OP_post_dec + op - TOK_DEC);
25127             put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND,
25128                        FALSE);
25129             if (next_token(s))
25130                 return -1;
25131         }
25132         break;
25133     }
25134     if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) {
25135 #ifdef CONFIG_BIGNUM
25136         if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) {
25137             /* Extended exponentiation syntax rules: we extend the ES7
25138                grammar in order to have more intuitive semantics:
25139                -2**2 evaluates to -4. */
25140             if (!(s->cur_func->js_mode & JS_MODE_MATH)) {
25141                 if (parse_flags & PF_POW_FORBIDDEN) {
25142                     JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
25143                     return -1;
25144                 }
25145             }
25146             if (next_token(s))
25147                 return -1;
25148             if (js_parse_unary(s, PF_POW_ALLOWED))
25149                 return -1;
25150             emit_op(s, OP_pow);
25151         }
25152 #else
25153         if (s->token.val == TOK_POW) {
25154             /* Strict ES7 exponentiation syntax rules: To solve
25155                conficting semantics between different implementations
25156                regarding the precedence of prefix operators and the
25157                postifx exponential, ES7 specifies that -2**2 is a
25158                syntax error. */
25159             if (parse_flags & PF_POW_FORBIDDEN) {
25160                 JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
25161                 return -1;
25162             }
25163             if (next_token(s))
25164                 return -1;
25165             if (js_parse_unary(s, PF_POW_ALLOWED))
25166                 return -1;
25167             emit_op(s, OP_pow);
25168         }
25169 #endif
25170     }
25171     return 0;
25172 }
25173 
25174 /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
js_parse_expr_binary(JSParseState * s,int level,int parse_flags)25175 static __exception int js_parse_expr_binary(JSParseState *s, int level,
25176                                             int parse_flags)
25177 {
25178     int op, opcode;
25179 
25180     if (level == 0) {
25181         return js_parse_unary(s, (parse_flags & PF_ARROW_FUNC) |
25182                               PF_POW_ALLOWED);
25183     }
25184     if (js_parse_expr_binary(s, level - 1, parse_flags))
25185         return -1;
25186     for(;;) {
25187         op = s->token.val;
25188         switch(level) {
25189         case 1:
25190             switch(op) {
25191             case '*':
25192                 opcode = OP_mul;
25193                 break;
25194             case '/':
25195                 opcode = OP_div;
25196                 break;
25197             case '%':
25198 #ifdef CONFIG_BIGNUM
25199                 if (s->cur_func->js_mode & JS_MODE_MATH)
25200                     opcode = OP_math_mod;
25201                 else
25202 #endif
25203                     opcode = OP_mod;
25204                 break;
25205             default:
25206                 return 0;
25207             }
25208             break;
25209         case 2:
25210             switch(op) {
25211             case '+':
25212                 opcode = OP_add;
25213                 break;
25214             case '-':
25215                 opcode = OP_sub;
25216                 break;
25217             default:
25218                 return 0;
25219             }
25220             break;
25221         case 3:
25222             switch(op) {
25223             case TOK_SHL:
25224                 opcode = OP_shl;
25225                 break;
25226             case TOK_SAR:
25227                 opcode = OP_sar;
25228                 break;
25229             case TOK_SHR:
25230                 opcode = OP_shr;
25231                 break;
25232             default:
25233                 return 0;
25234             }
25235             break;
25236         case 4:
25237             switch(op) {
25238             case '<':
25239                 opcode = OP_lt;
25240                 break;
25241             case '>':
25242                 opcode = OP_gt;
25243                 break;
25244             case TOK_LTE:
25245                 opcode = OP_lte;
25246                 break;
25247             case TOK_GTE:
25248                 opcode = OP_gte;
25249                 break;
25250             case TOK_INSTANCEOF:
25251                 opcode = OP_instanceof;
25252                 break;
25253             case TOK_IN:
25254                 if (parse_flags & PF_IN_ACCEPTED) {
25255                     opcode = OP_in;
25256                 } else {
25257                     return 0;
25258                 }
25259                 break;
25260             default:
25261                 return 0;
25262             }
25263             break;
25264         case 5:
25265             switch(op) {
25266             case TOK_EQ:
25267                 opcode = OP_eq;
25268                 break;
25269             case TOK_NEQ:
25270                 opcode = OP_neq;
25271                 break;
25272             case TOK_STRICT_EQ:
25273                 opcode = OP_strict_eq;
25274                 break;
25275             case TOK_STRICT_NEQ:
25276                 opcode = OP_strict_neq;
25277                 break;
25278             default:
25279                 return 0;
25280             }
25281             break;
25282         case 6:
25283             switch(op) {
25284             case '&':
25285                 opcode = OP_and;
25286                 break;
25287             default:
25288                 return 0;
25289             }
25290             break;
25291         case 7:
25292             switch(op) {
25293             case '^':
25294                 opcode = OP_xor;
25295                 break;
25296             default:
25297                 return 0;
25298             }
25299             break;
25300         case 8:
25301             switch(op) {
25302             case '|':
25303                 opcode = OP_or;
25304                 break;
25305             default:
25306                 return 0;
25307             }
25308             break;
25309         default:
25310             abort();
25311         }
25312         if (next_token(s))
25313             return -1;
25314         if (js_parse_expr_binary(s, level - 1, parse_flags & ~PF_ARROW_FUNC))
25315             return -1;
25316         emit_op(s, opcode);
25317     }
25318     return 0;
25319 }
25320 
25321 /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
js_parse_logical_and_or(JSParseState * s,int op,int parse_flags)25322 static __exception int js_parse_logical_and_or(JSParseState *s, int op,
25323                                                int parse_flags)
25324 {
25325     int label1;
25326 
25327     if (op == TOK_LAND) {
25328         if (js_parse_expr_binary(s, 8, parse_flags))
25329             return -1;
25330     } else {
25331         if (js_parse_logical_and_or(s, TOK_LAND, parse_flags))
25332             return -1;
25333     }
25334     if (s->token.val == op) {
25335         label1 = new_label(s);
25336 
25337         for(;;) {
25338             if (next_token(s))
25339                 return -1;
25340             emit_op(s, OP_dup);
25341             emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1);
25342             emit_op(s, OP_drop);
25343 
25344             if (op == TOK_LAND) {
25345                 if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC))
25346                     return -1;
25347             } else {
25348                 if (js_parse_logical_and_or(s, TOK_LAND,
25349                                             parse_flags & ~PF_ARROW_FUNC))
25350                     return -1;
25351             }
25352             if (s->token.val != op) {
25353                 if (s->token.val == TOK_DOUBLE_QUESTION_MARK)
25354                     return js_parse_error(s, "cannot mix ?? with && or ||");
25355                 break;
25356             }
25357         }
25358 
25359         emit_label(s, label1);
25360     }
25361     return 0;
25362 }
25363 
js_parse_coalesce_expr(JSParseState * s,int parse_flags)25364 static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
25365 {
25366     int label1;
25367 
25368     if (js_parse_logical_and_or(s, TOK_LOR, parse_flags))
25369         return -1;
25370     if (s->token.val == TOK_DOUBLE_QUESTION_MARK) {
25371         label1 = new_label(s);
25372         for(;;) {
25373             if (next_token(s))
25374                 return -1;
25375 
25376             emit_op(s, OP_dup);
25377             emit_op(s, OP_is_undefined_or_null);
25378             emit_goto(s, OP_if_false, label1);
25379             emit_op(s, OP_drop);
25380 
25381             if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC))
25382                 return -1;
25383             if (s->token.val != TOK_DOUBLE_QUESTION_MARK)
25384                 break;
25385         }
25386         emit_label(s, label1);
25387     }
25388     return 0;
25389 }
25390 
25391 /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
js_parse_cond_expr(JSParseState * s,int parse_flags)25392 static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags)
25393 {
25394     int label1, label2;
25395 
25396     if (js_parse_coalesce_expr(s, parse_flags))
25397         return -1;
25398     if (s->token.val == '?') {
25399         if (next_token(s))
25400             return -1;
25401         label1 = emit_goto(s, OP_if_false, -1);
25402 
25403         if (js_parse_assign_expr(s))
25404             return -1;
25405         if (js_parse_expect(s, ':'))
25406             return -1;
25407 
25408         label2 = emit_goto(s, OP_goto, -1);
25409 
25410         emit_label(s, label1);
25411 
25412         if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED))
25413             return -1;
25414 
25415         emit_label(s, label2);
25416     }
25417     return 0;
25418 }
25419 
25420 static void emit_return(JSParseState *s, BOOL hasval);
25421 
25422 /* allowed parse_flags: PF_IN_ACCEPTED */
js_parse_assign_expr2(JSParseState * s,int parse_flags)25423 static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
25424 {
25425     int opcode, op, scope;
25426     JSAtom name0 = JS_ATOM_NULL;
25427     JSAtom name;
25428 
25429     if (s->token.val == TOK_YIELD) {
25430         BOOL is_star = FALSE, is_async;
25431 
25432         if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR))
25433             return js_parse_error(s, "unexpected 'yield' keyword");
25434         if (!s->cur_func->in_function_body)
25435             return js_parse_error(s, "yield in default expression");
25436         if (next_token(s))
25437             return -1;
25438         /* XXX: is there a better method to detect 'yield' without
25439            parameters ? */
25440         if (s->token.val != ';' && s->token.val != ')' &&
25441             s->token.val != ']' && s->token.val != '}' &&
25442             s->token.val != ',' && s->token.val != ':' && !s->got_lf) {
25443             if (s->token.val == '*') {
25444                 is_star = TRUE;
25445                 if (next_token(s))
25446                     return -1;
25447             }
25448             if (js_parse_assign_expr2(s, parse_flags))
25449                 return -1;
25450         } else {
25451             emit_op(s, OP_undefined);
25452         }
25453         is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR);
25454 
25455         if (is_star) {
25456             int label_loop, label_return, label_next;
25457             int label_return1, label_yield, label_throw, label_throw1;
25458             int label_throw2;
25459 
25460             label_loop = new_label(s);
25461             label_yield = new_label(s);
25462 
25463             emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start);
25464 
25465             /* remove the catch offset (XXX: could avoid pushing back
25466                undefined) */
25467             emit_op(s, OP_drop);
25468             emit_op(s, OP_undefined);
25469 
25470             emit_op(s, OP_undefined); /* initial value */
25471 
25472             emit_label(s, label_loop);
25473             emit_op(s, OP_iterator_next);
25474             if (is_async)
25475                 emit_op(s, OP_await);
25476             emit_op(s, OP_iterator_check_object);
25477             emit_op(s, OP_get_field2);
25478             emit_atom(s, JS_ATOM_done);
25479             label_next = emit_goto(s, OP_if_true, -1); /* end of loop */
25480             emit_label(s, label_yield);
25481             if (is_async) {
25482                 /* OP_async_yield_star takes the value as parameter */
25483                 emit_op(s, OP_get_field);
25484                 emit_atom(s, JS_ATOM_value);
25485                 emit_op(s, OP_await);
25486                 emit_op(s, OP_async_yield_star);
25487             } else {
25488                 /* OP_yield_star takes (value, done) as parameter */
25489                 emit_op(s, OP_yield_star);
25490             }
25491             emit_op(s, OP_dup);
25492             label_return = emit_goto(s, OP_if_true, -1);
25493             emit_op(s, OP_drop);
25494             emit_goto(s, OP_goto, label_loop);
25495 
25496             emit_label(s, label_return);
25497             emit_op(s, OP_push_i32);
25498             emit_u32(s, 2);
25499             emit_op(s, OP_strict_eq);
25500             label_throw = emit_goto(s, OP_if_true, -1);
25501 
25502             /* return handling */
25503             if (is_async)
25504                 emit_op(s, OP_await);
25505             emit_op(s, OP_iterator_call);
25506             emit_u8(s, 0);
25507             label_return1 = emit_goto(s, OP_if_true, -1);
25508             if (is_async)
25509                 emit_op(s, OP_await);
25510             emit_op(s, OP_iterator_check_object);
25511             emit_op(s, OP_get_field2);
25512             emit_atom(s, JS_ATOM_done);
25513             emit_goto(s, OP_if_false, label_yield);
25514 
25515             emit_op(s, OP_get_field);
25516             emit_atom(s, JS_ATOM_value);
25517 
25518             emit_label(s, label_return1);
25519             emit_op(s, OP_nip);
25520             emit_op(s, OP_nip);
25521             emit_op(s, OP_nip);
25522             emit_return(s, TRUE);
25523 
25524             /* throw handling */
25525             emit_label(s, label_throw);
25526             emit_op(s, OP_iterator_call);
25527             emit_u8(s, 1);
25528             label_throw1 = emit_goto(s, OP_if_true, -1);
25529             if (is_async)
25530                 emit_op(s, OP_await);
25531             emit_op(s, OP_iterator_check_object);
25532             emit_op(s, OP_get_field2);
25533             emit_atom(s, JS_ATOM_done);
25534             emit_goto(s, OP_if_false, label_yield);
25535             emit_goto(s, OP_goto, label_next);
25536             /* close the iterator and throw a type error exception */
25537             emit_label(s, label_throw1);
25538             emit_op(s, OP_iterator_call);
25539             emit_u8(s, 2);
25540             label_throw2 = emit_goto(s, OP_if_true, -1);
25541             if (is_async)
25542                 emit_op(s, OP_await);
25543             emit_label(s, label_throw2);
25544 
25545             emit_op(s, OP_throw_error);
25546             emit_atom(s, JS_ATOM_NULL);
25547             emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW);
25548 
25549             emit_label(s, label_next);
25550             emit_op(s, OP_get_field);
25551             emit_atom(s, JS_ATOM_value);
25552             emit_op(s, OP_nip); /* keep the value associated with
25553                                    done = true */
25554             emit_op(s, OP_nip);
25555             emit_op(s, OP_nip);
25556         } else {
25557             int label_next;
25558 
25559             if (is_async)
25560                 emit_op(s, OP_await);
25561             emit_op(s, OP_yield);
25562             label_next = emit_goto(s, OP_if_false, -1);
25563             emit_return(s, TRUE);
25564             emit_label(s, label_next);
25565         }
25566         return 0;
25567     }
25568     if (s->token.val == TOK_IDENT) {
25569         /* name0 is used to check for OP_set_name pattern, not duplicated */
25570         name0 = s->token.u.ident.atom;
25571     }
25572     if (js_parse_cond_expr(s, parse_flags | PF_ARROW_FUNC))
25573         return -1;
25574 
25575     op = s->token.val;
25576     if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) {
25577         int label;
25578         if (next_token(s))
25579             return -1;
25580         if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0)
25581             return -1;
25582 
25583         if (js_parse_assign_expr2(s, parse_flags)) {
25584             JS_FreeAtom(s->ctx, name);
25585             return -1;
25586         }
25587 
25588         if (op == '=') {
25589             if (opcode == OP_get_ref_value && name == name0) {
25590                 set_object_name(s, name);
25591             }
25592         } else {
25593             static const uint8_t assign_opcodes[] = {
25594                 OP_mul, OP_div, OP_mod, OP_add, OP_sub,
25595                 OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or,
25596 #ifdef CONFIG_BIGNUM
25597                 OP_pow,
25598 #endif
25599                 OP_pow,
25600             };
25601             op = assign_opcodes[op - TOK_MUL_ASSIGN];
25602 #ifdef CONFIG_BIGNUM
25603             if (s->cur_func->js_mode & JS_MODE_MATH) {
25604                 if (op == OP_mod)
25605                     op = OP_math_mod;
25606             }
25607 #endif
25608             emit_op(s, op);
25609         }
25610         put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);
25611     } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) {
25612         int label, label1, depth_lvalue, label2;
25613 
25614         if (next_token(s))
25615             return -1;
25616         if (get_lvalue(s, &opcode, &scope, &name, &label,
25617                        &depth_lvalue, TRUE, op) < 0)
25618             return -1;
25619 
25620         emit_op(s, OP_dup);
25621         if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN)
25622             emit_op(s, OP_is_undefined_or_null);
25623         label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false,
25624                            -1);
25625         emit_op(s, OP_drop);
25626 
25627         if (js_parse_assign_expr2(s, parse_flags)) {
25628             JS_FreeAtom(s->ctx, name);
25629             return -1;
25630         }
25631 
25632         if (opcode == OP_get_ref_value && name == name0) {
25633             set_object_name(s, name);
25634         }
25635 
25636         switch(depth_lvalue) {
25637         case 1:
25638             emit_op(s, OP_insert2);
25639             break;
25640         case 2:
25641             emit_op(s, OP_insert3);
25642             break;
25643         case 3:
25644             emit_op(s, OP_insert4);
25645             break;
25646         default:
25647             abort();
25648         }
25649 
25650         /* XXX: we disable the OP_put_ref_value optimization by not
25651            using put_lvalue() otherwise depth_lvalue is not correct */
25652         put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH,
25653                    FALSE);
25654         label2 = emit_goto(s, OP_goto, -1);
25655 
25656         emit_label(s, label1);
25657 
25658         /* remove the lvalue stack entries */
25659         while (depth_lvalue != 0) {
25660             emit_op(s, OP_nip);
25661             depth_lvalue--;
25662         }
25663 
25664         emit_label(s, label2);
25665     }
25666     return 0;
25667 }
25668 
js_parse_assign_expr(JSParseState * s)25669 static __exception int js_parse_assign_expr(JSParseState *s)
25670 {
25671     return js_parse_assign_expr2(s, PF_IN_ACCEPTED);
25672 }
25673 
25674 /* allowed parse_flags: PF_IN_ACCEPTED */
js_parse_expr2(JSParseState * s,int parse_flags)25675 static __exception int js_parse_expr2(JSParseState *s, int parse_flags)
25676 {
25677     BOOL comma = FALSE;
25678     for(;;) {
25679         if (js_parse_assign_expr2(s, parse_flags))
25680             return -1;
25681         if (comma) {
25682             /* prevent get_lvalue from using the last expression
25683                as an lvalue. This also prevents the conversion of
25684                of get_var to get_ref for method lookup in function
25685                call inside `with` statement.
25686              */
25687             s->cur_func->last_opcode_pos = -1;
25688         }
25689         if (s->token.val != ',')
25690             break;
25691         comma = TRUE;
25692         if (next_token(s))
25693             return -1;
25694         emit_op(s, OP_drop);
25695     }
25696     return 0;
25697 }
25698 
js_parse_expr(JSParseState * s)25699 static __exception int js_parse_expr(JSParseState *s)
25700 {
25701     return js_parse_expr2(s, PF_IN_ACCEPTED);
25702 }
25703 
push_break_entry(JSFunctionDef * fd,BlockEnv * be,JSAtom label_name,int label_break,int label_cont,int drop_count)25704 static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
25705                              JSAtom label_name,
25706                              int label_break, int label_cont,
25707                              int drop_count)
25708 {
25709     be->prev = fd->top_break;
25710     fd->top_break = be;
25711     be->label_name = label_name;
25712     be->label_break = label_break;
25713     be->label_cont = label_cont;
25714     be->drop_count = drop_count;
25715     be->label_finally = -1;
25716     be->scope_level = fd->scope_level;
25717     be->has_iterator = FALSE;
25718 }
25719 
pop_break_entry(JSFunctionDef * fd)25720 static void pop_break_entry(JSFunctionDef *fd)
25721 {
25722     BlockEnv *be;
25723     be = fd->top_break;
25724     fd->top_break = be->prev;
25725 }
25726 
emit_break(JSParseState * s,JSAtom name,int is_cont)25727 static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
25728 {
25729     BlockEnv *top;
25730     int i, scope_level;
25731 
25732     scope_level = s->cur_func->scope_level;
25733     top = s->cur_func->top_break;
25734     while (top != NULL) {
25735         close_scopes(s, scope_level, top->scope_level);
25736         scope_level = top->scope_level;
25737         if (is_cont &&
25738             top->label_cont != -1 &&
25739             (name == JS_ATOM_NULL || top->label_name == name)) {
25740             /* continue stays inside the same block */
25741             emit_goto(s, OP_goto, top->label_cont);
25742             return 0;
25743         }
25744         if (!is_cont &&
25745             top->label_break != -1 &&
25746             (name == JS_ATOM_NULL || top->label_name == name)) {
25747             emit_goto(s, OP_goto, top->label_break);
25748             return 0;
25749         }
25750         i = 0;
25751         if (top->has_iterator) {
25752             emit_op(s, OP_iterator_close);
25753             i += 3;
25754         }
25755         for(; i < top->drop_count; i++)
25756             emit_op(s, OP_drop);
25757         if (top->label_finally != -1) {
25758             /* must push dummy value to keep same stack depth */
25759             emit_op(s, OP_undefined);
25760             emit_goto(s, OP_gosub, top->label_finally);
25761             emit_op(s, OP_drop);
25762         }
25763         top = top->prev;
25764     }
25765     if (name == JS_ATOM_NULL) {
25766         if (is_cont)
25767             return js_parse_error(s, "continue must be inside loop");
25768         else
25769             return js_parse_error(s, "break must be inside loop or switch");
25770     } else {
25771         return js_parse_error(s, "break/continue label not found");
25772     }
25773 }
25774 
25775 /* execute the finally blocks before return */
emit_return(JSParseState * s,BOOL hasval)25776 static void emit_return(JSParseState *s, BOOL hasval)
25777 {
25778     BlockEnv *top;
25779     int drop_count;
25780 
25781     drop_count = 0;
25782     top = s->cur_func->top_break;
25783     while (top != NULL) {
25784         /* XXX: emit the appropriate OP_leave_scope opcodes? Probably not
25785            required as all local variables will be closed upon returning
25786            from JS_CallInternal, but not in the same order. */
25787         if (top->has_iterator) {
25788             /* with 'yield', the exact number of OP_drop to emit is
25789                unknown, so we use a specific operation to look for
25790                the catch offset */
25791             if (!hasval) {
25792                 emit_op(s, OP_undefined);
25793                 hasval = TRUE;
25794             }
25795             emit_op(s, OP_iterator_close_return);
25796             if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
25797                 int label_next, label_next2;
25798 
25799                 emit_op(s, OP_drop); /* catch offset */
25800                 emit_op(s, OP_drop); /* next */
25801                 emit_op(s, OP_get_field2);
25802                 emit_atom(s, JS_ATOM_return);
25803                 /* stack: iter_obj return_func */
25804                 emit_op(s, OP_dup);
25805                 emit_op(s, OP_is_undefined_or_null);
25806                 label_next = emit_goto(s, OP_if_true, -1);
25807                 emit_op(s, OP_call_method);
25808                 emit_u16(s, 0);
25809                 emit_op(s, OP_iterator_check_object);
25810                 emit_op(s, OP_await);
25811                 label_next2 = emit_goto(s, OP_goto, -1);
25812                 emit_label(s, label_next);
25813                 emit_op(s, OP_drop);
25814                 emit_label(s, label_next2);
25815                 emit_op(s, OP_drop);
25816             } else {
25817                 emit_op(s, OP_iterator_close);
25818             }
25819             drop_count = -3;
25820         }
25821         drop_count += top->drop_count;
25822         if (top->label_finally != -1) {
25823             while(drop_count) {
25824                 /* must keep the stack top if hasval */
25825                 emit_op(s, hasval ? OP_nip : OP_drop);
25826                 drop_count--;
25827             }
25828             if (!hasval) {
25829                 /* must push return value to keep same stack size */
25830                 emit_op(s, OP_undefined);
25831                 hasval = TRUE;
25832             }
25833             emit_goto(s, OP_gosub, top->label_finally);
25834         }
25835         top = top->prev;
25836     }
25837     if (s->cur_func->is_derived_class_constructor) {
25838         int label_return;
25839 
25840         /* 'this' can be uninitialized, so it may be accessed only if
25841            the derived class constructor does not return an object */
25842         if (hasval) {
25843             emit_op(s, OP_check_ctor_return);
25844             label_return = emit_goto(s, OP_if_false, -1);
25845             emit_op(s, OP_drop);
25846         } else {
25847             label_return = -1;
25848         }
25849 
25850         /* XXX: if this is not initialized, should throw the
25851            ReferenceError in the caller realm */
25852         emit_op(s, OP_scope_get_var);
25853         emit_atom(s, JS_ATOM_this);
25854         emit_u16(s, 0);
25855 
25856         emit_label(s, label_return);
25857         emit_op(s, OP_return);
25858     } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
25859         if (!hasval) {
25860             emit_op(s, OP_undefined);
25861         } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
25862             emit_op(s, OP_await);
25863         }
25864         emit_op(s, OP_return_async);
25865     } else {
25866         emit_op(s, hasval ? OP_return : OP_return_undef);
25867     }
25868 }
25869 
25870 #define DECL_MASK_FUNC  (1 << 0) /* allow normal function declaration */
25871 /* ored with DECL_MASK_FUNC if function declarations are allowed with a label */
25872 #define DECL_MASK_FUNC_WITH_LABEL (1 << 1)
25873 #define DECL_MASK_OTHER (1 << 2) /* all other declarations */
25874 #define DECL_MASK_ALL   (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER)
25875 
25876 static __exception int js_parse_statement_or_decl(JSParseState *s,
25877                                                   int decl_mask);
25878 
js_parse_statement(JSParseState * s)25879 static __exception int js_parse_statement(JSParseState *s)
25880 {
25881     return js_parse_statement_or_decl(s, 0);
25882 }
25883 
js_parse_block(JSParseState * s)25884 static __exception int js_parse_block(JSParseState *s)
25885 {
25886     if (js_parse_expect(s, '{'))
25887         return -1;
25888     if (s->token.val != '}') {
25889         push_scope(s);
25890         for(;;) {
25891             if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
25892                 return -1;
25893             if (s->token.val == '}')
25894                 break;
25895         }
25896         pop_scope(s);
25897     }
25898     if (next_token(s))
25899         return -1;
25900     return 0;
25901 }
25902 
25903 /* allowed parse_flags: PF_IN_ACCEPTED */
js_parse_var(JSParseState * s,int parse_flags,int tok,BOOL export_flag)25904 static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,
25905                                     BOOL export_flag)
25906 {
25907     JSContext *ctx = s->ctx;
25908     JSFunctionDef *fd = s->cur_func;
25909     JSAtom name = JS_ATOM_NULL;
25910 
25911     for (;;) {
25912         if (s->token.val == TOK_IDENT) {
25913             if (s->token.u.ident.is_reserved) {
25914                 return js_parse_error_reserved_identifier(s);
25915             }
25916             name = JS_DupAtom(ctx, s->token.u.ident.atom);
25917             if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) {
25918                 js_parse_error(s, "'let' is not a valid lexical identifier");
25919                 goto var_error;
25920             }
25921             if (next_token(s))
25922                 goto var_error;
25923             if (js_define_var(s, name, tok))
25924                 goto var_error;
25925             if (export_flag) {
25926                 if (!add_export_entry(s, s->cur_func->module, name, name,
25927                                       JS_EXPORT_TYPE_LOCAL))
25928                     goto var_error;
25929             }
25930 
25931             if (s->token.val == '=') {
25932                 if (next_token(s))
25933                     goto var_error;
25934                 if (tok == TOK_VAR) {
25935                     /* Must make a reference for proper `with` semantics */
25936                     int opcode, scope, label;
25937                     JSAtom name1;
25938 
25939                     emit_op(s, OP_scope_get_var);
25940                     emit_atom(s, name);
25941                     emit_u16(s, fd->scope_level);
25942                     if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0)
25943                         goto var_error;
25944                     if (js_parse_assign_expr2(s, parse_flags)) {
25945                         JS_FreeAtom(ctx, name1);
25946                         goto var_error;
25947                     }
25948                     set_object_name(s, name);
25949                     put_lvalue(s, opcode, scope, name1, label,
25950                                PUT_LVALUE_NOKEEP, FALSE);
25951                 } else {
25952                     if (js_parse_assign_expr2(s, parse_flags))
25953                         goto var_error;
25954                     set_object_name(s, name);
25955                     emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
25956                         OP_scope_put_var_init : OP_scope_put_var);
25957                     emit_atom(s, name);
25958                     emit_u16(s, fd->scope_level);
25959                 }
25960             } else {
25961                 if (tok == TOK_CONST) {
25962                     js_parse_error(s, "missing initializer for const variable");
25963                     goto var_error;
25964                 }
25965                 if (tok == TOK_LET) {
25966                     /* initialize lexical variable upon entering its scope */
25967                     emit_op(s, OP_undefined);
25968                     emit_op(s, OP_scope_put_var_init);
25969                     emit_atom(s, name);
25970                     emit_u16(s, fd->scope_level);
25971                 }
25972             }
25973             JS_FreeAtom(ctx, name);
25974         } else {
25975             int skip_bits;
25976             if ((s->token.val == '[' || s->token.val == '{')
25977             &&  js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
25978                 emit_op(s, OP_undefined);
25979                 if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
25980                     return -1;
25981             } else {
25982                 return js_parse_error(s, "variable name expected");
25983             }
25984         }
25985         if (s->token.val != ',')
25986             break;
25987         if (next_token(s))
25988             return -1;
25989     }
25990     return 0;
25991 
25992  var_error:
25993     JS_FreeAtom(ctx, name);
25994     return -1;
25995 }
25996 
25997 /* test if the current token is a label. Use simplistic look-ahead scanner */
is_label(JSParseState * s)25998 static BOOL is_label(JSParseState *s)
25999 {
26000     return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
26001             peek_token(s, FALSE) == ':');
26002 }
26003 
26004 /* test if the current token is a let keyword. Use simplistic look-ahead scanner */
is_let(JSParseState * s,int decl_mask)26005 static int is_let(JSParseState *s, int decl_mask)
26006 {
26007     int res = FALSE;
26008 
26009     if (token_is_pseudo_keyword(s, JS_ATOM_let)) {
26010 #if 1
26011         JSParsePos pos;
26012         js_parse_get_pos(s, &pos);
26013         for (;;) {
26014             if (next_token(s)) {
26015                 res = -1;
26016                 break;
26017             }
26018             if (s->token.val == '[') {
26019                 /* let [ is a syntax restriction:
26020                    it never introduces an ExpressionStatement */
26021                 res = TRUE;
26022                 break;
26023             }
26024             if (s->token.val == '{' ||
26025                 (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) ||
26026                 s->token.val == TOK_LET ||
26027                 s->token.val == TOK_YIELD ||
26028                 s->token.val == TOK_AWAIT) {
26029                 /* Check for possible ASI if not scanning for Declaration */
26030                 /* XXX: should also check that `{` introduces a BindingPattern,
26031                    but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */
26032                 if (s->last_line_num == s->token.line_num || (decl_mask & DECL_MASK_OTHER)) {
26033                     res = TRUE;
26034                     break;
26035                 }
26036                 break;
26037             }
26038             break;
26039         }
26040         if (js_parse_seek_token(s, &pos)) {
26041             res = -1;
26042         }
26043 #else
26044         int tok = peek_token(s, TRUE);
26045         if (tok == '{' || tok == TOK_IDENT || peek_token(s, FALSE) == '[') {
26046             res = TRUE;
26047         }
26048 #endif
26049     }
26050     return res;
26051 }
26052 
26053 /* XXX: handle IteratorClose when exiting the loop before the
26054    enumeration is done */
js_parse_for_in_of(JSParseState * s,int label_name,BOOL is_async)26055 static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
26056                                           BOOL is_async)
26057 {
26058     JSContext *ctx = s->ctx;
26059     JSFunctionDef *fd = s->cur_func;
26060     JSAtom var_name;
26061     BOOL has_initializer, is_for_of, has_destructuring;
26062     int tok, tok1, opcode, scope, block_scope_level;
26063     int label_next, label_expr, label_cont, label_body, label_break;
26064     int pos_next, pos_expr;
26065     BlockEnv break_entry;
26066 
26067     has_initializer = FALSE;
26068     has_destructuring = FALSE;
26069     is_for_of = FALSE;
26070     block_scope_level = fd->scope_level;
26071     label_cont = new_label(s);
26072     label_body = new_label(s);
26073     label_break = new_label(s);
26074     label_next = new_label(s);
26075 
26076     /* create scope for the lexical variables declared in the enumeration
26077        expressions. XXX: Not completely correct because of weird capturing
26078        semantics in `for (i of o) a.push(function(){return i})` */
26079     push_scope(s);
26080 
26081     /* local for_in scope starts here so individual elements
26082        can be closed in statement. */
26083     push_break_entry(s->cur_func, &break_entry,
26084                      label_name, label_break, label_cont, 1);
26085     break_entry.scope_level = block_scope_level;
26086 
26087     label_expr = emit_goto(s, OP_goto, -1);
26088 
26089     pos_next = s->cur_func->byte_code.size;
26090     emit_label(s, label_next);
26091 
26092     tok = s->token.val;
26093     switch (is_let(s, DECL_MASK_OTHER)) {
26094     case TRUE:
26095         tok = TOK_LET;
26096         break;
26097     case FALSE:
26098         break;
26099     default:
26100         return -1;
26101     }
26102     if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
26103         if (next_token(s))
26104             return -1;
26105 
26106         if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
26107             if (s->token.val == '[' || s->token.val == '{') {
26108                 if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0)
26109                     return -1;
26110                 has_destructuring = TRUE;
26111             } else {
26112                 return js_parse_error(s, "variable name expected");
26113             }
26114             var_name = JS_ATOM_NULL;
26115         } else {
26116             var_name = JS_DupAtom(ctx, s->token.u.ident.atom);
26117             if (next_token(s)) {
26118                 JS_FreeAtom(s->ctx, var_name);
26119                 return -1;
26120             }
26121             if (js_define_var(s, var_name, tok)) {
26122                 JS_FreeAtom(s->ctx, var_name);
26123                 return -1;
26124             }
26125             emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
26126                     OP_scope_put_var_init : OP_scope_put_var);
26127             emit_atom(s, var_name);
26128             emit_u16(s, fd->scope_level);
26129         }
26130     } else {
26131         int skip_bits;
26132         if ((s->token.val == '[' || s->token.val == '{')
26133         &&  ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) {
26134             if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
26135                 return -1;
26136         } else {
26137             int lvalue_label;
26138             if (js_parse_left_hand_side_expr(s))
26139                 return -1;
26140             if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label,
26141                            NULL, FALSE, TOK_FOR))
26142                 return -1;
26143             put_lvalue(s, opcode, scope, var_name, lvalue_label,
26144                        PUT_LVALUE_NOKEEP_BOTTOM, FALSE);
26145         }
26146         var_name = JS_ATOM_NULL;
26147     }
26148     emit_goto(s, OP_goto, label_body);
26149 
26150     pos_expr = s->cur_func->byte_code.size;
26151     emit_label(s, label_expr);
26152     if (s->token.val == '=') {
26153         /* XXX: potential scoping issue if inside `with` statement */
26154         has_initializer = TRUE;
26155         /* parse and evaluate initializer prior to evaluating the
26156            object (only used with "for in" with a non lexical variable
26157            in non strict mode */
26158         if (next_token(s) || js_parse_assign_expr2(s, 0)) {
26159             JS_FreeAtom(ctx, var_name);
26160             return -1;
26161         }
26162         if (var_name != JS_ATOM_NULL) {
26163             emit_op(s, OP_scope_put_var);
26164             emit_atom(s, var_name);
26165             emit_u16(s, fd->scope_level);
26166         }
26167     }
26168     JS_FreeAtom(ctx, var_name);
26169 
26170     if (token_is_pseudo_keyword(s, JS_ATOM_of)) {
26171         break_entry.has_iterator = is_for_of = TRUE;
26172         break_entry.drop_count += 2;
26173         if (has_initializer)
26174             goto initializer_error;
26175     } else if (s->token.val == TOK_IN) {
26176         if (is_async)
26177             return js_parse_error(s, "'for await' loop should be used with 'of'");
26178         if (has_initializer &&
26179             (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) ||
26180              has_destructuring)) {
26181         initializer_error:
26182             return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer",
26183                                   is_for_of ? "of" : "in");
26184         }
26185     } else {
26186         return js_parse_error(s, "expected 'of' or 'in' in for control expression");
26187     }
26188     if (next_token(s))
26189         return -1;
26190     if (is_for_of) {
26191         if (js_parse_assign_expr(s))
26192             return -1;
26193     } else {
26194         if (js_parse_expr(s))
26195             return -1;
26196     }
26197     /* close the scope after having evaluated the expression so that
26198        the TDZ values are in the closures */
26199     close_scopes(s, s->cur_func->scope_level, block_scope_level);
26200     if (is_for_of) {
26201         if (is_async)
26202             emit_op(s, OP_for_await_of_start);
26203         else
26204             emit_op(s, OP_for_of_start);
26205         /* on stack: enum_rec */
26206     } else {
26207         emit_op(s, OP_for_in_start);
26208         /* on stack: enum_obj */
26209     }
26210     emit_goto(s, OP_goto, label_cont);
26211 
26212     if (js_parse_expect(s, ')'))
26213         return -1;
26214 
26215     if (OPTIMIZE) {
26216         /* move the `next` code here */
26217         DynBuf *bc = &s->cur_func->byte_code;
26218         int chunk_size = pos_expr - pos_next;
26219         int offset = bc->size - pos_next;
26220         int i;
26221         dbuf_realloc(bc, bc->size + chunk_size);
26222         dbuf_put(bc, bc->buf + pos_next, chunk_size);
26223         memset(bc->buf + pos_next, OP_nop, chunk_size);
26224         /* `next` part ends with a goto */
26225         s->cur_func->last_opcode_pos = bc->size - 5;
26226         /* relocate labels */
26227         for (i = label_cont; i < s->cur_func->label_count; i++) {
26228             LabelSlot *ls = &s->cur_func->label_slots[i];
26229             if (ls->pos >= pos_next && ls->pos < pos_expr)
26230                 ls->pos += offset;
26231         }
26232     }
26233 
26234     emit_label(s, label_body);
26235     if (js_parse_statement(s))
26236         return -1;
26237 
26238     close_scopes(s, s->cur_func->scope_level, block_scope_level);
26239 
26240     emit_label(s, label_cont);
26241     if (is_for_of) {
26242         if (is_async) {
26243             /* call the next method */
26244             /* stack: iter_obj next catch_offset */
26245             emit_op(s, OP_dup3);
26246             emit_op(s, OP_drop);
26247             emit_op(s, OP_call_method);
26248             emit_u16(s, 0);
26249             /* get the result of the promise */
26250             emit_op(s, OP_await);
26251             /* unwrap the value and done values */
26252             emit_op(s, OP_iterator_get_value_done);
26253         } else {
26254             emit_op(s, OP_for_of_next);
26255             emit_u8(s, 0);
26256         }
26257     } else {
26258         emit_op(s, OP_for_in_next);
26259     }
26260     /* on stack: enum_rec / enum_obj value bool */
26261     emit_goto(s, OP_if_false, label_next);
26262     /* drop the undefined value from for_xx_next */
26263     emit_op(s, OP_drop);
26264 
26265     emit_label(s, label_break);
26266     if (is_for_of) {
26267         /* close and drop enum_rec */
26268         emit_op(s, OP_iterator_close);
26269     } else {
26270         emit_op(s, OP_drop);
26271     }
26272     pop_break_entry(s->cur_func);
26273     pop_scope(s);
26274     return 0;
26275 }
26276 
set_eval_ret_undefined(JSParseState * s)26277 static void set_eval_ret_undefined(JSParseState *s)
26278 {
26279     if (s->cur_func->eval_ret_idx >= 0) {
26280         emit_op(s, OP_undefined);
26281         emit_op(s, OP_put_loc);
26282         emit_u16(s, s->cur_func->eval_ret_idx);
26283     }
26284 }
26285 
js_parse_statement_or_decl(JSParseState * s,int decl_mask)26286 static __exception int js_parse_statement_or_decl(JSParseState *s,
26287                                                   int decl_mask)
26288 {
26289     JSContext *ctx = s->ctx;
26290     JSAtom label_name;
26291     int tok;
26292 
26293     /* specific label handling */
26294     /* XXX: support multiple labels on loop statements */
26295     label_name = JS_ATOM_NULL;
26296     if (is_label(s)) {
26297         BlockEnv *be;
26298 
26299         label_name = JS_DupAtom(ctx, s->token.u.ident.atom);
26300 
26301         for (be = s->cur_func->top_break; be; be = be->prev) {
26302             if (be->label_name == label_name) {
26303                 js_parse_error(s, "duplicate label name");
26304                 goto fail;
26305             }
26306         }
26307 
26308         if (next_token(s))
26309             goto fail;
26310         if (js_parse_expect(s, ':'))
26311             goto fail;
26312         if (s->token.val != TOK_FOR
26313         &&  s->token.val != TOK_DO
26314         &&  s->token.val != TOK_WHILE) {
26315             /* labelled regular statement */
26316             int label_break, mask;
26317             BlockEnv break_entry;
26318 
26319             label_break = new_label(s);
26320             push_break_entry(s->cur_func, &break_entry,
26321                              label_name, label_break, -1, 0);
26322             if (!(s->cur_func->js_mode & JS_MODE_STRICT) &&
26323                 (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) {
26324                 mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL;
26325             } else {
26326                 mask = 0;
26327             }
26328             if (js_parse_statement_or_decl(s, mask))
26329                 goto fail;
26330             emit_label(s, label_break);
26331             pop_break_entry(s->cur_func);
26332             goto done;
26333         }
26334     }
26335 
26336     switch(tok = s->token.val) {
26337     case '{':
26338         if (js_parse_block(s))
26339             goto fail;
26340         break;
26341     case TOK_RETURN:
26342         if (s->cur_func->is_eval) {
26343             js_parse_error(s, "return not in a function");
26344             goto fail;
26345         }
26346         if (next_token(s))
26347             goto fail;
26348         if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {
26349             if (js_parse_expr(s))
26350                 goto fail;
26351             emit_return(s, TRUE);
26352         } else {
26353             emit_return(s, FALSE);
26354         }
26355         if (js_parse_expect_semi(s))
26356             goto fail;
26357         break;
26358     case TOK_THROW:
26359         if (next_token(s))
26360             goto fail;
26361         if (s->got_lf) {
26362             js_parse_error(s, "line terminator not allowed after throw");
26363             goto fail;
26364         }
26365         if (js_parse_expr(s))
26366             goto fail;
26367         emit_op(s, OP_throw);
26368         if (js_parse_expect_semi(s))
26369             goto fail;
26370         break;
26371     case TOK_LET:
26372     case TOK_CONST:
26373     haslet:
26374         if (!(decl_mask & DECL_MASK_OTHER)) {
26375             js_parse_error(s, "lexical declarations can't appear in single-statement context");
26376             goto fail;
26377         }
26378         /* fall thru */
26379     case TOK_VAR:
26380         if (next_token(s))
26381             goto fail;
26382         if (js_parse_var(s, TRUE, tok, FALSE))
26383             goto fail;
26384         if (js_parse_expect_semi(s))
26385             goto fail;
26386         break;
26387     case TOK_IF:
26388         {
26389             int label1, label2, mask;
26390             if (next_token(s))
26391                 goto fail;
26392             /* create a new scope for `let f;if(1) function f(){}` */
26393             push_scope(s);
26394             set_eval_ret_undefined(s);
26395             if (js_parse_expr_paren(s))
26396                 goto fail;
26397             label1 = emit_goto(s, OP_if_false, -1);
26398             if (s->cur_func->js_mode & JS_MODE_STRICT)
26399                 mask = 0;
26400             else
26401                 mask = DECL_MASK_FUNC; /* Annex B.3.4 */
26402 
26403             if (js_parse_statement_or_decl(s, mask))
26404                 goto fail;
26405 
26406             if (s->token.val == TOK_ELSE) {
26407                 label2 = emit_goto(s, OP_goto, -1);
26408                 if (next_token(s))
26409                     goto fail;
26410 
26411                 emit_label(s, label1);
26412                 if (js_parse_statement_or_decl(s, mask))
26413                     goto fail;
26414 
26415                 label1 = label2;
26416             }
26417             emit_label(s, label1);
26418             pop_scope(s);
26419         }
26420         break;
26421     case TOK_WHILE:
26422         {
26423             int label_cont, label_break;
26424             BlockEnv break_entry;
26425 
26426             label_cont = new_label(s);
26427             label_break = new_label(s);
26428 
26429             push_break_entry(s->cur_func, &break_entry,
26430                              label_name, label_break, label_cont, 0);
26431 
26432             if (next_token(s))
26433                 goto fail;
26434 
26435             set_eval_ret_undefined(s);
26436 
26437             emit_label(s, label_cont);
26438             if (js_parse_expr_paren(s))
26439                 goto fail;
26440             emit_goto(s, OP_if_false, label_break);
26441 
26442             if (js_parse_statement(s))
26443                 goto fail;
26444             emit_goto(s, OP_goto, label_cont);
26445 
26446             emit_label(s, label_break);
26447 
26448             pop_break_entry(s->cur_func);
26449         }
26450         break;
26451     case TOK_DO:
26452         {
26453             int label_cont, label_break, label1;
26454             BlockEnv break_entry;
26455 
26456             label_cont = new_label(s);
26457             label_break = new_label(s);
26458             label1 = new_label(s);
26459 
26460             push_break_entry(s->cur_func, &break_entry,
26461                              label_name, label_break, label_cont, 0);
26462 
26463             if (next_token(s))
26464                 goto fail;
26465 
26466             emit_label(s, label1);
26467 
26468             set_eval_ret_undefined(s);
26469 
26470             if (js_parse_statement(s))
26471                 goto fail;
26472 
26473             emit_label(s, label_cont);
26474             if (js_parse_expect(s, TOK_WHILE))
26475                 goto fail;
26476             if (js_parse_expr_paren(s))
26477                 goto fail;
26478             /* Insert semicolon if missing */
26479             if (s->token.val == ';') {
26480                 if (next_token(s))
26481                     goto fail;
26482             }
26483             emit_goto(s, OP_if_true, label1);
26484 
26485             emit_label(s, label_break);
26486 
26487             pop_break_entry(s->cur_func);
26488         }
26489         break;
26490     case TOK_FOR:
26491         {
26492             int label_cont, label_break, label_body, label_test;
26493             int pos_cont, pos_body, block_scope_level;
26494             BlockEnv break_entry;
26495             int tok, bits;
26496             BOOL is_async;
26497 
26498             if (next_token(s))
26499                 goto fail;
26500 
26501             set_eval_ret_undefined(s);
26502             bits = 0;
26503             is_async = FALSE;
26504             if (s->token.val == '(') {
26505                 js_parse_skip_parens_token(s, &bits, FALSE);
26506             } else if (s->token.val == TOK_AWAIT) {
26507                 if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) {
26508                     js_parse_error(s, "for await is only valid in asynchronous functions");
26509                     goto fail;
26510                 }
26511                 is_async = TRUE;
26512                 if (next_token(s))
26513                     goto fail;
26514             }
26515             if (js_parse_expect(s, '('))
26516                 goto fail;
26517 
26518             if (!(bits & SKIP_HAS_SEMI)) {
26519                 /* parse for/in or for/of */
26520                 if (js_parse_for_in_of(s, label_name, is_async))
26521                     goto fail;
26522                 break;
26523             }
26524             block_scope_level = s->cur_func->scope_level;
26525 
26526             /* create scope for the lexical variables declared in the initial,
26527                test and increment expressions */
26528             push_scope(s);
26529             /* initial expression */
26530             tok = s->token.val;
26531             if (tok != ';') {
26532                 switch (is_let(s, DECL_MASK_OTHER)) {
26533                 case TRUE:
26534                     tok = TOK_LET;
26535                     break;
26536                 case FALSE:
26537                     break;
26538                 default:
26539                     goto fail;
26540                 }
26541                 if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
26542                     if (next_token(s))
26543                         goto fail;
26544                     if (js_parse_var(s, FALSE, tok, FALSE))
26545                         goto fail;
26546                 } else {
26547                     if (js_parse_expr2(s, FALSE))
26548                         goto fail;
26549                     emit_op(s, OP_drop);
26550                 }
26551 
26552                 /* close the closures before the first iteration */
26553                 close_scopes(s, s->cur_func->scope_level, block_scope_level);
26554             }
26555             if (js_parse_expect(s, ';'))
26556                 goto fail;
26557 
26558             label_test = new_label(s);
26559             label_cont = new_label(s);
26560             label_body = new_label(s);
26561             label_break = new_label(s);
26562 
26563             push_break_entry(s->cur_func, &break_entry,
26564                              label_name, label_break, label_cont, 0);
26565 
26566             /* test expression */
26567             if (s->token.val == ';') {
26568                 /* no test expression */
26569                 label_test = label_body;
26570             } else {
26571                 emit_label(s, label_test);
26572                 if (js_parse_expr(s))
26573                     goto fail;
26574                 emit_goto(s, OP_if_false, label_break);
26575             }
26576             if (js_parse_expect(s, ';'))
26577                 goto fail;
26578 
26579             if (s->token.val == ')') {
26580                 /* no end expression */
26581                 break_entry.label_cont = label_cont = label_test;
26582                 pos_cont = 0; /* avoid warning */
26583             } else {
26584                 /* skip the end expression */
26585                 emit_goto(s, OP_goto, label_body);
26586 
26587                 pos_cont = s->cur_func->byte_code.size;
26588                 emit_label(s, label_cont);
26589                 if (js_parse_expr(s))
26590                     goto fail;
26591                 emit_op(s, OP_drop);
26592                 if (label_test != label_body)
26593                     emit_goto(s, OP_goto, label_test);
26594             }
26595             if (js_parse_expect(s, ')'))
26596                 goto fail;
26597 
26598             pos_body = s->cur_func->byte_code.size;
26599             emit_label(s, label_body);
26600             if (js_parse_statement(s))
26601                 goto fail;
26602 
26603             /* close the closures before the next iteration */
26604             /* XXX: check continue case */
26605             close_scopes(s, s->cur_func->scope_level, block_scope_level);
26606 
26607             if (OPTIMIZE && label_test != label_body && label_cont != label_test) {
26608                 /* move the increment code here */
26609                 DynBuf *bc = &s->cur_func->byte_code;
26610                 int chunk_size = pos_body - pos_cont;
26611                 int offset = bc->size - pos_cont;
26612                 int i;
26613                 dbuf_realloc(bc, bc->size + chunk_size);
26614                 dbuf_put(bc, bc->buf + pos_cont, chunk_size);
26615                 memset(bc->buf + pos_cont, OP_nop, chunk_size);
26616                 /* increment part ends with a goto */
26617                 s->cur_func->last_opcode_pos = bc->size - 5;
26618                 /* relocate labels */
26619                 for (i = label_cont; i < s->cur_func->label_count; i++) {
26620                     LabelSlot *ls = &s->cur_func->label_slots[i];
26621                     if (ls->pos >= pos_cont && ls->pos < pos_body)
26622                         ls->pos += offset;
26623                 }
26624             } else {
26625                 emit_goto(s, OP_goto, label_cont);
26626             }
26627 
26628             emit_label(s, label_break);
26629 
26630             pop_break_entry(s->cur_func);
26631             pop_scope(s);
26632         }
26633         break;
26634     case TOK_BREAK:
26635     case TOK_CONTINUE:
26636         {
26637             int is_cont = s->token.val - TOK_BREAK;
26638             int label;
26639 
26640             if (next_token(s))
26641                 goto fail;
26642             if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
26643                 label = s->token.u.ident.atom;
26644             else
26645                 label = JS_ATOM_NULL;
26646             if (emit_break(s, label, is_cont))
26647                 goto fail;
26648             if (label != JS_ATOM_NULL) {
26649                 if (next_token(s))
26650                     goto fail;
26651             }
26652             if (js_parse_expect_semi(s))
26653                 goto fail;
26654         }
26655         break;
26656     case TOK_SWITCH:
26657         {
26658             int label_case, label_break, label1;
26659             int default_label_pos;
26660             BlockEnv break_entry;
26661 
26662             if (next_token(s))
26663                 goto fail;
26664 
26665             set_eval_ret_undefined(s);
26666             if (js_parse_expr_paren(s))
26667                 goto fail;
26668 
26669             push_scope(s);
26670             label_break = new_label(s);
26671             push_break_entry(s->cur_func, &break_entry,
26672                              label_name, label_break, -1, 1);
26673 
26674             if (js_parse_expect(s, '{'))
26675                 goto fail;
26676 
26677             default_label_pos = -1;
26678             label_case = -1;
26679             while (s->token.val != '}') {
26680                 if (s->token.val == TOK_CASE) {
26681                     label1 = -1;
26682                     if (label_case >= 0) {
26683                         /* skip the case if needed */
26684                         label1 = emit_goto(s, OP_goto, -1);
26685                     }
26686                     emit_label(s, label_case);
26687                     label_case = -1;
26688                     for (;;) {
26689                         /* parse a sequence of case clauses */
26690                         if (next_token(s))
26691                             goto fail;
26692                         emit_op(s, OP_dup);
26693                         if (js_parse_expr(s))
26694                             goto fail;
26695                         if (js_parse_expect(s, ':'))
26696                             goto fail;
26697                         emit_op(s, OP_strict_eq);
26698                         if (s->token.val == TOK_CASE) {
26699                             label1 = emit_goto(s, OP_if_true, label1);
26700                         } else {
26701                             label_case = emit_goto(s, OP_if_false, -1);
26702                             emit_label(s, label1);
26703                             break;
26704                         }
26705                     }
26706                 } else if (s->token.val == TOK_DEFAULT) {
26707                     if (next_token(s))
26708                         goto fail;
26709                     if (js_parse_expect(s, ':'))
26710                         goto fail;
26711                     if (default_label_pos >= 0) {
26712                         js_parse_error(s, "duplicate default");
26713                         goto fail;
26714                     }
26715                     if (label_case < 0) {
26716                         /* falling thru direct from switch expression */
26717                         label_case = emit_goto(s, OP_goto, -1);
26718                     }
26719                     /* Emit a dummy label opcode. Label will be patched after
26720                        the end of the switch body. Do not use emit_label(s, 0)
26721                        because it would clobber label 0 address, preventing
26722                        proper optimizer operation.
26723                      */
26724                     emit_op(s, OP_label);
26725                     emit_u32(s, 0);
26726                     default_label_pos = s->cur_func->byte_code.size - 4;
26727                 } else {
26728                     if (label_case < 0) {
26729                         /* falling thru direct from switch expression */
26730                         js_parse_error(s, "invalid switch statement");
26731                         goto fail;
26732                     }
26733                     if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
26734                         goto fail;
26735                 }
26736             }
26737             if (js_parse_expect(s, '}'))
26738                 goto fail;
26739             if (default_label_pos >= 0) {
26740                 /* Ugly patch for the the `default` label, shameful and risky */
26741                 put_u32(s->cur_func->byte_code.buf + default_label_pos,
26742                         label_case);
26743                 s->cur_func->label_slots[label_case].pos = default_label_pos + 4;
26744             } else {
26745                 emit_label(s, label_case);
26746             }
26747             emit_label(s, label_break);
26748             emit_op(s, OP_drop); /* drop the switch expression */
26749 
26750             pop_break_entry(s->cur_func);
26751             pop_scope(s);
26752         }
26753         break;
26754     case TOK_TRY:
26755         {
26756             int label_catch, label_catch2, label_finally, label_end;
26757             JSAtom name;
26758             BlockEnv block_env;
26759 
26760             set_eval_ret_undefined(s);
26761             if (next_token(s))
26762                 goto fail;
26763             label_catch = new_label(s);
26764             label_catch2 = new_label(s);
26765             label_finally = new_label(s);
26766             label_end = new_label(s);
26767 
26768             emit_goto(s, OP_catch, label_catch);
26769 
26770             push_break_entry(s->cur_func, &block_env,
26771                              JS_ATOM_NULL, -1, -1, 1);
26772             block_env.label_finally = label_finally;
26773 
26774             if (js_parse_block(s))
26775                 goto fail;
26776 
26777             pop_break_entry(s->cur_func);
26778 
26779             if (js_is_live_code(s)) {
26780                 /* drop the catch offset */
26781                 emit_op(s, OP_drop);
26782                 /* must push dummy value to keep same stack size */
26783                 emit_op(s, OP_undefined);
26784                 emit_goto(s, OP_gosub, label_finally);
26785                 emit_op(s, OP_drop);
26786 
26787                 emit_goto(s, OP_goto, label_end);
26788             }
26789 
26790             if (s->token.val == TOK_CATCH) {
26791                 if (next_token(s))
26792                     goto fail;
26793 
26794                 push_scope(s);  /* catch variable */
26795                 emit_label(s, label_catch);
26796 
26797                 if (s->token.val == '{') {
26798                     /* support optional-catch-binding feature */
26799                     emit_op(s, OP_drop);    /* pop the exception object */
26800                 } else {
26801                     if (js_parse_expect(s, '('))
26802                         goto fail;
26803                     if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
26804                         if (s->token.val == '[' || s->token.val == '{') {
26805                             /* XXX: TOK_LET is not completely correct */
26806                             if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0)
26807                                 goto fail;
26808                         } else {
26809                             js_parse_error(s, "identifier expected");
26810                             goto fail;
26811                         }
26812                     } else {
26813                         name = JS_DupAtom(ctx, s->token.u.ident.atom);
26814                         if (next_token(s)
26815                         ||  js_define_var(s, name, TOK_CATCH) < 0) {
26816                             JS_FreeAtom(ctx, name);
26817                             goto fail;
26818                         }
26819                         /* store the exception value in the catch variable */
26820                         emit_op(s, OP_scope_put_var);
26821                         emit_u32(s, name);
26822                         emit_u16(s, s->cur_func->scope_level);
26823                     }
26824                     if (js_parse_expect(s, ')'))
26825                         goto fail;
26826                 }
26827                 /* XXX: should keep the address to nop it out if there is no finally block */
26828                 emit_goto(s, OP_catch, label_catch2);
26829 
26830                 push_scope(s);  /* catch block */
26831                 push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
26832                                  -1, -1, 1);
26833                 block_env.label_finally = label_finally;
26834 
26835                 if (js_parse_block(s))
26836                     goto fail;
26837 
26838                 pop_break_entry(s->cur_func);
26839                 pop_scope(s);  /* catch block */
26840                 pop_scope(s);  /* catch variable */
26841 
26842                 if (js_is_live_code(s)) {
26843                     /* drop the catch2 offset */
26844                     emit_op(s, OP_drop);
26845                     /* XXX: should keep the address to nop it out if there is no finally block */
26846                     /* must push dummy value to keep same stack size */
26847                     emit_op(s, OP_undefined);
26848                     emit_goto(s, OP_gosub, label_finally);
26849                     emit_op(s, OP_drop);
26850                     emit_goto(s, OP_goto, label_end);
26851                 }
26852                 /* catch exceptions thrown in the catch block to execute the
26853                  * finally clause and rethrow the exception */
26854                 emit_label(s, label_catch2);
26855                 /* catch value is at TOS, no need to push undefined */
26856                 emit_goto(s, OP_gosub, label_finally);
26857                 emit_op(s, OP_throw);
26858 
26859             } else if (s->token.val == TOK_FINALLY) {
26860                 /* finally without catch : execute the finally clause
26861                  * and rethrow the exception */
26862                 emit_label(s, label_catch);
26863                 /* catch value is at TOS, no need to push undefined */
26864                 emit_goto(s, OP_gosub, label_finally);
26865                 emit_op(s, OP_throw);
26866             } else {
26867                 js_parse_error(s, "expecting catch or finally");
26868                 goto fail;
26869             }
26870             emit_label(s, label_finally);
26871             if (s->token.val == TOK_FINALLY) {
26872                 int saved_eval_ret_idx = 0; /* avoid warning */
26873 
26874                 if (next_token(s))
26875                     goto fail;
26876                 /* on the stack: ret_value gosub_ret_value */
26877                 push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
26878                                  -1, -1, 2);
26879 
26880                 if (s->cur_func->eval_ret_idx >= 0) {
26881                     /* 'finally' updates eval_ret only if not a normal
26882                        termination */
26883                     saved_eval_ret_idx =
26884                         add_var(s->ctx, s->cur_func, JS_ATOM__ret_);
26885                     if (saved_eval_ret_idx < 0)
26886                         goto fail;
26887                     emit_op(s, OP_get_loc);
26888                     emit_u16(s, s->cur_func->eval_ret_idx);
26889                     emit_op(s, OP_put_loc);
26890                     emit_u16(s, saved_eval_ret_idx);
26891                     set_eval_ret_undefined(s);
26892                 }
26893 
26894                 if (js_parse_block(s))
26895                     goto fail;
26896 
26897                 if (s->cur_func->eval_ret_idx >= 0) {
26898                     emit_op(s, OP_get_loc);
26899                     emit_u16(s, saved_eval_ret_idx);
26900                     emit_op(s, OP_put_loc);
26901                     emit_u16(s, s->cur_func->eval_ret_idx);
26902                 }
26903                 pop_break_entry(s->cur_func);
26904             }
26905             emit_op(s, OP_ret);
26906             emit_label(s, label_end);
26907         }
26908         break;
26909     case ';':
26910         /* empty statement */
26911         if (next_token(s))
26912             goto fail;
26913         break;
26914     case TOK_WITH:
26915         if (s->cur_func->js_mode & JS_MODE_STRICT) {
26916             js_parse_error(s, "invalid keyword: with");
26917             goto fail;
26918         } else {
26919             int with_idx;
26920 
26921             if (next_token(s))
26922                 goto fail;
26923 
26924             if (js_parse_expr_paren(s))
26925                 goto fail;
26926 
26927             push_scope(s);
26928             with_idx = define_var(s, s->cur_func, JS_ATOM__with_,
26929                                   JS_VAR_DEF_WITH);
26930             if (with_idx < 0)
26931                 goto fail;
26932             emit_op(s, OP_to_object);
26933             emit_op(s, OP_put_loc);
26934             emit_u16(s, with_idx);
26935 
26936             set_eval_ret_undefined(s);
26937             if (js_parse_statement(s))
26938                 goto fail;
26939 
26940             /* Popping scope drops lexical context for the with object variable */
26941             pop_scope(s);
26942         }
26943         break;
26944     case TOK_FUNCTION:
26945         /* ES6 Annex B.3.2 and B.3.3 semantics */
26946         if (!(decl_mask & DECL_MASK_FUNC))
26947             goto func_decl_error;
26948         if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*')
26949             goto func_decl_error;
26950         goto parse_func_var;
26951     case TOK_IDENT:
26952         if (s->token.u.ident.is_reserved) {
26953             js_parse_error_reserved_identifier(s);
26954             goto fail;
26955         }
26956         /* Determine if `let` introduces a Declaration or an ExpressionStatement */
26957         switch (is_let(s, decl_mask)) {
26958         case TRUE:
26959             tok = TOK_LET;
26960             goto haslet;
26961         case FALSE:
26962             break;
26963         default:
26964             goto fail;
26965         }
26966         if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
26967             peek_token(s, TRUE) == TOK_FUNCTION) {
26968             if (!(decl_mask & DECL_MASK_OTHER)) {
26969             func_decl_error:
26970                 js_parse_error(s, "function declarations can't appear in single-statement context");
26971                 goto fail;
26972             }
26973         parse_func_var:
26974             if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR,
26975                                        JS_FUNC_NORMAL, JS_ATOM_NULL,
26976                                        s->token.ptr, s->token.line_num))
26977                 goto fail;
26978             break;
26979         }
26980         goto hasexpr;
26981 
26982     case TOK_CLASS:
26983         if (!(decl_mask & DECL_MASK_OTHER)) {
26984             js_parse_error(s, "class declarations can't appear in single-statement context");
26985             goto fail;
26986         }
26987         if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE))
26988             return -1;
26989         break;
26990 
26991     case TOK_DEBUGGER:
26992         /* currently no debugger, so just skip the keyword */
26993         if (next_token(s))
26994             goto fail;
26995         if (js_parse_expect_semi(s))
26996             goto fail;
26997         break;
26998 
26999     case TOK_ENUM:
27000     case TOK_EXPORT:
27001     case TOK_EXTENDS:
27002         js_unsupported_keyword(s, s->token.u.ident.atom);
27003         goto fail;
27004 
27005     default:
27006     hasexpr:
27007         if (js_parse_expr(s))
27008             goto fail;
27009         if (s->cur_func->eval_ret_idx >= 0) {
27010             /* store the expression value so that it can be returned
27011                by eval() */
27012             emit_op(s, OP_put_loc);
27013             emit_u16(s, s->cur_func->eval_ret_idx);
27014         } else {
27015             emit_op(s, OP_drop); /* drop the result */
27016         }
27017         if (js_parse_expect_semi(s))
27018             goto fail;
27019         break;
27020     }
27021 done:
27022     JS_FreeAtom(ctx, label_name);
27023     return 0;
27024 fail:
27025     JS_FreeAtom(ctx, label_name);
27026     return -1;
27027 }
27028 
27029 /* 'name' is freed */
js_new_module_def(JSContext * ctx,JSAtom name)27030 static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)
27031 {
27032     JSModuleDef *m;
27033     m = js_mallocz(ctx, sizeof(*m));
27034     if (!m) {
27035         JS_FreeAtom(ctx, name);
27036         return NULL;
27037     }
27038     m->header.ref_count = 1;
27039     m->module_name = name;
27040     m->module_ns = JS_UNDEFINED;
27041     m->func_obj = JS_UNDEFINED;
27042     m->eval_exception = JS_UNDEFINED;
27043     m->meta_obj = JS_UNDEFINED;
27044     list_add_tail(&m->link, &ctx->loaded_modules);
27045     return m;
27046 }
27047 
js_mark_module_def(JSRuntime * rt,JSModuleDef * m,JS_MarkFunc * mark_func)27048 static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
27049                                JS_MarkFunc *mark_func)
27050 {
27051     int i;
27052 
27053     for(i = 0; i < m->export_entries_count; i++) {
27054         JSExportEntry *me = &m->export_entries[i];
27055         if (me->export_type == JS_EXPORT_TYPE_LOCAL &&
27056             me->u.local.var_ref) {
27057             mark_func(rt, &me->u.local.var_ref->header);
27058         }
27059     }
27060 
27061     JS_MarkValue(rt, m->module_ns, mark_func);
27062     JS_MarkValue(rt, m->func_obj, mark_func);
27063     JS_MarkValue(rt, m->eval_exception, mark_func);
27064     JS_MarkValue(rt, m->meta_obj, mark_func);
27065 }
27066 
js_free_module_def(JSContext * ctx,JSModuleDef * m)27067 static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
27068 {
27069     int i;
27070 
27071     JS_FreeAtom(ctx, m->module_name);
27072 
27073     for(i = 0; i < m->req_module_entries_count; i++) {
27074         JSReqModuleEntry *rme = &m->req_module_entries[i];
27075         JS_FreeAtom(ctx, rme->module_name);
27076     }
27077     js_free(ctx, m->req_module_entries);
27078 
27079     for(i = 0; i < m->export_entries_count; i++) {
27080         JSExportEntry *me = &m->export_entries[i];
27081         if (me->export_type == JS_EXPORT_TYPE_LOCAL)
27082             free_var_ref(ctx->rt, me->u.local.var_ref);
27083         JS_FreeAtom(ctx, me->export_name);
27084         JS_FreeAtom(ctx, me->local_name);
27085     }
27086     js_free(ctx, m->export_entries);
27087 
27088     js_free(ctx, m->star_export_entries);
27089 
27090     for(i = 0; i < m->import_entries_count; i++) {
27091         JSImportEntry *mi = &m->import_entries[i];
27092         JS_FreeAtom(ctx, mi->import_name);
27093     }
27094     js_free(ctx, m->import_entries);
27095 
27096     JS_FreeValue(ctx, m->module_ns);
27097     JS_FreeValue(ctx, m->func_obj);
27098     JS_FreeValue(ctx, m->eval_exception);
27099     JS_FreeValue(ctx, m->meta_obj);
27100     list_del(&m->link);
27101     js_free(ctx, m);
27102 }
27103 
add_req_module_entry(JSContext * ctx,JSModuleDef * m,JSAtom module_name)27104 static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
27105                                 JSAtom module_name)
27106 {
27107     JSReqModuleEntry *rme;
27108     int i;
27109 
27110     /* no need to add the module request if it is already present */
27111     for(i = 0; i < m->req_module_entries_count; i++) {
27112         rme = &m->req_module_entries[i];
27113         if (rme->module_name == module_name)
27114             return i;
27115     }
27116 
27117     if (js_resize_array(ctx, (void **)&m->req_module_entries,
27118                         sizeof(JSReqModuleEntry),
27119                         &m->req_module_entries_size,
27120                         m->req_module_entries_count + 1))
27121         return -1;
27122     rme = &m->req_module_entries[m->req_module_entries_count++];
27123     rme->module_name = JS_DupAtom(ctx, module_name);
27124     rme->module = NULL;
27125     return i;
27126 }
27127 
find_export_entry(JSContext * ctx,JSModuleDef * m,JSAtom export_name)27128 static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m,
27129                                         JSAtom export_name)
27130 {
27131     JSExportEntry *me;
27132     int i;
27133     for(i = 0; i < m->export_entries_count; i++) {
27134         me = &m->export_entries[i];
27135         if (me->export_name == export_name)
27136             return me;
27137     }
27138     return NULL;
27139 }
27140 
add_export_entry2(JSContext * ctx,JSParseState * s,JSModuleDef * m,JSAtom local_name,JSAtom export_name,JSExportTypeEnum export_type)27141 static JSExportEntry *add_export_entry2(JSContext *ctx,
27142                                         JSParseState *s, JSModuleDef *m,
27143                                        JSAtom local_name, JSAtom export_name,
27144                                        JSExportTypeEnum export_type)
27145 {
27146     JSExportEntry *me;
27147 
27148     if (find_export_entry(ctx, m, export_name)) {
27149         char buf1[ATOM_GET_STR_BUF_SIZE];
27150         if (s) {
27151             js_parse_error(s, "duplicate exported name '%s'",
27152                            JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name));
27153         } else {
27154             JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name);
27155         }
27156         return NULL;
27157     }
27158 
27159     if (js_resize_array(ctx, (void **)&m->export_entries,
27160                         sizeof(JSExportEntry),
27161                         &m->export_entries_size,
27162                         m->export_entries_count + 1))
27163         return NULL;
27164     me = &m->export_entries[m->export_entries_count++];
27165     memset(me, 0, sizeof(*me));
27166     me->local_name = JS_DupAtom(ctx, local_name);
27167     me->export_name = JS_DupAtom(ctx, export_name);
27168     me->export_type = export_type;
27169     return me;
27170 }
27171 
add_export_entry(JSParseState * s,JSModuleDef * m,JSAtom local_name,JSAtom export_name,JSExportTypeEnum export_type)27172 static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
27173                                        JSAtom local_name, JSAtom export_name,
27174                                        JSExportTypeEnum export_type)
27175 {
27176     return add_export_entry2(s->ctx, s, m, local_name, export_name,
27177                              export_type);
27178 }
27179 
add_star_export_entry(JSContext * ctx,JSModuleDef * m,int req_module_idx)27180 static int add_star_export_entry(JSContext *ctx, JSModuleDef *m,
27181                                  int req_module_idx)
27182 {
27183     JSStarExportEntry *se;
27184 
27185     if (js_resize_array(ctx, (void **)&m->star_export_entries,
27186                         sizeof(JSStarExportEntry),
27187                         &m->star_export_entries_size,
27188                         m->star_export_entries_count + 1))
27189         return -1;
27190     se = &m->star_export_entries[m->star_export_entries_count++];
27191     se->req_module_idx = req_module_idx;
27192     return 0;
27193 }
27194 
27195 /* create a C module */
JS_NewCModule(JSContext * ctx,const char * name_str,JSModuleInitFunc * func)27196 JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,
27197                            JSModuleInitFunc *func)
27198 {
27199     JSModuleDef *m;
27200     JSAtom name;
27201     name = JS_NewAtom(ctx, name_str);
27202     if (name == JS_ATOM_NULL)
27203         return NULL;
27204     m = js_new_module_def(ctx, name);
27205     m->init_func = func;
27206     return m;
27207 }
27208 
JS_AddModuleExport(JSContext * ctx,JSModuleDef * m,const char * export_name)27209 int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name)
27210 {
27211     JSExportEntry *me;
27212     JSAtom name;
27213     name = JS_NewAtom(ctx, export_name);
27214     if (name == JS_ATOM_NULL)
27215         return -1;
27216     me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name,
27217                            JS_EXPORT_TYPE_LOCAL);
27218     JS_FreeAtom(ctx, name);
27219     if (!me)
27220         return -1;
27221     else
27222         return 0;
27223 }
27224 
JS_SetModuleExport(JSContext * ctx,JSModuleDef * m,const char * export_name,JSValue val)27225 int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
27226                        JSValue val)
27227 {
27228     JSExportEntry *me;
27229     JSAtom name;
27230     name = JS_NewAtom(ctx, export_name);
27231     if (name == JS_ATOM_NULL)
27232         goto fail;
27233     me = find_export_entry(ctx, m, name);
27234     JS_FreeAtom(ctx, name);
27235     if (!me)
27236         goto fail;
27237     set_value(ctx, me->u.local.var_ref->pvalue, val);
27238     return 0;
27239  fail:
27240     JS_FreeValue(ctx, val);
27241     return -1;
27242 }
27243 
JS_SetModuleLoaderFunc(JSRuntime * rt,JSModuleNormalizeFunc * module_normalize,JSModuleLoaderFunc * module_loader,void * opaque)27244 void JS_SetModuleLoaderFunc(JSRuntime *rt,
27245                             JSModuleNormalizeFunc *module_normalize,
27246                             JSModuleLoaderFunc *module_loader, void *opaque)
27247 {
27248     rt->module_normalize_func = module_normalize;
27249     rt->module_loader_func = module_loader;
27250     rt->module_loader_opaque = opaque;
27251 }
27252 
27253 /* default module filename normalizer */
js_default_module_normalize_name(JSContext * ctx,const char * base_name,const char * name)27254 static char *js_default_module_normalize_name(JSContext *ctx,
27255                                               const char *base_name,
27256                                               const char *name)
27257 {
27258     char *filename, *p;
27259     const char *r;
27260     int len;
27261 
27262     if (name[0] != '.') {
27263         /* if no initial dot, the module name is not modified */
27264         return js_strdup(ctx, name);
27265     }
27266 
27267     p = strrchr(base_name, '/');
27268     if (p)
27269         len = p - base_name;
27270     else
27271         len = 0;
27272 
27273     filename = js_malloc(ctx, len + strlen(name) + 1 + 1);
27274     if (!filename)
27275         return NULL;
27276     memcpy(filename, base_name, len);
27277     filename[len] = '\0';
27278 
27279     /* we only normalize the leading '..' or '.' */
27280     r = name;
27281     for(;;) {
27282         if (r[0] == '.' && r[1] == '/') {
27283             r += 2;
27284         } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') {
27285             /* remove the last path element of filename, except if "."
27286                or ".." */
27287             if (filename[0] == '\0')
27288                 break;
27289             p = strrchr(filename, '/');
27290             if (!p)
27291                 p = filename;
27292             else
27293                 p++;
27294             if (!strcmp(p, ".") || !strcmp(p, ".."))
27295                 break;
27296             if (p > filename)
27297                 p--;
27298             *p = '\0';
27299             r += 3;
27300         } else {
27301             break;
27302         }
27303     }
27304     if (filename[0] != '\0')
27305         strcat(filename, "/");
27306     strcat(filename, r);
27307     //    printf("normalize: %s %s -> %s\n", base_name, name, filename);
27308     return filename;
27309 }
27310 
js_find_loaded_module(JSContext * ctx,JSAtom name)27311 static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name)
27312 {
27313     struct list_head *el;
27314     JSModuleDef *m;
27315 
27316     /* first look at the loaded modules */
27317     list_for_each(el, &ctx->loaded_modules) {
27318         m = list_entry(el, JSModuleDef, link);
27319         if (m->module_name == name)
27320             return m;
27321     }
27322     return NULL;
27323 }
27324 
27325 /* 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)27326 static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,
27327                                                     const char *base_cname,
27328                                                     const char *cname1)
27329 {
27330     JSRuntime *rt = ctx->rt;
27331     JSModuleDef *m;
27332     char *cname;
27333     JSAtom module_name;
27334 
27335     if (!rt->module_normalize_func) {
27336         cname = js_default_module_normalize_name(ctx, base_cname, cname1);
27337     } else {
27338         cname = rt->module_normalize_func(ctx, base_cname, cname1,
27339                                           rt->module_loader_opaque);
27340     }
27341     if (!cname)
27342         return NULL;
27343 
27344     module_name = JS_NewAtom(ctx, cname);
27345     if (module_name == JS_ATOM_NULL) {
27346         js_free(ctx, cname);
27347         return NULL;
27348     }
27349 
27350     /* first look at the loaded modules */
27351     m = js_find_loaded_module(ctx, module_name);
27352     if (m) {
27353         js_free(ctx, cname);
27354         JS_FreeAtom(ctx, module_name);
27355         return m;
27356     }
27357 
27358     JS_FreeAtom(ctx, module_name);
27359 
27360     /* load the module */
27361     if (!rt->module_loader_func) {
27362         /* XXX: use a syntax error ? */
27363         JS_ThrowReferenceError(ctx, "could not load module '%s'",
27364                                cname);
27365         js_free(ctx, cname);
27366         return NULL;
27367     }
27368 
27369     m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque);
27370     js_free(ctx, cname);
27371     return m;
27372 }
27373 
js_host_resolve_imported_module_atom(JSContext * ctx,JSAtom base_module_name,JSAtom module_name1)27374 static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx,
27375                                                     JSAtom base_module_name,
27376                                                     JSAtom module_name1)
27377 {
27378     const char *base_cname, *cname;
27379     JSModuleDef *m;
27380 
27381     base_cname = JS_AtomToCString(ctx, base_module_name);
27382     if (!base_cname)
27383         return NULL;
27384     cname = JS_AtomToCString(ctx, module_name1);
27385     if (!cname) {
27386         JS_FreeCString(ctx, base_cname);
27387         return NULL;
27388     }
27389     m = js_host_resolve_imported_module(ctx, base_cname, cname);
27390     JS_FreeCString(ctx, base_cname);
27391     JS_FreeCString(ctx, cname);
27392     return m;
27393 }
27394 
27395 typedef struct JSResolveEntry {
27396     JSModuleDef *module;
27397     JSAtom name;
27398 } JSResolveEntry;
27399 
27400 typedef struct JSResolveState {
27401     JSResolveEntry *array;
27402     int size;
27403     int count;
27404 } JSResolveState;
27405 
find_resolve_entry(JSResolveState * s,JSModuleDef * m,JSAtom name)27406 static int find_resolve_entry(JSResolveState *s,
27407                               JSModuleDef *m, JSAtom name)
27408 {
27409     int i;
27410     for(i = 0; i < s->count; i++) {
27411         JSResolveEntry *re = &s->array[i];
27412         if (re->module == m && re->name == name)
27413             return i;
27414     }
27415     return -1;
27416 }
27417 
add_resolve_entry(JSContext * ctx,JSResolveState * s,JSModuleDef * m,JSAtom name)27418 static int add_resolve_entry(JSContext *ctx, JSResolveState *s,
27419                              JSModuleDef *m, JSAtom name)
27420 {
27421     JSResolveEntry *re;
27422 
27423     if (js_resize_array(ctx, (void **)&s->array,
27424                         sizeof(JSResolveEntry),
27425                         &s->size, s->count + 1))
27426         return -1;
27427     re = &s->array[s->count++];
27428     re->module = m;
27429     re->name = JS_DupAtom(ctx, name);
27430     return 0;
27431 }
27432 
27433 typedef enum JSResolveResultEnum {
27434     JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */
27435     JS_RESOLVE_RES_FOUND = 0,
27436     JS_RESOLVE_RES_NOT_FOUND,
27437     JS_RESOLVE_RES_CIRCULAR,
27438     JS_RESOLVE_RES_AMBIGUOUS,
27439 } JSResolveResultEnum;
27440 
js_resolve_export1(JSContext * ctx,JSModuleDef ** pmodule,JSExportEntry ** pme,JSModuleDef * m,JSAtom export_name,JSResolveState * s)27441 static JSResolveResultEnum js_resolve_export1(JSContext *ctx,
27442                                               JSModuleDef **pmodule,
27443                                               JSExportEntry **pme,
27444                                               JSModuleDef *m,
27445                                               JSAtom export_name,
27446                                               JSResolveState *s)
27447 {
27448     JSExportEntry *me;
27449 
27450     *pmodule = NULL;
27451     *pme = NULL;
27452     if (find_resolve_entry(s, m, export_name) >= 0)
27453         return JS_RESOLVE_RES_CIRCULAR;
27454     if (add_resolve_entry(ctx, s, m, export_name) < 0)
27455         return JS_RESOLVE_RES_EXCEPTION;
27456     me = find_export_entry(ctx, m, export_name);
27457     if (me) {
27458         if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
27459             /* local export */
27460             *pmodule = m;
27461             *pme = me;
27462             return JS_RESOLVE_RES_FOUND;
27463         } else {
27464             /* indirect export */
27465             JSModuleDef *m1;
27466             m1 = m->req_module_entries[me->u.req_module_idx].module;
27467             if (me->local_name == JS_ATOM__star_) {
27468                 /* export ns from */
27469                 *pmodule = m;
27470                 *pme = me;
27471                 return JS_RESOLVE_RES_FOUND;
27472             } else {
27473                 return js_resolve_export1(ctx, pmodule, pme, m1,
27474                                           me->local_name, s);
27475             }
27476         }
27477     } else {
27478         if (export_name != JS_ATOM_default) {
27479             /* not found in direct or indirect exports: try star exports */
27480             int i;
27481 
27482             for(i = 0; i < m->star_export_entries_count; i++) {
27483                 JSStarExportEntry *se = &m->star_export_entries[i];
27484                 JSModuleDef *m1, *res_m;
27485                 JSExportEntry *res_me;
27486                 JSResolveResultEnum ret;
27487 
27488                 m1 = m->req_module_entries[se->req_module_idx].module;
27489                 ret = js_resolve_export1(ctx, &res_m, &res_me, m1,
27490                                          export_name, s);
27491                 if (ret == JS_RESOLVE_RES_AMBIGUOUS ||
27492                     ret == JS_RESOLVE_RES_EXCEPTION) {
27493                     return ret;
27494                 } else if (ret == JS_RESOLVE_RES_FOUND) {
27495                     if (*pme != NULL) {
27496                         if (*pmodule != res_m ||
27497                             res_me->local_name != (*pme)->local_name) {
27498                             *pmodule = NULL;
27499                             *pme = NULL;
27500                             return JS_RESOLVE_RES_AMBIGUOUS;
27501                         }
27502                     } else {
27503                         *pmodule = res_m;
27504                         *pme = res_me;
27505                     }
27506                 }
27507             }
27508             if (*pme != NULL)
27509                 return JS_RESOLVE_RES_FOUND;
27510         }
27511         return JS_RESOLVE_RES_NOT_FOUND;
27512     }
27513 }
27514 
27515 /* If the return value is JS_RESOLVE_RES_FOUND, return the module
27516   (*pmodule) and the corresponding local export entry
27517   (*pme). Otherwise return (NULL, NULL) */
js_resolve_export(JSContext * ctx,JSModuleDef ** pmodule,JSExportEntry ** pme,JSModuleDef * m,JSAtom export_name)27518 static JSResolveResultEnum js_resolve_export(JSContext *ctx,
27519                                              JSModuleDef **pmodule,
27520                                              JSExportEntry **pme,
27521                                              JSModuleDef *m,
27522                                              JSAtom export_name)
27523 {
27524     JSResolveState ss, *s = &ss;
27525     int i;
27526     JSResolveResultEnum ret;
27527 
27528     s->array = NULL;
27529     s->size = 0;
27530     s->count = 0;
27531 
27532     ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s);
27533 
27534     for(i = 0; i < s->count; i++)
27535         JS_FreeAtom(ctx, s->array[i].name);
27536     js_free(ctx, s->array);
27537 
27538     return ret;
27539 }
27540 
js_resolve_export_throw_error(JSContext * ctx,JSResolveResultEnum res,JSModuleDef * m,JSAtom export_name)27541 static void js_resolve_export_throw_error(JSContext *ctx,
27542                                           JSResolveResultEnum res,
27543                                           JSModuleDef *m, JSAtom export_name)
27544 {
27545     char buf1[ATOM_GET_STR_BUF_SIZE];
27546     char buf2[ATOM_GET_STR_BUF_SIZE];
27547     switch(res) {
27548     case JS_RESOLVE_RES_EXCEPTION:
27549         break;
27550     default:
27551     case JS_RESOLVE_RES_NOT_FOUND:
27552         JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'",
27553                             JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
27554                             JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
27555         break;
27556     case JS_RESOLVE_RES_CIRCULAR:
27557         JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'",
27558                             JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
27559                             JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
27560         break;
27561     case JS_RESOLVE_RES_AMBIGUOUS:
27562         JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous",
27563                             JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
27564                             JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
27565         break;
27566     }
27567 }
27568 
27569 
27570 typedef enum {
27571     EXPORTED_NAME_AMBIGUOUS,
27572     EXPORTED_NAME_NORMAL,
27573     EXPORTED_NAME_NS,
27574 } ExportedNameEntryEnum;
27575 
27576 typedef struct ExportedNameEntry {
27577     JSAtom export_name;
27578     ExportedNameEntryEnum export_type;
27579     union {
27580         JSExportEntry *me; /* using when the list is built */
27581         JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */
27582         JSModuleDef *module; /* for EXPORTED_NAME_NS */
27583     } u;
27584 } ExportedNameEntry;
27585 
27586 typedef struct GetExportNamesState {
27587     JSModuleDef **modules;
27588     int modules_size;
27589     int modules_count;
27590 
27591     ExportedNameEntry *exported_names;
27592     int exported_names_size;
27593     int exported_names_count;
27594 } GetExportNamesState;
27595 
find_exported_name(GetExportNamesState * s,JSAtom name)27596 static int find_exported_name(GetExportNamesState *s, JSAtom name)
27597 {
27598     int i;
27599     for(i = 0; i < s->exported_names_count; i++) {
27600         if (s->exported_names[i].export_name == name)
27601             return i;
27602     }
27603     return -1;
27604 }
27605 
get_exported_names(JSContext * ctx,GetExportNamesState * s,JSModuleDef * m,BOOL from_star)27606 static __exception int get_exported_names(JSContext *ctx,
27607                                           GetExportNamesState *s,
27608                                           JSModuleDef *m, BOOL from_star)
27609 {
27610     ExportedNameEntry *en;
27611     int i, j;
27612 
27613     /* check circular reference */
27614     for(i = 0; i < s->modules_count; i++) {
27615         if (s->modules[i] == m)
27616             return 0;
27617     }
27618     if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]),
27619                         &s->modules_size, s->modules_count + 1))
27620         return -1;
27621     s->modules[s->modules_count++] = m;
27622 
27623     for(i = 0; i < m->export_entries_count; i++) {
27624         JSExportEntry *me = &m->export_entries[i];
27625         if (from_star && me->export_name == JS_ATOM_default)
27626             continue;
27627         j = find_exported_name(s, me->export_name);
27628         if (j < 0) {
27629             if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]),
27630                                 &s->exported_names_size,
27631                                 s->exported_names_count + 1))
27632                 return -1;
27633             en = &s->exported_names[s->exported_names_count++];
27634             en->export_name = me->export_name;
27635             /* avoid a second lookup for simple module exports */
27636             if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL)
27637                 en->u.me = NULL;
27638             else
27639                 en->u.me = me;
27640         } else {
27641             en = &s->exported_names[j];
27642             en->u.me = NULL;
27643         }
27644     }
27645     for(i = 0; i < m->star_export_entries_count; i++) {
27646         JSStarExportEntry *se = &m->star_export_entries[i];
27647         JSModuleDef *m1;
27648         m1 = m->req_module_entries[se->req_module_idx].module;
27649         if (get_exported_names(ctx, s, m1, TRUE))
27650             return -1;
27651     }
27652     return 0;
27653 }
27654 
27655 /* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */
js_module_ns_has(JSContext * ctx,JSValueConst obj,JSAtom atom)27656 static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
27657 {
27658     return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL);
27659 }
27660 
27661 static const JSClassExoticMethods js_module_ns_exotic_methods = {
27662     .has_property = js_module_ns_has,
27663 };
27664 
exported_names_cmp(const void * p1,const void * p2,void * opaque)27665 static int exported_names_cmp(const void *p1, const void *p2, void *opaque)
27666 {
27667     JSContext *ctx = opaque;
27668     const ExportedNameEntry *me1 = p1;
27669     const ExportedNameEntry *me2 = p2;
27670     JSValue str1, str2;
27671     int ret;
27672 
27673     /* XXX: should avoid allocation memory in atom comparison */
27674     str1 = JS_AtomToString(ctx, me1->export_name);
27675     str2 = JS_AtomToString(ctx, me2->export_name);
27676     if (JS_IsException(str1) || JS_IsException(str2)) {
27677         /* XXX: raise an error ? */
27678         ret = 0;
27679     } else {
27680         ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1),
27681                                 JS_VALUE_GET_STRING(str2));
27682     }
27683     JS_FreeValue(ctx, str1);
27684     JS_FreeValue(ctx, str2);
27685     return ret;
27686 }
27687 
27688 static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m);
27689 
js_module_ns_autoinit(JSContext * ctx,JSObject * p,JSAtom atom,void * opaque)27690 static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
27691                                      void *opaque)
27692 {
27693     JSModuleDef *m = opaque;
27694     return js_get_module_ns(ctx, m);
27695 }
27696 
js_build_module_ns(JSContext * ctx,JSModuleDef * m)27697 static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
27698 {
27699     JSValue obj;
27700     JSObject *p;
27701     GetExportNamesState s_s, *s = &s_s;
27702     int i, ret;
27703     JSProperty *pr;
27704 
27705     obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS);
27706     if (JS_IsException(obj))
27707         return obj;
27708     p = JS_VALUE_GET_OBJ(obj);
27709 
27710     memset(s, 0, sizeof(*s));
27711     ret = get_exported_names(ctx, s, m, FALSE);
27712     js_free(ctx, s->modules);
27713     if (ret)
27714         goto fail;
27715 
27716     /* Resolve the exported names. The ambiguous exports are removed */
27717     for(i = 0; i < s->exported_names_count; i++) {
27718         ExportedNameEntry *en = &s->exported_names[i];
27719         JSResolveResultEnum res;
27720         JSExportEntry *res_me;
27721         JSModuleDef *res_m;
27722 
27723         if (en->u.me) {
27724             res_me = en->u.me; /* fast case: no resolution needed */
27725             res_m = m;
27726             res = JS_RESOLVE_RES_FOUND;
27727         } else {
27728             res = js_resolve_export(ctx, &res_m, &res_me, m,
27729                                     en->export_name);
27730         }
27731         if (res != JS_RESOLVE_RES_FOUND) {
27732             if (res != JS_RESOLVE_RES_AMBIGUOUS) {
27733                 js_resolve_export_throw_error(ctx, res, m, en->export_name);
27734                 goto fail;
27735             }
27736             en->export_type = EXPORTED_NAME_AMBIGUOUS;
27737         } else {
27738             if (res_me->local_name == JS_ATOM__star_) {
27739                 en->export_type = EXPORTED_NAME_NS;
27740                 en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module;
27741             } else {
27742                 en->export_type = EXPORTED_NAME_NORMAL;
27743                 if (res_me->u.local.var_ref) {
27744                     en->u.var_ref = res_me->u.local.var_ref;
27745                 } else {
27746                     JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
27747                     p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
27748                     en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
27749                 }
27750             }
27751         }
27752     }
27753 
27754     /* sort the exported names */
27755     rqsort(s->exported_names, s->exported_names_count,
27756            sizeof(s->exported_names[0]), exported_names_cmp, ctx);
27757 
27758     for(i = 0; i < s->exported_names_count; i++) {
27759         ExportedNameEntry *en = &s->exported_names[i];
27760         switch(en->export_type) {
27761         case EXPORTED_NAME_NORMAL:
27762             {
27763                 JSVarRef *var_ref = en->u.var_ref;
27764                 pr = add_property(ctx, p, en->export_name,
27765                                   JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
27766                                   JS_PROP_VARREF);
27767                 if (!pr)
27768                     goto fail;
27769                 var_ref->header.ref_count++;
27770                 pr->u.var_ref = var_ref;
27771             }
27772             break;
27773         case EXPORTED_NAME_NS:
27774             /* the exported namespace must be created on demand */
27775             if (JS_DefineAutoInitProperty(ctx, obj,
27776                                           en->export_name,
27777                                           JS_AUTOINIT_ID_MODULE_NS,
27778                                           en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
27779                 goto fail;
27780             break;
27781         default:
27782             break;
27783         }
27784     }
27785 
27786     js_free(ctx, s->exported_names);
27787 
27788     JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag,
27789                            JS_AtomToString(ctx, JS_ATOM_Module),
27790                            0);
27791 
27792     p->extensible = FALSE;
27793     return obj;
27794  fail:
27795     js_free(ctx, s->exported_names);
27796     JS_FreeValue(ctx, obj);
27797     return JS_EXCEPTION;
27798 }
27799 
js_get_module_ns(JSContext * ctx,JSModuleDef * m)27800 static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m)
27801 {
27802     if (JS_IsUndefined(m->module_ns)) {
27803         JSValue val;
27804         val = js_build_module_ns(ctx, m);
27805         if (JS_IsException(val))
27806             return JS_EXCEPTION;
27807         m->module_ns = val;
27808     }
27809     return JS_DupValue(ctx, m->module_ns);
27810 }
27811 
27812 /* Load all the required modules for module 'm' */
js_resolve_module(JSContext * ctx,JSModuleDef * m)27813 static int js_resolve_module(JSContext *ctx, JSModuleDef *m)
27814 {
27815     int i;
27816     JSModuleDef *m1;
27817 
27818     if (m->resolved)
27819         return 0;
27820 #ifdef DUMP_MODULE_RESOLVE
27821     {
27822         char buf1[ATOM_GET_STR_BUF_SIZE];
27823         printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
27824     }
27825 #endif
27826     m->resolved = TRUE;
27827     /* resolve each requested module */
27828     for(i = 0; i < m->req_module_entries_count; i++) {
27829         JSReqModuleEntry *rme = &m->req_module_entries[i];
27830         m1 = js_host_resolve_imported_module_atom(ctx, m->module_name,
27831                                                   rme->module_name);
27832         if (!m1)
27833             return -1;
27834         rme->module = m1;
27835         /* already done in js_host_resolve_imported_module() except if
27836            the module was loaded with JS_EvalBinary() */
27837         if (js_resolve_module(ctx, m1) < 0)
27838             return -1;
27839     }
27840     return 0;
27841 }
27842 
js_create_module_var(JSContext * ctx,BOOL is_lexical)27843 static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical)
27844 {
27845     JSVarRef *var_ref;
27846     var_ref = js_malloc(ctx, sizeof(JSVarRef));
27847     if (!var_ref)
27848         return NULL;
27849     var_ref->header.ref_count = 1;
27850     if (is_lexical)
27851         var_ref->value = JS_UNINITIALIZED;
27852     else
27853         var_ref->value = JS_UNDEFINED;
27854     var_ref->pvalue = &var_ref->value;
27855     var_ref->is_detached = TRUE;
27856     add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
27857     return var_ref;
27858 }
27859 
27860 /* Create the <eval> function associated with the module */
js_create_module_bytecode_function(JSContext * ctx,JSModuleDef * m)27861 static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)
27862 {
27863     JSFunctionBytecode *b;
27864     int i;
27865     JSVarRef **var_refs;
27866     JSValue func_obj, bfunc;
27867     JSObject *p;
27868 
27869     bfunc = m->func_obj;
27870     func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
27871                                       JS_CLASS_BYTECODE_FUNCTION);
27872 
27873     if (JS_IsException(func_obj))
27874         return -1;
27875     b = JS_VALUE_GET_PTR(bfunc);
27876 
27877     p = JS_VALUE_GET_OBJ(func_obj);
27878     p->u.func.function_bytecode = b;
27879     b->header.ref_count++;
27880     p->u.func.home_object = NULL;
27881     p->u.func.var_refs = NULL;
27882     if (b->closure_var_count) {
27883         var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
27884         if (!var_refs)
27885             goto fail;
27886         p->u.func.var_refs = var_refs;
27887 
27888         /* create the global variables. The other variables are
27889            imported from other modules */
27890         for(i = 0; i < b->closure_var_count; i++) {
27891             JSClosureVar *cv = &b->closure_var[i];
27892             JSVarRef *var_ref;
27893             if (cv->is_local) {
27894                 var_ref = js_create_module_var(ctx, cv->is_lexical);
27895                 if (!var_ref)
27896                     goto fail;
27897 #ifdef DUMP_MODULE_RESOLVE
27898                 printf("local %d: %p\n", i, var_ref);
27899 #endif
27900                 var_refs[i] = var_ref;
27901             }
27902         }
27903     }
27904     m->func_obj = func_obj;
27905     JS_FreeValue(ctx, bfunc);
27906     return 0;
27907  fail:
27908     JS_FreeValue(ctx, func_obj);
27909     return -1;
27910 }
27911 
27912 /* must be done before js_link_module() because of cyclic references */
js_create_module_function(JSContext * ctx,JSModuleDef * m)27913 static int js_create_module_function(JSContext *ctx, JSModuleDef *m)
27914 {
27915     BOOL is_c_module;
27916     int i;
27917     JSVarRef *var_ref;
27918 
27919     if (m->func_created)
27920         return 0;
27921 
27922     is_c_module = (m->init_func != NULL);
27923 
27924     if (is_c_module) {
27925         /* initialize the exported variables */
27926         for(i = 0; i < m->export_entries_count; i++) {
27927             JSExportEntry *me = &m->export_entries[i];
27928             if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
27929                 var_ref = js_create_module_var(ctx, FALSE);
27930                 if (!var_ref)
27931                     return -1;
27932                 me->u.local.var_ref = var_ref;
27933             }
27934         }
27935     } else {
27936         if (js_create_module_bytecode_function(ctx, m))
27937             return -1;
27938     }
27939     m->func_created = TRUE;
27940 
27941     /* do it on the dependencies */
27942 
27943     for(i = 0; i < m->req_module_entries_count; i++) {
27944         JSReqModuleEntry *rme = &m->req_module_entries[i];
27945         if (js_create_module_function(ctx, rme->module) < 0)
27946             return -1;
27947     }
27948 
27949     return 0;
27950 }
27951 
27952 
27953 /* Prepare a module to be executed by resolving all the imported
27954    variables. */
js_link_module(JSContext * ctx,JSModuleDef * m)27955 static int js_link_module(JSContext *ctx, JSModuleDef *m)
27956 {
27957     int i;
27958     JSImportEntry *mi;
27959     JSModuleDef *m1;
27960     JSVarRef **var_refs, *var_ref;
27961     JSObject *p;
27962     BOOL is_c_module;
27963     JSValue ret_val;
27964 
27965     if (m->instantiated)
27966         return 0;
27967     m->instantiated = TRUE;
27968 
27969 #ifdef DUMP_MODULE_RESOLVE
27970     {
27971         char buf1[ATOM_GET_STR_BUF_SIZE];
27972         printf("start instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
27973     }
27974 #endif
27975 
27976     for(i = 0; i < m->req_module_entries_count; i++) {
27977         JSReqModuleEntry *rme = &m->req_module_entries[i];
27978         if (js_link_module(ctx, rme->module) < 0)
27979             goto fail;
27980     }
27981 
27982 #ifdef DUMP_MODULE_RESOLVE
27983     {
27984         char buf1[ATOM_GET_STR_BUF_SIZE];
27985         printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
27986     }
27987 #endif
27988     /* check the indirect exports */
27989     for(i = 0; i < m->export_entries_count; i++) {
27990         JSExportEntry *me = &m->export_entries[i];
27991         if (me->export_type == JS_EXPORT_TYPE_INDIRECT &&
27992             me->local_name != JS_ATOM__star_) {
27993             JSResolveResultEnum ret;
27994             JSExportEntry *res_me;
27995             JSModuleDef *res_m, *m1;
27996             m1 = m->req_module_entries[me->u.req_module_idx].module;
27997             ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name);
27998             if (ret != JS_RESOLVE_RES_FOUND) {
27999                 js_resolve_export_throw_error(ctx, ret, m, me->export_name);
28000                 goto fail;
28001             }
28002         }
28003     }
28004 
28005 #ifdef DUMP_MODULE_RESOLVE
28006     {
28007         printf("exported bindings:\n");
28008         for(i = 0; i < m->export_entries_count; i++) {
28009             JSExportEntry *me = &m->export_entries[i];
28010             printf(" name="); print_atom(ctx, me->export_name);
28011             printf(" local="); print_atom(ctx, me->local_name);
28012             printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx);
28013         }
28014     }
28015 #endif
28016 
28017     is_c_module = (m->init_func != NULL);
28018 
28019     if (!is_c_module) {
28020         p = JS_VALUE_GET_OBJ(m->func_obj);
28021         var_refs = p->u.func.var_refs;
28022 
28023         for(i = 0; i < m->import_entries_count; i++) {
28024             mi = &m->import_entries[i];
28025 #ifdef DUMP_MODULE_RESOLVE
28026             printf("import var_idx=%d name=", mi->var_idx);
28027             print_atom(ctx, mi->import_name);
28028             printf(": ");
28029 #endif
28030             m1 = m->req_module_entries[mi->req_module_idx].module;
28031             if (mi->import_name == JS_ATOM__star_) {
28032                 JSValue val;
28033                 /* name space import */
28034                 val = js_get_module_ns(ctx, m1);
28035                 if (JS_IsException(val))
28036                     goto fail;
28037                 set_value(ctx, &var_refs[mi->var_idx]->value, val);
28038 #ifdef DUMP_MODULE_RESOLVE
28039                 printf("namespace\n");
28040 #endif
28041             } else {
28042                 JSResolveResultEnum ret;
28043                 JSExportEntry *res_me;
28044                 JSModuleDef *res_m;
28045                 JSObject *p1;
28046 
28047                 ret = js_resolve_export(ctx, &res_m,
28048                                         &res_me, m1, mi->import_name);
28049                 if (ret != JS_RESOLVE_RES_FOUND) {
28050                     js_resolve_export_throw_error(ctx, ret, m1, mi->import_name);
28051                     goto fail;
28052                 }
28053                 if (res_me->local_name == JS_ATOM__star_) {
28054                     JSValue val;
28055                     JSModuleDef *m2;
28056                     /* name space import from */
28057                     m2 = res_m->req_module_entries[res_me->u.req_module_idx].module;
28058                     val = js_get_module_ns(ctx, m2);
28059                     if (JS_IsException(val))
28060                         goto fail;
28061                     var_ref = js_create_module_var(ctx, TRUE);
28062                     if (!var_ref) {
28063                         JS_FreeValue(ctx, val);
28064                         goto fail;
28065                     }
28066                     set_value(ctx, &var_ref->value, val);
28067                     var_refs[mi->var_idx] = var_ref;
28068 #ifdef DUMP_MODULE_RESOLVE
28069                     printf("namespace from\n");
28070 #endif
28071                 } else {
28072                     var_ref = res_me->u.local.var_ref;
28073                     if (!var_ref) {
28074                         p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
28075                         var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
28076                     }
28077                     var_ref->header.ref_count++;
28078                     var_refs[mi->var_idx] = var_ref;
28079 #ifdef DUMP_MODULE_RESOLVE
28080                     printf("local export (var_ref=%p)\n", var_ref);
28081 #endif
28082                 }
28083             }
28084         }
28085 
28086         /* keep the exported variables in the module export entries (they
28087            are used when the eval function is deleted and cannot be
28088            initialized before in case imports are exported) */
28089         for(i = 0; i < m->export_entries_count; i++) {
28090             JSExportEntry *me = &m->export_entries[i];
28091             if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
28092                 var_ref = var_refs[me->u.local.var_idx];
28093                 var_ref->header.ref_count++;
28094                 me->u.local.var_ref = var_ref;
28095             }
28096         }
28097 
28098         /* initialize the global variables */
28099         ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL);
28100         if (JS_IsException(ret_val))
28101             goto fail;
28102         JS_FreeValue(ctx, ret_val);
28103     }
28104 
28105 #ifdef DUMP_MODULE_RESOLVE
28106     printf("done instantiate\n");
28107 #endif
28108     return 0;
28109  fail:
28110     return -1;
28111 }
28112 
28113 /* return JS_ATOM_NULL if the name cannot be found. Only works with
28114    not striped bytecode functions. */
JS_GetScriptOrModuleName(JSContext * ctx,int n_stack_levels)28115 JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels)
28116 {
28117     JSStackFrame *sf;
28118     JSFunctionBytecode *b;
28119     JSObject *p;
28120     /* XXX: currently we just use the filename of the englobing
28121        function. It does not work for eval(). Need to add a
28122        ScriptOrModule info in JSFunctionBytecode */
28123     sf = ctx->rt->current_stack_frame;
28124     if (!sf)
28125         return JS_ATOM_NULL;
28126     while (n_stack_levels-- > 0) {
28127         sf = sf->prev_frame;
28128         if (!sf)
28129             return JS_ATOM_NULL;
28130     }
28131     if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT)
28132         return JS_ATOM_NULL;
28133     p = JS_VALUE_GET_OBJ(sf->cur_func);
28134     if (!js_class_has_bytecode(p->class_id))
28135         return JS_ATOM_NULL;
28136     b = p->u.func.function_bytecode;
28137     if (!b->has_debug)
28138         return JS_ATOM_NULL;
28139     return JS_DupAtom(ctx, b->debug.filename);
28140 }
28141 
JS_GetModuleName(JSContext * ctx,JSModuleDef * m)28142 JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m)
28143 {
28144     return JS_DupAtom(ctx, m->module_name);
28145 }
28146 
JS_GetImportMeta(JSContext * ctx,JSModuleDef * m)28147 JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m)
28148 {
28149     JSValue obj;
28150     /* allocate meta_obj only if requested to save memory */
28151     obj = m->meta_obj;
28152     if (JS_IsUndefined(obj)) {
28153         obj = JS_NewObjectProto(ctx, JS_NULL);
28154         if (JS_IsException(obj))
28155             return JS_EXCEPTION;
28156         m->meta_obj = obj;
28157     }
28158     return JS_DupValue(ctx, obj);
28159 }
28160 
js_import_meta(JSContext * ctx)28161 static JSValue js_import_meta(JSContext *ctx)
28162 {
28163     JSAtom filename;
28164     JSModuleDef *m;
28165 
28166     filename = JS_GetScriptOrModuleName(ctx, 0);
28167     if (filename == JS_ATOM_NULL)
28168         goto fail;
28169 
28170     /* XXX: inefficient, need to add a module or script pointer in
28171        JSFunctionBytecode */
28172     m = js_find_loaded_module(ctx, filename);
28173     JS_FreeAtom(ctx, filename);
28174     if (!m) {
28175     fail:
28176         JS_ThrowTypeError(ctx, "import.meta not supported in this context");
28177         return JS_EXCEPTION;
28178     }
28179     return JS_GetImportMeta(ctx, m);
28180 }
28181 
28182 /* used by os.Worker() and import() */
JS_RunModule(JSContext * ctx,const char * basename,const char * filename)28183 JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,
28184                           const char *filename)
28185 {
28186     JSModuleDef *m;
28187     JSValue ret, func_obj;
28188 
28189     m = js_host_resolve_imported_module(ctx, basename, filename);
28190     if (!m)
28191         return NULL;
28192 
28193     if (js_resolve_module(ctx, m) < 0) {
28194         js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
28195         return NULL;
28196     }
28197 
28198     /* Evaluate the module code */
28199     func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
28200     ret = JS_EvalFunction(ctx, func_obj);
28201     if (JS_IsException(ret))
28202         return NULL;
28203     JS_FreeValue(ctx, ret);
28204     return m;
28205 }
28206 
js_dynamic_import_job(JSContext * ctx,int argc,JSValueConst * argv)28207 static JSValue js_dynamic_import_job(JSContext *ctx,
28208                                      int argc, JSValueConst *argv)
28209 {
28210     JSValueConst *resolving_funcs = argv;
28211     JSValueConst basename_val = argv[2];
28212     JSValueConst specifier = argv[3];
28213     JSModuleDef *m;
28214     const char *basename = NULL, *filename;
28215     JSValue ret, err, ns;
28216 
28217     if (!JS_IsString(basename_val)) {
28218         JS_ThrowTypeError(ctx, "no function filename for import()");
28219         goto exception;
28220     }
28221     basename = JS_ToCString(ctx, basename_val);
28222     if (!basename)
28223         goto exception;
28224 
28225     filename = JS_ToCString(ctx, specifier);
28226     if (!filename)
28227         goto exception;
28228 
28229     m = JS_RunModule(ctx, basename, filename);
28230     JS_FreeCString(ctx, filename);
28231     if (!m)
28232         goto exception;
28233 
28234     /* return the module namespace */
28235     ns = js_get_module_ns(ctx, m);
28236     if (JS_IsException(ns))
28237         goto exception;
28238 
28239     ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
28240                    1, (JSValueConst *)&ns);
28241     JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
28242     JS_FreeValue(ctx, ns);
28243     JS_FreeCString(ctx, basename);
28244     return JS_UNDEFINED;
28245  exception:
28246 
28247     err = JS_GetException(ctx);
28248     ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
28249                    1, (JSValueConst *)&err);
28250     JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
28251     JS_FreeValue(ctx, err);
28252     JS_FreeCString(ctx, basename);
28253     return JS_UNDEFINED;
28254 }
28255 
js_dynamic_import(JSContext * ctx,JSValueConst specifier)28256 static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)
28257 {
28258     JSAtom basename;
28259     JSValue promise, resolving_funcs[2], basename_val;
28260     JSValueConst args[4];
28261 
28262     basename = JS_GetScriptOrModuleName(ctx, 0);
28263     if (basename == JS_ATOM_NULL)
28264         basename_val = JS_NULL;
28265     else
28266         basename_val = JS_AtomToValue(ctx, basename);
28267     JS_FreeAtom(ctx, basename);
28268     if (JS_IsException(basename_val))
28269         return basename_val;
28270 
28271     promise = JS_NewPromiseCapability(ctx, resolving_funcs);
28272     if (JS_IsException(promise)) {
28273         JS_FreeValue(ctx, basename_val);
28274         return promise;
28275     }
28276 
28277     args[0] = resolving_funcs[0];
28278     args[1] = resolving_funcs[1];
28279     args[2] = basename_val;
28280     args[3] = specifier;
28281 
28282     JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args);
28283 
28284     JS_FreeValue(ctx, basename_val);
28285     JS_FreeValue(ctx, resolving_funcs[0]);
28286     JS_FreeValue(ctx, resolving_funcs[1]);
28287     return promise;
28288 }
28289 
28290 /* Run the <eval> function of the module and of all its requested
28291    modules. */
js_evaluate_module(JSContext * ctx,JSModuleDef * m)28292 static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
28293 {
28294     JSModuleDef *m1;
28295     int i;
28296     JSValue ret_val;
28297 
28298     if (m->eval_mark)
28299         return JS_UNDEFINED; /* avoid cycles */
28300 
28301     if (m->evaluated) {
28302         /* if the module was already evaluated, rethrow the exception
28303            it raised */
28304         if (m->eval_has_exception) {
28305             return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception));
28306         } else {
28307             return JS_UNDEFINED;
28308         }
28309     }
28310 
28311     m->eval_mark = TRUE;
28312 
28313     for(i = 0; i < m->req_module_entries_count; i++) {
28314         JSReqModuleEntry *rme = &m->req_module_entries[i];
28315         m1 = rme->module;
28316         if (!m1->eval_mark) {
28317             ret_val = js_evaluate_module(ctx, m1);
28318             if (JS_IsException(ret_val)) {
28319                 m->eval_mark = FALSE;
28320                 return ret_val;
28321             }
28322             JS_FreeValue(ctx, ret_val);
28323         }
28324     }
28325 
28326     if (m->init_func) {
28327         /* C module init */
28328         if (m->init_func(ctx, m) < 0)
28329             ret_val = JS_EXCEPTION;
28330         else
28331             ret_val = JS_UNDEFINED;
28332     } else {
28333         ret_val = JS_CallFree(ctx, m->func_obj, JS_UNDEFINED, 0, NULL);
28334         m->func_obj = JS_UNDEFINED;
28335     }
28336     if (JS_IsException(ret_val)) {
28337         /* save the thrown exception value */
28338         m->eval_has_exception = TRUE;
28339         m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception);
28340     }
28341     m->eval_mark = FALSE;
28342     m->evaluated = TRUE;
28343     return ret_val;
28344 }
28345 
js_parse_from_clause(JSParseState * s)28346 static __exception JSAtom js_parse_from_clause(JSParseState *s)
28347 {
28348     JSAtom module_name;
28349     if (!token_is_pseudo_keyword(s, JS_ATOM_from)) {
28350         js_parse_error(s, "from clause expected");
28351         return JS_ATOM_NULL;
28352     }
28353     if (next_token(s))
28354         return JS_ATOM_NULL;
28355     if (s->token.val != TOK_STRING) {
28356         js_parse_error(s, "string expected");
28357         return JS_ATOM_NULL;
28358     }
28359     module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
28360     if (module_name == JS_ATOM_NULL)
28361         return JS_ATOM_NULL;
28362     if (next_token(s)) {
28363         JS_FreeAtom(s->ctx, module_name);
28364         return JS_ATOM_NULL;
28365     }
28366     return module_name;
28367 }
28368 
js_parse_export(JSParseState * s)28369 static __exception int js_parse_export(JSParseState *s)
28370 {
28371     JSContext *ctx = s->ctx;
28372     JSModuleDef *m = s->cur_func->module;
28373     JSAtom local_name, export_name;
28374     int first_export, idx, i, tok;
28375     JSAtom module_name;
28376     JSExportEntry *me;
28377 
28378     if (next_token(s))
28379         return -1;
28380 
28381     tok = s->token.val;
28382     if (tok == TOK_CLASS) {
28383         return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED);
28384     } else if (tok == TOK_FUNCTION ||
28385                (token_is_pseudo_keyword(s, JS_ATOM_async) &&
28386                 peek_token(s, TRUE) == TOK_FUNCTION)) {
28387         return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
28388                                        JS_FUNC_NORMAL, JS_ATOM_NULL,
28389                                        s->token.ptr, s->token.line_num,
28390                                        JS_PARSE_EXPORT_NAMED, NULL);
28391     }
28392 
28393     if (next_token(s))
28394         return -1;
28395 
28396     switch(tok) {
28397     case '{':
28398         first_export = m->export_entries_count;
28399         while (s->token.val != '}') {
28400             if (!token_is_ident(s->token.val)) {
28401                 js_parse_error(s, "identifier expected");
28402                 return -1;
28403             }
28404             local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28405             export_name = JS_ATOM_NULL;
28406             if (next_token(s))
28407                 goto fail;
28408             if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
28409                 if (next_token(s))
28410                     goto fail;
28411                 if (!token_is_ident(s->token.val)) {
28412                     js_parse_error(s, "identifier expected");
28413                     goto fail;
28414                 }
28415                 export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28416                 if (next_token(s)) {
28417                 fail:
28418                     JS_FreeAtom(ctx, local_name);
28419                 fail1:
28420                     JS_FreeAtom(ctx, export_name);
28421                     return -1;
28422                 }
28423             } else {
28424                 export_name = JS_DupAtom(ctx, local_name);
28425             }
28426             me = add_export_entry(s, m, local_name, export_name,
28427                                   JS_EXPORT_TYPE_LOCAL);
28428             JS_FreeAtom(ctx, local_name);
28429             JS_FreeAtom(ctx, export_name);
28430             if (!me)
28431                 return -1;
28432             if (s->token.val != ',')
28433                 break;
28434             if (next_token(s))
28435                 return -1;
28436         }
28437         if (js_parse_expect(s, '}'))
28438             return -1;
28439         if (token_is_pseudo_keyword(s, JS_ATOM_from)) {
28440             module_name = js_parse_from_clause(s);
28441             if (module_name == JS_ATOM_NULL)
28442                 return -1;
28443             idx = add_req_module_entry(ctx, m, module_name);
28444             JS_FreeAtom(ctx, module_name);
28445             if (idx < 0)
28446                 return -1;
28447             for(i = first_export; i < m->export_entries_count; i++) {
28448                 me = &m->export_entries[i];
28449                 me->export_type = JS_EXPORT_TYPE_INDIRECT;
28450                 me->u.req_module_idx = idx;
28451             }
28452         }
28453         break;
28454     case '*':
28455         if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
28456             /* export ns from */
28457             if (next_token(s))
28458                 return -1;
28459             if (!token_is_ident(s->token.val)) {
28460                 js_parse_error(s, "identifier expected");
28461                 return -1;
28462             }
28463             export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28464             if (next_token(s))
28465                 goto fail1;
28466             module_name = js_parse_from_clause(s);
28467             if (module_name == JS_ATOM_NULL)
28468                 goto fail1;
28469             idx = add_req_module_entry(ctx, m, module_name);
28470             JS_FreeAtom(ctx, module_name);
28471             if (idx < 0)
28472                 goto fail1;
28473             me = add_export_entry(s, m, JS_ATOM__star_, export_name,
28474                                   JS_EXPORT_TYPE_INDIRECT);
28475             JS_FreeAtom(ctx, export_name);
28476             if (!me)
28477                 return -1;
28478             me->u.req_module_idx = idx;
28479         } else {
28480             module_name = js_parse_from_clause(s);
28481             if (module_name == JS_ATOM_NULL)
28482                 return -1;
28483             idx = add_req_module_entry(ctx, m, module_name);
28484             JS_FreeAtom(ctx, module_name);
28485             if (idx < 0)
28486                 return -1;
28487             if (add_star_export_entry(ctx, m, idx) < 0)
28488                 return -1;
28489         }
28490         break;
28491     case TOK_DEFAULT:
28492         if (s->token.val == TOK_CLASS) {
28493             return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT);
28494         } else if (s->token.val == TOK_FUNCTION ||
28495                    (token_is_pseudo_keyword(s, JS_ATOM_async) &&
28496                     peek_token(s, TRUE) == TOK_FUNCTION)) {
28497             return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
28498                                            JS_FUNC_NORMAL, JS_ATOM_NULL,
28499                                            s->token.ptr, s->token.line_num,
28500                                            JS_PARSE_EXPORT_DEFAULT, NULL);
28501         } else {
28502             if (js_parse_assign_expr(s))
28503                 return -1;
28504         }
28505         /* set the name of anonymous functions */
28506         set_object_name(s, JS_ATOM_default);
28507 
28508         /* store the value in the _default_ global variable and export
28509            it */
28510         local_name = JS_ATOM__default_;
28511         if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0)
28512             return -1;
28513         emit_op(s, OP_scope_put_var_init);
28514         emit_atom(s, local_name);
28515         emit_u16(s, 0);
28516 
28517         if (!add_export_entry(s, m, local_name, JS_ATOM_default,
28518                               JS_EXPORT_TYPE_LOCAL))
28519             return -1;
28520         break;
28521     case TOK_VAR:
28522     case TOK_LET:
28523     case TOK_CONST:
28524         return js_parse_var(s, TRUE, tok, TRUE);
28525     default:
28526         return js_parse_error(s, "invalid export syntax");
28527     }
28528     return js_parse_expect_semi(s);
28529 }
28530 
28531 static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
28532                            BOOL is_local, BOOL is_arg,
28533                            int var_idx, JSAtom var_name,
28534                            BOOL is_const, BOOL is_lexical,
28535                            JSVarKindEnum var_kind);
28536 
add_import(JSParseState * s,JSModuleDef * m,JSAtom local_name,JSAtom import_name)28537 static int add_import(JSParseState *s, JSModuleDef *m,
28538                       JSAtom local_name, JSAtom import_name)
28539 {
28540     JSContext *ctx = s->ctx;
28541     int i, var_idx;
28542     JSImportEntry *mi;
28543     BOOL is_local;
28544 
28545     if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval)
28546         return js_parse_error(s, "invalid import binding");
28547 
28548     if (local_name != JS_ATOM_default) {
28549         for (i = 0; i < s->cur_func->closure_var_count; i++) {
28550             if (s->cur_func->closure_var[i].var_name == local_name)
28551                 return js_parse_error(s, "duplicate import binding");
28552         }
28553     }
28554 
28555     is_local = (import_name == JS_ATOM__star_);
28556     var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE,
28557                               m->import_entries_count,
28558                               local_name, TRUE, TRUE, FALSE);
28559     if (var_idx < 0)
28560         return -1;
28561     if (js_resize_array(ctx, (void **)&m->import_entries,
28562                         sizeof(JSImportEntry),
28563                         &m->import_entries_size,
28564                         m->import_entries_count + 1))
28565         return -1;
28566     mi = &m->import_entries[m->import_entries_count++];
28567     mi->import_name = JS_DupAtom(ctx, import_name);
28568     mi->var_idx = var_idx;
28569     return 0;
28570 }
28571 
js_parse_import(JSParseState * s)28572 static __exception int js_parse_import(JSParseState *s)
28573 {
28574     JSContext *ctx = s->ctx;
28575     JSModuleDef *m = s->cur_func->module;
28576     JSAtom local_name, import_name, module_name;
28577     int first_import, i, idx;
28578 
28579     if (next_token(s))
28580         return -1;
28581 
28582     first_import = m->import_entries_count;
28583     if (s->token.val == TOK_STRING) {
28584         module_name = JS_ValueToAtom(ctx, s->token.u.str.str);
28585         if (module_name == JS_ATOM_NULL)
28586             return -1;
28587         if (next_token(s)) {
28588             JS_FreeAtom(ctx, module_name);
28589             return -1;
28590         }
28591     } else {
28592         if (s->token.val == TOK_IDENT) {
28593             if (s->token.u.ident.is_reserved) {
28594                 return js_parse_error_reserved_identifier(s);
28595             }
28596             /* "default" import */
28597             local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28598             import_name = JS_ATOM_default;
28599             if (next_token(s))
28600                 goto fail;
28601             if (add_import(s, m, local_name, import_name))
28602                 goto fail;
28603             JS_FreeAtom(ctx, local_name);
28604 
28605             if (s->token.val != ',')
28606                 goto end_import_clause;
28607             if (next_token(s))
28608                 return -1;
28609         }
28610 
28611         if (s->token.val == '*') {
28612             /* name space import */
28613             if (next_token(s))
28614                 return -1;
28615             if (!token_is_pseudo_keyword(s, JS_ATOM_as))
28616                 return js_parse_error(s, "expecting 'as'");
28617             if (next_token(s))
28618                 return -1;
28619             if (!token_is_ident(s->token.val)) {
28620                 js_parse_error(s, "identifier expected");
28621                 return -1;
28622             }
28623             local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28624             import_name = JS_ATOM__star_;
28625             if (next_token(s))
28626                 goto fail;
28627             if (add_import(s, m, local_name, import_name))
28628                 goto fail;
28629             JS_FreeAtom(ctx, local_name);
28630         } else if (s->token.val == '{') {
28631             if (next_token(s))
28632                 return -1;
28633 
28634             while (s->token.val != '}') {
28635                 if (!token_is_ident(s->token.val)) {
28636                     js_parse_error(s, "identifier expected");
28637                     return -1;
28638                 }
28639                 import_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28640                 local_name = JS_ATOM_NULL;
28641                 if (next_token(s))
28642                     goto fail;
28643                 if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
28644                     if (next_token(s))
28645                         goto fail;
28646                     if (!token_is_ident(s->token.val)) {
28647                         js_parse_error(s, "identifier expected");
28648                         goto fail;
28649                     }
28650                     local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28651                     if (next_token(s)) {
28652                     fail:
28653                         JS_FreeAtom(ctx, local_name);
28654                         JS_FreeAtom(ctx, import_name);
28655                         return -1;
28656                     }
28657                 } else {
28658                     local_name = JS_DupAtom(ctx, import_name);
28659                 }
28660                 if (add_import(s, m, local_name, import_name))
28661                     goto fail;
28662                 JS_FreeAtom(ctx, local_name);
28663                 JS_FreeAtom(ctx, import_name);
28664                 if (s->token.val != ',')
28665                     break;
28666                 if (next_token(s))
28667                     return -1;
28668             }
28669             if (js_parse_expect(s, '}'))
28670                 return -1;
28671         }
28672     end_import_clause:
28673         module_name = js_parse_from_clause(s);
28674         if (module_name == JS_ATOM_NULL)
28675             return -1;
28676     }
28677     idx = add_req_module_entry(ctx, m, module_name);
28678     JS_FreeAtom(ctx, module_name);
28679     if (idx < 0)
28680         return -1;
28681     for(i = first_import; i < m->import_entries_count; i++)
28682         m->import_entries[i].req_module_idx = idx;
28683 
28684     return js_parse_expect_semi(s);
28685 }
28686 
js_parse_source_element(JSParseState * s)28687 static __exception int js_parse_source_element(JSParseState *s)
28688 {
28689     JSFunctionDef *fd = s->cur_func;
28690     int tok;
28691 
28692     if (s->token.val == TOK_FUNCTION ||
28693         (token_is_pseudo_keyword(s, JS_ATOM_async) &&
28694          peek_token(s, TRUE) == TOK_FUNCTION)) {
28695         if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT,
28696                                    JS_FUNC_NORMAL, JS_ATOM_NULL,
28697                                    s->token.ptr, s->token.line_num))
28698             return -1;
28699     } else if (s->token.val == TOK_EXPORT && fd->module) {
28700         if (js_parse_export(s))
28701             return -1;
28702     } else if (s->token.val == TOK_IMPORT && fd->module &&
28703                ((tok = peek_token(s, FALSE)) != '(' && tok != '.'))  {
28704         /* the peek_token is needed to avoid confusion with ImportCall
28705            (dynamic import) or import.meta */
28706         if (js_parse_import(s))
28707             return -1;
28708     } else {
28709         if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
28710             return -1;
28711     }
28712     return 0;
28713 }
28714 
js_new_function_def(JSContext * ctx,JSFunctionDef * parent,BOOL is_eval,BOOL is_func_expr,const char * filename,int line_num)28715 static JSFunctionDef *js_new_function_def(JSContext *ctx,
28716                                           JSFunctionDef *parent,
28717                                           BOOL is_eval,
28718                                           BOOL is_func_expr,
28719                                           const char *filename, int line_num)
28720 {
28721     JSFunctionDef *fd;
28722 
28723     fd = js_mallocz(ctx, sizeof(*fd));
28724     if (!fd)
28725         return NULL;
28726 
28727     fd->ctx = ctx;
28728     init_list_head(&fd->child_list);
28729 
28730     /* insert in parent list */
28731     fd->parent = parent;
28732     fd->parent_cpool_idx = -1;
28733     if (parent) {
28734         list_add_tail(&fd->link, &parent->child_list);
28735         fd->js_mode = parent->js_mode;
28736         fd->parent_scope_level = parent->scope_level;
28737     }
28738 
28739     fd->is_eval = is_eval;
28740     fd->is_func_expr = is_func_expr;
28741     js_dbuf_init(ctx, &fd->byte_code);
28742     fd->last_opcode_pos = -1;
28743     fd->func_name = JS_ATOM_NULL;
28744     fd->var_object_idx = -1;
28745     fd->arg_var_object_idx = -1;
28746     fd->arguments_var_idx = -1;
28747     fd->arguments_arg_idx = -1;
28748     fd->func_var_idx = -1;
28749     fd->eval_ret_idx = -1;
28750     fd->this_var_idx = -1;
28751     fd->new_target_var_idx = -1;
28752     fd->this_active_func_var_idx = -1;
28753     fd->home_object_var_idx = -1;
28754 
28755     /* XXX: should distinguish arg, var and var object and body scopes */
28756     fd->scopes = fd->def_scope_array;
28757     fd->scope_size = countof(fd->def_scope_array);
28758     fd->scope_count = 1;
28759     fd->scopes[0].first = -1;
28760     fd->scopes[0].parent = -1;
28761     fd->scope_level = 0;  /* 0: var/arg scope */
28762     fd->scope_first = -1;
28763     fd->body_scope = -1;
28764 
28765     fd->filename = JS_NewAtom(ctx, filename);
28766     fd->line_num = line_num;
28767 
28768     js_dbuf_init(ctx, &fd->pc2line);
28769     //fd->pc2line_last_line_num = line_num;
28770     //fd->pc2line_last_pc = 0;
28771     fd->last_opcode_line_num = line_num;
28772 
28773     return fd;
28774 }
28775 
free_bytecode_atoms(JSRuntime * rt,const uint8_t * bc_buf,int bc_len,BOOL use_short_opcodes)28776 static void free_bytecode_atoms(JSRuntime *rt,
28777                                 const uint8_t *bc_buf, int bc_len,
28778                                 BOOL use_short_opcodes)
28779 {
28780     int pos, len, op;
28781     JSAtom atom;
28782     const JSOpCode *oi;
28783 
28784     pos = 0;
28785     while (pos < bc_len) {
28786         op = bc_buf[pos];
28787         if (use_short_opcodes)
28788             oi = &short_opcode_info(op);
28789         else
28790             oi = &opcode_info[op];
28791 
28792         len = oi->size;
28793         switch(oi->fmt) {
28794         case OP_FMT_atom:
28795         case OP_FMT_atom_u8:
28796         case OP_FMT_atom_u16:
28797         case OP_FMT_atom_label_u8:
28798         case OP_FMT_atom_label_u16:
28799             atom = get_u32(bc_buf + pos + 1);
28800             JS_FreeAtomRT(rt, atom);
28801             break;
28802         default:
28803             break;
28804         }
28805         pos += len;
28806     }
28807 }
28808 
js_free_function_def(JSContext * ctx,JSFunctionDef * fd)28809 static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)
28810 {
28811     int i;
28812     struct list_head *el, *el1;
28813 
28814     /* free the child functions */
28815     list_for_each_safe(el, el1, &fd->child_list) {
28816         JSFunctionDef *fd1;
28817         fd1 = list_entry(el, JSFunctionDef, link);
28818         js_free_function_def(ctx, fd1);
28819     }
28820 
28821     free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size,
28822                         fd->use_short_opcodes);
28823     dbuf_free(&fd->byte_code);
28824     js_free(ctx, fd->jump_slots);
28825     js_free(ctx, fd->label_slots);
28826     js_free(ctx, fd->line_number_slots);
28827 
28828     for(i = 0; i < fd->cpool_count; i++) {
28829         JS_FreeValue(ctx, fd->cpool[i]);
28830     }
28831     js_free(ctx, fd->cpool);
28832 
28833     JS_FreeAtom(ctx, fd->func_name);
28834 
28835     for(i = 0; i < fd->var_count; i++) {
28836         JS_FreeAtom(ctx, fd->vars[i].var_name);
28837     }
28838     js_free(ctx, fd->vars);
28839     for(i = 0; i < fd->arg_count; i++) {
28840         JS_FreeAtom(ctx, fd->args[i].var_name);
28841     }
28842     js_free(ctx, fd->args);
28843 
28844     for(i = 0; i < fd->global_var_count; i++) {
28845         JS_FreeAtom(ctx, fd->global_vars[i].var_name);
28846     }
28847     js_free(ctx, fd->global_vars);
28848 
28849     for(i = 0; i < fd->closure_var_count; i++) {
28850         JSClosureVar *cv = &fd->closure_var[i];
28851         JS_FreeAtom(ctx, cv->var_name);
28852     }
28853     js_free(ctx, fd->closure_var);
28854 
28855     if (fd->scopes != fd->def_scope_array)
28856         js_free(ctx, fd->scopes);
28857 
28858     JS_FreeAtom(ctx, fd->filename);
28859     dbuf_free(&fd->pc2line);
28860 
28861     js_free(ctx, fd->source);
28862 
28863     if (fd->parent) {
28864         /* remove in parent list */
28865         list_del(&fd->link);
28866     }
28867     js_free(ctx, fd);
28868 }
28869 
28870 #ifdef DUMP_BYTECODE
skip_lines(const char * p,int n)28871 static const char *skip_lines(const char *p, int n) {
28872     while (n-- > 0 && *p) {
28873         while (*p && *p++ != '\n')
28874             continue;
28875     }
28876     return p;
28877 }
28878 
print_lines(const char * source,int line,int line1)28879 static void print_lines(const char *source, int line, int line1) {
28880     const char *s = source;
28881     const char *p = skip_lines(s, line);
28882     if (*p) {
28883         while (line++ < line1) {
28884             p = skip_lines(s = p, 1);
28885             printf(";; %.*s", (int)(p - s), s);
28886             if (!*p) {
28887                 if (p[-1] != '\n')
28888                     printf("\n");
28889                 break;
28890             }
28891         }
28892     }
28893 }
28894 
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)28895 static void dump_byte_code(JSContext *ctx, int pass,
28896                            const uint8_t *tab, int len,
28897                            const JSVarDef *args, int arg_count,
28898                            const JSVarDef *vars, int var_count,
28899                            const JSClosureVar *closure_var, int closure_var_count,
28900                            const JSValue *cpool, uint32_t cpool_count,
28901                            const char *source, int line_num,
28902                            const LabelSlot *label_slots, JSFunctionBytecode *b)
28903 {
28904     const JSOpCode *oi;
28905     int pos, pos_next, op, size, idx, addr, line, line1, in_source;
28906     uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits));
28907     BOOL use_short_opcodes = (b != NULL);
28908 
28909     /* scan for jump targets */
28910     for (pos = 0; pos < len; pos = pos_next) {
28911         op = tab[pos];
28912         if (use_short_opcodes)
28913             oi = &short_opcode_info(op);
28914         else
28915             oi = &opcode_info[op];
28916         pos_next = pos + oi->size;
28917         if (op < OP_COUNT) {
28918             switch (oi->fmt) {
28919 #if SHORT_OPCODES
28920             case OP_FMT_label8:
28921                 pos++;
28922                 addr = (int8_t)tab[pos];
28923                 goto has_addr;
28924             case OP_FMT_label16:
28925                 pos++;
28926                 addr = (int16_t)get_u16(tab + pos);
28927                 goto has_addr;
28928 #endif
28929             case OP_FMT_atom_label_u8:
28930             case OP_FMT_atom_label_u16:
28931                 pos += 4;
28932                 /* fall thru */
28933             case OP_FMT_label:
28934             case OP_FMT_label_u16:
28935                 pos++;
28936                 addr = get_u32(tab + pos);
28937                 goto has_addr;
28938             has_addr:
28939                 if (pass == 1)
28940                     addr = label_slots[addr].pos;
28941                 if (pass == 2)
28942                     addr = label_slots[addr].pos2;
28943                 if (pass == 3)
28944                     addr += pos;
28945                 if (addr >= 0 && addr < len)
28946                     bits[addr] |= 1;
28947                 break;
28948             }
28949         }
28950     }
28951     in_source = 0;
28952     if (source) {
28953         /* Always print first line: needed if single line */
28954         print_lines(source, 0, 1);
28955         in_source = 1;
28956     }
28957     line1 = line = 1;
28958     pos = 0;
28959     while (pos < len) {
28960         op = tab[pos];
28961         if (source) {
28962             if (b) {
28963                 line1 = find_line_num(ctx, b, pos) - line_num + 1;
28964             } else if (op == OP_line_num) {
28965                 line1 = get_u32(tab + pos + 1) - line_num + 1;
28966             }
28967             if (line1 > line) {
28968                 if (!in_source)
28969                     printf("\n");
28970                 in_source = 1;
28971                 print_lines(source, line, line1);
28972                 line = line1;
28973                 //bits[pos] |= 2;
28974             }
28975         }
28976         if (in_source)
28977             printf("\n");
28978         in_source = 0;
28979         if (op >= OP_COUNT) {
28980             printf("invalid opcode (0x%02x)\n", op);
28981             pos++;
28982             continue;
28983         }
28984         if (use_short_opcodes)
28985             oi = &short_opcode_info(op);
28986         else
28987             oi = &opcode_info[op];
28988         size = oi->size;
28989         if (pos + size > len) {
28990             printf("truncated opcode (0x%02x)\n", op);
28991             break;
28992         }
28993 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 16)
28994         {
28995             int i, x, x0;
28996             x = x0 = printf("%5d ", pos);
28997             for (i = 0; i < size; i++) {
28998                 if (i == 6) {
28999                     printf("\n%*s", x = x0, "");
29000                 }
29001                 x += printf(" %02X", tab[pos + i]);
29002             }
29003             printf("%*s", x0 + 20 - x, "");
29004         }
29005 #endif
29006         if (bits[pos]) {
29007             printf("%5d:  ", pos);
29008         } else {
29009             printf("        ");
29010         }
29011         printf("%s", oi->name);
29012         pos++;
29013         switch(oi->fmt) {
29014         case OP_FMT_none_int:
29015             printf(" %d", op - OP_push_0);
29016             break;
29017         case OP_FMT_npopx:
29018             printf(" %d", op - OP_call0);
29019             break;
29020         case OP_FMT_u8:
29021             printf(" %u", get_u8(tab + pos));
29022             break;
29023         case OP_FMT_i8:
29024             printf(" %d", get_i8(tab + pos));
29025             break;
29026         case OP_FMT_u16:
29027         case OP_FMT_npop:
29028             printf(" %u", get_u16(tab + pos));
29029             break;
29030         case OP_FMT_npop_u16:
29031             printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2));
29032             break;
29033         case OP_FMT_i16:
29034             printf(" %d", get_i16(tab + pos));
29035             break;
29036         case OP_FMT_i32:
29037             printf(" %d", get_i32(tab + pos));
29038             break;
29039         case OP_FMT_u32:
29040             printf(" %u", get_u32(tab + pos));
29041             break;
29042 #if SHORT_OPCODES
29043         case OP_FMT_label8:
29044             addr = get_i8(tab + pos);
29045             goto has_addr1;
29046         case OP_FMT_label16:
29047             addr = get_i16(tab + pos);
29048             goto has_addr1;
29049 #endif
29050         case OP_FMT_label:
29051             addr = get_u32(tab + pos);
29052             goto has_addr1;
29053         has_addr1:
29054             if (pass == 1)
29055                 printf(" %u:%u", addr, label_slots[addr].pos);
29056             if (pass == 2)
29057                 printf(" %u:%u", addr, label_slots[addr].pos2);
29058             if (pass == 3)
29059                 printf(" %u", addr + pos);
29060             break;
29061         case OP_FMT_label_u16:
29062             addr = get_u32(tab + pos);
29063             if (pass == 1)
29064                 printf(" %u:%u", addr, label_slots[addr].pos);
29065             if (pass == 2)
29066                 printf(" %u:%u", addr, label_slots[addr].pos2);
29067             if (pass == 3)
29068                 printf(" %u", addr + pos);
29069             printf(",%u", get_u16(tab + pos + 4));
29070             break;
29071 #if SHORT_OPCODES
29072         case OP_FMT_const8:
29073             idx = get_u8(tab + pos);
29074             goto has_pool_idx;
29075 #endif
29076         case OP_FMT_const:
29077             idx = get_u32(tab + pos);
29078             goto has_pool_idx;
29079         has_pool_idx:
29080             printf(" %u: ", idx);
29081             if (idx < cpool_count) {
29082                 JS_DumpValue(ctx, cpool[idx]);
29083             }
29084             break;
29085         case OP_FMT_atom:
29086             printf(" ");
29087             print_atom(ctx, get_u32(tab + pos));
29088             break;
29089         case OP_FMT_atom_u8:
29090             printf(" ");
29091             print_atom(ctx, get_u32(tab + pos));
29092             printf(",%d", get_u8(tab + pos + 4));
29093             break;
29094         case OP_FMT_atom_u16:
29095             printf(" ");
29096             print_atom(ctx, get_u32(tab + pos));
29097             printf(",%d", get_u16(tab + pos + 4));
29098             break;
29099         case OP_FMT_atom_label_u8:
29100         case OP_FMT_atom_label_u16:
29101             printf(" ");
29102             print_atom(ctx, get_u32(tab + pos));
29103             addr = get_u32(tab + pos + 4);
29104             if (pass == 1)
29105                 printf(",%u:%u", addr, label_slots[addr].pos);
29106             if (pass == 2)
29107                 printf(",%u:%u", addr, label_slots[addr].pos2);
29108             if (pass == 3)
29109                 printf(",%u", addr + pos + 4);
29110             if (oi->fmt == OP_FMT_atom_label_u8)
29111                 printf(",%u", get_u8(tab + pos + 8));
29112             else
29113                 printf(",%u", get_u16(tab + pos + 8));
29114             break;
29115         case OP_FMT_none_loc:
29116             idx = (op - OP_get_loc0) % 4;
29117             goto has_loc;
29118         case OP_FMT_loc8:
29119             idx = get_u8(tab + pos);
29120             goto has_loc;
29121         case OP_FMT_loc:
29122             idx = get_u16(tab + pos);
29123         has_loc:
29124             printf(" %d: ", idx);
29125             if (idx < var_count) {
29126                 print_atom(ctx, vars[idx].var_name);
29127             }
29128             break;
29129         case OP_FMT_none_arg:
29130             idx = (op - OP_get_arg0) % 4;
29131             goto has_arg;
29132         case OP_FMT_arg:
29133             idx = get_u16(tab + pos);
29134         has_arg:
29135             printf(" %d: ", idx);
29136             if (idx < arg_count) {
29137                 print_atom(ctx, args[idx].var_name);
29138             }
29139             break;
29140         case OP_FMT_none_var_ref:
29141             idx = (op - OP_get_var_ref0) % 4;
29142             goto has_var_ref;
29143         case OP_FMT_var_ref:
29144             idx = get_u16(tab + pos);
29145         has_var_ref:
29146             printf(" %d: ", idx);
29147             if (idx < closure_var_count) {
29148                 print_atom(ctx, closure_var[idx].var_name);
29149             }
29150             break;
29151         default:
29152             break;
29153         }
29154         printf("\n");
29155         pos += oi->size - 1;
29156     }
29157     if (source) {
29158         if (!in_source)
29159             printf("\n");
29160         print_lines(source, line, INT32_MAX);
29161     }
29162     js_free(ctx, bits);
29163 }
29164 
dump_pc2line(JSContext * ctx,const uint8_t * buf,int len,int line_num)29165 static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len,
29166                                                  int line_num)
29167 {
29168     const uint8_t *p_end, *p_next, *p;
29169     int pc, v;
29170     unsigned int op;
29171 
29172     if (len <= 0)
29173         return;
29174 
29175     printf("%5s %5s\n", "PC", "LINE");
29176 
29177     p = buf;
29178     p_end = buf + len;
29179     pc = 0;
29180     while (p < p_end) {
29181         op = *p++;
29182         if (op == 0) {
29183             v = unicode_from_utf8(p, p_end - p, &p_next);
29184             if (v < 0)
29185                 goto fail;
29186             pc += v;
29187             p = p_next;
29188             v = unicode_from_utf8(p, p_end - p, &p_next);
29189             if (v < 0) {
29190             fail:
29191                 printf("invalid pc2line encode pos=%d\n", (int)(p - buf));
29192                 return;
29193             }
29194             if (!(v & 1)) {
29195                 v = v >> 1;
29196             } else {
29197                 v = -(v >> 1) - 1;
29198             }
29199             line_num += v;
29200             p = p_next;
29201         } else {
29202             op -= PC2LINE_OP_FIRST;
29203             pc += (op / PC2LINE_RANGE);
29204             line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE;
29205         }
29206         printf("%5d %5d\n", pc, line_num);
29207     }
29208 }
29209 
js_dump_function_bytecode(JSContext * ctx,JSFunctionBytecode * b)29210 static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b)
29211 {
29212     int i;
29213     char atom_buf[ATOM_GET_STR_BUF_SIZE];
29214     const char *str;
29215 
29216     if (b->has_debug && b->debug.filename != JS_ATOM_NULL) {
29217         str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename);
29218         printf("%s:%d: ", str, b->debug.line_num);
29219     }
29220 
29221     str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name);
29222     printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str);
29223     if (b->js_mode) {
29224         printf("  mode:");
29225         if (b->js_mode & JS_MODE_STRICT)
29226             printf(" strict");
29227 #ifdef CONFIG_BIGNUM
29228         if (b->js_mode & JS_MODE_MATH)
29229             printf(" math");
29230 #endif
29231         printf("\n");
29232     }
29233     if (b->arg_count && b->vardefs) {
29234         printf("  args:");
29235         for(i = 0; i < b->arg_count; i++) {
29236             printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf),
29237                                         b->vardefs[i].var_name));
29238         }
29239         printf("\n");
29240     }
29241     if (b->var_count && b->vardefs) {
29242         printf("  locals:\n");
29243         for(i = 0; i < b->var_count; i++) {
29244             JSVarDef *vd = &b->vardefs[b->arg_count + i];
29245             printf("%5d: %s %s", i,
29246                    vd->var_kind == JS_VAR_CATCH ? "catch" :
29247                    (vd->var_kind == JS_VAR_FUNCTION_DECL ||
29248                     vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" :
29249                    vd->is_const ? "const" :
29250                    vd->is_lexical ? "let" : "var",
29251                    JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name));
29252             if (vd->scope_level)
29253                 printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next);
29254             printf("\n");
29255         }
29256     }
29257     if (b->closure_var_count) {
29258         printf("  closure vars:\n");
29259         for(i = 0; i < b->closure_var_count; i++) {
29260             JSClosureVar *cv = &b->closure_var[i];
29261             printf("%5d: %s %s:%s%d %s\n", i,
29262                    JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name),
29263                    cv->is_local ? "local" : "parent",
29264                    cv->is_arg ? "arg" : "loc", cv->var_idx,
29265                    cv->is_const ? "const" :
29266                    cv->is_lexical ? "let" : "var");
29267         }
29268     }
29269     printf("  stack_size: %d\n", b->stack_size);
29270     printf("  opcodes:\n");
29271     dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len,
29272                    b->vardefs, b->arg_count,
29273                    b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count,
29274                    b->closure_var, b->closure_var_count,
29275                    b->cpool, b->cpool_count,
29276                    b->has_debug ? b->debug.source : NULL,
29277                    b->has_debug ? b->debug.line_num : -1, NULL, b);
29278 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32)
29279     if (b->has_debug)
29280         dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len, b->debug.line_num);
29281 #endif
29282     printf("\n");
29283 }
29284 #endif
29285 
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)29286 static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
29287                            BOOL is_local, BOOL is_arg,
29288                            int var_idx, JSAtom var_name,
29289                            BOOL is_const, BOOL is_lexical,
29290                            JSVarKindEnum var_kind)
29291 {
29292     JSClosureVar *cv;
29293 
29294     /* the closure variable indexes are currently stored on 16 bits */
29295     if (s->closure_var_count >= JS_MAX_LOCAL_VARS) {
29296         JS_ThrowInternalError(ctx, "too many closure variables");
29297         return -1;
29298     }
29299 
29300     if (js_resize_array(ctx, (void **)&s->closure_var,
29301                         sizeof(s->closure_var[0]),
29302                         &s->closure_var_size, s->closure_var_count + 1))
29303         return -1;
29304     cv = &s->closure_var[s->closure_var_count++];
29305     cv->is_local = is_local;
29306     cv->is_arg = is_arg;
29307     cv->is_const = is_const;
29308     cv->is_lexical = is_lexical;
29309     cv->var_kind = var_kind;
29310     cv->var_idx = var_idx;
29311     cv->var_name = JS_DupAtom(ctx, var_name);
29312     return s->closure_var_count - 1;
29313 }
29314 
find_closure_var(JSContext * ctx,JSFunctionDef * s,JSAtom var_name)29315 static int find_closure_var(JSContext *ctx, JSFunctionDef *s,
29316                             JSAtom var_name)
29317 {
29318     int i;
29319     for(i = 0; i < s->closure_var_count; i++) {
29320         JSClosureVar *cv = &s->closure_var[i];
29321         if (cv->var_name == var_name)
29322             return i;
29323     }
29324     return -1;
29325 }
29326 
29327 /* 'fd' must be a parent of 's'. Create in 's' a closure referencing a
29328    local variable (is_local = TRUE) or a closure (is_local = FALSE) in
29329    '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)29330 static int get_closure_var2(JSContext *ctx, JSFunctionDef *s,
29331                             JSFunctionDef *fd, BOOL is_local,
29332                             BOOL is_arg, int var_idx, JSAtom var_name,
29333                             BOOL is_const, BOOL is_lexical,
29334                             JSVarKindEnum var_kind)
29335 {
29336     int i;
29337 
29338     if (fd != s->parent) {
29339         var_idx = get_closure_var2(ctx, s->parent, fd, is_local,
29340                                    is_arg, var_idx, var_name,
29341                                    is_const, is_lexical, var_kind);
29342         if (var_idx < 0)
29343             return -1;
29344         is_local = FALSE;
29345     }
29346     for(i = 0; i < s->closure_var_count; i++) {
29347         JSClosureVar *cv = &s->closure_var[i];
29348         if (cv->var_idx == var_idx && cv->is_arg == is_arg &&
29349             cv->is_local == is_local)
29350             return i;
29351     }
29352     return add_closure_var(ctx, s, is_local, is_arg, var_idx, var_name,
29353                            is_const, is_lexical, var_kind);
29354 }
29355 
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)29356 static int get_closure_var(JSContext *ctx, JSFunctionDef *s,
29357                            JSFunctionDef *fd, BOOL is_arg,
29358                            int var_idx, JSAtom var_name,
29359                            BOOL is_const, BOOL is_lexical,
29360                            JSVarKindEnum var_kind)
29361 {
29362     return get_closure_var2(ctx, s, fd, TRUE, is_arg,
29363                             var_idx, var_name, is_const, is_lexical,
29364                             var_kind);
29365 }
29366 
get_with_scope_opcode(int op)29367 static int get_with_scope_opcode(int op)
29368 {
29369     if (op == OP_scope_get_var_undef)
29370         return OP_with_get_var;
29371     else
29372         return OP_with_get_var + (op - OP_scope_get_var);
29373 }
29374 
can_opt_put_ref_value(const uint8_t * bc_buf,int pos)29375 static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos)
29376 {
29377     int opcode = bc_buf[pos];
29378     return (bc_buf[pos + 1] == OP_put_ref_value &&
29379             (opcode == OP_insert3 ||
29380              opcode == OP_perm4 ||
29381              opcode == OP_nop ||
29382              opcode == OP_rot3l));
29383 }
29384 
can_opt_put_global_ref_value(const uint8_t * bc_buf,int pos)29385 static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos)
29386 {
29387     int opcode = bc_buf[pos];
29388     return (bc_buf[pos + 1] == OP_put_ref_value &&
29389             (opcode == OP_insert3 ||
29390              opcode == OP_perm4 ||
29391              opcode == OP_nop ||
29392              opcode == OP_rot3l));
29393 }
29394 
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)29395 static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s,
29396                                    DynBuf *bc, uint8_t *bc_buf,
29397                                    LabelSlot *ls, int pos_next,
29398                                    int get_op, int var_idx)
29399 {
29400     int label_pos, end_pos, pos;
29401 
29402     /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)`
29403        but only if expr does not modify `a`.
29404        should scan the code between pos_next and label_pos
29405        for operations that can potentially change `a`:
29406        OP_scope_make_ref(a), function calls, jumps and gosub.
29407      */
29408     /* replace the reference get/put with normal variable
29409        accesses */
29410     if (bc_buf[pos_next] == OP_get_ref_value) {
29411         dbuf_putc(bc, get_op);
29412         dbuf_put_u16(bc, var_idx);
29413         pos_next++;
29414     }
29415     /* remove the OP_label to make room for replacement */
29416     /* label should have a refcount of 0 anyway */
29417     /* XXX: should avoid this patch by inserting nops in phase 1 */
29418     label_pos = ls->pos;
29419     pos = label_pos - 5;
29420     assert(bc_buf[pos] == OP_label);
29421     /* label points to an instruction pair:
29422        - insert3 / put_ref_value
29423        - perm4 / put_ref_value
29424        - rot3l / put_ref_value
29425        - nop / put_ref_value
29426      */
29427     end_pos = label_pos + 2;
29428     if (bc_buf[label_pos] == OP_insert3)
29429         bc_buf[pos++] = OP_dup;
29430     bc_buf[pos] = get_op + 1;
29431     put_u16(bc_buf + pos + 1, var_idx);
29432     pos += 3;
29433     /* pad with OP_nop */
29434     while (pos < end_pos)
29435         bc_buf[pos++] = OP_nop;
29436     return pos_next;
29437 }
29438 
optimize_scope_make_global_ref(JSContext * ctx,JSFunctionDef * s,DynBuf * bc,uint8_t * bc_buf,LabelSlot * ls,int pos_next,JSAtom var_name)29439 static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,
29440                                           DynBuf *bc, uint8_t *bc_buf,
29441                                           LabelSlot *ls, int pos_next,
29442                                           JSAtom var_name)
29443 {
29444     int label_pos, end_pos, pos, op;
29445     BOOL is_strict;
29446     is_strict = ((s->js_mode & JS_MODE_STRICT) != 0);
29447 
29448     /* replace the reference get/put with normal variable
29449        accesses */
29450     if (is_strict) {
29451         /* need to check if the variable exists before evaluating the right
29452            expression */
29453         /* XXX: need an extra OP_true if destructuring an array */
29454         dbuf_putc(bc, OP_check_var);
29455         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29456     } else {
29457         /* XXX: need 2 extra OP_true if destructuring an array */
29458     }
29459     if (bc_buf[pos_next] == OP_get_ref_value) {
29460         dbuf_putc(bc, OP_get_var);
29461         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29462         pos_next++;
29463     }
29464     /* remove the OP_label to make room for replacement */
29465     /* label should have a refcount of 0 anyway */
29466     /* XXX: should have emitted several OP_nop to avoid this kludge */
29467     label_pos = ls->pos;
29468     pos = label_pos - 5;
29469     assert(bc_buf[pos] == OP_label);
29470     end_pos = label_pos + 2;
29471     op = bc_buf[label_pos];
29472     if (is_strict) {
29473         if (op != OP_nop) {
29474             switch(op) {
29475             case OP_insert3:
29476                 op = OP_insert2;
29477                 break;
29478             case OP_perm4:
29479                 op = OP_perm3;
29480                 break;
29481             case OP_rot3l:
29482                 op = OP_swap;
29483                 break;
29484             default:
29485                 abort();
29486             }
29487             bc_buf[pos++] = op;
29488         }
29489     } else {
29490         if (op == OP_insert3)
29491             bc_buf[pos++] = OP_dup;
29492     }
29493     if (is_strict) {
29494         bc_buf[pos] = OP_put_var_strict;
29495         /* XXX: need 1 extra OP_drop if destructuring an array */
29496     } else {
29497         bc_buf[pos] = OP_put_var;
29498         /* XXX: need 2 extra OP_drop if destructuring an array */
29499     }
29500     put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name));
29501     pos += 5;
29502     /* pad with OP_nop */
29503     while (pos < end_pos)
29504         bc_buf[pos++] = OP_nop;
29505     return pos_next;
29506 }
29507 
add_var_this(JSContext * ctx,JSFunctionDef * fd)29508 static int add_var_this(JSContext *ctx, JSFunctionDef *fd)
29509 {
29510     int idx;
29511     idx = add_var(ctx, fd, JS_ATOM_this);
29512     if (idx >= 0 && fd->is_derived_class_constructor) {
29513         JSVarDef *vd = &fd->vars[idx];
29514         /* XXX: should have is_this flag or var type */
29515         vd->is_lexical = 1; /* used to trigger 'uninitialized' checks
29516                                in a derived class constructor */
29517     }
29518     return idx;
29519 }
29520 
resolve_pseudo_var(JSContext * ctx,JSFunctionDef * s,JSAtom var_name)29521 static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s,
29522                                JSAtom var_name)
29523 {
29524     int var_idx;
29525 
29526     if (!s->has_this_binding)
29527         return -1;
29528     switch(var_name) {
29529     case JS_ATOM_home_object:
29530         /* 'home_object' pseudo variable */
29531         if (s->home_object_var_idx < 0)
29532             s->home_object_var_idx = add_var(ctx, s, var_name);
29533         var_idx = s->home_object_var_idx;
29534         break;
29535     case JS_ATOM_this_active_func:
29536         /* 'this.active_func' pseudo variable */
29537         if (s->this_active_func_var_idx < 0)
29538             s->this_active_func_var_idx = add_var(ctx, s, var_name);
29539         var_idx = s->this_active_func_var_idx;
29540         break;
29541     case JS_ATOM_new_target:
29542         /* 'new.target' pseudo variable */
29543         if (s->new_target_var_idx < 0)
29544             s->new_target_var_idx = add_var(ctx, s, var_name);
29545         var_idx = s->new_target_var_idx;
29546         break;
29547     case JS_ATOM_this:
29548         /* 'this' pseudo variable */
29549         if (s->this_var_idx < 0)
29550             s->this_var_idx = add_var_this(ctx, s);
29551         var_idx = s->this_var_idx;
29552         break;
29553     default:
29554         var_idx = -1;
29555         break;
29556     }
29557     return var_idx;
29558 }
29559 
29560 /* test if 'var_name' is in the variable object on the stack. If is it
29561    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)29562 static void var_object_test(JSContext *ctx, JSFunctionDef *s,
29563                             JSAtom var_name, int op, DynBuf *bc,
29564                             int *plabel_done, BOOL is_with)
29565 {
29566     dbuf_putc(bc, get_with_scope_opcode(op));
29567     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29568     *plabel_done = new_label_fd(s, *plabel_done);
29569     dbuf_put_u32(bc, *plabel_done);
29570     dbuf_putc(bc, is_with);
29571     update_label(s, *plabel_done, 1);
29572     s->jump_size++;
29573 }
29574 
29575 /* 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)29576 static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
29577                              JSAtom var_name, int scope_level, int op,
29578                              DynBuf *bc, uint8_t *bc_buf,
29579                              LabelSlot *ls, int pos_next)
29580 {
29581     int idx, var_idx, is_put;
29582     int label_done;
29583     JSFunctionDef *fd;
29584     JSVarDef *vd;
29585     BOOL is_pseudo_var, is_arg_scope;
29586 
29587     label_done = -1;
29588 
29589     /* XXX: could be simpler to use a specific function to
29590        resolve the pseudo variables */
29591     is_pseudo_var = (var_name == JS_ATOM_home_object ||
29592                      var_name == JS_ATOM_this_active_func ||
29593                      var_name == JS_ATOM_new_target ||
29594                      var_name == JS_ATOM_this);
29595 
29596     /* resolve local scoped variables */
29597     var_idx = -1;
29598     for (idx = s->scopes[scope_level].first; idx >= 0;) {
29599         vd = &s->vars[idx];
29600         if (vd->var_name == var_name) {
29601             if (op == OP_scope_put_var || op == OP_scope_make_ref) {
29602                 if (vd->is_const) {
29603                     dbuf_putc(bc, OP_throw_error);
29604                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29605                     dbuf_putc(bc, JS_THROW_VAR_RO);
29606                     goto done;
29607                 }
29608             }
29609             var_idx = idx;
29610             break;
29611         } else
29612         if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
29613             dbuf_putc(bc, OP_get_loc);
29614             dbuf_put_u16(bc, idx);
29615             var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
29616         }
29617         idx = vd->scope_next;
29618     }
29619     is_arg_scope = (idx == ARG_SCOPE_END);
29620     if (var_idx < 0) {
29621         /* argument scope: variables are not visible but pseudo
29622            variables are visible */
29623         if (!is_arg_scope) {
29624             var_idx = find_var(ctx, s, var_name);
29625         }
29626 
29627         if (var_idx < 0 && is_pseudo_var)
29628             var_idx = resolve_pseudo_var(ctx, s, var_name);
29629 
29630         if (var_idx < 0 && var_name == JS_ATOM_arguments &&
29631             s->has_arguments_binding) {
29632             /* 'arguments' pseudo variable */
29633             var_idx = add_arguments_var(ctx, s);
29634         }
29635         if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) {
29636             /* add a new variable with the function name */
29637             var_idx = add_func_var(ctx, s, var_name);
29638         }
29639     }
29640     if (var_idx >= 0) {
29641         if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
29642             !(var_idx & ARGUMENT_VAR_OFFSET) &&
29643             s->vars[var_idx].is_const) {
29644             /* only happens when assigning a function expression name
29645                in strict mode */
29646             dbuf_putc(bc, OP_throw_error);
29647             dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29648             dbuf_putc(bc, JS_THROW_VAR_RO);
29649             goto done;
29650         }
29651         /* OP_scope_put_var_init is only used to initialize a
29652            lexical variable, so it is never used in a with or var object. It
29653            can be used with a closure (module global variable case). */
29654         switch (op) {
29655         case OP_scope_make_ref:
29656             if (!(var_idx & ARGUMENT_VAR_OFFSET) &&
29657                 s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) {
29658                 /* Create a dummy object reference for the func_var */
29659                 dbuf_putc(bc, OP_object);
29660                 dbuf_putc(bc, OP_get_loc);
29661                 dbuf_put_u16(bc, var_idx);
29662                 dbuf_putc(bc, OP_define_field);
29663                 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29664                 dbuf_putc(bc, OP_push_atom_value);
29665                 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29666             } else
29667             if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) {
29668                 int get_op;
29669                 if (var_idx & ARGUMENT_VAR_OFFSET) {
29670                     get_op = OP_get_arg;
29671                     var_idx -= ARGUMENT_VAR_OFFSET;
29672                 } else {
29673                     if (s->vars[var_idx].is_lexical)
29674                         get_op = OP_get_loc_check;
29675                     else
29676                         get_op = OP_get_loc;
29677                 }
29678                 pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
29679                                                    pos_next, get_op, var_idx);
29680             } else {
29681                 /* Create a dummy object with a named slot that is
29682                    a reference to the local variable */
29683                 if (var_idx & ARGUMENT_VAR_OFFSET) {
29684                     dbuf_putc(bc, OP_make_arg_ref);
29685                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29686                     dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
29687                 } else {
29688                     dbuf_putc(bc, OP_make_loc_ref);
29689                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29690                     dbuf_put_u16(bc, var_idx);
29691                 }
29692             }
29693             break;
29694         case OP_scope_get_ref:
29695             dbuf_putc(bc, OP_undefined);
29696             /* fall thru */
29697         case OP_scope_get_var_undef:
29698         case OP_scope_get_var:
29699         case OP_scope_put_var:
29700         case OP_scope_put_var_init:
29701             is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init);
29702             if (var_idx & ARGUMENT_VAR_OFFSET) {
29703                 dbuf_putc(bc, OP_get_arg + is_put);
29704                 dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
29705             } else {
29706                 if (is_put) {
29707                     if (s->vars[var_idx].is_lexical) {
29708                         if (op == OP_scope_put_var_init) {
29709                             /* 'this' can only be initialized once */
29710                             if (var_name == JS_ATOM_this)
29711                                 dbuf_putc(bc, OP_put_loc_check_init);
29712                             else
29713                                 dbuf_putc(bc, OP_put_loc);
29714                         } else {
29715                             dbuf_putc(bc, OP_put_loc_check);
29716                         }
29717                     } else {
29718                         dbuf_putc(bc, OP_put_loc);
29719                     }
29720                 } else {
29721                     if (s->vars[var_idx].is_lexical) {
29722                         dbuf_putc(bc, OP_get_loc_check);
29723                     } else {
29724                         dbuf_putc(bc, OP_get_loc);
29725                     }
29726                 }
29727                 dbuf_put_u16(bc, var_idx);
29728             }
29729             break;
29730         case OP_scope_delete_var:
29731             dbuf_putc(bc, OP_push_false);
29732             break;
29733         }
29734         goto done;
29735     }
29736     /* check eval object */
29737     if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) {
29738         dbuf_putc(bc, OP_get_loc);
29739         dbuf_put_u16(bc, s->var_object_idx);
29740         var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
29741     }
29742     /* check eval object in argument scope */
29743     if (s->arg_var_object_idx >= 0 && !is_pseudo_var) {
29744         dbuf_putc(bc, OP_get_loc);
29745         dbuf_put_u16(bc, s->arg_var_object_idx);
29746         var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
29747     }
29748 
29749     /* check parent scopes */
29750     for (fd = s; fd->parent;) {
29751         scope_level = fd->parent_scope_level;
29752         fd = fd->parent;
29753         for (idx = fd->scopes[scope_level].first; idx >= 0;) {
29754             vd = &fd->vars[idx];
29755             if (vd->var_name == var_name) {
29756                 if (op == OP_scope_put_var || op == OP_scope_make_ref) {
29757                     if (vd->is_const) {
29758                         dbuf_putc(bc, OP_throw_error);
29759                         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29760                         dbuf_putc(bc, JS_THROW_VAR_RO);
29761                         goto done;
29762                     }
29763                 }
29764                 var_idx = idx;
29765                 break;
29766             } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
29767                 vd->is_captured = 1;
29768                 idx = get_closure_var(ctx, s, fd, FALSE, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL);
29769                 if (idx >= 0) {
29770                     dbuf_putc(bc, OP_get_var_ref);
29771                     dbuf_put_u16(bc, idx);
29772                     var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
29773                 }
29774             }
29775             idx = vd->scope_next;
29776         }
29777         is_arg_scope = (idx == ARG_SCOPE_END);
29778         if (var_idx >= 0)
29779             break;
29780 
29781         if (!is_arg_scope) {
29782             var_idx = find_var(ctx, fd, var_name);
29783             if (var_idx >= 0)
29784                 break;
29785         }
29786         if (is_pseudo_var) {
29787             var_idx = resolve_pseudo_var(ctx, fd, var_name);
29788             if (var_idx >= 0)
29789                 break;
29790         }
29791         if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) {
29792             var_idx = add_arguments_var(ctx, fd);
29793             break;
29794         }
29795         if (fd->is_func_expr && fd->func_name == var_name) {
29796             /* add a new variable with the function name */
29797             var_idx = add_func_var(ctx, fd, var_name);
29798             break;
29799         }
29800 
29801         /* check eval object */
29802         if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) {
29803             vd = &fd->vars[fd->var_object_idx];
29804             vd->is_captured = 1;
29805             idx = get_closure_var(ctx, s, fd, FALSE,
29806                                   fd->var_object_idx, vd->var_name,
29807                                   FALSE, FALSE, JS_VAR_NORMAL);
29808             dbuf_putc(bc, OP_get_var_ref);
29809             dbuf_put_u16(bc, idx);
29810             var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
29811         }
29812 
29813         /* check eval object in argument scope */
29814         if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) {
29815             vd = &fd->vars[fd->arg_var_object_idx];
29816             vd->is_captured = 1;
29817             idx = get_closure_var(ctx, s, fd, FALSE,
29818                                   fd->arg_var_object_idx, vd->var_name,
29819                                   FALSE, FALSE, JS_VAR_NORMAL);
29820             dbuf_putc(bc, OP_get_var_ref);
29821             dbuf_put_u16(bc, idx);
29822             var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
29823         }
29824 
29825         if (fd->is_eval)
29826             break; /* it it necessarily the top level function */
29827     }
29828 
29829     /* check direct eval scope (in the closure of the eval function
29830        which is necessarily at the top level) */
29831     if (!fd)
29832         fd = s;
29833     if (var_idx < 0 && fd->is_eval) {
29834         int idx1;
29835         for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) {
29836             JSClosureVar *cv = &fd->closure_var[idx1];
29837             if (var_name == cv->var_name) {
29838                 if (fd != s) {
29839                     idx = get_closure_var2(ctx, s, fd,
29840                                            FALSE,
29841                                            cv->is_arg, idx1,
29842                                            cv->var_name, cv->is_const,
29843                                            cv->is_lexical, cv->var_kind);
29844                 } else {
29845                     idx = idx1;
29846                 }
29847                 goto has_idx;
29848             } else if ((cv->var_name == JS_ATOM__var_ ||
29849                         cv->var_name == JS_ATOM__arg_var_ ||
29850                         cv->var_name == JS_ATOM__with_) && !is_pseudo_var) {
29851                 int is_with = (cv->var_name == JS_ATOM__with_);
29852                 if (fd != s) {
29853                     idx = get_closure_var2(ctx, s, fd,
29854                                            FALSE,
29855                                            cv->is_arg, idx1,
29856                                            cv->var_name, FALSE, FALSE,
29857                                            JS_VAR_NORMAL);
29858                 } else {
29859                     idx = idx1;
29860                 }
29861                 dbuf_putc(bc, OP_get_var_ref);
29862                 dbuf_put_u16(bc, idx);
29863                 var_object_test(ctx, s, var_name, op, bc, &label_done, is_with);
29864             }
29865         }
29866     }
29867 
29868     if (var_idx >= 0) {
29869         /* find the corresponding closure variable */
29870         if (var_idx & ARGUMENT_VAR_OFFSET) {
29871             fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1;
29872             idx = get_closure_var(ctx, s, fd,
29873                                   TRUE, var_idx - ARGUMENT_VAR_OFFSET,
29874                                   var_name, FALSE, FALSE, JS_VAR_NORMAL);
29875         } else {
29876             fd->vars[var_idx].is_captured = 1;
29877             idx = get_closure_var(ctx, s, fd,
29878                                   FALSE, var_idx,
29879                                   var_name,
29880                                   fd->vars[var_idx].is_const,
29881                                   fd->vars[var_idx].is_lexical,
29882                                   fd->vars[var_idx].var_kind);
29883         }
29884         if (idx >= 0) {
29885         has_idx:
29886             if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
29887                 s->closure_var[idx].is_const) {
29888                 dbuf_putc(bc, OP_throw_error);
29889                 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29890                 dbuf_putc(bc, JS_THROW_VAR_RO);
29891                 goto done;
29892             }
29893             switch (op) {
29894             case OP_scope_make_ref:
29895                 if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) {
29896                     /* Create a dummy object reference for the func_var */
29897                     dbuf_putc(bc, OP_object);
29898                     dbuf_putc(bc, OP_get_var_ref);
29899                     dbuf_put_u16(bc, idx);
29900                     dbuf_putc(bc, OP_define_field);
29901                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29902                     dbuf_putc(bc, OP_push_atom_value);
29903                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29904                 } else
29905                 if (label_done == -1 &&
29906                     can_opt_put_ref_value(bc_buf, ls->pos)) {
29907                     int get_op;
29908                     if (s->closure_var[idx].is_lexical)
29909                         get_op = OP_get_var_ref_check;
29910                     else
29911                         get_op = OP_get_var_ref;
29912                     pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
29913                                                        pos_next,
29914                                                        get_op, idx);
29915                 } else {
29916                     /* Create a dummy object with a named slot that is
29917                        a reference to the closure variable */
29918                     dbuf_putc(bc, OP_make_var_ref_ref);
29919                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29920                     dbuf_put_u16(bc, idx);
29921                 }
29922                 break;
29923             case OP_scope_get_ref:
29924                 /* XXX: should create a dummy object with a named slot that is
29925                    a reference to the closure variable */
29926                 dbuf_putc(bc, OP_undefined);
29927                 /* fall thru */
29928             case OP_scope_get_var_undef:
29929             case OP_scope_get_var:
29930             case OP_scope_put_var:
29931             case OP_scope_put_var_init:
29932                 is_put = (op == OP_scope_put_var ||
29933                           op == OP_scope_put_var_init);
29934                 if (is_put) {
29935                     if (s->closure_var[idx].is_lexical) {
29936                         if (op == OP_scope_put_var_init) {
29937                             /* 'this' can only be initialized once */
29938                             if (var_name == JS_ATOM_this)
29939                                 dbuf_putc(bc, OP_put_var_ref_check_init);
29940                             else
29941                                 dbuf_putc(bc, OP_put_var_ref);
29942                         } else {
29943                             dbuf_putc(bc, OP_put_var_ref_check);
29944                         }
29945                     } else {
29946                         dbuf_putc(bc, OP_put_var_ref);
29947                     }
29948                 } else {
29949                     if (s->closure_var[idx].is_lexical) {
29950                         dbuf_putc(bc, OP_get_var_ref_check);
29951                     } else {
29952                         dbuf_putc(bc, OP_get_var_ref);
29953                     }
29954                 }
29955                 dbuf_put_u16(bc, idx);
29956                 break;
29957             case OP_scope_delete_var:
29958                 dbuf_putc(bc, OP_push_false);
29959                 break;
29960             }
29961             goto done;
29962         }
29963     }
29964 
29965     /* global variable access */
29966 
29967     switch (op) {
29968     case OP_scope_make_ref:
29969         if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) {
29970             pos_next = optimize_scope_make_global_ref(ctx, s, bc, bc_buf, ls,
29971                                                       pos_next, var_name);
29972         } else {
29973             dbuf_putc(bc, OP_make_var_ref);
29974             dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29975         }
29976         break;
29977     case OP_scope_get_ref:
29978         /* XXX: should create a dummy object with a named slot that is
29979            a reference to the global variable */
29980         dbuf_putc(bc, OP_undefined);
29981         dbuf_putc(bc, OP_get_var);
29982         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29983         break;
29984     case OP_scope_get_var_undef:
29985     case OP_scope_get_var:
29986     case OP_scope_put_var:
29987         dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef));
29988         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29989         break;
29990     case OP_scope_put_var_init:
29991         dbuf_putc(bc, OP_put_var_init);
29992         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29993         break;
29994     case OP_scope_delete_var:
29995         dbuf_putc(bc, OP_delete_var);
29996         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
29997         break;
29998     }
29999 done:
30000     if (label_done >= 0) {
30001         dbuf_putc(bc, OP_label);
30002         dbuf_put_u32(bc, label_done);
30003         s->label_slots[label_done].pos2 = bc->size;
30004     }
30005     return pos_next;
30006 }
30007 
30008 /* search in all scopes */
find_private_class_field_all(JSContext * ctx,JSFunctionDef * fd,JSAtom name,int scope_level)30009 static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd,
30010                                         JSAtom name, int scope_level)
30011 {
30012     int idx;
30013 
30014     idx = fd->scopes[scope_level].first;
30015     while (idx >= 0) {
30016         if (fd->vars[idx].var_name == name)
30017             return idx;
30018         idx = fd->vars[idx].scope_next;
30019     }
30020     return -1;
30021 }
30022 
get_loc_or_ref(DynBuf * bc,BOOL is_ref,int idx)30023 static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx)
30024 {
30025     /* if the field is not initialized, the error is catched when
30026        accessing it */
30027     if (is_ref)
30028         dbuf_putc(bc, OP_get_var_ref);
30029     else
30030         dbuf_putc(bc, OP_get_loc);
30031     dbuf_put_u16(bc, idx);
30032 }
30033 
resolve_scope_private_field1(JSContext * ctx,BOOL * pis_ref,int * pvar_kind,JSFunctionDef * s,JSAtom var_name,int scope_level)30034 static int resolve_scope_private_field1(JSContext *ctx,
30035                                         BOOL *pis_ref, int *pvar_kind,
30036                                         JSFunctionDef *s,
30037                                         JSAtom var_name, int scope_level)
30038 {
30039     int idx, var_kind;
30040     JSFunctionDef *fd;
30041     BOOL is_ref;
30042 
30043     fd = s;
30044     is_ref = FALSE;
30045     for(;;) {
30046         idx = find_private_class_field_all(ctx, fd, var_name, scope_level);
30047         if (idx >= 0) {
30048             var_kind = fd->vars[idx].var_kind;
30049             if (is_ref) {
30050                 idx = get_closure_var(ctx, s, fd, FALSE, idx, var_name,
30051                                       TRUE, TRUE, JS_VAR_NORMAL);
30052                 if (idx < 0)
30053                     return -1;
30054             }
30055             break;
30056         }
30057         scope_level = fd->parent_scope_level;
30058         if (!fd->parent) {
30059             if (fd->is_eval) {
30060                 /* closure of the eval function (top level) */
30061                 for (idx = 0; idx < fd->closure_var_count; idx++) {
30062                     JSClosureVar *cv = &fd->closure_var[idx];
30063                     if (cv->var_name == var_name) {
30064                         var_kind = cv->var_kind;
30065                         is_ref = TRUE;
30066                         if (fd != s) {
30067                             idx = get_closure_var2(ctx, s, fd,
30068                                                    FALSE,
30069                                                    cv->is_arg, idx,
30070                                                    cv->var_name, cv->is_const,
30071                                                    cv->is_lexical,
30072                                                    cv->var_kind);
30073                             if (idx < 0)
30074                                 return -1;
30075                         }
30076                         goto done;
30077                     }
30078                 }
30079             }
30080             /* XXX: no line number info */
30081             JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'",
30082                                     var_name);
30083             return -1;
30084         } else {
30085             fd = fd->parent;
30086         }
30087         is_ref = TRUE;
30088     }
30089  done:
30090     *pis_ref = is_ref;
30091     *pvar_kind = var_kind;
30092     return idx;
30093 }
30094 
30095 /* 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)30096 static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s,
30097                                        JSAtom var_name, int scope_level, int op,
30098                                        DynBuf *bc)
30099 {
30100     int idx, var_kind;
30101     BOOL is_ref;
30102 
30103     idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s,
30104                                        var_name, scope_level);
30105     if (idx < 0)
30106         return -1;
30107     assert(var_kind != JS_VAR_NORMAL);
30108     switch (op) {
30109     case OP_scope_get_private_field:
30110     case OP_scope_get_private_field2:
30111         switch(var_kind) {
30112         case JS_VAR_PRIVATE_FIELD:
30113             if (op == OP_scope_get_private_field2)
30114                 dbuf_putc(bc, OP_dup);
30115             get_loc_or_ref(bc, is_ref, idx);
30116             dbuf_putc(bc, OP_get_private_field);
30117             break;
30118         case JS_VAR_PRIVATE_METHOD:
30119             get_loc_or_ref(bc, is_ref, idx);
30120             dbuf_putc(bc, OP_check_brand);
30121             if (op != OP_scope_get_private_field2)
30122                 dbuf_putc(bc, OP_nip);
30123             break;
30124         case JS_VAR_PRIVATE_GETTER:
30125         case JS_VAR_PRIVATE_GETTER_SETTER:
30126             if (op == OP_scope_get_private_field2)
30127                 dbuf_putc(bc, OP_dup);
30128             get_loc_or_ref(bc, is_ref, idx);
30129             dbuf_putc(bc, OP_check_brand);
30130             dbuf_putc(bc, OP_call_method);
30131             dbuf_put_u16(bc, 0);
30132             break;
30133         case JS_VAR_PRIVATE_SETTER:
30134             /* XXX: add clearer error message */
30135             dbuf_putc(bc, OP_throw_error);
30136             dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30137             dbuf_putc(bc, JS_THROW_VAR_RO);
30138             break;
30139         default:
30140             abort();
30141         }
30142         break;
30143     case OP_scope_put_private_field:
30144         switch(var_kind) {
30145         case JS_VAR_PRIVATE_FIELD:
30146             get_loc_or_ref(bc, is_ref, idx);
30147             dbuf_putc(bc, OP_put_private_field);
30148             break;
30149         case JS_VAR_PRIVATE_METHOD:
30150         case JS_VAR_PRIVATE_GETTER:
30151             /* XXX: add clearer error message */
30152             dbuf_putc(bc, OP_throw_error);
30153             dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30154             dbuf_putc(bc, JS_THROW_VAR_RO);
30155             break;
30156         case JS_VAR_PRIVATE_SETTER:
30157         case JS_VAR_PRIVATE_GETTER_SETTER:
30158             {
30159                 JSAtom setter_name = get_private_setter_name(ctx, var_name);
30160                 if (setter_name == JS_ATOM_NULL)
30161                     return -1;
30162                 idx = resolve_scope_private_field1(ctx, &is_ref,
30163                                                    &var_kind, s,
30164                                                    setter_name, scope_level);
30165                 JS_FreeAtom(ctx, setter_name);
30166                 if (idx < 0)
30167                     return -1;
30168                 assert(var_kind == JS_VAR_PRIVATE_SETTER);
30169                 get_loc_or_ref(bc, is_ref, idx);
30170                 dbuf_putc(bc, OP_swap);
30171                 /* obj func value */
30172                 dbuf_putc(bc, OP_rot3r);
30173                 /* value obj func */
30174                 dbuf_putc(bc, OP_check_brand);
30175                 dbuf_putc(bc, OP_rot3l);
30176                 /* obj func value */
30177                 dbuf_putc(bc, OP_call_method);
30178                 dbuf_put_u16(bc, 1);
30179             }
30180             break;
30181         default:
30182             abort();
30183         }
30184         break;
30185     default:
30186         abort();
30187     }
30188     return 0;
30189 }
30190 
mark_eval_captured_variables(JSContext * ctx,JSFunctionDef * s,int scope_level)30191 static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s,
30192                                          int scope_level)
30193 {
30194     int idx;
30195     JSVarDef *vd;
30196 
30197     for (idx = s->scopes[scope_level].first; idx >= 0;) {
30198         vd = &s->vars[idx];
30199         vd->is_captured = 1;
30200         idx = vd->scope_next;
30201     }
30202 }
30203 
30204 /* XXX: should handle the argument scope generically */
is_var_in_arg_scope(const JSVarDef * vd)30205 static BOOL is_var_in_arg_scope(const JSVarDef *vd)
30206 {
30207     return (vd->var_name == JS_ATOM_home_object ||
30208             vd->var_name == JS_ATOM_this_active_func ||
30209             vd->var_name == JS_ATOM_new_target ||
30210             vd->var_name == JS_ATOM_this ||
30211             vd->var_name == JS_ATOM__arg_var_ ||
30212             vd->var_kind == JS_VAR_FUNCTION_NAME);
30213 }
30214 
add_eval_variables(JSContext * ctx,JSFunctionDef * s)30215 static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
30216 {
30217     JSFunctionDef *fd;
30218     JSVarDef *vd;
30219     int i, scope_level, scope_idx;
30220     BOOL has_arguments_binding, has_this_binding, is_arg_scope;
30221 
30222     /* in non strict mode, variables are created in the caller's
30223        environment object */
30224     if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) {
30225         s->var_object_idx = add_var(ctx, s, JS_ATOM__var_);
30226         if (s->has_parameter_expressions) {
30227             /* an additional variable object is needed for the
30228                argument scope */
30229             s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_);
30230         }
30231     }
30232 
30233     /* eval can potentially use 'arguments' so we must define it */
30234     has_this_binding = s->has_this_binding;
30235     if (has_this_binding) {
30236         if (s->this_var_idx < 0)
30237             s->this_var_idx = add_var_this(ctx, s);
30238         if (s->new_target_var_idx < 0)
30239             s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target);
30240         if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0)
30241             s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func);
30242         if (s->has_home_object && s->home_object_var_idx < 0)
30243             s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object);
30244     }
30245     has_arguments_binding = s->has_arguments_binding;
30246     if (has_arguments_binding) {
30247         add_arguments_var(ctx, s);
30248         /* also add an arguments binding in the argument scope to
30249            raise an error if a direct eval in the argument scope tries
30250            to redefine it */
30251         if (s->has_parameter_expressions && !(s->js_mode & JS_MODE_STRICT))
30252             add_arguments_arg(ctx, s);
30253     }
30254     if (s->is_func_expr && s->func_name != JS_ATOM_NULL)
30255         add_func_var(ctx, s, s->func_name);
30256 
30257     /* eval can use all the variables of the enclosing functions, so
30258        they must be all put in the closure. The closure variables are
30259        ordered by scope. It works only because no closure are created
30260        before. */
30261     assert(s->is_eval || s->closure_var_count == 0);
30262 
30263     /* XXX: inefficient, but eval performance is less critical */
30264     fd = s;
30265     for(;;) {
30266         scope_level = fd->parent_scope_level;
30267         fd = fd->parent;
30268         if (!fd)
30269             break;
30270         /* add 'this' if it was not previously added */
30271         if (!has_this_binding && fd->has_this_binding) {
30272             if (fd->this_var_idx < 0)
30273                 fd->this_var_idx = add_var_this(ctx, fd);
30274             if (fd->new_target_var_idx < 0)
30275                 fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target);
30276             if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0)
30277                 fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func);
30278             if (fd->has_home_object && fd->home_object_var_idx < 0)
30279                 fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object);
30280             has_this_binding = TRUE;
30281         }
30282         /* add 'arguments' if it was not previously added */
30283         if (!has_arguments_binding && fd->has_arguments_binding) {
30284             add_arguments_var(ctx, fd);
30285             has_arguments_binding = TRUE;
30286         }
30287         /* add function name */
30288         if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL)
30289             add_func_var(ctx, fd, fd->func_name);
30290 
30291         /* add lexical variables */
30292         scope_idx = fd->scopes[scope_level].first;
30293         while (scope_idx >= 0) {
30294             vd = &fd->vars[scope_idx];
30295             vd->is_captured = 1;
30296             get_closure_var(ctx, s, fd, FALSE, scope_idx,
30297                             vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind);
30298             scope_idx = vd->scope_next;
30299         }
30300         is_arg_scope = (scope_idx == ARG_SCOPE_END);
30301         if (!is_arg_scope) {
30302             /* add unscoped variables */
30303             for(i = 0; i < fd->arg_count; i++) {
30304                 vd = &fd->args[i];
30305                 if (vd->var_name != JS_ATOM_NULL) {
30306                     get_closure_var(ctx, s, fd,
30307                                     TRUE, i, vd->var_name, FALSE, FALSE,
30308                                     JS_VAR_NORMAL);
30309                 }
30310             }
30311             for(i = 0; i < fd->var_count; i++) {
30312                 vd = &fd->vars[i];
30313                 /* do not close top level last result */
30314                 if (vd->scope_level == 0 &&
30315                     vd->var_name != JS_ATOM__ret_ &&
30316                     vd->var_name != JS_ATOM_NULL) {
30317                     get_closure_var(ctx, s, fd,
30318                                     FALSE, i, vd->var_name, FALSE, FALSE,
30319                                     JS_VAR_NORMAL);
30320                 }
30321             }
30322         } else {
30323             for(i = 0; i < fd->var_count; i++) {
30324                 vd = &fd->vars[i];
30325                 /* do not close top level last result */
30326                 if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {
30327                     get_closure_var(ctx, s, fd,
30328                                     FALSE, i, vd->var_name, FALSE, FALSE,
30329                                     JS_VAR_NORMAL);
30330                 }
30331             }
30332         }
30333         if (fd->is_eval) {
30334             int idx;
30335             /* add direct eval variables (we are necessarily at the
30336                top level) */
30337             for (idx = 0; idx < fd->closure_var_count; idx++) {
30338                 JSClosureVar *cv = &fd->closure_var[idx];
30339                 get_closure_var2(ctx, s, fd,
30340                                  FALSE, cv->is_arg,
30341                                  idx, cv->var_name, cv->is_const,
30342                                  cv->is_lexical, cv->var_kind);
30343             }
30344         }
30345     }
30346 }
30347 
set_closure_from_var(JSContext * ctx,JSClosureVar * cv,JSVarDef * vd,int var_idx)30348 static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv,
30349                                  JSVarDef *vd, int var_idx)
30350 {
30351     cv->is_local = TRUE;
30352     cv->is_arg = FALSE;
30353     cv->is_const = vd->is_const;
30354     cv->is_lexical = vd->is_lexical;
30355     cv->var_kind = vd->var_kind;
30356     cv->var_idx = var_idx;
30357     cv->var_name = JS_DupAtom(ctx, vd->var_name);
30358 }
30359 
30360 /* for direct eval compilation: add references to the variables of the
30361    calling function */
add_closure_variables(JSContext * ctx,JSFunctionDef * s,JSFunctionBytecode * b,int scope_idx)30362 static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
30363                                              JSFunctionBytecode *b, int scope_idx)
30364 {
30365     int i, count;
30366     JSVarDef *vd;
30367     BOOL is_arg_scope;
30368 
30369     count = b->arg_count + b->var_count + b->closure_var_count;
30370     s->closure_var = NULL;
30371     s->closure_var_count = 0;
30372     s->closure_var_size = count;
30373     if (count == 0)
30374         return 0;
30375     s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count);
30376     if (!s->closure_var)
30377         return -1;
30378     /* Add lexical variables in scope at the point of evaluation */
30379     for (i = scope_idx; i >= 0;) {
30380         vd = &b->vardefs[b->arg_count + i];
30381         if (vd->scope_level > 0) {
30382             JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
30383             set_closure_from_var(ctx, cv, vd, i);
30384         }
30385         i = vd->scope_next;
30386     }
30387     is_arg_scope = (i == ARG_SCOPE_END);
30388     if (!is_arg_scope) {
30389         /* Add argument variables */
30390         for(i = 0; i < b->arg_count; i++) {
30391             JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
30392             vd = &b->vardefs[i];
30393             cv->is_local = TRUE;
30394             cv->is_arg = TRUE;
30395             cv->is_const = FALSE;
30396             cv->is_lexical = FALSE;
30397             cv->var_kind = JS_VAR_NORMAL;
30398             cv->var_idx = i;
30399             cv->var_name = JS_DupAtom(ctx, vd->var_name);
30400         }
30401         /* Add local non lexical variables */
30402         for(i = 0; i < b->var_count; i++) {
30403             vd = &b->vardefs[b->arg_count + i];
30404             if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) {
30405                 JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
30406                 set_closure_from_var(ctx, cv, vd, i);
30407             }
30408         }
30409     } else {
30410         /* only add pseudo variables */
30411         for(i = 0; i < b->var_count; i++) {
30412             vd = &b->vardefs[b->arg_count + i];
30413             if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {
30414                 JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
30415                 set_closure_from_var(ctx, cv, vd, i);
30416             }
30417         }
30418     }
30419     for(i = 0; i < b->closure_var_count; i++) {
30420         JSClosureVar *cv0 = &b->closure_var[i];
30421         JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
30422         cv->is_local = FALSE;
30423         cv->is_arg = cv0->is_arg;
30424         cv->is_const = cv0->is_const;
30425         cv->is_lexical = cv0->is_lexical;
30426         cv->var_kind = cv0->var_kind;
30427         cv->var_idx = i;
30428         cv->var_name = JS_DupAtom(ctx, cv0->var_name);
30429     }
30430     return 0;
30431 }
30432 
30433 typedef struct CodeContext {
30434     const uint8_t *bc_buf; /* code buffer */
30435     int bc_len;   /* length of the code buffer */
30436     int pos;      /* position past the matched code pattern */
30437     int line_num; /* last visited OP_line_num parameter or -1 */
30438     int op;
30439     int idx;
30440     int label;
30441     int val;
30442     JSAtom atom;
30443 } CodeContext;
30444 
30445 #define M2(op1, op2)            ((op1) | ((op2) << 8))
30446 #define M3(op1, op2, op3)       ((op1) | ((op2) << 8) | ((op3) << 16))
30447 #define M4(op1, op2, op3, op4)  ((op1) | ((op2) << 8) | ((op3) << 16) | ((op4) << 24))
30448 
code_match(CodeContext * s,int pos,...)30449 static BOOL code_match(CodeContext *s, int pos, ...)
30450 {
30451     const uint8_t *tab = s->bc_buf;
30452     int op, len, op1, line_num, pos_next;
30453     va_list ap;
30454     BOOL ret = FALSE;
30455 
30456     line_num = -1;
30457     va_start(ap, pos);
30458 
30459     for(;;) {
30460         op1 = va_arg(ap, int);
30461         if (op1 == -1) {
30462             s->pos = pos;
30463             s->line_num = line_num;
30464             ret = TRUE;
30465             break;
30466         }
30467         for (;;) {
30468             if (pos >= s->bc_len)
30469                 goto done;
30470             op = tab[pos];
30471             len = opcode_info[op].size;
30472             pos_next = pos + len;
30473             if (pos_next > s->bc_len)
30474                 goto done;
30475             if (op == OP_line_num) {
30476                 line_num = get_u32(tab + pos + 1);
30477                 pos = pos_next;
30478             } else {
30479                 break;
30480             }
30481         }
30482         if (op != op1) {
30483             if (op1 == (uint8_t)op1 || !op)
30484                 break;
30485             if (op != (uint8_t)op1
30486             &&  op != (uint8_t)(op1 >> 8)
30487             &&  op != (uint8_t)(op1 >> 16)
30488             &&  op != (uint8_t)(op1 >> 24)) {
30489                 break;
30490             }
30491             s->op = op;
30492         }
30493 
30494         pos++;
30495         switch(opcode_info[op].fmt) {
30496         case OP_FMT_loc8:
30497         case OP_FMT_u8:
30498             {
30499                 int idx = tab[pos];
30500                 int arg = va_arg(ap, int);
30501                 if (arg == -1) {
30502                     s->idx = idx;
30503                 } else {
30504                     if (arg != idx)
30505                         goto done;
30506                 }
30507                 break;
30508             }
30509         case OP_FMT_u16:
30510         case OP_FMT_npop:
30511         case OP_FMT_loc:
30512         case OP_FMT_arg:
30513         case OP_FMT_var_ref:
30514             {
30515                 int idx = get_u16(tab + pos);
30516                 int arg = va_arg(ap, int);
30517                 if (arg == -1) {
30518                     s->idx = idx;
30519                 } else {
30520                     if (arg != idx)
30521                         goto done;
30522                 }
30523                 break;
30524             }
30525         case OP_FMT_i32:
30526         case OP_FMT_u32:
30527         case OP_FMT_label:
30528         case OP_FMT_const:
30529             {
30530                 s->label = get_u32(tab + pos);
30531                 break;
30532             }
30533         case OP_FMT_label_u16:
30534             {
30535                 s->label = get_u32(tab + pos);
30536                 s->val = get_u16(tab + pos + 4);
30537                 break;
30538             }
30539         case OP_FMT_atom:
30540             {
30541                 s->atom = get_u32(tab + pos);
30542                 break;
30543             }
30544         case OP_FMT_atom_u8:
30545             {
30546                 s->atom = get_u32(tab + pos);
30547                 s->val = get_u8(tab + pos + 4);
30548                 break;
30549             }
30550         case OP_FMT_atom_u16:
30551             {
30552                 s->atom = get_u32(tab + pos);
30553                 s->val = get_u16(tab + pos + 4);
30554                 break;
30555             }
30556         case OP_FMT_atom_label_u8:
30557             {
30558                 s->atom = get_u32(tab + pos);
30559                 s->label = get_u32(tab + pos + 4);
30560                 s->val = get_u8(tab + pos + 8);
30561                 break;
30562             }
30563         default:
30564             break;
30565         }
30566         pos = pos_next;
30567     }
30568  done:
30569     va_end(ap);
30570     return ret;
30571 }
30572 
instantiate_hoisted_definitions(JSContext * ctx,JSFunctionDef * s,DynBuf * bc)30573 static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc)
30574 {
30575     int i, idx, label_next = -1;
30576 
30577     /* add the hoisted functions in arguments and local variables */
30578     for(i = 0; i < s->arg_count; i++) {
30579         JSVarDef *vd = &s->args[i];
30580         if (vd->func_pool_idx >= 0) {
30581             dbuf_putc(bc, OP_fclosure);
30582             dbuf_put_u32(bc, vd->func_pool_idx);
30583             dbuf_putc(bc, OP_put_arg);
30584             dbuf_put_u16(bc, i);
30585         }
30586     }
30587     for(i = 0; i < s->var_count; i++) {
30588         JSVarDef *vd = &s->vars[i];
30589         if (vd->scope_level == 0 && vd->func_pool_idx >= 0) {
30590             dbuf_putc(bc, OP_fclosure);
30591             dbuf_put_u32(bc, vd->func_pool_idx);
30592             dbuf_putc(bc, OP_put_loc);
30593             dbuf_put_u16(bc, i);
30594         }
30595     }
30596 
30597     /* the module global variables must be initialized before
30598        evaluating the module so that the exported functions are
30599        visible if there are cyclic module references */
30600     if (s->module) {
30601         label_next = new_label_fd(s, -1);
30602 
30603         /* if 'this' is true, initialize the global variables and return */
30604         dbuf_putc(bc, OP_push_this);
30605         dbuf_putc(bc, OP_if_false);
30606         dbuf_put_u32(bc, label_next);
30607         update_label(s, label_next, 1);
30608         s->jump_size++;
30609     }
30610 
30611     /* add the global variables (only happens if s->is_global_var is
30612        true) */
30613     for(i = 0; i < s->global_var_count; i++) {
30614         JSGlobalVar *hf = &s->global_vars[i];
30615         int has_closure = 0;
30616         BOOL force_init = hf->force_init;
30617         /* we are in an eval, so the closure contains all the
30618            enclosing variables */
30619         /* If the outer function has a variable environment,
30620            create a property for the variable there */
30621         for(idx = 0; idx < s->closure_var_count; idx++) {
30622             JSClosureVar *cv = &s->closure_var[idx];
30623             if (cv->var_name == hf->var_name) {
30624                 has_closure = 2;
30625                 force_init = FALSE;
30626                 break;
30627             }
30628             if (cv->var_name == JS_ATOM__var_ ||
30629                 cv->var_name == JS_ATOM__arg_var_) {
30630                 dbuf_putc(bc, OP_get_var_ref);
30631                 dbuf_put_u16(bc, idx);
30632                 has_closure = 1;
30633                 force_init = TRUE;
30634                 break;
30635             }
30636         }
30637         if (!has_closure) {
30638             int flags;
30639 
30640             flags = 0;
30641             if (s->eval_type != JS_EVAL_TYPE_GLOBAL)
30642                 flags |= JS_PROP_CONFIGURABLE;
30643             if (hf->cpool_idx >= 0 && !hf->is_lexical) {
30644                 /* global function definitions need a specific handling */
30645                 dbuf_putc(bc, OP_fclosure);
30646                 dbuf_put_u32(bc, hf->cpool_idx);
30647 
30648                 dbuf_putc(bc, OP_define_func);
30649                 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
30650                 dbuf_putc(bc, flags);
30651 
30652                 goto done_global_var;
30653             } else {
30654                 if (hf->is_lexical) {
30655                     flags |= DEFINE_GLOBAL_LEX_VAR;
30656                     if (!hf->is_const)
30657                         flags |= JS_PROP_WRITABLE;
30658                 }
30659                 dbuf_putc(bc, OP_define_var);
30660                 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
30661                 dbuf_putc(bc, flags);
30662             }
30663         }
30664         if (hf->cpool_idx >= 0 || force_init) {
30665             if (hf->cpool_idx >= 0) {
30666                 dbuf_putc(bc, OP_fclosure);
30667                 dbuf_put_u32(bc, hf->cpool_idx);
30668                 if (hf->var_name == JS_ATOM__default_) {
30669                     /* set default export function name */
30670                     dbuf_putc(bc, OP_set_name);
30671                     dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default));
30672                 }
30673             } else {
30674                 dbuf_putc(bc, OP_undefined);
30675             }
30676             if (has_closure == 2) {
30677                 dbuf_putc(bc, OP_put_var_ref);
30678                 dbuf_put_u16(bc, idx);
30679             } else if (has_closure == 1) {
30680                 dbuf_putc(bc, OP_define_field);
30681                 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
30682                 dbuf_putc(bc, OP_drop);
30683             } else {
30684                 /* XXX: Check if variable is writable and enumerable */
30685                 dbuf_putc(bc, OP_put_var);
30686                 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
30687             }
30688         }
30689     done_global_var:
30690         JS_FreeAtom(ctx, hf->var_name);
30691     }
30692 
30693     if (s->module) {
30694         dbuf_putc(bc, OP_return_undef);
30695 
30696         dbuf_putc(bc, OP_label);
30697         dbuf_put_u32(bc, label_next);
30698         s->label_slots[label_next].pos2 = bc->size;
30699     }
30700 
30701     js_free(ctx, s->global_vars);
30702     s->global_vars = NULL;
30703     s->global_var_count = 0;
30704     s->global_var_size = 0;
30705 }
30706 
skip_dead_code(JSFunctionDef * s,const uint8_t * bc_buf,int bc_len,int pos,int * linep)30707 static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len,
30708                           int pos, int *linep)
30709 {
30710     int op, len, label;
30711 
30712     for (; pos < bc_len; pos += len) {
30713         op = bc_buf[pos];
30714         len = opcode_info[op].size;
30715         if (op == OP_line_num) {
30716             *linep = get_u32(bc_buf + pos + 1);
30717         } else
30718         if (op == OP_label) {
30719             label = get_u32(bc_buf + pos + 1);
30720             if (update_label(s, label, 0) > 0)
30721                 break;
30722 #if 0
30723             if (s->label_slots[label].first_reloc) {
30724                 printf("line %d: unreferenced label %d:%d has relocations\n",
30725                        *linep, label, s->label_slots[label].pos2);
30726             }
30727 #endif
30728             assert(s->label_slots[label].first_reloc == NULL);
30729         } else {
30730             /* XXX: output a warning for unreachable code? */
30731             JSAtom atom;
30732             switch(opcode_info[op].fmt) {
30733             case OP_FMT_label:
30734             case OP_FMT_label_u16:
30735                 label = get_u32(bc_buf + pos + 1);
30736                 update_label(s, label, -1);
30737                 break;
30738             case OP_FMT_atom_label_u8:
30739             case OP_FMT_atom_label_u16:
30740                 label = get_u32(bc_buf + pos + 5);
30741                 update_label(s, label, -1);
30742                 /* fall thru */
30743             case OP_FMT_atom:
30744             case OP_FMT_atom_u8:
30745             case OP_FMT_atom_u16:
30746                 atom = get_u32(bc_buf + pos + 1);
30747                 JS_FreeAtom(s->ctx, atom);
30748                 break;
30749             default:
30750                 break;
30751             }
30752         }
30753     }
30754     return pos;
30755 }
30756 
get_label_pos(JSFunctionDef * s,int label)30757 static int get_label_pos(JSFunctionDef *s, int label)
30758 {
30759     int i, pos;
30760     for (i = 0; i < 20; i++) {
30761         pos = s->label_slots[label].pos;
30762         for (;;) {
30763             switch (s->byte_code.buf[pos]) {
30764             case OP_line_num:
30765             case OP_label:
30766                 pos += 5;
30767                 continue;
30768             case OP_goto:
30769                 label = get_u32(s->byte_code.buf + pos + 1);
30770                 break;
30771             default:
30772                 return pos;
30773             }
30774             break;
30775         }
30776     }
30777     return pos;
30778 }
30779 
30780 /* convert global variable accesses to local variables or closure
30781    variables when necessary */
resolve_variables(JSContext * ctx,JSFunctionDef * s)30782 static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
30783 {
30784     int pos, pos_next, bc_len, op, len, i, idx, line_num;
30785     uint8_t *bc_buf;
30786     JSAtom var_name;
30787     DynBuf bc_out;
30788     CodeContext cc;
30789     int scope;
30790 
30791     cc.bc_buf = bc_buf = s->byte_code.buf;
30792     cc.bc_len = bc_len = s->byte_code.size;
30793     js_dbuf_init(ctx, &bc_out);
30794 
30795     /* first pass for runtime checks (must be done before the
30796        variables are created) */
30797     for(i = 0; i < s->global_var_count; i++) {
30798         JSGlobalVar *hf = &s->global_vars[i];
30799         int flags;
30800 
30801         /* check if global variable (XXX: simplify) */
30802         for(idx = 0; idx < s->closure_var_count; idx++) {
30803             JSClosureVar *cv = &s->closure_var[idx];
30804             if (cv->var_name == hf->var_name) {
30805                 if (s->eval_type == JS_EVAL_TYPE_DIRECT &&
30806                     cv->is_lexical) {
30807                     /* Check if a lexical variable is
30808                        redefined as 'var'. XXX: Could abort
30809                        compilation here, but for consistency
30810                        with the other checks, we delay the
30811                        error generation. */
30812                     dbuf_putc(&bc_out, OP_throw_error);
30813                     dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
30814                     dbuf_putc(&bc_out, JS_THROW_VAR_REDECL);
30815                 }
30816                 goto next;
30817             }
30818             if (cv->var_name == JS_ATOM__var_ ||
30819                 cv->var_name == JS_ATOM__arg_var_)
30820                 goto next;
30821         }
30822 
30823         dbuf_putc(&bc_out, OP_check_define_var);
30824         dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
30825         flags = 0;
30826         if (hf->is_lexical)
30827             flags |= DEFINE_GLOBAL_LEX_VAR;
30828         if (hf->cpool_idx >= 0)
30829             flags |= DEFINE_GLOBAL_FUNC_VAR;
30830         dbuf_putc(&bc_out, flags);
30831     next: ;
30832     }
30833 
30834     line_num = 0; /* avoid warning */
30835     for (pos = 0; pos < bc_len; pos = pos_next) {
30836         op = bc_buf[pos];
30837         len = opcode_info[op].size;
30838         pos_next = pos + len;
30839         switch(op) {
30840         case OP_line_num:
30841             line_num = get_u32(bc_buf + pos + 1);
30842             s->line_number_size++;
30843             goto no_change;
30844 
30845         case OP_eval: /* convert scope index to adjusted variable index */
30846             {
30847                 int call_argc = get_u16(bc_buf + pos + 1);
30848                 scope = get_u16(bc_buf + pos + 1 + 2);
30849                 mark_eval_captured_variables(ctx, s, scope);
30850                 dbuf_putc(&bc_out, op);
30851                 dbuf_put_u16(&bc_out, call_argc);
30852                 dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);
30853             }
30854             break;
30855         case OP_apply_eval: /* convert scope index to adjusted variable index */
30856             scope = get_u16(bc_buf + pos + 1);
30857             mark_eval_captured_variables(ctx, s, scope);
30858             dbuf_putc(&bc_out, op);
30859             dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);
30860             break;
30861         case OP_scope_get_var_undef:
30862         case OP_scope_get_var:
30863         case OP_scope_put_var:
30864         case OP_scope_delete_var:
30865         case OP_scope_get_ref:
30866         case OP_scope_put_var_init:
30867             var_name = get_u32(bc_buf + pos + 1);
30868             scope = get_u16(bc_buf + pos + 5);
30869             pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
30870                                          NULL, NULL, pos_next);
30871             JS_FreeAtom(ctx, var_name);
30872             break;
30873         case OP_scope_make_ref:
30874             {
30875                 int label;
30876                 LabelSlot *ls;
30877                 var_name = get_u32(bc_buf + pos + 1);
30878                 label = get_u32(bc_buf + pos + 5);
30879                 scope = get_u16(bc_buf + pos + 9);
30880                 ls = &s->label_slots[label];
30881                 ls->ref_count--;  /* always remove label reference */
30882                 pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
30883                                              bc_buf, ls, pos_next);
30884                 JS_FreeAtom(ctx, var_name);
30885             }
30886             break;
30887         case OP_scope_get_private_field:
30888         case OP_scope_get_private_field2:
30889         case OP_scope_put_private_field:
30890             {
30891                 int ret;
30892                 var_name = get_u32(bc_buf + pos + 1);
30893                 scope = get_u16(bc_buf + pos + 5);
30894                 ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out);
30895                 if (ret < 0)
30896                     goto fail;
30897                 JS_FreeAtom(ctx, var_name);
30898             }
30899             break;
30900         case OP_gosub:
30901             s->jump_size++;
30902             if (OPTIMIZE) {
30903                 /* remove calls to empty finalizers  */
30904                 int label;
30905                 LabelSlot *ls;
30906 
30907                 label = get_u32(bc_buf + pos + 1);
30908                 assert(label >= 0 && label < s->label_count);
30909                 ls = &s->label_slots[label];
30910                 if (code_match(&cc, ls->pos, OP_ret, -1)) {
30911                     ls->ref_count--;
30912                     break;
30913                 }
30914             }
30915             goto no_change;
30916         case OP_drop:
30917             if (0) {
30918                 /* remove drops before return_undef */
30919                 /* do not perform this optimization in pass2 because
30920                    it breaks patterns recognised in resolve_labels */
30921                 int pos1 = pos_next;
30922                 int line1 = line_num;
30923                 while (code_match(&cc, pos1, OP_drop, -1)) {
30924                     if (cc.line_num >= 0) line1 = cc.line_num;
30925                     pos1 = cc.pos;
30926                 }
30927                 if (code_match(&cc, pos1, OP_return_undef, -1)) {
30928                     pos_next = pos1;
30929                     if (line1 != -1 && line1 != line_num) {
30930                         line_num = line1;
30931                         s->line_number_size++;
30932                         dbuf_putc(&bc_out, OP_line_num);
30933                         dbuf_put_u32(&bc_out, line_num);
30934                     }
30935                     break;
30936                 }
30937             }
30938             goto no_change;
30939         case OP_insert3:
30940             if (OPTIMIZE) {
30941                 /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */
30942                 if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) {
30943                     dbuf_putc(&bc_out, cc.op);
30944                     pos_next = cc.pos;
30945                     if (cc.line_num != -1 && cc.line_num != line_num) {
30946                         line_num = cc.line_num;
30947                         s->line_number_size++;
30948                         dbuf_putc(&bc_out, OP_line_num);
30949                         dbuf_put_u32(&bc_out, line_num);
30950                     }
30951                     break;
30952                 }
30953             }
30954             goto no_change;
30955 
30956         case OP_goto:
30957             s->jump_size++;
30958             /* fall thru */
30959         case OP_tail_call:
30960         case OP_tail_call_method:
30961         case OP_return:
30962         case OP_return_undef:
30963         case OP_throw:
30964         case OP_throw_error:
30965         case OP_ret:
30966             if (OPTIMIZE) {
30967                 /* remove dead code */
30968                 int line = -1;
30969                 dbuf_put(&bc_out, bc_buf + pos, len);
30970                 pos = skip_dead_code(s, bc_buf, bc_len, pos + len, &line);
30971                 pos_next = pos;
30972                 if (pos < bc_len && line >= 0 && line_num != line) {
30973                     line_num = line;
30974                     s->line_number_size++;
30975                     dbuf_putc(&bc_out, OP_line_num);
30976                     dbuf_put_u32(&bc_out, line_num);
30977                 }
30978                 break;
30979             }
30980             goto no_change;
30981 
30982         case OP_label:
30983             {
30984                 int label;
30985                 LabelSlot *ls;
30986 
30987                 label = get_u32(bc_buf + pos + 1);
30988                 assert(label >= 0 && label < s->label_count);
30989                 ls = &s->label_slots[label];
30990                 ls->pos2 = bc_out.size + opcode_info[op].size;
30991             }
30992             goto no_change;
30993 
30994         case OP_enter_scope:
30995             {
30996                 int scope_idx, scope = get_u16(bc_buf + pos + 1);
30997 
30998                 if (scope == s->body_scope) {
30999                     instantiate_hoisted_definitions(ctx, s, &bc_out);
31000                 }
31001 
31002                 for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
31003                     JSVarDef *vd = &s->vars[scope_idx];
31004                     if (vd->scope_level == scope) {
31005                         if (scope_idx != s->arguments_arg_idx) {
31006                             if (vd->var_kind == JS_VAR_FUNCTION_DECL ||
31007                                 vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) {
31008                                 /* Initialize lexical variable upon entering scope */
31009                                 dbuf_putc(&bc_out, OP_fclosure);
31010                                 dbuf_put_u32(&bc_out, vd->func_pool_idx);
31011                                 dbuf_putc(&bc_out, OP_put_loc);
31012                                 dbuf_put_u16(&bc_out, scope_idx);
31013                             } else {
31014                                 /* XXX: should check if variable can be used
31015                                    before initialization */
31016                                 dbuf_putc(&bc_out, OP_set_loc_uninitialized);
31017                                 dbuf_put_u16(&bc_out, scope_idx);
31018                             }
31019                         }
31020                         scope_idx = vd->scope_next;
31021                     } else {
31022                         break;
31023                     }
31024                 }
31025             }
31026             break;
31027 
31028         case OP_leave_scope:
31029             {
31030                 int scope_idx, scope = get_u16(bc_buf + pos + 1);
31031 
31032                 for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
31033                     JSVarDef *vd = &s->vars[scope_idx];
31034                     if (vd->scope_level == scope) {
31035                         if (vd->is_captured) {
31036                             dbuf_putc(&bc_out, OP_close_loc);
31037                             dbuf_put_u16(&bc_out, scope_idx);
31038                         }
31039                         scope_idx = vd->scope_next;
31040                     } else {
31041                         break;
31042                     }
31043                 }
31044             }
31045             break;
31046 
31047         case OP_set_name:
31048             {
31049                 /* remove dummy set_name opcodes */
31050                 JSAtom name = get_u32(bc_buf + pos + 1);
31051                 if (name == JS_ATOM_NULL)
31052                     break;
31053             }
31054             goto no_change;
31055 
31056         case OP_if_false:
31057         case OP_if_true:
31058         case OP_catch:
31059             s->jump_size++;
31060             goto no_change;
31061 
31062         case OP_dup:
31063             if (OPTIMIZE) {
31064                 /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */
31065                 /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */
31066                 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) {
31067                     int lab0, lab1, op1, pos1, line1, pos2;
31068                     lab0 = lab1 = cc.label;
31069                     assert(lab1 >= 0 && lab1 < s->label_count);
31070                     op1 = cc.op;
31071                     pos1 = cc.pos;
31072                     line1 = cc.line_num;
31073                     while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) {
31074                         lab1 = cc.label;
31075                     }
31076                     if (code_match(&cc, pos2, op1, -1)) {
31077                         s->jump_size++;
31078                         update_label(s, lab0, -1);
31079                         update_label(s, cc.label, +1);
31080                         dbuf_putc(&bc_out, op1);
31081                         dbuf_put_u32(&bc_out, cc.label);
31082                         pos_next = pos1;
31083                         if (line1 != -1 && line1 != line_num) {
31084                             line_num = line1;
31085                             s->line_number_size++;
31086                             dbuf_putc(&bc_out, OP_line_num);
31087                             dbuf_put_u32(&bc_out, line_num);
31088                         }
31089                         break;
31090                     }
31091                 }
31092             }
31093             goto no_change;
31094 
31095         case OP_nop:
31096             /* remove erased code */
31097             break;
31098         case OP_set_class_name:
31099             /* only used during parsing */
31100             break;
31101 
31102         default:
31103         no_change:
31104             dbuf_put(&bc_out, bc_buf + pos, len);
31105             break;
31106         }
31107     }
31108 
31109     /* set the new byte code */
31110     dbuf_free(&s->byte_code);
31111     s->byte_code = bc_out;
31112     if (dbuf_error(&s->byte_code)) {
31113         JS_ThrowOutOfMemory(ctx);
31114         return -1;
31115     }
31116     return 0;
31117  fail:
31118     /* continue the copy to keep the atom refcounts consistent */
31119     /* XXX: find a better solution ? */
31120     for (; pos < bc_len; pos = pos_next) {
31121         op = bc_buf[pos];
31122         len = opcode_info[op].size;
31123         pos_next = pos + len;
31124         dbuf_put(&bc_out, bc_buf + pos, len);
31125     }
31126     dbuf_free(&s->byte_code);
31127     s->byte_code = bc_out;
31128     return -1;
31129 }
31130 
31131 /* the pc2line table gives a line number for each PC value */
add_pc2line_info(JSFunctionDef * s,uint32_t pc,int line_num)31132 static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, int line_num)
31133 {
31134     if (s->line_number_slots != NULL
31135     &&  s->line_number_count < s->line_number_size
31136     &&  pc >= s->line_number_last_pc
31137     &&  line_num != s->line_number_last) {
31138         s->line_number_slots[s->line_number_count].pc = pc;
31139         s->line_number_slots[s->line_number_count].line_num = line_num;
31140         s->line_number_count++;
31141         s->line_number_last_pc = pc;
31142         s->line_number_last = line_num;
31143     }
31144 }
31145 
compute_pc2line_info(JSFunctionDef * s)31146 static void compute_pc2line_info(JSFunctionDef *s)
31147 {
31148     if (!(s->js_mode & JS_MODE_STRIP) && s->line_number_slots) {
31149         int last_line_num = s->line_num;
31150         uint32_t last_pc = 0;
31151         int i;
31152 
31153         js_dbuf_init(s->ctx, &s->pc2line);
31154         for (i = 0; i < s->line_number_count; i++) {
31155             uint32_t pc = s->line_number_slots[i].pc;
31156             int line_num = s->line_number_slots[i].line_num;
31157             int diff_pc, diff_line;
31158 
31159             if (line_num < 0)
31160                 continue;
31161 
31162             diff_pc = pc - last_pc;
31163             diff_line = line_num - last_line_num;
31164             if (diff_line == 0 || diff_pc < 0)
31165                 continue;
31166 
31167             if (diff_line >= PC2LINE_BASE &&
31168                 diff_line < PC2LINE_BASE + PC2LINE_RANGE &&
31169                 diff_pc <= PC2LINE_DIFF_PC_MAX) {
31170                 dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) +
31171                           diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST);
31172             } else {
31173                 /* longer encoding */
31174                 dbuf_putc(&s->pc2line, 0);
31175                 dbuf_put_leb128(&s->pc2line, diff_pc);
31176                 dbuf_put_sleb128(&s->pc2line, diff_line);
31177             }
31178             last_pc = pc;
31179             last_line_num = line_num;
31180         }
31181     }
31182 }
31183 
add_reloc(JSContext * ctx,LabelSlot * ls,uint32_t addr,int size)31184 static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size)
31185 {
31186     RelocEntry *re;
31187     re = js_malloc(ctx, sizeof(*re));
31188     if (!re)
31189         return NULL;
31190     re->addr = addr;
31191     re->size = size;
31192     re->next = ls->first_reloc;
31193     ls->first_reloc = re;
31194     return re;
31195 }
31196 
code_has_label(CodeContext * s,int pos,int label)31197 static BOOL code_has_label(CodeContext *s, int pos, int label)
31198 {
31199     while (pos < s->bc_len) {
31200         int op = s->bc_buf[pos];
31201         if (op == OP_line_num) {
31202             pos += 5;
31203             continue;
31204         }
31205         if (op == OP_label) {
31206             int lab = get_u32(s->bc_buf + pos + 1);
31207             if (lab == label)
31208                 return TRUE;
31209             pos += 5;
31210             continue;
31211         }
31212         if (op == OP_goto) {
31213             int lab = get_u32(s->bc_buf + pos + 1);
31214             if (lab == label)
31215                 return TRUE;
31216         }
31217         break;
31218     }
31219     return FALSE;
31220 }
31221 
31222 /* return the target label, following the OP_goto jumps
31223    the first opcode at destination is stored in *pop
31224  */
find_jump_target(JSFunctionDef * s,int label,int * pop,int * pline)31225 static int find_jump_target(JSFunctionDef *s, int label, int *pop, int *pline)
31226 {
31227     int i, pos, op;
31228 
31229     update_label(s, label, -1);
31230     for (i = 0; i < 10; i++) {
31231         assert(label >= 0 && label < s->label_count);
31232         pos = s->label_slots[label].pos2;
31233         for (;;) {
31234             switch(op = s->byte_code.buf[pos]) {
31235             case OP_line_num:
31236                 if (pline)
31237                     *pline = get_u32(s->byte_code.buf + pos + 1);
31238                 /* fall thru */
31239             case OP_label:
31240                 pos += opcode_info[op].size;
31241                 continue;
31242             case OP_goto:
31243                 label = get_u32(s->byte_code.buf + pos + 1);
31244                 break;
31245             case OP_drop:
31246                 /* ignore drop opcodes if followed by OP_return_undef */
31247                 while (s->byte_code.buf[++pos] == OP_drop)
31248                     continue;
31249                 if (s->byte_code.buf[pos] == OP_return_undef)
31250                     op = OP_return_undef;
31251                 /* fall thru */
31252             default:
31253                 goto done;
31254             }
31255             break;
31256         }
31257     }
31258     /* cycle detected, could issue a warning */
31259  done:
31260     *pop = op;
31261     update_label(s, label, +1);
31262     return label;
31263 }
31264 
push_short_int(DynBuf * bc_out,int val)31265 static void push_short_int(DynBuf *bc_out, int val)
31266 {
31267 #if SHORT_OPCODES
31268     if (val >= -1 && val <= 7) {
31269         dbuf_putc(bc_out, OP_push_0 + val);
31270         return;
31271     }
31272     if (val == (int8_t)val) {
31273         dbuf_putc(bc_out, OP_push_i8);
31274         dbuf_putc(bc_out, val);
31275         return;
31276     }
31277     if (val == (int16_t)val) {
31278         dbuf_putc(bc_out, OP_push_i16);
31279         dbuf_put_u16(bc_out, val);
31280         return;
31281     }
31282 #endif
31283     dbuf_putc(bc_out, OP_push_i32);
31284     dbuf_put_u32(bc_out, val);
31285 }
31286 
put_short_code(DynBuf * bc_out,int op,int idx)31287 static void put_short_code(DynBuf *bc_out, int op, int idx)
31288 {
31289 #if SHORT_OPCODES
31290     if (idx < 4) {
31291         switch (op) {
31292         case OP_get_loc:
31293             dbuf_putc(bc_out, OP_get_loc0 + idx);
31294             return;
31295         case OP_put_loc:
31296             dbuf_putc(bc_out, OP_put_loc0 + idx);
31297             return;
31298         case OP_set_loc:
31299             dbuf_putc(bc_out, OP_set_loc0 + idx);
31300             return;
31301         case OP_get_arg:
31302             dbuf_putc(bc_out, OP_get_arg0 + idx);
31303             return;
31304         case OP_put_arg:
31305             dbuf_putc(bc_out, OP_put_arg0 + idx);
31306             return;
31307         case OP_set_arg:
31308             dbuf_putc(bc_out, OP_set_arg0 + idx);
31309             return;
31310         case OP_get_var_ref:
31311             dbuf_putc(bc_out, OP_get_var_ref0 + idx);
31312             return;
31313         case OP_put_var_ref:
31314             dbuf_putc(bc_out, OP_put_var_ref0 + idx);
31315             return;
31316         case OP_set_var_ref:
31317             dbuf_putc(bc_out, OP_set_var_ref0 + idx);
31318             return;
31319         case OP_call:
31320             dbuf_putc(bc_out, OP_call0 + idx);
31321             return;
31322         }
31323     }
31324     if (idx < 256) {
31325         switch (op) {
31326         case OP_get_loc:
31327             dbuf_putc(bc_out, OP_get_loc8);
31328             dbuf_putc(bc_out, idx);
31329             return;
31330         case OP_put_loc:
31331             dbuf_putc(bc_out, OP_put_loc8);
31332             dbuf_putc(bc_out, idx);
31333             return;
31334         case OP_set_loc:
31335             dbuf_putc(bc_out, OP_set_loc8);
31336             dbuf_putc(bc_out, idx);
31337             return;
31338         }
31339     }
31340 #endif
31341     dbuf_putc(bc_out, op);
31342     dbuf_put_u16(bc_out, idx);
31343 }
31344 
31345 /* peephole optimizations and resolve goto/labels */
resolve_labels(JSContext * ctx,JSFunctionDef * s)31346 static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
31347 {
31348     int pos, pos_next, bc_len, op, op1, len, i, line_num;
31349     const uint8_t *bc_buf;
31350     DynBuf bc_out;
31351     LabelSlot *label_slots, *ls;
31352     RelocEntry *re, *re_next;
31353     CodeContext cc;
31354     int label;
31355 #if SHORT_OPCODES
31356     JumpSlot *jp;
31357 #endif
31358 
31359     label_slots = s->label_slots;
31360 
31361     line_num = s->line_num;
31362 
31363     cc.bc_buf = bc_buf = s->byte_code.buf;
31364     cc.bc_len = bc_len = s->byte_code.size;
31365     js_dbuf_init(ctx, &bc_out);
31366 
31367 #if SHORT_OPCODES
31368     if (s->jump_size) {
31369         s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size);
31370         if (s->jump_slots == NULL)
31371             return -1;
31372     }
31373 #endif
31374     /* XXX: Should skip this phase if not generating SHORT_OPCODES */
31375     if (s->line_number_size && !(s->js_mode & JS_MODE_STRIP)) {
31376         s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size);
31377         if (s->line_number_slots == NULL)
31378             return -1;
31379         s->line_number_last = s->line_num;
31380         s->line_number_last_pc = 0;
31381     }
31382 
31383     /* initialize the 'home_object' variable if needed */
31384     if (s->home_object_var_idx >= 0) {
31385         dbuf_putc(&bc_out, OP_special_object);
31386         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT);
31387         put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx);
31388     }
31389     /* initialize the 'this.active_func' variable if needed */
31390     if (s->this_active_func_var_idx >= 0) {
31391         dbuf_putc(&bc_out, OP_special_object);
31392         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
31393         put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx);
31394     }
31395     /* initialize the 'new.target' variable if needed */
31396     if (s->new_target_var_idx >= 0) {
31397         dbuf_putc(&bc_out, OP_special_object);
31398         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET);
31399         put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx);
31400     }
31401     /* initialize the 'this' variable if needed. In a derived class
31402        constructor, this is initially uninitialized. */
31403     if (s->this_var_idx >= 0) {
31404         if (s->is_derived_class_constructor) {
31405             dbuf_putc(&bc_out, OP_set_loc_uninitialized);
31406             dbuf_put_u16(&bc_out, s->this_var_idx);
31407         } else {
31408             dbuf_putc(&bc_out, OP_push_this);
31409             put_short_code(&bc_out, OP_put_loc, s->this_var_idx);
31410         }
31411     }
31412     /* initialize the 'arguments' variable if needed */
31413     if (s->arguments_var_idx >= 0) {
31414         if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) {
31415             dbuf_putc(&bc_out, OP_special_object);
31416             dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS);
31417         } else {
31418             dbuf_putc(&bc_out, OP_special_object);
31419             dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS);
31420         }
31421         if (s->arguments_arg_idx >= 0)
31422             put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx);
31423         put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx);
31424     }
31425     /* initialize a reference to the current function if needed */
31426     if (s->func_var_idx >= 0) {
31427         dbuf_putc(&bc_out, OP_special_object);
31428         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
31429         put_short_code(&bc_out, OP_put_loc, s->func_var_idx);
31430     }
31431     /* initialize the variable environment object if needed */
31432     if (s->var_object_idx >= 0) {
31433         dbuf_putc(&bc_out, OP_special_object);
31434         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
31435         put_short_code(&bc_out, OP_put_loc, s->var_object_idx);
31436     }
31437     if (s->arg_var_object_idx >= 0) {
31438         dbuf_putc(&bc_out, OP_special_object);
31439         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
31440         put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx);
31441     }
31442 
31443     for (pos = 0; pos < bc_len; pos = pos_next) {
31444         int val;
31445         op = bc_buf[pos];
31446         len = opcode_info[op].size;
31447         pos_next = pos + len;
31448         switch(op) {
31449         case OP_line_num:
31450             /* line number info (for debug). We put it in a separate
31451                compressed table to reduce memory usage and get better
31452                performance */
31453             line_num = get_u32(bc_buf + pos + 1);
31454             break;
31455 
31456         case OP_label:
31457             {
31458                 label = get_u32(bc_buf + pos + 1);
31459                 assert(label >= 0 && label < s->label_count);
31460                 ls = &label_slots[label];
31461                 assert(ls->addr == -1);
31462                 ls->addr = bc_out.size;
31463                 /* resolve the relocation entries */
31464                 for(re = ls->first_reloc; re != NULL; re = re_next) {
31465                     int diff = ls->addr - re->addr;
31466                     re_next = re->next;
31467                     switch (re->size) {
31468                     case 4:
31469                         put_u32(bc_out.buf + re->addr, diff);
31470                         break;
31471                     case 2:
31472                         assert(diff == (int16_t)diff);
31473                         put_u16(bc_out.buf + re->addr, diff);
31474                         break;
31475                     case 1:
31476                         assert(diff == (int8_t)diff);
31477                         put_u8(bc_out.buf + re->addr, diff);
31478                         break;
31479                     }
31480                     js_free(ctx, re);
31481                 }
31482                 ls->first_reloc = NULL;
31483             }
31484             break;
31485 
31486         case OP_call:
31487         case OP_call_method:
31488             {
31489                 /* detect and transform tail calls */
31490                 int argc;
31491                 argc = get_u16(bc_buf + pos + 1);
31492                 if (code_match(&cc, pos_next, OP_return, -1)) {
31493                     if (cc.line_num >= 0) line_num = cc.line_num;
31494                     add_pc2line_info(s, bc_out.size, line_num);
31495                     put_short_code(&bc_out, op + 1, argc);
31496                     pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num);
31497                     break;
31498                 }
31499                 add_pc2line_info(s, bc_out.size, line_num);
31500                 put_short_code(&bc_out, op, argc);
31501                 break;
31502             }
31503             goto no_change;
31504 
31505         case OP_return:
31506         case OP_return_undef:
31507         case OP_return_async:
31508         case OP_throw:
31509         case OP_throw_error:
31510             pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
31511             goto no_change;
31512 
31513         case OP_goto:
31514             label = get_u32(bc_buf + pos + 1);
31515         has_goto:
31516             if (OPTIMIZE) {
31517                 int line1 = -1;
31518                 /* Use custom matcher because multiple labels can follow */
31519                 label = find_jump_target(s, label, &op1, &line1);
31520                 if (code_has_label(&cc, pos_next, label)) {
31521                     /* jump to next instruction: remove jump */
31522                     update_label(s, label, -1);
31523                     break;
31524                 }
31525                 if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) {
31526                     /* jump to return/throw: remove jump, append return/throw */
31527                     /* updating the line number obfuscates assembly listing */
31528                     //if (line1 >= 0) line_num = line1;
31529                     update_label(s, label, -1);
31530                     add_pc2line_info(s, bc_out.size, line_num);
31531                     dbuf_putc(&bc_out, op1);
31532                     pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
31533                     break;
31534                 }
31535                 /* XXX: should duplicate single instructions followed by goto or return */
31536                 /* For example, can match one of these followed by return:
31537                    push_i32 / push_const / push_atom_value / get_var /
31538                    undefined / null / push_false / push_true / get_ref_value /
31539                    get_loc / get_arg / get_var_ref
31540                  */
31541             }
31542             goto has_label;
31543 
31544         case OP_gosub:
31545             label = get_u32(bc_buf + pos + 1);
31546             if (0 && OPTIMIZE) {
31547                 label = find_jump_target(s, label, &op1, NULL);
31548                 if (op1 == OP_ret) {
31549                     update_label(s, label, -1);
31550                     /* empty finally clause: remove gosub */
31551                     break;
31552                 }
31553             }
31554             goto has_label;
31555 
31556         case OP_catch:
31557             label = get_u32(bc_buf + pos + 1);
31558             goto has_label;
31559 
31560         case OP_if_true:
31561         case OP_if_false:
31562             label = get_u32(bc_buf + pos + 1);
31563             if (OPTIMIZE) {
31564                 label = find_jump_target(s, label, &op1, NULL);
31565                 /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */
31566                 if (code_has_label(&cc, pos_next, label)) {
31567                     update_label(s, label, -1);
31568                     dbuf_putc(&bc_out, OP_drop);
31569                     break;
31570                 }
31571                 /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */
31572                 if (code_match(&cc, pos_next, OP_goto, -1)) {
31573                     int pos1 = cc.pos;
31574                     int line1 = cc.line_num;
31575                     if (code_has_label(&cc, pos1, label)) {
31576                         if (line1 >= 0) line_num = line1;
31577                         pos_next = pos1;
31578                         update_label(s, label, -1);
31579                         label = cc.label;
31580                         op ^= OP_if_true ^ OP_if_false;
31581                     }
31582                 }
31583             }
31584         has_label:
31585             add_pc2line_info(s, bc_out.size, line_num);
31586             if (op == OP_goto) {
31587                 pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
31588             }
31589             assert(label >= 0 && label < s->label_count);
31590             ls = &label_slots[label];
31591 #if SHORT_OPCODES
31592             jp = &s->jump_slots[s->jump_count++];
31593             jp->op = op;
31594             jp->size = 4;
31595             jp->pos = bc_out.size + 1;
31596             jp->label = label;
31597 
31598             if (ls->addr == -1) {
31599                 int diff = ls->pos2 - pos - 1;
31600                 if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
31601                     jp->size = 1;
31602                     jp->op = OP_if_false8 + (op - OP_if_false);
31603                     dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
31604                     dbuf_putc(&bc_out, 0);
31605                     if (!add_reloc(ctx, ls, bc_out.size - 1, 1))
31606                         goto fail;
31607                     break;
31608                 }
31609                 if (diff < 32768 && op == OP_goto) {
31610                     jp->size = 2;
31611                     jp->op = OP_goto16;
31612                     dbuf_putc(&bc_out, OP_goto16);
31613                     dbuf_put_u16(&bc_out, 0);
31614                     if (!add_reloc(ctx, ls, bc_out.size - 2, 2))
31615                         goto fail;
31616                     break;
31617                 }
31618             } else {
31619                 int diff = ls->addr - bc_out.size - 1;
31620                 if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
31621                     jp->size = 1;
31622                     jp->op = OP_if_false8 + (op - OP_if_false);
31623                     dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
31624                     dbuf_putc(&bc_out, diff);
31625                     break;
31626                 }
31627                 if (diff == (int16_t)diff && op == OP_goto) {
31628                     jp->size = 2;
31629                     jp->op = OP_goto16;
31630                     dbuf_putc(&bc_out, OP_goto16);
31631                     dbuf_put_u16(&bc_out, diff);
31632                     break;
31633                 }
31634             }
31635 #endif
31636             dbuf_putc(&bc_out, op);
31637             dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
31638             if (ls->addr == -1) {
31639                 /* unresolved yet: create a new relocation entry */
31640                 if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
31641                     goto fail;
31642             }
31643             break;
31644         case OP_with_get_var:
31645         case OP_with_put_var:
31646         case OP_with_delete_var:
31647         case OP_with_make_ref:
31648         case OP_with_get_ref:
31649         case OP_with_get_ref_undef:
31650             {
31651                 JSAtom atom;
31652                 int is_with;
31653 
31654                 atom = get_u32(bc_buf + pos + 1);
31655                 label = get_u32(bc_buf + pos + 5);
31656                 is_with = bc_buf[pos + 9];
31657                 if (OPTIMIZE) {
31658                     label = find_jump_target(s, label, &op1, NULL);
31659                 }
31660                 assert(label >= 0 && label < s->label_count);
31661                 ls = &label_slots[label];
31662                 add_pc2line_info(s, bc_out.size, line_num);
31663 #if SHORT_OPCODES
31664                 jp = &s->jump_slots[s->jump_count++];
31665                 jp->op = op;
31666                 jp->size = 4;
31667                 jp->pos = bc_out.size + 5;
31668                 jp->label = label;
31669 #endif
31670                 dbuf_putc(&bc_out, op);
31671                 dbuf_put_u32(&bc_out, atom);
31672                 dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
31673                 if (ls->addr == -1) {
31674                     /* unresolved yet: create a new relocation entry */
31675                     if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
31676                         goto fail;
31677                 }
31678                 dbuf_putc(&bc_out, is_with);
31679             }
31680             break;
31681 
31682         case OP_drop:
31683             if (OPTIMIZE) {
31684                 /* remove useless drops before return */
31685                 if (code_match(&cc, pos_next, OP_return_undef, -1)) {
31686                     if (cc.line_num >= 0) line_num = cc.line_num;
31687                     break;
31688                 }
31689             }
31690             goto no_change;
31691 
31692         case OP_null:
31693 #if SHORT_OPCODES
31694             if (OPTIMIZE) {
31695                 /* transform null strict_eq into is_null */
31696                 if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
31697                     if (cc.line_num >= 0) line_num = cc.line_num;
31698                     add_pc2line_info(s, bc_out.size, line_num);
31699                     dbuf_putc(&bc_out, OP_is_null);
31700                     pos_next = cc.pos;
31701                     break;
31702                 }
31703                 /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */
31704                 if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
31705                     if (cc.line_num >= 0) line_num = cc.line_num;
31706                     add_pc2line_info(s, bc_out.size, line_num);
31707                     dbuf_putc(&bc_out, OP_is_null);
31708                     pos_next = cc.pos;
31709                     label = cc.label;
31710                     op = cc.op ^ OP_if_false ^ OP_if_true;
31711                     goto has_label;
31712                 }
31713             }
31714 #endif
31715             /* fall thru */
31716         case OP_push_false:
31717         case OP_push_true:
31718             if (OPTIMIZE) {
31719                 val = (op == OP_push_true);
31720                 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
31721                 has_constant_test:
31722                     if (cc.line_num >= 0) line_num = cc.line_num;
31723                     if (val == cc.op - OP_if_false) {
31724                         /* transform null if_false(l1) -> goto l1 */
31725                         /* transform false if_false(l1) -> goto l1 */
31726                         /* transform true if_true(l1) -> goto l1 */
31727                         pos_next = cc.pos;
31728                         op = OP_goto;
31729                         label = cc.label;
31730                         goto has_goto;
31731                     } else {
31732                         /* transform null if_true(l1) -> nop */
31733                         /* transform false if_true(l1) -> nop */
31734                         /* transform true if_false(l1) -> nop */
31735                         pos_next = cc.pos;
31736                         update_label(s, cc.label, -1);
31737                         break;
31738                     }
31739                 }
31740             }
31741             goto no_change;
31742 
31743         case OP_push_i32:
31744             if (OPTIMIZE) {
31745                 /* transform i32(val) neg -> i32(-val) */
31746                 val = get_i32(bc_buf + pos + 1);
31747                 if ((val != INT32_MIN && val != 0)
31748                 &&  code_match(&cc, pos_next, OP_neg, -1)) {
31749                     if (cc.line_num >= 0) line_num = cc.line_num;
31750                     if (code_match(&cc, cc.pos, OP_drop, -1)) {
31751                         if (cc.line_num >= 0) line_num = cc.line_num;
31752                     } else {
31753                         add_pc2line_info(s, bc_out.size, line_num);
31754                         push_short_int(&bc_out, -val);
31755                     }
31756                     pos_next = cc.pos;
31757                     break;
31758                 }
31759                 /* remove push/drop pairs generated by the parser */
31760                 if (code_match(&cc, pos_next, OP_drop, -1)) {
31761                     if (cc.line_num >= 0) line_num = cc.line_num;
31762                     pos_next = cc.pos;
31763                     break;
31764                 }
31765                 /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */
31766                 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
31767                     val = (val != 0);
31768                     goto has_constant_test;
31769                 }
31770                 add_pc2line_info(s, bc_out.size, line_num);
31771                 push_short_int(&bc_out, val);
31772                 break;
31773             }
31774             goto no_change;
31775 
31776 #if SHORT_OPCODES
31777         case OP_push_const:
31778         case OP_fclosure:
31779             if (OPTIMIZE) {
31780                 int idx = get_u32(bc_buf + pos + 1);
31781                 if (idx < 256) {
31782                     add_pc2line_info(s, bc_out.size, line_num);
31783                     dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const);
31784                     dbuf_putc(&bc_out, idx);
31785                     break;
31786                 }
31787             }
31788             goto no_change;
31789 
31790         case OP_get_field:
31791             if (OPTIMIZE) {
31792                 JSAtom atom = get_u32(bc_buf + pos + 1);
31793                 if (atom == JS_ATOM_length) {
31794                     JS_FreeAtom(ctx, atom);
31795                     add_pc2line_info(s, bc_out.size, line_num);
31796                     dbuf_putc(&bc_out, OP_get_length);
31797                     break;
31798                 }
31799             }
31800             goto no_change;
31801 #endif
31802         case OP_push_atom_value:
31803             if (OPTIMIZE) {
31804                 JSAtom atom = get_u32(bc_buf + pos + 1);
31805                 /* remove push/drop pairs generated by the parser */
31806                 if (code_match(&cc, pos_next, OP_drop, -1)) {
31807                     JS_FreeAtom(ctx, atom);
31808                     if (cc.line_num >= 0) line_num = cc.line_num;
31809                     pos_next = cc.pos;
31810                     break;
31811                 }
31812 #if SHORT_OPCODES
31813                 if (atom == JS_ATOM_empty_string) {
31814                     JS_FreeAtom(ctx, atom);
31815                     add_pc2line_info(s, bc_out.size, line_num);
31816                     dbuf_putc(&bc_out, OP_push_empty_string);
31817                     break;
31818                 }
31819 #endif
31820             }
31821             goto no_change;
31822 
31823         case OP_to_propkey:
31824         case OP_to_propkey2:
31825             if (OPTIMIZE) {
31826                 /* remove redundant to_propkey/to_propkey2 opcodes when storing simple data */
31827                 if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1)
31828                 ||  code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1)
31829                 ||  code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) {
31830                     break;
31831                 }
31832             }
31833             goto no_change;
31834 
31835         case OP_undefined:
31836             if (OPTIMIZE) {
31837                 /* remove push/drop pairs generated by the parser */
31838                 if (code_match(&cc, pos_next, OP_drop, -1)) {
31839                     if (cc.line_num >= 0) line_num = cc.line_num;
31840                     pos_next = cc.pos;
31841                     break;
31842                 }
31843                 /* transform undefined return -> return_undefined */
31844                 if (code_match(&cc, pos_next, OP_return, -1)) {
31845                     if (cc.line_num >= 0) line_num = cc.line_num;
31846                     add_pc2line_info(s, bc_out.size, line_num);
31847                     dbuf_putc(&bc_out, OP_return_undef);
31848                     pos_next = cc.pos;
31849                     break;
31850                 }
31851                 /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */
31852                 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
31853                     val = 0;
31854                     goto has_constant_test;
31855                 }
31856 #if SHORT_OPCODES
31857                 /* transform undefined strict_eq -> is_undefined */
31858                 if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
31859                     if (cc.line_num >= 0) line_num = cc.line_num;
31860                     add_pc2line_info(s, bc_out.size, line_num);
31861                     dbuf_putc(&bc_out, OP_is_undefined);
31862                     pos_next = cc.pos;
31863                     break;
31864                 }
31865                 /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */
31866                 if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
31867                     if (cc.line_num >= 0) line_num = cc.line_num;
31868                     add_pc2line_info(s, bc_out.size, line_num);
31869                     dbuf_putc(&bc_out, OP_is_undefined);
31870                     pos_next = cc.pos;
31871                     label = cc.label;
31872                     op = cc.op ^ OP_if_false ^ OP_if_true;
31873                     goto has_label;
31874                 }
31875 #endif
31876             }
31877             goto no_change;
31878 
31879         case OP_insert2:
31880             if (OPTIMIZE) {
31881                 /* Transformation:
31882                    insert2 put_field(a) drop -> put_field(a)
31883                    insert2 put_var_strict(a) drop -> put_var_strict(a)
31884                 */
31885                 if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
31886                     if (cc.line_num >= 0) line_num = cc.line_num;
31887                     add_pc2line_info(s, bc_out.size, line_num);
31888                     dbuf_putc(&bc_out, cc.op);
31889                     dbuf_put_u32(&bc_out, cc.atom);
31890                     pos_next = cc.pos;
31891                     break;
31892                 }
31893             }
31894             goto no_change;
31895 
31896         case OP_dup:
31897             if (OPTIMIZE) {
31898                 /* Transformation: dup put_x(n) drop -> put_x(n) */
31899                 int op1, line2 = -1;
31900                 /* Transformation: dup put_x(n) -> set_x(n) */
31901                 if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, -1)) {
31902                     if (cc.line_num >= 0) line_num = cc.line_num;
31903                     op1 = cc.op + 1;  /* put_x -> set_x */
31904                     pos_next = cc.pos;
31905                     if (code_match(&cc, cc.pos, OP_drop, -1)) {
31906                         if (cc.line_num >= 0) line_num = cc.line_num;
31907                         op1 -= 1; /* set_x drop -> put_x */
31908                         pos_next = cc.pos;
31909                         if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) {
31910                             line2 = cc.line_num; /* delay line number update */
31911                             op1 += 1;   /* put_x(n) get_x(n) -> set_x(n) */
31912                             pos_next = cc.pos;
31913                         }
31914                     }
31915                     add_pc2line_info(s, bc_out.size, line_num);
31916                     put_short_code(&bc_out, op1, cc.idx);
31917                     if (line2 >= 0) line_num = line2;
31918                     break;
31919                 }
31920             }
31921             goto no_change;
31922 
31923         case OP_get_loc:
31924             if (OPTIMIZE) {
31925                 /* transformation:
31926                    get_loc(n) post_dec put_loc(n) drop -> dec_loc(n)
31927                    get_loc(n) post_inc put_loc(n) drop -> inc_loc(n)
31928                    get_loc(n) dec dup put_loc(n) drop -> dec_loc(n)
31929                    get_loc(n) inc dup put_loc(n) drop -> inc_loc(n)
31930                  */
31931                 int idx;
31932                 idx = get_u16(bc_buf + pos + 1);
31933                 if (idx >= 256)
31934                     goto no_change;
31935                 if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) ||
31936                     code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) {
31937                     if (cc.line_num >= 0) line_num = cc.line_num;
31938                     add_pc2line_info(s, bc_out.size, line_num);
31939                     dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc);
31940                     dbuf_putc(&bc_out, idx);
31941                     pos_next = cc.pos;
31942                     break;
31943                 }
31944                 /* transformation:
31945                    get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n)
31946                  */
31947                 if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
31948                     if (cc.line_num >= 0) line_num = cc.line_num;
31949                     add_pc2line_info(s, bc_out.size, line_num);
31950 #if SHORT_OPCODES
31951                     if (cc.atom == JS_ATOM_empty_string) {
31952                         JS_FreeAtom(ctx, cc.atom);
31953                         dbuf_putc(&bc_out, OP_push_empty_string);
31954                     } else
31955 #endif
31956                     {
31957                         dbuf_putc(&bc_out, OP_push_atom_value);
31958                         dbuf_put_u32(&bc_out, cc.atom);
31959                     }
31960                     dbuf_putc(&bc_out, OP_add_loc);
31961                     dbuf_putc(&bc_out, idx);
31962                     pos_next = cc.pos;
31963                     break;
31964                 }
31965                 /* transformation:
31966                    get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n)
31967                  */
31968                 if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
31969                     if (cc.line_num >= 0) line_num = cc.line_num;
31970                     add_pc2line_info(s, bc_out.size, line_num);
31971                     push_short_int(&bc_out, cc.label);
31972                     dbuf_putc(&bc_out, OP_add_loc);
31973                     dbuf_putc(&bc_out, idx);
31974                     pos_next = cc.pos;
31975                     break;
31976                 }
31977                 /* transformation: XXX: also do these:
31978                    get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n)
31979                    get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n)
31980                    get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n)
31981                  */
31982                 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)) {
31983                     if (cc.line_num >= 0) line_num = cc.line_num;
31984                     add_pc2line_info(s, bc_out.size, line_num);
31985                     put_short_code(&bc_out, cc.op, cc.idx);
31986                     dbuf_putc(&bc_out, OP_add_loc);
31987                     dbuf_putc(&bc_out, idx);
31988                     pos_next = cc.pos;
31989                     break;
31990                 }
31991                 add_pc2line_info(s, bc_out.size, line_num);
31992                 put_short_code(&bc_out, op, idx);
31993                 break;
31994             }
31995             goto no_change;
31996 #if SHORT_OPCODES
31997         case OP_get_arg:
31998         case OP_get_var_ref:
31999             if (OPTIMIZE) {
32000                 int idx;
32001                 idx = get_u16(bc_buf + pos + 1);
32002                 add_pc2line_info(s, bc_out.size, line_num);
32003                 put_short_code(&bc_out, op, idx);
32004                 break;
32005             }
32006             goto no_change;
32007 #endif
32008         case OP_put_loc:
32009         case OP_put_arg:
32010         case OP_put_var_ref:
32011             if (OPTIMIZE) {
32012                 /* transformation: put_x(n) get_x(n) -> set_x(n) */
32013                 int idx;
32014                 idx = get_u16(bc_buf + pos + 1);
32015                 if (code_match(&cc, pos_next, op - 1, idx, -1)) {
32016                     if (cc.line_num >= 0) line_num = cc.line_num;
32017                     add_pc2line_info(s, bc_out.size, line_num);
32018                     put_short_code(&bc_out, op + 1, idx);
32019                     pos_next = cc.pos;
32020                     break;
32021                 }
32022                 add_pc2line_info(s, bc_out.size, line_num);
32023                 put_short_code(&bc_out, op, idx);
32024                 break;
32025             }
32026             goto no_change;
32027 
32028         case OP_post_inc:
32029         case OP_post_dec:
32030             if (OPTIMIZE) {
32031                 /* transformation:
32032                    post_inc put_x drop -> inc put_x
32033                    post_inc perm3 put_field drop -> inc put_field
32034                    post_inc perm3 put_var_strict drop -> inc put_var_strict
32035                    post_inc perm4 put_array_el drop -> inc put_array_el
32036                  */
32037                 int op1, idx;
32038                 if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) {
32039                     if (cc.line_num >= 0) line_num = cc.line_num;
32040                     op1 = cc.op;
32041                     idx = cc.idx;
32042                     pos_next = cc.pos;
32043                     if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) {
32044                         if (cc.line_num >= 0) line_num = cc.line_num;
32045                         op1 += 1;   /* put_x(n) get_x(n) -> set_x(n) */
32046                         pos_next = cc.pos;
32047                     }
32048                     add_pc2line_info(s, bc_out.size, line_num);
32049                     dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
32050                     put_short_code(&bc_out, op1, idx);
32051                     break;
32052                 }
32053                 if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
32054                     if (cc.line_num >= 0) line_num = cc.line_num;
32055                     add_pc2line_info(s, bc_out.size, line_num);
32056                     dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
32057                     dbuf_putc(&bc_out, cc.op);
32058                     dbuf_put_u32(&bc_out, cc.atom);
32059                     pos_next = cc.pos;
32060                     break;
32061                 }
32062                 if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) {
32063                     if (cc.line_num >= 0) line_num = cc.line_num;
32064                     add_pc2line_info(s, bc_out.size, line_num);
32065                     dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
32066                     dbuf_putc(&bc_out, OP_put_array_el);
32067                     pos_next = cc.pos;
32068                     break;
32069                 }
32070             }
32071             goto no_change;
32072 
32073 #if SHORT_OPCODES
32074         case OP_typeof:
32075             if (OPTIMIZE) {
32076                 /* simplify typeof tests */
32077                 if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) {
32078                     if (cc.line_num >= 0) line_num = cc.line_num;
32079                     int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq;
32080                     int op2 = -1;
32081                     switch (cc.atom) {
32082                     case JS_ATOM_undefined:
32083                         op2 = OP_typeof_is_undefined;
32084                         break;
32085                     case JS_ATOM_function:
32086                         op2 = OP_typeof_is_function;
32087                         break;
32088                     }
32089                     if (op2 >= 0) {
32090                         /* transform typeof(s) == "<type>" into is_<type> */
32091                         if (op1 == OP_strict_eq) {
32092                             add_pc2line_info(s, bc_out.size, line_num);
32093                             dbuf_putc(&bc_out, op2);
32094                             JS_FreeAtom(ctx, cc.atom);
32095                             pos_next = cc.pos;
32096                             break;
32097                         }
32098                         if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) {
32099                             /* transform typeof(s) != "<type>" if_false into is_<type> if_true */
32100                             if (cc.line_num >= 0) line_num = cc.line_num;
32101                             add_pc2line_info(s, bc_out.size, line_num);
32102                             dbuf_putc(&bc_out, op2);
32103                             JS_FreeAtom(ctx, cc.atom);
32104                             pos_next = cc.pos;
32105                             label = cc.label;
32106                             op = OP_if_true;
32107                             goto has_label;
32108                         }
32109                     }
32110                 }
32111             }
32112             goto no_change;
32113 #endif
32114 
32115         default:
32116         no_change:
32117             add_pc2line_info(s, bc_out.size, line_num);
32118             dbuf_put(&bc_out, bc_buf + pos, len);
32119             break;
32120         }
32121     }
32122 
32123     /* check that there were no missing labels */
32124     for(i = 0; i < s->label_count; i++) {
32125         assert(label_slots[i].first_reloc == NULL);
32126     }
32127 #if SHORT_OPCODES
32128     if (OPTIMIZE) {
32129         /* more jump optimizations */
32130         int patch_offsets = 0;
32131         for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) {
32132             LabelSlot *ls;
32133             JumpSlot *jp1;
32134             int j, pos, diff, delta;
32135 
32136             delta = 3;
32137             switch (op = jp->op) {
32138             case OP_goto16:
32139                 delta = 1;
32140                 /* fall thru */
32141             case OP_if_false:
32142             case OP_if_true:
32143             case OP_goto:
32144                 pos = jp->pos;
32145                 diff = s->label_slots[jp->label].addr - pos;
32146                 if (diff >= -128 && diff <= 127 + delta) {
32147                     //put_u8(bc_out.buf + pos, diff);
32148                     jp->size = 1;
32149                     if (op == OP_goto16) {
32150                         bc_out.buf[pos - 1] = jp->op = OP_goto8;
32151                     } else {
32152                         bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false);
32153                     }
32154                     goto shrink;
32155                 } else
32156                 if (diff == (int16_t)diff && op == OP_goto) {
32157                     //put_u16(bc_out.buf + pos, diff);
32158                     jp->size = 2;
32159                     delta = 2;
32160                     bc_out.buf[pos - 1] = jp->op = OP_goto16;
32161                 shrink:
32162                     /* XXX: should reduce complexity, using 2 finger copy scheme */
32163                     memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta,
32164                             bc_out.size - pos - jp->size - delta);
32165                     bc_out.size -= delta;
32166                     patch_offsets++;
32167                     for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) {
32168                         if (ls->addr > pos)
32169                             ls->addr -= delta;
32170                     }
32171                     for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) {
32172                         if (jp1->pos > pos)
32173                             jp1->pos -= delta;
32174                     }
32175                     for (j = 0; j < s->line_number_count; j++) {
32176                         if (s->line_number_slots[j].pc > pos)
32177                             s->line_number_slots[j].pc -= delta;
32178                     }
32179                     continue;
32180                 }
32181                 break;
32182             }
32183         }
32184         if (patch_offsets) {
32185             JumpSlot *jp1;
32186             int j;
32187             for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) {
32188                 int diff1 = s->label_slots[jp1->label].addr - jp1->pos;
32189                 switch (jp1->size) {
32190                 case 1:
32191                     put_u8(bc_out.buf + jp1->pos, diff1);
32192                     break;
32193                 case 2:
32194                     put_u16(bc_out.buf + jp1->pos, diff1);
32195                     break;
32196                 case 4:
32197                     put_u32(bc_out.buf + jp1->pos, diff1);
32198                     break;
32199                 }
32200             }
32201         }
32202     }
32203     js_free(ctx, s->jump_slots);
32204     s->jump_slots = NULL;
32205 #endif
32206     js_free(ctx, s->label_slots);
32207     s->label_slots = NULL;
32208     /* XXX: should delay until copying to runtime bytecode function */
32209     compute_pc2line_info(s);
32210     js_free(ctx, s->line_number_slots);
32211     s->line_number_slots = NULL;
32212     /* set the new byte code */
32213     dbuf_free(&s->byte_code);
32214     s->byte_code = bc_out;
32215     s->use_short_opcodes = TRUE;
32216     if (dbuf_error(&s->byte_code)) {
32217         JS_ThrowOutOfMemory(ctx);
32218         return -1;
32219     }
32220     return 0;
32221  fail:
32222     /* XXX: not safe */
32223     dbuf_free(&bc_out);
32224     return -1;
32225 }
32226 
32227 /* compute the maximum stack size needed by the function */
32228 
32229 typedef struct StackSizeState {
32230     int bc_len;
32231     int stack_len_max;
32232     uint16_t *stack_level_tab;
32233     int *pc_stack;
32234     int pc_stack_len;
32235     int pc_stack_size;
32236 } StackSizeState;
32237 
32238 /* 'op' is only used for error indication */
ss_check(JSContext * ctx,StackSizeState * s,int pos,int op,int stack_len)32239 static __exception int ss_check(JSContext *ctx, StackSizeState *s,
32240                                 int pos, int op, int stack_len)
32241 {
32242     if ((unsigned)pos >= s->bc_len) {
32243         JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
32244         return -1;
32245     }
32246     if (stack_len > s->stack_len_max) {
32247         s->stack_len_max = stack_len;
32248         if (s->stack_len_max > JS_STACK_SIZE_MAX) {
32249             JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
32250             return -1;
32251         }
32252     }
32253     if (s->stack_level_tab[pos] != 0xffff) {
32254         /* already explored: check that the stack size is consistent */
32255         if (s->stack_level_tab[pos] != stack_len) {
32256             JS_ThrowInternalError(ctx, "unconsistent stack size: %d %d (pc=%d)",
32257                                   s->stack_level_tab[pos], stack_len, pos);
32258             return -1;
32259         } else {
32260             return 0;
32261         }
32262     }
32263 
32264     /* mark as explored and store the stack size */
32265     s->stack_level_tab[pos] = stack_len;
32266 
32267     /* queue the new PC to explore */
32268     if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]),
32269                         &s->pc_stack_size, s->pc_stack_len + 1))
32270         return -1;
32271     s->pc_stack[s->pc_stack_len++] = pos;
32272     return 0;
32273 }
32274 
compute_stack_size(JSContext * ctx,JSFunctionDef * fd,int * pstack_size)32275 static __exception int compute_stack_size(JSContext *ctx,
32276                                           JSFunctionDef *fd,
32277                                           int *pstack_size)
32278 {
32279     StackSizeState s_s, *s = &s_s;
32280     int i, diff, n_pop, pos_next, stack_len, pos, op;
32281     const JSOpCode *oi;
32282     const uint8_t *bc_buf;
32283 
32284     bc_buf = fd->byte_code.buf;
32285     s->bc_len = fd->byte_code.size;
32286     /* bc_len > 0 */
32287     s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) *
32288                                    s->bc_len);
32289     if (!s->stack_level_tab)
32290         return -1;
32291     for(i = 0; i < s->bc_len; i++)
32292         s->stack_level_tab[i] = 0xffff;
32293     s->stack_len_max = 0;
32294     s->pc_stack = NULL;
32295     s->pc_stack_len = 0;
32296     s->pc_stack_size = 0;
32297 
32298     /* breadth-first graph exploration */
32299     if (ss_check(ctx, s, 0, OP_invalid, 0))
32300         goto fail;
32301 
32302     while (s->pc_stack_len > 0) {
32303         pos = s->pc_stack[--s->pc_stack_len];
32304         stack_len = s->stack_level_tab[pos];
32305         op = bc_buf[pos];
32306         if (op == 0 || op >= OP_COUNT) {
32307             JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos);
32308             goto fail;
32309         }
32310         oi = &short_opcode_info(op);
32311         pos_next = pos + oi->size;
32312         if (pos_next > s->bc_len) {
32313             JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
32314             goto fail;
32315         }
32316         n_pop = oi->n_pop;
32317         /* call pops a variable number of arguments */
32318         if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) {
32319             n_pop += get_u16(bc_buf + pos + 1);
32320         } else {
32321 #if SHORT_OPCODES
32322             if (oi->fmt == OP_FMT_npopx) {
32323                 n_pop += op - OP_call0;
32324             }
32325 #endif
32326         }
32327 
32328         if (stack_len < n_pop) {
32329             JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos);
32330             goto fail;
32331         }
32332         stack_len += oi->n_push - n_pop;
32333         if (stack_len > s->stack_len_max) {
32334             s->stack_len_max = stack_len;
32335             if (s->stack_len_max > JS_STACK_SIZE_MAX) {
32336                 JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
32337                 goto fail;
32338             }
32339         }
32340         switch(op) {
32341         case OP_tail_call:
32342         case OP_tail_call_method:
32343         case OP_return:
32344         case OP_return_undef:
32345         case OP_return_async:
32346         case OP_throw:
32347         case OP_throw_error:
32348         case OP_ret:
32349             goto done_insn;
32350         case OP_goto:
32351             diff = get_u32(bc_buf + pos + 1);
32352             pos_next = pos + 1 + diff;
32353             break;
32354 #if SHORT_OPCODES
32355         case OP_goto16:
32356             diff = (int16_t)get_u16(bc_buf + pos + 1);
32357             pos_next = pos + 1 + diff;
32358             break;
32359         case OP_goto8:
32360             diff = (int8_t)bc_buf[pos + 1];
32361             pos_next = pos + 1 + diff;
32362             break;
32363         case OP_if_true8:
32364         case OP_if_false8:
32365             diff = (int8_t)bc_buf[pos + 1];
32366             if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
32367                 goto fail;
32368             break;
32369 #endif
32370         case OP_if_true:
32371         case OP_if_false:
32372         case OP_catch:
32373             diff = get_u32(bc_buf + pos + 1);
32374             if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
32375                 goto fail;
32376             break;
32377         case OP_gosub:
32378             diff = get_u32(bc_buf + pos + 1);
32379             if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1))
32380                 goto fail;
32381             break;
32382         case OP_with_get_var:
32383         case OP_with_delete_var:
32384             diff = get_u32(bc_buf + pos + 5);
32385             if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1))
32386                 goto fail;
32387             break;
32388         case OP_with_make_ref:
32389         case OP_with_get_ref:
32390         case OP_with_get_ref_undef:
32391             diff = get_u32(bc_buf + pos + 5);
32392             if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2))
32393                 goto fail;
32394             break;
32395         case OP_with_put_var:
32396             diff = get_u32(bc_buf + pos + 5);
32397             if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1))
32398                 goto fail;
32399             break;
32400 
32401         default:
32402             break;
32403         }
32404         if (ss_check(ctx, s, pos_next, op, stack_len))
32405             goto fail;
32406     done_insn: ;
32407     }
32408     js_free(ctx, s->stack_level_tab);
32409     js_free(ctx, s->pc_stack);
32410     *pstack_size = s->stack_len_max;
32411     return 0;
32412  fail:
32413     js_free(ctx, s->stack_level_tab);
32414     js_free(ctx, s->pc_stack);
32415     *pstack_size = 0;
32416     return -1;
32417 }
32418 
add_module_variables(JSContext * ctx,JSFunctionDef * fd)32419 static int add_module_variables(JSContext *ctx, JSFunctionDef *fd)
32420 {
32421     int i, idx;
32422     JSModuleDef *m = fd->module;
32423     JSExportEntry *me;
32424     JSGlobalVar *hf;
32425 
32426     /* The imported global variables were added as closure variables
32427        in js_parse_import(). We add here the module global
32428        variables. */
32429 
32430     for(i = 0; i < fd->global_var_count; i++) {
32431         hf = &fd->global_vars[i];
32432         if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const,
32433                             hf->is_lexical, FALSE) < 0)
32434             return -1;
32435     }
32436 
32437     /* resolve the variable names of the local exports */
32438     for(i = 0; i < m->export_entries_count; i++) {
32439         me = &m->export_entries[i];
32440         if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
32441             idx = find_closure_var(ctx, fd, me->local_name);
32442             if (idx < 0) {
32443                 JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist",
32444                                         me->local_name);
32445                 return -1;
32446             }
32447             me->u.local.var_idx = idx;
32448         }
32449     }
32450     return 0;
32451 }
32452 
32453 /* create a function object from a function definition. The function
32454    definition is freed. All the child functions are also created. It
32455    must be done this way to resolve all the variables. */
js_create_function(JSContext * ctx,JSFunctionDef * fd)32456 static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
32457 {
32458     JSValue func_obj;
32459     JSFunctionBytecode *b;
32460     struct list_head *el, *el1;
32461     int stack_size, scope, idx;
32462     int function_size, byte_code_offset, cpool_offset;
32463     int closure_var_offset, vardefs_offset;
32464 
32465     /* recompute scope linkage */
32466     for (scope = 0; scope < fd->scope_count; scope++) {
32467         fd->scopes[scope].first = -1;
32468     }
32469     if (fd->has_parameter_expressions) {
32470         /* special end of variable list marker for the argument scope */
32471         fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END;
32472     }
32473     for (idx = 0; idx < fd->var_count; idx++) {
32474         JSVarDef *vd = &fd->vars[idx];
32475         vd->scope_next = fd->scopes[vd->scope_level].first;
32476         fd->scopes[vd->scope_level].first = idx;
32477     }
32478     for (scope = 2; scope < fd->scope_count; scope++) {
32479         JSVarScope *sd = &fd->scopes[scope];
32480         if (sd->first < 0)
32481             sd->first = fd->scopes[sd->parent].first;
32482     }
32483     for (idx = 0; idx < fd->var_count; idx++) {
32484         JSVarDef *vd = &fd->vars[idx];
32485         if (vd->scope_next < 0 && vd->scope_level > 1) {
32486             scope = fd->scopes[vd->scope_level].parent;
32487             vd->scope_next = fd->scopes[scope].first;
32488         }
32489     }
32490 
32491     /* if the function contains an eval call, the closure variables
32492        are used to compile the eval and they must be ordered by scope,
32493        so it is necessary to create the closure variables before any
32494        other variable lookup is done. */
32495     if (fd->has_eval_call)
32496         add_eval_variables(ctx, fd);
32497 
32498     /* add the module global variables in the closure */
32499     if (fd->module) {
32500         if (add_module_variables(ctx, fd))
32501             goto fail;
32502     }
32503 
32504     /* first create all the child functions */
32505     list_for_each_safe(el, el1, &fd->child_list) {
32506         JSFunctionDef *fd1;
32507         int cpool_idx;
32508 
32509         fd1 = list_entry(el, JSFunctionDef, link);
32510         cpool_idx = fd1->parent_cpool_idx;
32511         func_obj = js_create_function(ctx, fd1);
32512         if (JS_IsException(func_obj))
32513             goto fail;
32514         /* save it in the constant pool */
32515         assert(cpool_idx >= 0);
32516         fd->cpool[cpool_idx] = func_obj;
32517     }
32518 
32519 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4)
32520     if (!(fd->js_mode & JS_MODE_STRIP)) {
32521         printf("pass 1\n");
32522         dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size,
32523                        fd->args, fd->arg_count, fd->vars, fd->var_count,
32524                        fd->closure_var, fd->closure_var_count,
32525                        fd->cpool, fd->cpool_count, fd->source, fd->line_num,
32526                        fd->label_slots, NULL);
32527         printf("\n");
32528     }
32529 #endif
32530 
32531     if (resolve_variables(ctx, fd))
32532         goto fail;
32533 
32534 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)
32535     if (!(fd->js_mode & JS_MODE_STRIP)) {
32536         printf("pass 2\n");
32537         dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size,
32538                        fd->args, fd->arg_count, fd->vars, fd->var_count,
32539                        fd->closure_var, fd->closure_var_count,
32540                        fd->cpool, fd->cpool_count, fd->source, fd->line_num,
32541                        fd->label_slots, NULL);
32542         printf("\n");
32543     }
32544 #endif
32545 
32546     if (resolve_labels(ctx, fd))
32547         goto fail;
32548 
32549     if (compute_stack_size(ctx, fd, &stack_size) < 0)
32550         goto fail;
32551 
32552     if (fd->js_mode & JS_MODE_STRIP) {
32553         function_size = offsetof(JSFunctionBytecode, debug);
32554     } else {
32555         function_size = sizeof(*b);
32556     }
32557     cpool_offset = function_size;
32558     function_size += fd->cpool_count * sizeof(*fd->cpool);
32559     vardefs_offset = function_size;
32560     if (!(fd->js_mode & JS_MODE_STRIP) || fd->has_eval_call) {
32561         function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs);
32562     }
32563     closure_var_offset = function_size;
32564     function_size += fd->closure_var_count * sizeof(*fd->closure_var);
32565     byte_code_offset = function_size;
32566     function_size += fd->byte_code.size;
32567 
32568     b = js_mallocz(ctx, function_size);
32569     if (!b)
32570         goto fail;
32571     b->header.ref_count = 1;
32572 
32573     b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset);
32574     b->byte_code_len = fd->byte_code.size;
32575     memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size);
32576     js_free(ctx, fd->byte_code.buf);
32577     fd->byte_code.buf = NULL;
32578 
32579     b->func_name = fd->func_name;
32580     if (fd->arg_count + fd->var_count > 0) {
32581         if ((fd->js_mode & JS_MODE_STRIP) && !fd->has_eval_call) {
32582             /* Strip variable definitions not needed at runtime */
32583             int i;
32584             for(i = 0; i < fd->var_count; i++) {
32585                 JS_FreeAtom(ctx, fd->vars[i].var_name);
32586             }
32587             for(i = 0; i < fd->arg_count; i++) {
32588                 JS_FreeAtom(ctx, fd->args[i].var_name);
32589             }
32590             for(i = 0; i < fd->closure_var_count; i++) {
32591                 JS_FreeAtom(ctx, fd->closure_var[i].var_name);
32592                 fd->closure_var[i].var_name = JS_ATOM_NULL;
32593             }
32594         } else {
32595             b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
32596             memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));
32597             memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));
32598         }
32599         b->var_count = fd->var_count;
32600         b->arg_count = fd->arg_count;
32601         b->defined_arg_count = fd->defined_arg_count;
32602         js_free(ctx, fd->args);
32603         js_free(ctx, fd->vars);
32604     }
32605     b->cpool_count = fd->cpool_count;
32606     if (b->cpool_count) {
32607         b->cpool = (void *)((uint8_t*)b + cpool_offset);
32608         memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool));
32609     }
32610     js_free(ctx, fd->cpool);
32611     fd->cpool = NULL;
32612 
32613     b->stack_size = stack_size;
32614 
32615     if (fd->js_mode & JS_MODE_STRIP) {
32616         JS_FreeAtom(ctx, fd->filename);
32617         dbuf_free(&fd->pc2line);    // probably useless
32618     } else {
32619         /* XXX: source and pc2line info should be packed at the end of the
32620            JSFunctionBytecode structure, avoiding allocation overhead
32621          */
32622         b->has_debug = 1;
32623         b->debug.filename = fd->filename;
32624         b->debug.line_num = fd->line_num;
32625 
32626         //DynBuf pc2line;
32627         //compute_pc2line_info(fd, &pc2line);
32628         //js_free(ctx, fd->line_number_slots)
32629         b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size);
32630         if (!b->debug.pc2line_buf)
32631             b->debug.pc2line_buf = fd->pc2line.buf;
32632         b->debug.pc2line_len = fd->pc2line.size;
32633         b->debug.source = fd->source;
32634         b->debug.source_len = fd->source_len;
32635     }
32636     if (fd->scopes != fd->def_scope_array)
32637         js_free(ctx, fd->scopes);
32638 
32639     b->closure_var_count = fd->closure_var_count;
32640     if (b->closure_var_count) {
32641         b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
32642         memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var));
32643     }
32644     js_free(ctx, fd->closure_var);
32645     fd->closure_var = NULL;
32646 
32647     b->has_prototype = fd->has_prototype;
32648     b->has_simple_parameter_list = fd->has_simple_parameter_list;
32649     b->js_mode = fd->js_mode;
32650     b->is_derived_class_constructor = fd->is_derived_class_constructor;
32651     b->func_kind = fd->func_kind;
32652     b->need_home_object = (fd->home_object_var_idx >= 0 ||
32653                            fd->need_home_object);
32654     b->new_target_allowed = fd->new_target_allowed;
32655     b->super_call_allowed = fd->super_call_allowed;
32656     b->super_allowed = fd->super_allowed;
32657     b->arguments_allowed = fd->arguments_allowed;
32658     b->backtrace_barrier = fd->backtrace_barrier;
32659     b->realm = JS_DupContext(ctx);
32660 
32661     add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
32662 
32663 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1)
32664     if (!(fd->js_mode & JS_MODE_STRIP)) {
32665         js_dump_function_bytecode(ctx, b);
32666     }
32667 #endif
32668 
32669     if (fd->parent) {
32670         /* remove from parent list */
32671         list_del(&fd->link);
32672     }
32673 
32674     js_free(ctx, fd);
32675     return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
32676  fail:
32677     js_free_function_def(ctx, fd);
32678     return JS_EXCEPTION;
32679 }
32680 
free_function_bytecode(JSRuntime * rt,JSFunctionBytecode * b)32681 static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
32682 {
32683     int i;
32684 
32685 #if 0
32686     {
32687         char buf[ATOM_GET_STR_BUF_SIZE];
32688         printf("freeing %s\n",
32689                JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
32690     }
32691 #endif
32692     free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE);
32693 
32694     if (b->vardefs) {
32695         for(i = 0; i < b->arg_count + b->var_count; i++) {
32696             JS_FreeAtomRT(rt, b->vardefs[i].var_name);
32697         }
32698     }
32699     for(i = 0; i < b->cpool_count; i++)
32700         JS_FreeValueRT(rt, b->cpool[i]);
32701 
32702     for(i = 0; i < b->closure_var_count; i++) {
32703         JSClosureVar *cv = &b->closure_var[i];
32704         JS_FreeAtomRT(rt, cv->var_name);
32705     }
32706     if (b->realm)
32707         JS_FreeContext(b->realm);
32708 
32709     JS_FreeAtomRT(rt, b->func_name);
32710     if (b->has_debug) {
32711         JS_FreeAtomRT(rt, b->debug.filename);
32712         js_free_rt(rt, b->debug.pc2line_buf);
32713         js_free_rt(rt, b->debug.source);
32714     }
32715 
32716     remove_gc_object(&b->header);
32717     if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) {
32718         list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);
32719     } else {
32720         js_free_rt(rt, b);
32721     }
32722 }
32723 
js_parse_directives(JSParseState * s)32724 static __exception int js_parse_directives(JSParseState *s)
32725 {
32726     char str[20];
32727     JSParsePos pos;
32728     BOOL has_semi;
32729 
32730     if (s->token.val != TOK_STRING)
32731         return 0;
32732 
32733     js_parse_get_pos(s, &pos);
32734 
32735     while(s->token.val == TOK_STRING) {
32736         /* Copy actual source string representation */
32737         snprintf(str, sizeof str, "%.*s",
32738                  (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1);
32739 
32740         if (next_token(s))
32741             return -1;
32742 
32743         has_semi = FALSE;
32744         switch (s->token.val) {
32745         case ';':
32746             if (next_token(s))
32747                 return -1;
32748             has_semi = TRUE;
32749             break;
32750         case '}':
32751         case TOK_EOF:
32752             has_semi = TRUE;
32753             break;
32754         case TOK_NUMBER:
32755         case TOK_STRING:
32756         case TOK_TEMPLATE:
32757         case TOK_IDENT:
32758         case TOK_REGEXP:
32759         case TOK_DEC:
32760         case TOK_INC:
32761         case TOK_NULL:
32762         case TOK_FALSE:
32763         case TOK_TRUE:
32764         case TOK_IF:
32765         case TOK_RETURN:
32766         case TOK_VAR:
32767         case TOK_THIS:
32768         case TOK_DELETE:
32769         case TOK_TYPEOF:
32770         case TOK_NEW:
32771         case TOK_DO:
32772         case TOK_WHILE:
32773         case TOK_FOR:
32774         case TOK_SWITCH:
32775         case TOK_THROW:
32776         case TOK_TRY:
32777         case TOK_FUNCTION:
32778         case TOK_DEBUGGER:
32779         case TOK_WITH:
32780         case TOK_CLASS:
32781         case TOK_CONST:
32782         case TOK_ENUM:
32783         case TOK_EXPORT:
32784         case TOK_IMPORT:
32785         case TOK_SUPER:
32786         case TOK_INTERFACE:
32787         case TOK_LET:
32788         case TOK_PACKAGE:
32789         case TOK_PRIVATE:
32790         case TOK_PROTECTED:
32791         case TOK_PUBLIC:
32792         case TOK_STATIC:
32793             /* automatic insertion of ';' */
32794             if (s->got_lf)
32795                 has_semi = TRUE;
32796             break;
32797         default:
32798             break;
32799         }
32800         if (!has_semi)
32801             break;
32802         if (!strcmp(str, "use strict")) {
32803             s->cur_func->has_use_strict = TRUE;
32804             s->cur_func->js_mode |= JS_MODE_STRICT;
32805         }
32806 #if !defined(DUMP_BYTECODE) || !(DUMP_BYTECODE & 8)
32807         else if (!strcmp(str, "use strip")) {
32808             s->cur_func->js_mode |= JS_MODE_STRIP;
32809         }
32810 #endif
32811 #ifdef CONFIG_BIGNUM
32812         else if (s->ctx->bignum_ext && !strcmp(str, "use math")) {
32813             s->cur_func->js_mode |= JS_MODE_MATH;
32814         }
32815 #endif
32816     }
32817     return js_parse_seek_token(s, &pos);
32818 }
32819 
js_parse_function_check_names(JSParseState * s,JSFunctionDef * fd,JSAtom func_name)32820 static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd,
32821                                          JSAtom func_name)
32822 {
32823     JSAtom name;
32824     int i, idx;
32825 
32826     if (fd->js_mode & JS_MODE_STRICT) {
32827         if (!fd->has_simple_parameter_list && fd->has_use_strict) {
32828             return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter");
32829         }
32830         if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments) {
32831             return js_parse_error(s, "invalid function name in strict code");
32832         }
32833         for (idx = 0; idx < fd->arg_count; idx++) {
32834             name = fd->args[idx].var_name;
32835 
32836             if (name == JS_ATOM_eval || name == JS_ATOM_arguments) {
32837                 return js_parse_error(s, "invalid argument name in strict code");
32838             }
32839         }
32840     }
32841     /* check async_generator case */
32842     if ((fd->js_mode & JS_MODE_STRICT)
32843     ||  !fd->has_simple_parameter_list
32844     ||  (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC)
32845     ||  fd->func_type == JS_PARSE_FUNC_ARROW
32846     ||  fd->func_type == JS_PARSE_FUNC_METHOD) {
32847         for (idx = 0; idx < fd->arg_count; idx++) {
32848             name = fd->args[idx].var_name;
32849             if (name != JS_ATOM_NULL) {
32850                 for (i = 0; i < idx; i++) {
32851                     if (fd->args[i].var_name == name)
32852                         goto duplicate;
32853                 }
32854                 /* Check if argument name duplicates a destructuring parameter */
32855                 /* XXX: should have a flag for such variables */
32856                 for (i = 0; i < fd->var_count; i++) {
32857                     if (fd->vars[i].var_name == name &&
32858                         fd->vars[i].scope_level == 0)
32859                         goto duplicate;
32860                 }
32861             }
32862         }
32863     }
32864     return 0;
32865 
32866 duplicate:
32867     return js_parse_error(s, "duplicate argument names not allowed in this context");
32868 }
32869 
32870 /* create a function to initialize class fields */
js_parse_function_class_fields_init(JSParseState * s)32871 static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s)
32872 {
32873     JSFunctionDef *fd;
32874 
32875     fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE,
32876                              s->filename, 0);
32877     if (!fd)
32878         return NULL;
32879     fd->func_name = JS_ATOM_NULL;
32880     fd->has_prototype = FALSE;
32881     fd->has_home_object = TRUE;
32882 
32883     fd->has_arguments_binding = FALSE;
32884     fd->has_this_binding = TRUE;
32885     fd->is_derived_class_constructor = FALSE;
32886     fd->new_target_allowed = TRUE;
32887     fd->super_call_allowed = FALSE;
32888     fd->super_allowed = fd->has_home_object;
32889     fd->arguments_allowed = FALSE;
32890 
32891     fd->func_kind = JS_FUNC_NORMAL;
32892     fd->func_type = JS_PARSE_FUNC_METHOD;
32893     return fd;
32894 }
32895 
32896 /* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and
32897    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)32898 static __exception int js_parse_function_decl2(JSParseState *s,
32899                                                JSParseFunctionEnum func_type,
32900                                                JSFunctionKindEnum func_kind,
32901                                                JSAtom func_name,
32902                                                const uint8_t *ptr,
32903                                                int function_line_num,
32904                                                JSParseExportEnum export_flag,
32905                                                JSFunctionDef **pfd)
32906 {
32907     JSContext *ctx = s->ctx;
32908     JSFunctionDef *fd = s->cur_func;
32909     BOOL is_expr;
32910     int func_idx, lexical_func_idx = -1;
32911     BOOL has_opt_arg;
32912     BOOL create_func_var = FALSE;
32913 
32914     is_expr = (func_type != JS_PARSE_FUNC_STATEMENT &&
32915                func_type != JS_PARSE_FUNC_VAR);
32916 
32917     if (func_type == JS_PARSE_FUNC_STATEMENT ||
32918         func_type == JS_PARSE_FUNC_VAR ||
32919         func_type == JS_PARSE_FUNC_EXPR) {
32920         if (func_kind == JS_FUNC_NORMAL &&
32921             token_is_pseudo_keyword(s, JS_ATOM_async) &&
32922             peek_token(s, TRUE) != '\n') {
32923             if (next_token(s))
32924                 return -1;
32925             func_kind = JS_FUNC_ASYNC;
32926         }
32927         if (next_token(s))
32928             return -1;
32929         if (s->token.val == '*') {
32930             if (next_token(s))
32931                 return -1;
32932             func_kind |= JS_FUNC_GENERATOR;
32933         }
32934 
32935         if (s->token.val == TOK_IDENT) {
32936             if (s->token.u.ident.is_reserved ||
32937                 (s->token.u.ident.atom == JS_ATOM_yield &&
32938                  func_type == JS_PARSE_FUNC_EXPR &&
32939                  (func_kind & JS_FUNC_GENERATOR)) ||
32940                 (s->token.u.ident.atom == JS_ATOM_await &&
32941                  func_type == JS_PARSE_FUNC_EXPR &&
32942                  (func_kind & JS_FUNC_ASYNC))) {
32943                 return js_parse_error_reserved_identifier(s);
32944             }
32945         }
32946         if (s->token.val == TOK_IDENT ||
32947             (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) ||
32948              (s->token.val == TOK_AWAIT && !s->is_module)) &&
32949              func_type == JS_PARSE_FUNC_EXPR)) {
32950             func_name = JS_DupAtom(ctx, s->token.u.ident.atom);
32951             if (next_token(s)) {
32952                 JS_FreeAtom(ctx, func_name);
32953                 return -1;
32954             }
32955         } else {
32956             if (func_type != JS_PARSE_FUNC_EXPR &&
32957                 export_flag != JS_PARSE_EXPORT_DEFAULT) {
32958                 return js_parse_error(s, "function name expected");
32959             }
32960         }
32961     } else if (func_type != JS_PARSE_FUNC_ARROW) {
32962         func_name = JS_DupAtom(ctx, func_name);
32963     }
32964 
32965     if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE &&
32966         (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) {
32967         JSGlobalVar *hf;
32968         hf = find_global_var(fd, func_name);
32969         /* XXX: should check scope chain */
32970         if (hf && hf->scope_level == fd->scope_level) {
32971             js_parse_error(s, "invalid redefinition of global identifier in module code");
32972             JS_FreeAtom(ctx, func_name);
32973             return -1;
32974         }
32975     }
32976 
32977     if (func_type == JS_PARSE_FUNC_VAR) {
32978         if (!(fd->js_mode & JS_MODE_STRICT)
32979         && func_kind == JS_FUNC_NORMAL
32980         &&  find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0
32981         &&  !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET))
32982         &&  !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) {
32983             create_func_var = TRUE;
32984         }
32985         /* Create the lexical name here so that the function closure
32986            contains it */
32987         if (fd->is_eval &&
32988             (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
32989              fd->eval_type == JS_EVAL_TYPE_MODULE) &&
32990             fd->scope_level == fd->body_scope) {
32991             /* avoid creating a lexical variable in the global
32992                scope. XXX: check annex B */
32993             JSGlobalVar *hf;
32994             hf = find_global_var(fd, func_name);
32995             /* XXX: should check scope chain */
32996             if (hf && hf->scope_level == fd->scope_level) {
32997                 js_parse_error(s, "invalid redefinition of global identifier");
32998                 JS_FreeAtom(ctx, func_name);
32999                 return -1;
33000             }
33001         } else {
33002             /* Always create a lexical name, fail if at the same scope as
33003                existing name */
33004             /* Lexical variable will be initialized upon entering scope */
33005             lexical_func_idx = define_var(s, fd, func_name,
33006                                           func_kind != JS_FUNC_NORMAL ?
33007                                           JS_VAR_DEF_NEW_FUNCTION_DECL :
33008                                           JS_VAR_DEF_FUNCTION_DECL);
33009             if (lexical_func_idx < 0) {
33010                 JS_FreeAtom(ctx, func_name);
33011                 return -1;
33012             }
33013         }
33014     }
33015 
33016     fd = js_new_function_def(ctx, fd, FALSE, is_expr,
33017                              s->filename, function_line_num);
33018     if (!fd) {
33019         JS_FreeAtom(ctx, func_name);
33020         return -1;
33021     }
33022     if (pfd)
33023         *pfd = fd;
33024     s->cur_func = fd;
33025     fd->func_name = func_name;
33026     /* XXX: test !fd->is_generator is always false */
33027     fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT ||
33028                          func_type == JS_PARSE_FUNC_VAR ||
33029                          func_type == JS_PARSE_FUNC_EXPR) &&
33030                         func_kind == JS_FUNC_NORMAL;
33031     fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD ||
33032                            func_type == JS_PARSE_FUNC_GETTER ||
33033                            func_type == JS_PARSE_FUNC_SETTER ||
33034                            func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
33035                            func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
33036     fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW);
33037     fd->has_this_binding = fd->has_arguments_binding;
33038     fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
33039     if (func_type == JS_PARSE_FUNC_ARROW) {
33040         fd->new_target_allowed = fd->parent->new_target_allowed;
33041         fd->super_call_allowed = fd->parent->super_call_allowed;
33042         fd->super_allowed = fd->parent->super_allowed;
33043         fd->arguments_allowed = fd->parent->arguments_allowed;
33044     } else {
33045         fd->new_target_allowed = TRUE;
33046         fd->super_call_allowed = fd->is_derived_class_constructor;
33047         fd->super_allowed = fd->has_home_object;
33048         fd->arguments_allowed = TRUE;
33049     }
33050 
33051     /* fd->in_function_body == FALSE prevents yield/await during the parsing
33052        of the arguments in generator/async functions. They are parsed as
33053        regular identifiers for other function kinds. */
33054     fd->func_kind = func_kind;
33055     fd->func_type = func_type;
33056 
33057     if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
33058         func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
33059         /* error if not invoked as a constructor */
33060         emit_op(s, OP_check_ctor);
33061     }
33062 
33063     if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
33064         emit_class_field_init(s);
33065     }
33066 
33067     /* parse arguments */
33068     fd->has_simple_parameter_list = TRUE;
33069     fd->has_parameter_expressions = FALSE;
33070     has_opt_arg = FALSE;
33071     if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) {
33072         JSAtom name;
33073         if (s->token.u.ident.is_reserved) {
33074             js_parse_error_reserved_identifier(s);
33075             goto fail;
33076         }
33077         name = s->token.u.ident.atom;
33078         if (add_arg(ctx, fd, name) < 0)
33079             goto fail;
33080         fd->defined_arg_count = 1;
33081     } else {
33082         if (s->token.val == '(') {
33083             int skip_bits;
33084             /* if there is an '=' inside the parameter list, we
33085                consider there is a parameter expression inside */
33086             js_parse_skip_parens_token(s, &skip_bits, FALSE);
33087             if (skip_bits & SKIP_HAS_ASSIGNMENT)
33088                 fd->has_parameter_expressions = TRUE;
33089             if (next_token(s))
33090                 goto fail;
33091         } else {
33092             if (js_parse_expect(s, '('))
33093                 goto fail;
33094         }
33095 
33096         if (fd->has_parameter_expressions) {
33097             fd->scope_level = -1; /* force no parent scope */
33098             if (push_scope(s) < 0)
33099                 return -1;
33100         }
33101 
33102         while (s->token.val != ')') {
33103             JSAtom name;
33104             BOOL rest = FALSE;
33105             int idx, has_initializer;
33106 
33107             if (s->token.val == TOK_ELLIPSIS) {
33108                 fd->has_simple_parameter_list = FALSE;
33109                 rest = TRUE;
33110                 if (next_token(s))
33111                     goto fail;
33112             }
33113             if (s->token.val == '[' || s->token.val == '{') {
33114                 fd->has_simple_parameter_list = FALSE;
33115                 if (rest) {
33116                     emit_op(s, OP_rest);
33117                     emit_u16(s, fd->arg_count);
33118                 } else {
33119                     /* unnamed arg for destructuring */
33120                     idx = add_arg(ctx, fd, JS_ATOM_NULL);
33121                     emit_op(s, OP_get_arg);
33122                     emit_u16(s, idx);
33123                 }
33124                 has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE);
33125                 if (has_initializer < 0)
33126                     goto fail;
33127                 if (has_initializer)
33128                     has_opt_arg = TRUE;
33129                 if (!has_opt_arg)
33130                     fd->defined_arg_count++;
33131             } else if (s->token.val == TOK_IDENT) {
33132                 if (s->token.u.ident.is_reserved) {
33133                     js_parse_error_reserved_identifier(s);
33134                     goto fail;
33135                 }
33136                 name = s->token.u.ident.atom;
33137                 if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
33138                     js_parse_error_reserved_identifier(s);
33139                     goto fail;
33140                 }
33141                 if (fd->has_parameter_expressions) {
33142                     if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0)
33143                         goto fail;
33144                 }
33145                 /* XXX: could avoid allocating an argument if rest is true */
33146                 idx = add_arg(ctx, fd, name);
33147                 if (idx < 0)
33148                     goto fail;
33149                 if (next_token(s))
33150                     goto fail;
33151                 if (rest) {
33152                     emit_op(s, OP_rest);
33153                     emit_u16(s, idx);
33154                     if (fd->has_parameter_expressions) {
33155                         emit_op(s, OP_dup);
33156                         emit_op(s, OP_scope_put_var_init);
33157                         emit_atom(s, name);
33158                         emit_u16(s, fd->scope_level);
33159                     }
33160                     emit_op(s, OP_put_arg);
33161                     emit_u16(s, idx);
33162                     fd->has_simple_parameter_list = FALSE;
33163                     has_opt_arg = TRUE;
33164                 } else if (s->token.val == '=') {
33165                     int label;
33166 
33167                     fd->has_simple_parameter_list = FALSE;
33168                     has_opt_arg = TRUE;
33169 
33170                     if (next_token(s))
33171                         goto fail;
33172 
33173                     label = new_label(s);
33174                     emit_op(s, OP_get_arg);
33175                     emit_u16(s, idx);
33176                     emit_op(s, OP_dup);
33177                     emit_op(s, OP_undefined);
33178                     emit_op(s, OP_strict_eq);
33179                     emit_goto(s, OP_if_false, label);
33180                     emit_op(s, OP_drop);
33181                     if (js_parse_assign_expr(s))
33182                         goto fail;
33183                     set_object_name(s, name);
33184                     emit_op(s, OP_dup);
33185                     emit_op(s, OP_put_arg);
33186                     emit_u16(s, idx);
33187                     emit_label(s, label);
33188                     emit_op(s, OP_scope_put_var_init);
33189                     emit_atom(s, name);
33190                     emit_u16(s, fd->scope_level);
33191                 } else {
33192                     if (!has_opt_arg) {
33193                         fd->defined_arg_count++;
33194                     }
33195                     if (fd->has_parameter_expressions) {
33196                         /* copy the argument to the argument scope */
33197                         emit_op(s, OP_get_arg);
33198                         emit_u16(s, idx);
33199                         emit_op(s, OP_scope_put_var_init);
33200                         emit_atom(s, name);
33201                         emit_u16(s, fd->scope_level);
33202                     }
33203                 }
33204             } else {
33205                 js_parse_error(s, "missing formal parameter");
33206                 goto fail;
33207             }
33208             if (rest && s->token.val != ')') {
33209                 js_parse_expect(s, ')');
33210                 goto fail;
33211             }
33212             if (s->token.val == ')')
33213                 break;
33214             if (js_parse_expect(s, ','))
33215                 goto fail;
33216         }
33217         if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) ||
33218             (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) {
33219             js_parse_error(s, "invalid number of arguments for getter or setter");
33220             goto fail;
33221         }
33222     }
33223 
33224     if (fd->has_parameter_expressions) {
33225         int idx;
33226 
33227         /* Copy the variables in the argument scope to the variable
33228            scope (see FunctionDeclarationInstantiation() in spec). The
33229            normal arguments are already present, so no need to copy
33230            them. */
33231         idx = fd->scopes[fd->scope_level].first;
33232         while (idx >= 0) {
33233             JSVarDef *vd = &fd->vars[idx];
33234             if (vd->scope_level != fd->scope_level)
33235                 break;
33236             if (find_var(ctx, fd, vd->var_name) < 0) {
33237                 if (add_var(ctx, fd, vd->var_name) < 0)
33238                     goto fail;
33239                 vd = &fd->vars[idx]; /* fd->vars may have been reallocated */
33240                 emit_op(s, OP_scope_get_var);
33241                 emit_atom(s, vd->var_name);
33242                 emit_u16(s, fd->scope_level);
33243                 emit_op(s, OP_scope_put_var);
33244                 emit_atom(s, vd->var_name);
33245                 emit_u16(s, 0);
33246             }
33247             idx = vd->scope_next;
33248         }
33249 
33250         /* the argument scope has no parent, hence we don't use pop_scope(s) */
33251         emit_op(s, OP_leave_scope);
33252         emit_u16(s, fd->scope_level);
33253 
33254         /* set the variable scope as the current scope */
33255         fd->scope_level = 0;
33256         fd->scope_first = fd->scopes[fd->scope_level].first;
33257     }
33258 
33259     if (next_token(s))
33260         goto fail;
33261 
33262     /* generator function: yield after the parameters are evaluated */
33263     if (func_kind == JS_FUNC_GENERATOR ||
33264         func_kind == JS_FUNC_ASYNC_GENERATOR)
33265         emit_op(s, OP_initial_yield);
33266 
33267     /* in generators, yield expression is forbidden during the parsing
33268        of the arguments */
33269     fd->in_function_body = TRUE;
33270     push_scope(s);  /* enter body scope */
33271     fd->body_scope = fd->scope_level;
33272 
33273     if (s->token.val == TOK_ARROW) {
33274         if (next_token(s))
33275             goto fail;
33276 
33277         if (s->token.val != '{') {
33278             if (js_parse_function_check_names(s, fd, func_name))
33279                 goto fail;
33280 
33281             if (js_parse_assign_expr(s))
33282                 goto fail;
33283 
33284             if (func_kind != JS_FUNC_NORMAL)
33285                 emit_op(s, OP_return_async);
33286             else
33287                 emit_op(s, OP_return);
33288 
33289             if (!(fd->js_mode & JS_MODE_STRIP)) {
33290                 /* save the function source code */
33291                 /* the end of the function source code is after the last
33292                    token of the function source stored into s->last_ptr */
33293                 fd->source_len = s->last_ptr - ptr;
33294                 fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
33295                 if (!fd->source)
33296                     goto fail;
33297             }
33298             goto done;
33299         }
33300     }
33301 
33302     if (js_parse_expect(s, '{'))
33303         goto fail;
33304 
33305     if (js_parse_directives(s))
33306         goto fail;
33307 
33308     /* in strict_mode, check function and argument names */
33309     if (js_parse_function_check_names(s, fd, func_name))
33310         goto fail;
33311 
33312     while (s->token.val != '}') {
33313         if (js_parse_source_element(s))
33314             goto fail;
33315     }
33316     if (!(fd->js_mode & JS_MODE_STRIP)) {
33317         /* save the function source code */
33318         fd->source_len = s->buf_ptr - ptr;
33319         fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
33320         if (!fd->source)
33321             goto fail;
33322     }
33323 
33324     if (next_token(s)) {
33325         /* consume the '}' */
33326         goto fail;
33327     }
33328 
33329     /* in case there is no return, add one */
33330     if (js_is_live_code(s)) {
33331         emit_return(s, FALSE);
33332     }
33333 done:
33334     s->cur_func = fd->parent;
33335 
33336     /* create the function object */
33337     {
33338         int idx;
33339         JSAtom func_name = fd->func_name;
33340 
33341         /* the real object will be set at the end of the compilation */
33342         idx = cpool_add(s, JS_NULL);
33343         fd->parent_cpool_idx = idx;
33344 
33345         if (is_expr) {
33346             /* for constructors, no code needs to be generated here */
33347             if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR &&
33348                 func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
33349                 /* OP_fclosure creates the function object from the bytecode
33350                    and adds the scope information */
33351                 emit_op(s, OP_fclosure);
33352                 emit_u32(s, idx);
33353                 if (func_name == JS_ATOM_NULL) {
33354                     emit_op(s, OP_set_name);
33355                     emit_u32(s, JS_ATOM_NULL);
33356                 }
33357             }
33358         } else if (func_type == JS_PARSE_FUNC_VAR) {
33359             emit_op(s, OP_fclosure);
33360             emit_u32(s, idx);
33361             if (create_func_var) {
33362                 if (s->cur_func->is_global_var) {
33363                     JSGlobalVar *hf;
33364                     /* the global variable must be defined at the start of the
33365                        function */
33366                     hf = add_global_var(ctx, s->cur_func, func_name);
33367                     if (!hf)
33368                         goto fail;
33369                     /* it is considered as defined at the top level
33370                        (needed for annex B.3.3.4 and B.3.3.5
33371                        checks) */
33372                     hf->scope_level = 0;
33373                     hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0);
33374                     /* store directly into global var, bypass lexical scope */
33375                     emit_op(s, OP_dup);
33376                     emit_op(s, OP_scope_put_var);
33377                     emit_atom(s, func_name);
33378                     emit_u16(s, 0);
33379                 } else {
33380                     /* do not call define_var to bypass lexical scope check */
33381                     func_idx = find_var(ctx, s->cur_func, func_name);
33382                     if (func_idx < 0) {
33383                         func_idx = add_var(ctx, s->cur_func, func_name);
33384                         if (func_idx < 0)
33385                             goto fail;
33386                     }
33387                     /* store directly into local var, bypass lexical catch scope */
33388                     emit_op(s, OP_dup);
33389                     emit_op(s, OP_scope_put_var);
33390                     emit_atom(s, func_name);
33391                     emit_u16(s, 0);
33392                 }
33393             }
33394             if (lexical_func_idx >= 0) {
33395                 /* lexical variable will be initialized upon entering scope */
33396                 s->cur_func->vars[lexical_func_idx].func_pool_idx = idx;
33397                 emit_op(s, OP_drop);
33398             } else {
33399                 /* store function object into its lexical name */
33400                 /* XXX: could use OP_put_loc directly */
33401                 emit_op(s, OP_scope_put_var_init);
33402                 emit_atom(s, func_name);
33403                 emit_u16(s, s->cur_func->scope_level);
33404             }
33405         } else {
33406             if (!s->cur_func->is_global_var) {
33407                 int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR);
33408 
33409                 if (var_idx < 0)
33410                     goto fail;
33411                 /* the variable will be assigned at the top of the function */
33412                 if (var_idx & ARGUMENT_VAR_OFFSET) {
33413                     s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx;
33414                 } else {
33415                     s->cur_func->vars[var_idx].func_pool_idx = idx;
33416                 }
33417             } else {
33418                 JSAtom func_var_name;
33419                 JSGlobalVar *hf;
33420                 if (func_name == JS_ATOM_NULL)
33421                     func_var_name = JS_ATOM__default_; /* export default */
33422                 else
33423                     func_var_name = func_name;
33424                 /* the variable will be assigned at the top of the function */
33425                 hf = add_global_var(ctx, s->cur_func, func_var_name);
33426                 if (!hf)
33427                     goto fail;
33428                 hf->cpool_idx = idx;
33429                 if (export_flag != JS_PARSE_EXPORT_NONE) {
33430                     if (!add_export_entry(s, s->cur_func->module, func_var_name,
33431                                           export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL))
33432                         goto fail;
33433                 }
33434             }
33435         }
33436     }
33437     return 0;
33438  fail:
33439     s->cur_func = fd->parent;
33440     js_free_function_def(ctx, fd);
33441     if (pfd)
33442         *pfd = NULL;
33443     return -1;
33444 }
33445 
js_parse_function_decl(JSParseState * s,JSParseFunctionEnum func_type,JSFunctionKindEnum func_kind,JSAtom func_name,const uint8_t * ptr,int function_line_num)33446 static __exception int js_parse_function_decl(JSParseState *s,
33447                                               JSParseFunctionEnum func_type,
33448                                               JSFunctionKindEnum func_kind,
33449                                               JSAtom func_name,
33450                                               const uint8_t *ptr,
33451                                               int function_line_num)
33452 {
33453     return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr,
33454                                    function_line_num, JS_PARSE_EXPORT_NONE,
33455                                    NULL);
33456 }
33457 
js_parse_program(JSParseState * s)33458 static __exception int js_parse_program(JSParseState *s)
33459 {
33460     JSFunctionDef *fd = s->cur_func;
33461     int idx;
33462 
33463     if (next_token(s))
33464         return -1;
33465 
33466     if (js_parse_directives(s))
33467         return -1;
33468 
33469     fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) ||
33470         (fd->eval_type == JS_EVAL_TYPE_MODULE) ||
33471         !(fd->js_mode & JS_MODE_STRICT);
33472 
33473     if (!s->is_module) {
33474         /* hidden variable for the return value */
33475         fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_);
33476         if (idx < 0)
33477             return -1;
33478     }
33479 
33480     while (s->token.val != TOK_EOF) {
33481         if (js_parse_source_element(s))
33482             return -1;
33483     }
33484 
33485     if (!s->is_module) {
33486         /* return the value of the hidden variable eval_ret_idx  */
33487         emit_op(s, OP_get_loc);
33488         emit_u16(s, fd->eval_ret_idx);
33489 
33490         emit_op(s, OP_return);
33491     } else {
33492         emit_op(s, OP_return_undef);
33493     }
33494 
33495     return 0;
33496 }
33497 
js_parse_init(JSContext * ctx,JSParseState * s,const char * input,size_t input_len,const char * filename)33498 static void js_parse_init(JSContext *ctx, JSParseState *s,
33499                           const char *input, size_t input_len,
33500                           const char *filename)
33501 {
33502     memset(s, 0, sizeof(*s));
33503     s->ctx = ctx;
33504     s->filename = filename;
33505     s->line_num = 1;
33506     s->buf_ptr = (const uint8_t *)input;
33507     s->buf_end = s->buf_ptr + input_len;
33508     s->token.val = ' ';
33509     s->token.line_num = 1;
33510 }
33511 
JS_EvalFunctionInternal(JSContext * ctx,JSValue fun_obj,JSValueConst this_obj,JSVarRef ** var_refs,JSStackFrame * sf)33512 static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,
33513                                        JSValueConst this_obj,
33514                                        JSVarRef **var_refs, JSStackFrame *sf)
33515 {
33516     JSValue ret_val;
33517     uint32_t tag;
33518 
33519     tag = JS_VALUE_GET_TAG(fun_obj);
33520     if (tag == JS_TAG_FUNCTION_BYTECODE) {
33521         fun_obj = js_closure(ctx, fun_obj, var_refs, sf);
33522         ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL);
33523     } else if (tag == JS_TAG_MODULE) {
33524         JSModuleDef *m;
33525         m = JS_VALUE_GET_PTR(fun_obj);
33526         /* the module refcount should be >= 2 */
33527         JS_FreeValue(ctx, fun_obj);
33528         if (js_create_module_function(ctx, m) < 0)
33529             goto fail;
33530         if (js_link_module(ctx, m) < 0)
33531             goto fail;
33532         ret_val = js_evaluate_module(ctx, m);
33533         if (JS_IsException(ret_val)) {
33534         fail:
33535             js_free_modules(ctx, JS_FREE_MODULE_NOT_EVALUATED);
33536             return JS_EXCEPTION;
33537         }
33538     } else {
33539         JS_FreeValue(ctx, fun_obj);
33540         ret_val = JS_ThrowTypeError(ctx, "bytecode function expected");
33541     }
33542     return ret_val;
33543 }
33544 
JS_EvalFunction(JSContext * ctx,JSValue fun_obj)33545 JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj)
33546 {
33547     return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL);
33548 }
33549 
skip_shebang(JSParseState * s)33550 static void skip_shebang(JSParseState *s)
33551 {
33552     const uint8_t *p = s->buf_ptr;
33553     int c;
33554 
33555     if (p[0] == '#' && p[1] == '!') {
33556         p += 2;
33557         while (p < s->buf_end) {
33558             if (*p == '\n' || *p == '\r') {
33559                 break;
33560             } else if (*p >= 0x80) {
33561                 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
33562                 if (c == CP_LS || c == CP_PS) {
33563                     break;
33564                 } else if (c == -1) {
33565                     p++; /* skip invalid UTF-8 */
33566                 }
33567             } else {
33568                 p++;
33569             }
33570         }
33571         s->buf_ptr = p;
33572     }
33573 }
33574 
33575 /* '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)33576 static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
33577                                  const char *input, size_t input_len,
33578                                  const char *filename, int flags, int scope_idx)
33579 {
33580     JSParseState s1, *s = &s1;
33581     int err, js_mode, eval_type;
33582     JSValue fun_obj, ret_val;
33583     JSStackFrame *sf;
33584     JSVarRef **var_refs;
33585     JSFunctionBytecode *b;
33586     JSFunctionDef *fd;
33587     JSModuleDef *m;
33588 
33589     js_parse_init(ctx, s, input, input_len, filename);
33590     skip_shebang(s);
33591 
33592     eval_type = flags & JS_EVAL_TYPE_MASK;
33593     m = NULL;
33594     if (eval_type == JS_EVAL_TYPE_DIRECT) {
33595         JSObject *p;
33596         sf = ctx->rt->current_stack_frame;
33597         assert(sf != NULL);
33598         assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT);
33599         p = JS_VALUE_GET_OBJ(sf->cur_func);
33600         assert(js_class_has_bytecode(p->class_id));
33601         b = p->u.func.function_bytecode;
33602         var_refs = p->u.func.var_refs;
33603         js_mode = b->js_mode;
33604     } else {
33605         sf = NULL;
33606         b = NULL;
33607         var_refs = NULL;
33608         js_mode = 0;
33609         if (flags & JS_EVAL_FLAG_STRICT)
33610             js_mode |= JS_MODE_STRICT;
33611         if (flags & JS_EVAL_FLAG_STRIP)
33612             js_mode |= JS_MODE_STRIP;
33613         if (eval_type == JS_EVAL_TYPE_MODULE) {
33614             JSAtom module_name = JS_NewAtom(ctx, filename);
33615             if (module_name == JS_ATOM_NULL)
33616                 return JS_EXCEPTION;
33617             m = js_new_module_def(ctx, module_name);
33618             if (!m)
33619                 return JS_EXCEPTION;
33620             js_mode |= JS_MODE_STRICT;
33621         }
33622     }
33623     fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1);
33624     if (!fd)
33625         goto fail1;
33626     s->cur_func = fd;
33627     fd->eval_type = eval_type;
33628     fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);
33629     fd->backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0);
33630     if (eval_type == JS_EVAL_TYPE_DIRECT) {
33631         fd->new_target_allowed = b->new_target_allowed;
33632         fd->super_call_allowed = b->super_call_allowed;
33633         fd->super_allowed = b->super_allowed;
33634         fd->arguments_allowed = b->arguments_allowed;
33635     } else {
33636         fd->new_target_allowed = FALSE;
33637         fd->super_call_allowed = FALSE;
33638         fd->super_allowed = FALSE;
33639         fd->arguments_allowed = TRUE;
33640     }
33641     fd->js_mode = js_mode;
33642     fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_);
33643     if (b) {
33644         if (add_closure_variables(ctx, fd, b, scope_idx))
33645             goto fail;
33646     }
33647     fd->module = m;
33648     s->is_module = (m != NULL);
33649     s->allow_html_comments = !s->is_module;
33650 
33651     push_scope(s); /* body scope */
33652     fd->body_scope = fd->scope_level;
33653 
33654     err = js_parse_program(s);
33655     if (err) {
33656     fail:
33657         free_token(s, &s->token);
33658         js_free_function_def(ctx, fd);
33659         goto fail1;
33660     }
33661 
33662     /* create the function object and all the enclosed functions */
33663     fun_obj = js_create_function(ctx, fd);
33664     if (JS_IsException(fun_obj))
33665         goto fail1;
33666     /* Could add a flag to avoid resolution if necessary */
33667     if (m) {
33668         m->func_obj = fun_obj;
33669         if (js_resolve_module(ctx, m) < 0)
33670             goto fail1;
33671         fun_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
33672     }
33673     if (flags & JS_EVAL_FLAG_COMPILE_ONLY) {
33674         ret_val = fun_obj;
33675     } else {
33676         ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf);
33677     }
33678     return ret_val;
33679  fail1:
33680     /* XXX: should free all the unresolved dependencies */
33681     if (m)
33682         js_free_module_def(ctx, m);
33683     return JS_EXCEPTION;
33684 }
33685 
33686 /* 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)33687 static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
33688                                const char *input, size_t input_len,
33689                                const char *filename, int flags, int scope_idx)
33690 {
33691     if (unlikely(!ctx->eval_internal)) {
33692         return JS_ThrowTypeError(ctx, "eval is not supported");
33693     }
33694     return ctx->eval_internal(ctx, this_obj, input, input_len, filename,
33695                               flags, scope_idx);
33696 }
33697 
JS_EvalObject(JSContext * ctx,JSValueConst this_obj,JSValueConst val,int flags,int scope_idx)33698 static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
33699                              JSValueConst val, int flags, int scope_idx)
33700 {
33701     JSValue ret;
33702     const char *str;
33703     size_t len;
33704 
33705     if (!JS_IsString(val))
33706         return JS_DupValue(ctx, val);
33707     str = JS_ToCStringLen(ctx, &len, val);
33708     if (!str)
33709         return JS_EXCEPTION;
33710     ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx);
33711     JS_FreeCString(ctx, str);
33712     return ret;
33713 
33714 }
33715 
JS_EvalThis(JSContext * ctx,JSValueConst this_obj,const char * input,size_t input_len,const char * filename,int eval_flags)33716 JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
33717                     const char *input, size_t input_len,
33718                     const char *filename, int eval_flags)
33719 {
33720     int eval_type = eval_flags & JS_EVAL_TYPE_MASK;
33721     JSValue ret;
33722 
33723     assert(eval_type == JS_EVAL_TYPE_GLOBAL ||
33724            eval_type == JS_EVAL_TYPE_MODULE);
33725     ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename,
33726                           eval_flags, -1);
33727     return ret;
33728 }
33729 
JS_Eval(JSContext * ctx,const char * input,size_t input_len,const char * filename,int eval_flags)33730 JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
33731                 const char *filename, int eval_flags)
33732 {
33733     return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename,
33734                        eval_flags);
33735 }
33736 
JS_ResolveModule(JSContext * ctx,JSValueConst obj)33737 int JS_ResolveModule(JSContext *ctx, JSValueConst obj)
33738 {
33739     if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
33740         JSModuleDef *m = JS_VALUE_GET_PTR(obj);
33741         if (js_resolve_module(ctx, m) < 0) {
33742             js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
33743             return -1;
33744         }
33745     }
33746     return 0;
33747 }
33748 
33749 /*******************************************************************/
33750 /* object list */
33751 
33752 typedef struct {
33753     JSObject *obj;
33754     uint32_t hash_next; /* -1 if no next entry */
33755 } JSObjectListEntry;
33756 
33757 /* XXX: reuse it to optimize weak references */
33758 typedef struct {
33759     JSObjectListEntry *object_tab;
33760     int object_count;
33761     int object_size;
33762     uint32_t *hash_table;
33763     uint32_t hash_size;
33764 } JSObjectList;
33765 
js_object_list_init(JSObjectList * s)33766 static void js_object_list_init(JSObjectList *s)
33767 {
33768     memset(s, 0, sizeof(*s));
33769 }
33770 
js_object_list_get_hash(JSObject * p,uint32_t hash_size)33771 static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size)
33772 {
33773     return ((uintptr_t)p * 3163) & (hash_size - 1);
33774 }
33775 
js_object_list_resize_hash(JSContext * ctx,JSObjectList * s,uint32_t new_hash_size)33776 static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s,
33777                                  uint32_t new_hash_size)
33778 {
33779     JSObjectListEntry *e;
33780     uint32_t i, h, *new_hash_table;
33781 
33782     new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);
33783     if (!new_hash_table)
33784         return -1;
33785     js_free(ctx, s->hash_table);
33786     s->hash_table = new_hash_table;
33787     s->hash_size = new_hash_size;
33788 
33789     for(i = 0; i < s->hash_size; i++) {
33790         s->hash_table[i] = -1;
33791     }
33792     for(i = 0; i < s->object_count; i++) {
33793         e = &s->object_tab[i];
33794         h = js_object_list_get_hash(e->obj, s->hash_size);
33795         e->hash_next = s->hash_table[h];
33796         s->hash_table[h] = i;
33797     }
33798     return 0;
33799 }
33800 
33801 /* the reference count of 'obj' is not modified. Return 0 if OK, -1 if
33802    memory error */
js_object_list_add(JSContext * ctx,JSObjectList * s,JSObject * obj)33803 static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj)
33804 {
33805     JSObjectListEntry *e;
33806     uint32_t h, new_hash_size;
33807 
33808     if (js_resize_array(ctx, (void *)&s->object_tab,
33809                         sizeof(s->object_tab[0]),
33810                         &s->object_size, s->object_count + 1))
33811         return -1;
33812     if (unlikely((s->object_count + 1) >= s->hash_size)) {
33813         new_hash_size = max_uint32(s->hash_size, 4);
33814         while (new_hash_size <= s->object_count)
33815             new_hash_size *= 2;
33816         if (js_object_list_resize_hash(ctx, s, new_hash_size))
33817             return -1;
33818     }
33819     e = &s->object_tab[s->object_count++];
33820     h = js_object_list_get_hash(obj, s->hash_size);
33821     e->obj = obj;
33822     e->hash_next = s->hash_table[h];
33823     s->hash_table[h] = s->object_count - 1;
33824     return 0;
33825 }
33826 
33827 /* return -1 if not present or the object index */
js_object_list_find(JSContext * ctx,JSObjectList * s,JSObject * obj)33828 static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj)
33829 {
33830     JSObjectListEntry *e;
33831     uint32_t h, p;
33832 
33833     /* must test empty size because there is no hash table */
33834     if (s->object_count == 0)
33835         return -1;
33836     h = js_object_list_get_hash(obj, s->hash_size);
33837     p = s->hash_table[h];
33838     while (p != -1) {
33839         e = &s->object_tab[p];
33840         if (e->obj == obj)
33841             return p;
33842         p = e->hash_next;
33843     }
33844     return -1;
33845 }
33846 
js_object_list_end(JSContext * ctx,JSObjectList * s)33847 static void js_object_list_end(JSContext *ctx, JSObjectList *s)
33848 {
33849     js_free(ctx, s->object_tab);
33850     js_free(ctx, s->hash_table);
33851 }
33852 
33853 /*******************************************************************/
33854 /* binary object writer & reader */
33855 
33856 typedef enum BCTagEnum {
33857     BC_TAG_NULL = 1,
33858     BC_TAG_UNDEFINED,
33859     BC_TAG_BOOL_FALSE,
33860     BC_TAG_BOOL_TRUE,
33861     BC_TAG_INT32,
33862     BC_TAG_FLOAT64,
33863     BC_TAG_STRING,
33864     BC_TAG_OBJECT,
33865     BC_TAG_ARRAY,
33866     BC_TAG_BIG_INT,
33867     BC_TAG_BIG_FLOAT,
33868     BC_TAG_BIG_DECIMAL,
33869     BC_TAG_TEMPLATE_OBJECT,
33870     BC_TAG_FUNCTION_BYTECODE,
33871     BC_TAG_MODULE,
33872     BC_TAG_TYPED_ARRAY,
33873     BC_TAG_ARRAY_BUFFER,
33874     BC_TAG_SHARED_ARRAY_BUFFER,
33875     BC_TAG_DATE,
33876     BC_TAG_OBJECT_VALUE,
33877     BC_TAG_OBJECT_REFERENCE,
33878 } BCTagEnum;
33879 
33880 #ifdef CONFIG_BIGNUM
33881 #define BC_BASE_VERSION 2
33882 #else
33883 #define BC_BASE_VERSION 1
33884 #endif
33885 #define BC_BE_VERSION 0x40
33886 #ifdef WORDS_BIGENDIAN
33887 #define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION)
33888 #else
33889 #define BC_VERSION BC_BASE_VERSION
33890 #endif
33891 
33892 typedef struct BCWriterState {
33893     JSContext *ctx;
33894     DynBuf dbuf;
33895     BOOL byte_swap : 8;
33896     BOOL allow_bytecode : 8;
33897     BOOL allow_sab : 8;
33898     BOOL allow_reference : 8;
33899     uint32_t first_atom;
33900     uint32_t *atom_to_idx;
33901     int atom_to_idx_size;
33902     JSAtom *idx_to_atom;
33903     int idx_to_atom_count;
33904     int idx_to_atom_size;
33905     uint8_t **sab_tab;
33906     int sab_tab_len;
33907     int sab_tab_size;
33908     /* list of referenced objects (used if allow_reference = TRUE) */
33909     JSObjectList object_list;
33910 } BCWriterState;
33911 
33912 #ifdef DUMP_READ_OBJECT
33913 static const char * const bc_tag_str[] = {
33914     "invalid",
33915     "null",
33916     "undefined",
33917     "false",
33918     "true",
33919     "int32",
33920     "float64",
33921     "string",
33922     "object",
33923     "array",
33924     "bigint",
33925     "bigfloat",
33926     "bigdecimal",
33927     "template",
33928     "function",
33929     "module",
33930     "TypedArray",
33931     "ArrayBuffer",
33932     "SharedArrayBuffer",
33933     "Date",
33934     "ObjectValue",
33935     "ObjectReference",
33936 };
33937 #endif
33938 
bc_put_u8(BCWriterState * s,uint8_t v)33939 static void bc_put_u8(BCWriterState *s, uint8_t v)
33940 {
33941     dbuf_putc(&s->dbuf, v);
33942 }
33943 
bc_put_u16(BCWriterState * s,uint16_t v)33944 static void bc_put_u16(BCWriterState *s, uint16_t v)
33945 {
33946     if (s->byte_swap)
33947         v = bswap16(v);
33948     dbuf_put_u16(&s->dbuf, v);
33949 }
33950 
bc_put_u32(BCWriterState * s,uint32_t v)33951 static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)
33952 {
33953     if (s->byte_swap)
33954         v = bswap32(v);
33955     dbuf_put_u32(&s->dbuf, v);
33956 }
33957 
bc_put_u64(BCWriterState * s,uint64_t v)33958 static void bc_put_u64(BCWriterState *s, uint64_t v)
33959 {
33960     if (s->byte_swap)
33961         v = bswap64(v);
33962     dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v));
33963 }
33964 
bc_put_leb128(BCWriterState * s,uint32_t v)33965 static void bc_put_leb128(BCWriterState *s, uint32_t v)
33966 {
33967     dbuf_put_leb128(&s->dbuf, v);
33968 }
33969 
bc_put_sleb128(BCWriterState * s,int32_t v)33970 static void bc_put_sleb128(BCWriterState *s, int32_t v)
33971 {
33972     dbuf_put_sleb128(&s->dbuf, v);
33973 }
33974 
bc_set_flags(uint32_t * pflags,int * pidx,uint32_t val,int n)33975 static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n)
33976 {
33977     *pflags = *pflags | (val << *pidx);
33978     *pidx += n;
33979 }
33980 
bc_atom_to_idx(BCWriterState * s,uint32_t * pres,JSAtom atom)33981 static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)
33982 {
33983     uint32_t v;
33984 
33985     if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) {
33986         *pres = atom;
33987         return 0;
33988     }
33989     atom -= s->first_atom;
33990     if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) {
33991         *pres = s->atom_to_idx[atom];
33992         return 0;
33993     }
33994     if (atom >= s->atom_to_idx_size) {
33995         int old_size, i;
33996         old_size = s->atom_to_idx_size;
33997         if (js_resize_array(s->ctx, (void **)&s->atom_to_idx,
33998                             sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size,
33999                             atom + 1))
34000             return -1;
34001         /* XXX: could add a specific js_resize_array() function to do it */
34002         for(i = old_size; i < s->atom_to_idx_size; i++)
34003             s->atom_to_idx[i] = 0;
34004     }
34005     if (js_resize_array(s->ctx, (void **)&s->idx_to_atom,
34006                         sizeof(s->idx_to_atom[0]),
34007                         &s->idx_to_atom_size, s->idx_to_atom_count + 1))
34008         goto fail;
34009 
34010     v = s->idx_to_atom_count++;
34011     s->idx_to_atom[v] = atom + s->first_atom;
34012     v += s->first_atom;
34013     s->atom_to_idx[atom] = v;
34014     *pres = v;
34015     return 0;
34016  fail:
34017     *pres = 0;
34018     return -1;
34019 }
34020 
bc_put_atom(BCWriterState * s,JSAtom atom)34021 static int bc_put_atom(BCWriterState *s, JSAtom atom)
34022 {
34023     uint32_t v;
34024 
34025     if (__JS_AtomIsTaggedInt(atom)) {
34026         v = (__JS_AtomToUInt32(atom) << 1) | 1;
34027     } else {
34028         if (bc_atom_to_idx(s, &v, atom))
34029             return -1;
34030         v <<= 1;
34031     }
34032     bc_put_leb128(s, v);
34033     return 0;
34034 }
34035 
bc_byte_swap(uint8_t * bc_buf,int bc_len)34036 static void bc_byte_swap(uint8_t *bc_buf, int bc_len)
34037 {
34038     int pos, len, op, fmt;
34039 
34040     pos = 0;
34041     while (pos < bc_len) {
34042         op = bc_buf[pos];
34043         len = short_opcode_info(op).size;
34044         fmt = short_opcode_info(op).fmt;
34045         switch(fmt) {
34046         case OP_FMT_u16:
34047         case OP_FMT_i16:
34048         case OP_FMT_label16:
34049         case OP_FMT_npop:
34050         case OP_FMT_loc:
34051         case OP_FMT_arg:
34052         case OP_FMT_var_ref:
34053             put_u16(bc_buf + pos + 1,
34054                     bswap16(get_u16(bc_buf + pos + 1)));
34055             break;
34056         case OP_FMT_i32:
34057         case OP_FMT_u32:
34058         case OP_FMT_const:
34059         case OP_FMT_label:
34060         case OP_FMT_atom:
34061         case OP_FMT_atom_u8:
34062             put_u32(bc_buf + pos + 1,
34063                     bswap32(get_u32(bc_buf + pos + 1)));
34064             break;
34065         case OP_FMT_atom_u16:
34066         case OP_FMT_label_u16:
34067             put_u32(bc_buf + pos + 1,
34068                     bswap32(get_u32(bc_buf + pos + 1)));
34069             put_u16(bc_buf + pos + 1 + 4,
34070                     bswap16(get_u16(bc_buf + pos + 1 + 4)));
34071             break;
34072         case OP_FMT_atom_label_u8:
34073         case OP_FMT_atom_label_u16:
34074             put_u32(bc_buf + pos + 1,
34075                     bswap32(get_u32(bc_buf + pos + 1)));
34076             put_u32(bc_buf + pos + 1 + 4,
34077                     bswap32(get_u32(bc_buf + pos + 1 + 4)));
34078             if (fmt == OP_FMT_atom_label_u16) {
34079                 put_u16(bc_buf + pos + 1 + 4 + 4,
34080                         bswap16(get_u16(bc_buf + pos + 1 + 4 + 4)));
34081             }
34082             break;
34083         case OP_FMT_npop_u16:
34084             put_u16(bc_buf + pos + 1,
34085                     bswap16(get_u16(bc_buf + pos + 1)));
34086             put_u16(bc_buf + pos + 1 + 2,
34087                     bswap16(get_u16(bc_buf + pos + 1 + 2)));
34088             break;
34089         default:
34090             break;
34091         }
34092         pos += len;
34093     }
34094 }
34095 
JS_WriteFunctionBytecode(BCWriterState * s,const uint8_t * bc_buf1,int bc_len)34096 static int JS_WriteFunctionBytecode(BCWriterState *s,
34097                                     const uint8_t *bc_buf1, int bc_len)
34098 {
34099     int pos, len, op;
34100     JSAtom atom;
34101     uint8_t *bc_buf;
34102     uint32_t val;
34103 
34104     bc_buf = js_malloc(s->ctx, bc_len);
34105     if (!bc_buf)
34106         return -1;
34107     memcpy(bc_buf, bc_buf1, bc_len);
34108 
34109     pos = 0;
34110     while (pos < bc_len) {
34111         op = bc_buf[pos];
34112         len = short_opcode_info(op).size;
34113         switch(short_opcode_info(op).fmt) {
34114         case OP_FMT_atom:
34115         case OP_FMT_atom_u8:
34116         case OP_FMT_atom_u16:
34117         case OP_FMT_atom_label_u8:
34118         case OP_FMT_atom_label_u16:
34119             atom = get_u32(bc_buf + pos + 1);
34120             if (bc_atom_to_idx(s, &val, atom))
34121                 goto fail;
34122             put_u32(bc_buf + pos + 1, val);
34123             break;
34124         default:
34125             break;
34126         }
34127         pos += len;
34128     }
34129 
34130     if (s->byte_swap)
34131         bc_byte_swap(bc_buf, bc_len);
34132 
34133     dbuf_put(&s->dbuf, bc_buf, bc_len);
34134 
34135     js_free(s->ctx, bc_buf);
34136     return 0;
34137  fail:
34138     js_free(s->ctx, bc_buf);
34139     return -1;
34140 }
34141 
JS_WriteString(BCWriterState * s,JSString * p)34142 static void JS_WriteString(BCWriterState *s, JSString *p)
34143 {
34144     int i;
34145     bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char);
34146     if (p->is_wide_char) {
34147         for(i = 0; i < p->len; i++)
34148             bc_put_u16(s, p->u.str16[i]);
34149     } else {
34150         dbuf_put(&s->dbuf, p->u.str8, p->len);
34151     }
34152 }
34153 
34154 #ifdef CONFIG_BIGNUM
JS_WriteBigNum(BCWriterState * s,JSValueConst obj)34155 static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj)
34156 {
34157     uint32_t tag, tag1;
34158     int64_t e;
34159     JSBigFloat *bf = JS_VALUE_GET_PTR(obj);
34160     bf_t *a = &bf->num;
34161     size_t len, i, n1, j;
34162     limb_t v;
34163 
34164     tag = JS_VALUE_GET_TAG(obj);
34165     switch(tag) {
34166     case JS_TAG_BIG_INT:
34167         tag1 = BC_TAG_BIG_INT;
34168         break;
34169     case JS_TAG_BIG_FLOAT:
34170         tag1 = BC_TAG_BIG_FLOAT;
34171         break;
34172     case JS_TAG_BIG_DECIMAL:
34173         tag1 = BC_TAG_BIG_DECIMAL;
34174         break;
34175     default:
34176         abort();
34177     }
34178     bc_put_u8(s, tag1);
34179 
34180     /* sign + exponent */
34181     if (a->expn == BF_EXP_ZERO)
34182         e = 0;
34183     else if (a->expn == BF_EXP_INF)
34184         e = 1;
34185     else if (a->expn == BF_EXP_NAN)
34186         e = 2;
34187     else if (a->expn >= 0)
34188         e = a->expn + 3;
34189     else
34190         e = a->expn;
34191     e = (e << 1) | a->sign;
34192     if (e < INT32_MIN || e > INT32_MAX) {
34193         JS_ThrowInternalError(s->ctx, "bignum exponent is too large");
34194         return -1;
34195     }
34196     bc_put_sleb128(s, e);
34197 
34198     /* mantissa */
34199     if (a->len != 0) {
34200         if (tag != JS_TAG_BIG_DECIMAL) {
34201             i = 0;
34202             while (i < a->len && a->tab[i] == 0)
34203                 i++;
34204             assert(i < a->len);
34205             v = a->tab[i];
34206             n1 = sizeof(limb_t);
34207             while ((v & 0xff) == 0) {
34208                 n1--;
34209                 v >>= 8;
34210             }
34211             i++;
34212             len = (a->len - i) * sizeof(limb_t) + n1;
34213             if (len > INT32_MAX) {
34214                 JS_ThrowInternalError(s->ctx, "bignum is too large");
34215                 return -1;
34216             }
34217             bc_put_leb128(s, len);
34218             /* always saved in byte based little endian representation */
34219             for(j = 0; j < n1; j++) {
34220                 dbuf_putc(&s->dbuf, v >> (j * 8));
34221             }
34222             for(; i < a->len; i++) {
34223                 limb_t v = a->tab[i];
34224 #if LIMB_BITS == 32
34225 #ifdef WORDS_BIGENDIAN
34226                 v = bswap32(v);
34227 #endif
34228                 dbuf_put_u32(&s->dbuf, v);
34229 #else
34230 #ifdef WORDS_BIGENDIAN
34231                 v = bswap64(v);
34232 #endif
34233                 dbuf_put_u64(&s->dbuf, v);
34234 #endif
34235             }
34236         } else {
34237             int bpos, d;
34238             uint8_t v8;
34239             size_t i0;
34240 
34241             /* little endian BCD */
34242             i = 0;
34243             while (i < a->len && a->tab[i] == 0)
34244                 i++;
34245             assert(i < a->len);
34246             len = a->len * LIMB_DIGITS;
34247             v = a->tab[i];
34248             j = 0;
34249             while ((v % 10) == 0) {
34250                 j++;
34251                 v /= 10;
34252             }
34253             len -= j;
34254             assert(len > 0);
34255             if (len > INT32_MAX) {
34256                 JS_ThrowInternalError(s->ctx, "bignum is too large");
34257                 return -1;
34258             }
34259             bc_put_leb128(s, len);
34260 
34261             bpos = 0;
34262             v8 = 0;
34263             i0 = i;
34264             for(; i < a->len; i++) {
34265                 if (i != i0) {
34266                     v = a->tab[i];
34267                     j = 0;
34268                 }
34269                 for(; j < LIMB_DIGITS; j++) {
34270                     d = v % 10;
34271                     v /= 10;
34272                     if (bpos == 0) {
34273                         v8 = d;
34274                         bpos = 1;
34275                     } else {
34276                         dbuf_putc(&s->dbuf, v8 | (d << 4));
34277                         bpos = 0;
34278                     }
34279                 }
34280             }
34281             /* flush the last digit */
34282             if (bpos) {
34283                 dbuf_putc(&s->dbuf, v8);
34284             }
34285         }
34286     }
34287     return 0;
34288 }
34289 #endif /* CONFIG_BIGNUM */
34290 
34291 static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj);
34292 
JS_WriteFunctionTag(BCWriterState * s,JSValueConst obj)34293 static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)
34294 {
34295     JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj);
34296     uint32_t flags;
34297     int idx, i;
34298 
34299     bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE);
34300     flags = idx = 0;
34301     bc_set_flags(&flags, &idx, b->has_prototype, 1);
34302     bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1);
34303     bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1);
34304     bc_set_flags(&flags, &idx, b->need_home_object, 1);
34305     bc_set_flags(&flags, &idx, b->func_kind, 2);
34306     bc_set_flags(&flags, &idx, b->new_target_allowed, 1);
34307     bc_set_flags(&flags, &idx, b->super_call_allowed, 1);
34308     bc_set_flags(&flags, &idx, b->super_allowed, 1);
34309     bc_set_flags(&flags, &idx, b->arguments_allowed, 1);
34310     bc_set_flags(&flags, &idx, b->has_debug, 1);
34311     bc_set_flags(&flags, &idx, b->backtrace_barrier, 1);
34312     assert(idx <= 16);
34313     bc_put_u16(s, flags);
34314     bc_put_u8(s, b->js_mode);
34315     bc_put_atom(s, b->func_name);
34316 
34317     bc_put_leb128(s, b->arg_count);
34318     bc_put_leb128(s, b->var_count);
34319     bc_put_leb128(s, b->defined_arg_count);
34320     bc_put_leb128(s, b->stack_size);
34321     bc_put_leb128(s, b->closure_var_count);
34322     bc_put_leb128(s, b->cpool_count);
34323     bc_put_leb128(s, b->byte_code_len);
34324     if (b->vardefs) {
34325         bc_put_leb128(s, b->arg_count + b->var_count);
34326         for(i = 0; i < b->arg_count + b->var_count; i++) {
34327             JSVarDef *vd = &b->vardefs[i];
34328             bc_put_atom(s, vd->var_name);
34329             bc_put_leb128(s, vd->scope_level);
34330             bc_put_leb128(s, vd->scope_next + 1);
34331             flags = idx = 0;
34332             bc_set_flags(&flags, &idx, vd->var_kind, 4);
34333             bc_set_flags(&flags, &idx, vd->is_const, 1);
34334             bc_set_flags(&flags, &idx, vd->is_lexical, 1);
34335             bc_set_flags(&flags, &idx, vd->is_captured, 1);
34336             assert(idx <= 8);
34337             bc_put_u8(s, flags);
34338         }
34339     } else {
34340         bc_put_leb128(s, 0);
34341     }
34342 
34343     for(i = 0; i < b->closure_var_count; i++) {
34344         JSClosureVar *cv = &b->closure_var[i];
34345         bc_put_atom(s, cv->var_name);
34346         bc_put_leb128(s, cv->var_idx);
34347         flags = idx = 0;
34348         bc_set_flags(&flags, &idx, cv->is_local, 1);
34349         bc_set_flags(&flags, &idx, cv->is_arg, 1);
34350         bc_set_flags(&flags, &idx, cv->is_const, 1);
34351         bc_set_flags(&flags, &idx, cv->is_lexical, 1);
34352         bc_set_flags(&flags, &idx, cv->var_kind, 4);
34353         assert(idx <= 8);
34354         bc_put_u8(s, flags);
34355     }
34356 
34357     if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))
34358         goto fail;
34359 
34360     if (b->has_debug) {
34361         bc_put_atom(s, b->debug.filename);
34362         bc_put_leb128(s, b->debug.line_num);
34363         bc_put_leb128(s, b->debug.pc2line_len);
34364         dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);
34365     }
34366 
34367     for(i = 0; i < b->cpool_count; i++) {
34368         if (JS_WriteObjectRec(s, b->cpool[i]))
34369             goto fail;
34370     }
34371     return 0;
34372  fail:
34373     return -1;
34374 }
34375 
JS_WriteModule(BCWriterState * s,JSValueConst obj)34376 static int JS_WriteModule(BCWriterState *s, JSValueConst obj)
34377 {
34378     JSModuleDef *m = JS_VALUE_GET_PTR(obj);
34379     int i;
34380 
34381     bc_put_u8(s, BC_TAG_MODULE);
34382     bc_put_atom(s, m->module_name);
34383 
34384     bc_put_leb128(s, m->req_module_entries_count);
34385     for(i = 0; i < m->req_module_entries_count; i++) {
34386         JSReqModuleEntry *rme = &m->req_module_entries[i];
34387         bc_put_atom(s, rme->module_name);
34388     }
34389 
34390     bc_put_leb128(s, m->export_entries_count);
34391     for(i = 0; i < m->export_entries_count; i++) {
34392         JSExportEntry *me = &m->export_entries[i];
34393         bc_put_u8(s, me->export_type);
34394         if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
34395             bc_put_leb128(s, me->u.local.var_idx);
34396         } else {
34397             bc_put_leb128(s, me->u.req_module_idx);
34398             bc_put_atom(s, me->local_name);
34399         }
34400         bc_put_atom(s, me->export_name);
34401     }
34402 
34403     bc_put_leb128(s, m->star_export_entries_count);
34404     for(i = 0; i < m->star_export_entries_count; i++) {
34405         JSStarExportEntry *se = &m->star_export_entries[i];
34406         bc_put_leb128(s, se->req_module_idx);
34407     }
34408 
34409     bc_put_leb128(s, m->import_entries_count);
34410     for(i = 0; i < m->import_entries_count; i++) {
34411         JSImportEntry *mi = &m->import_entries[i];
34412         bc_put_leb128(s, mi->var_idx);
34413         bc_put_atom(s, mi->import_name);
34414         bc_put_leb128(s, mi->req_module_idx);
34415     }
34416 
34417     if (JS_WriteObjectRec(s, m->func_obj))
34418         goto fail;
34419     return 0;
34420  fail:
34421     return -1;
34422 }
34423 
JS_WriteArray(BCWriterState * s,JSValueConst obj)34424 static int JS_WriteArray(BCWriterState *s, JSValueConst obj)
34425 {
34426     JSObject *p = JS_VALUE_GET_OBJ(obj);
34427     uint32_t i, len;
34428     JSValue val;
34429     int ret;
34430     BOOL is_template;
34431 
34432     if (s->allow_bytecode && !p->extensible) {
34433         /* not extensible array: we consider it is a
34434            template when we are saving bytecode */
34435         bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT);
34436         is_template = TRUE;
34437     } else {
34438         bc_put_u8(s, BC_TAG_ARRAY);
34439         is_template = FALSE;
34440     }
34441     if (js_get_length32(s->ctx, &len, obj))
34442         goto fail1;
34443     bc_put_leb128(s, len);
34444     for(i = 0; i < len; i++) {
34445         val = JS_GetPropertyUint32(s->ctx, obj, i);
34446         if (JS_IsException(val))
34447             goto fail1;
34448         ret = JS_WriteObjectRec(s, val);
34449         JS_FreeValue(s->ctx, val);
34450         if (ret)
34451             goto fail1;
34452     }
34453     if (is_template) {
34454         val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw);
34455         if (JS_IsException(val))
34456             goto fail1;
34457         ret = JS_WriteObjectRec(s, val);
34458         JS_FreeValue(s->ctx, val);
34459         if (ret)
34460             goto fail1;
34461     }
34462     return 0;
34463  fail1:
34464     return -1;
34465 }
34466 
JS_WriteObjectTag(BCWriterState * s,JSValueConst obj)34467 static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj)
34468 {
34469     JSObject *p = JS_VALUE_GET_OBJ(obj);
34470     uint32_t i, prop_count;
34471     JSShape *sh;
34472     JSShapeProperty *pr;
34473     int pass;
34474     JSAtom atom;
34475 
34476     bc_put_u8(s, BC_TAG_OBJECT);
34477     prop_count = 0;
34478     sh = p->shape;
34479     for(pass = 0; pass < 2; pass++) {
34480         if (pass == 1)
34481             bc_put_leb128(s, prop_count);
34482         for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
34483             atom = pr->atom;
34484             if (atom != JS_ATOM_NULL &&
34485                 JS_AtomIsString(s->ctx, atom) &&
34486                 (pr->flags & JS_PROP_ENUMERABLE)) {
34487                 if (pr->flags & JS_PROP_TMASK) {
34488                     JS_ThrowTypeError(s->ctx, "only value properties are supported");
34489                     goto fail;
34490                 }
34491                 if (pass == 0) {
34492                     prop_count++;
34493                 } else {
34494                     bc_put_atom(s, atom);
34495                     if (JS_WriteObjectRec(s, p->prop[i].u.value))
34496                         goto fail;
34497                 }
34498             }
34499         }
34500     }
34501     return 0;
34502  fail:
34503     return -1;
34504 }
34505 
JS_WriteTypedArray(BCWriterState * s,JSValueConst obj)34506 static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj)
34507 {
34508     JSObject *p = JS_VALUE_GET_OBJ(obj);
34509     JSTypedArray *ta = p->u.typed_array;
34510 
34511     bc_put_u8(s, BC_TAG_TYPED_ARRAY);
34512     bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY);
34513     bc_put_leb128(s, p->u.array.count);
34514     bc_put_leb128(s, ta->offset);
34515     if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)))
34516         return -1;
34517     return 0;
34518 }
34519 
JS_WriteArrayBuffer(BCWriterState * s,JSValueConst obj)34520 static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj)
34521 {
34522     JSObject *p = JS_VALUE_GET_OBJ(obj);
34523     JSArrayBuffer *abuf = p->u.array_buffer;
34524     if (abuf->detached) {
34525         JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx);
34526         return -1;
34527     }
34528     bc_put_u8(s, BC_TAG_ARRAY_BUFFER);
34529     bc_put_leb128(s, abuf->byte_length);
34530     dbuf_put(&s->dbuf, abuf->data, abuf->byte_length);
34531     return 0;
34532 }
34533 
JS_WriteSharedArrayBuffer(BCWriterState * s,JSValueConst obj)34534 static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj)
34535 {
34536     JSObject *p = JS_VALUE_GET_OBJ(obj);
34537     JSArrayBuffer *abuf = p->u.array_buffer;
34538     assert(!abuf->detached); /* SharedArrayBuffer are never detached */
34539     bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER);
34540     bc_put_leb128(s, abuf->byte_length);
34541     bc_put_u64(s, (uintptr_t)abuf->data);
34542     if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]),
34543                         &s->sab_tab_size, s->sab_tab_len + 1))
34544         return -1;
34545     /* keep the SAB pointer so that the user can clone it or free it */
34546     s->sab_tab[s->sab_tab_len++] = abuf->data;
34547     return 0;
34548 }
34549 
JS_WriteObjectRec(BCWriterState * s,JSValueConst obj)34550 static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)
34551 {
34552     uint32_t tag;
34553 
34554     if (js_check_stack_overflow(s->ctx->rt, 0)) {
34555         JS_ThrowStackOverflow(s->ctx);
34556         return -1;
34557     }
34558 
34559     tag = JS_VALUE_GET_NORM_TAG(obj);
34560     switch(tag) {
34561     case JS_TAG_NULL:
34562         bc_put_u8(s, BC_TAG_NULL);
34563         break;
34564     case JS_TAG_UNDEFINED:
34565         bc_put_u8(s, BC_TAG_UNDEFINED);
34566         break;
34567     case JS_TAG_BOOL:
34568         bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj));
34569         break;
34570     case JS_TAG_INT:
34571         bc_put_u8(s, BC_TAG_INT32);
34572         bc_put_sleb128(s, JS_VALUE_GET_INT(obj));
34573         break;
34574     case JS_TAG_FLOAT64:
34575         {
34576             JSFloat64Union u;
34577             bc_put_u8(s, BC_TAG_FLOAT64);
34578             u.d = JS_VALUE_GET_FLOAT64(obj);
34579             bc_put_u64(s, u.u64);
34580         }
34581         break;
34582     case JS_TAG_STRING:
34583         {
34584             JSString *p = JS_VALUE_GET_STRING(obj);
34585             bc_put_u8(s, BC_TAG_STRING);
34586             JS_WriteString(s, p);
34587         }
34588         break;
34589     case JS_TAG_FUNCTION_BYTECODE:
34590         if (!s->allow_bytecode)
34591             goto invalid_tag;
34592         if (JS_WriteFunctionTag(s, obj))
34593             goto fail;
34594         break;
34595     case JS_TAG_MODULE:
34596         if (!s->allow_bytecode)
34597             goto invalid_tag;
34598         if (JS_WriteModule(s, obj))
34599             goto fail;
34600         break;
34601     case JS_TAG_OBJECT:
34602         {
34603             JSObject *p = JS_VALUE_GET_OBJ(obj);
34604             int ret, idx;
34605 
34606             if (s->allow_reference) {
34607                 idx = js_object_list_find(s->ctx, &s->object_list, p);
34608                 if (idx >= 0) {
34609                     bc_put_u8(s, BC_TAG_OBJECT_REFERENCE);
34610                     bc_put_leb128(s, idx);
34611                     break;
34612                 } else {
34613                     if (js_object_list_add(s->ctx, &s->object_list, p))
34614                         goto fail;
34615                 }
34616             } else {
34617                 if (p->tmp_mark) {
34618                     JS_ThrowTypeError(s->ctx, "circular reference");
34619                     goto fail;
34620                 }
34621                 p->tmp_mark = 1;
34622             }
34623             switch(p->class_id) {
34624             case JS_CLASS_ARRAY:
34625                 ret = JS_WriteArray(s, obj);
34626                 break;
34627             case JS_CLASS_OBJECT:
34628                 ret = JS_WriteObjectTag(s, obj);
34629                 break;
34630             case JS_CLASS_ARRAY_BUFFER:
34631                 ret = JS_WriteArrayBuffer(s, obj);
34632                 break;
34633             case JS_CLASS_SHARED_ARRAY_BUFFER:
34634                 if (!s->allow_sab)
34635                     goto invalid_tag;
34636                 ret = JS_WriteSharedArrayBuffer(s, obj);
34637                 break;
34638             case JS_CLASS_DATE:
34639                 bc_put_u8(s, BC_TAG_DATE);
34640                 ret = JS_WriteObjectRec(s, p->u.object_data);
34641                 break;
34642             case JS_CLASS_NUMBER:
34643             case JS_CLASS_STRING:
34644             case JS_CLASS_BOOLEAN:
34645 #ifdef CONFIG_BIGNUM
34646             case JS_CLASS_BIG_INT:
34647             case JS_CLASS_BIG_FLOAT:
34648             case JS_CLASS_BIG_DECIMAL:
34649 #endif
34650                 bc_put_u8(s, BC_TAG_OBJECT_VALUE);
34651                 ret = JS_WriteObjectRec(s, p->u.object_data);
34652                 break;
34653             default:
34654                 if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
34655                     p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
34656                     ret = JS_WriteTypedArray(s, obj);
34657                 } else {
34658                     JS_ThrowTypeError(s->ctx, "unsupported object class");
34659                     ret = -1;
34660                 }
34661                 break;
34662             }
34663             p->tmp_mark = 0;
34664             if (ret)
34665                 goto fail;
34666         }
34667         break;
34668 #ifdef CONFIG_BIGNUM
34669     case JS_TAG_BIG_INT:
34670     case JS_TAG_BIG_FLOAT:
34671     case JS_TAG_BIG_DECIMAL:
34672         if (JS_WriteBigNum(s, obj))
34673             goto fail;
34674         break;
34675 #endif
34676     default:
34677     invalid_tag:
34678         JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag);
34679         goto fail;
34680     }
34681     return 0;
34682 
34683  fail:
34684     return -1;
34685 }
34686 
34687 /* create the atom table */
JS_WriteObjectAtoms(BCWriterState * s)34688 static int JS_WriteObjectAtoms(BCWriterState *s)
34689 {
34690     JSRuntime *rt = s->ctx->rt;
34691     DynBuf dbuf1;
34692     int i, atoms_size;
34693     uint8_t version;
34694 
34695     dbuf1 = s->dbuf;
34696     js_dbuf_init(s->ctx, &s->dbuf);
34697 
34698     version = BC_VERSION;
34699     if (s->byte_swap)
34700         version ^= BC_BE_VERSION;
34701     bc_put_u8(s, version);
34702 
34703     bc_put_leb128(s, s->idx_to_atom_count);
34704     for(i = 0; i < s->idx_to_atom_count; i++) {
34705         JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]];
34706         JS_WriteString(s, p);
34707     }
34708     /* XXX: should check for OOM in above phase */
34709 
34710     /* move the atoms at the start */
34711     /* XXX: could just append dbuf1 data, but it uses more memory if
34712        dbuf1 is larger than dbuf */
34713     atoms_size = s->dbuf.size;
34714     if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size))
34715         goto fail;
34716     memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size);
34717     memcpy(dbuf1.buf, s->dbuf.buf, atoms_size);
34718     dbuf1.size += atoms_size;
34719     dbuf_free(&s->dbuf);
34720     s->dbuf = dbuf1;
34721     return 0;
34722  fail:
34723     dbuf_free(&dbuf1);
34724     return -1;
34725 }
34726 
JS_WriteObject2(JSContext * ctx,size_t * psize,JSValueConst obj,int flags,uint8_t *** psab_tab,size_t * psab_tab_len)34727 uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,
34728                          int flags, uint8_t ***psab_tab, size_t *psab_tab_len)
34729 {
34730     BCWriterState ss, *s = &ss;
34731 
34732     memset(s, 0, sizeof(*s));
34733     s->ctx = ctx;
34734     /* XXX: byte swapped output is untested */
34735     s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0);
34736     s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0);
34737     s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0);
34738     s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0);
34739     /* XXX: could use a different version when bytecode is included */
34740     if (s->allow_bytecode)
34741         s->first_atom = JS_ATOM_END;
34742     else
34743         s->first_atom = 1;
34744     js_dbuf_init(ctx, &s->dbuf);
34745     js_object_list_init(&s->object_list);
34746 
34747     if (JS_WriteObjectRec(s, obj))
34748         goto fail;
34749     if (JS_WriteObjectAtoms(s))
34750         goto fail;
34751     js_object_list_end(ctx, &s->object_list);
34752     js_free(ctx, s->atom_to_idx);
34753     js_free(ctx, s->idx_to_atom);
34754     *psize = s->dbuf.size;
34755     if (psab_tab)
34756         *psab_tab = s->sab_tab;
34757     if (psab_tab_len)
34758         *psab_tab_len = s->sab_tab_len;
34759     return s->dbuf.buf;
34760  fail:
34761     js_object_list_end(ctx, &s->object_list);
34762     js_free(ctx, s->atom_to_idx);
34763     js_free(ctx, s->idx_to_atom);
34764     dbuf_free(&s->dbuf);
34765     *psize = 0;
34766     if (psab_tab)
34767         *psab_tab = NULL;
34768     if (psab_tab_len)
34769         *psab_tab_len = 0;
34770     return NULL;
34771 }
34772 
JS_WriteObject(JSContext * ctx,size_t * psize,JSValueConst obj,int flags)34773 uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
34774                         int flags)
34775 {
34776     return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL);
34777 }
34778 
34779 typedef struct BCReaderState {
34780     JSContext *ctx;
34781     const uint8_t *buf_start, *ptr, *buf_end;
34782     uint32_t first_atom;
34783     uint32_t idx_to_atom_count;
34784     JSAtom *idx_to_atom;
34785     int error_state;
34786     BOOL allow_sab : 8;
34787     BOOL allow_bytecode : 8;
34788     BOOL is_rom_data : 8;
34789     BOOL allow_reference : 8;
34790     /* object references */
34791     JSObject **objects;
34792     int objects_count;
34793     int objects_size;
34794 
34795 #ifdef DUMP_READ_OBJECT
34796     const uint8_t *ptr_last;
34797     int level;
34798 #endif
34799 } BCReaderState;
34800 
34801 #ifdef DUMP_READ_OBJECT
bc_read_trace(BCReaderState * s,const char * fmt,...)34802 static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) {
34803     va_list ap;
34804     int i, n, n0;
34805 
34806     if (!s->ptr_last)
34807         s->ptr_last = s->buf_start;
34808 
34809     n = n0 = 0;
34810     if (s->ptr > s->ptr_last || s->ptr == s->buf_start) {
34811         n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start));
34812         n += n0;
34813     }
34814     for (i = 0; s->ptr_last < s->ptr; i++) {
34815         if ((i & 7) == 0 && i > 0) {
34816             printf("\n%*s", n0, "");
34817             n = n0;
34818         }
34819         n += printf(" %02x", *s->ptr_last++);
34820     }
34821     if (*fmt == '}')
34822         s->level--;
34823     if (n < 32 + s->level * 2) {
34824         printf("%*s", 32 + s->level * 2 - n, "");
34825     }
34826     va_start(ap, fmt);
34827     vfprintf(stdout, fmt, ap);
34828     va_end(ap);
34829     if (strchr(fmt, '{'))
34830         s->level++;
34831 }
34832 #else
34833 #define bc_read_trace(...)
34834 #endif
34835 
bc_read_error_end(BCReaderState * s)34836 static int bc_read_error_end(BCReaderState *s)
34837 {
34838     if (!s->error_state) {
34839         JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer");
34840     }
34841     return s->error_state = -1;
34842 }
34843 
bc_get_u8(BCReaderState * s,uint8_t * pval)34844 static int bc_get_u8(BCReaderState *s, uint8_t *pval)
34845 {
34846     if (unlikely(s->buf_end - s->ptr < 1)) {
34847         *pval = 0; /* avoid warning */
34848         return bc_read_error_end(s);
34849     }
34850     *pval = *s->ptr++;
34851     return 0;
34852 }
34853 
bc_get_u16(BCReaderState * s,uint16_t * pval)34854 static int bc_get_u16(BCReaderState *s, uint16_t *pval)
34855 {
34856     if (unlikely(s->buf_end - s->ptr < 2)) {
34857         *pval = 0; /* avoid warning */
34858         return bc_read_error_end(s);
34859     }
34860     *pval = get_u16(s->ptr);
34861     s->ptr += 2;
34862     return 0;
34863 }
34864 
bc_get_u32(BCReaderState * s,uint32_t * pval)34865 static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)
34866 {
34867     if (unlikely(s->buf_end - s->ptr < 4)) {
34868         *pval = 0; /* avoid warning */
34869         return bc_read_error_end(s);
34870     }
34871     *pval = get_u32(s->ptr);
34872     s->ptr += 4;
34873     return 0;
34874 }
34875 
bc_get_u64(BCReaderState * s,uint64_t * pval)34876 static int bc_get_u64(BCReaderState *s, uint64_t *pval)
34877 {
34878     if (unlikely(s->buf_end - s->ptr < 8)) {
34879         *pval = 0; /* avoid warning */
34880         return bc_read_error_end(s);
34881     }
34882     *pval = get_u64(s->ptr);
34883     s->ptr += 8;
34884     return 0;
34885 }
34886 
bc_get_leb128(BCReaderState * s,uint32_t * pval)34887 static int bc_get_leb128(BCReaderState *s, uint32_t *pval)
34888 {
34889     int ret;
34890     ret = get_leb128(pval, s->ptr, s->buf_end);
34891     if (unlikely(ret < 0))
34892         return bc_read_error_end(s);
34893     s->ptr += ret;
34894     return 0;
34895 }
34896 
bc_get_sleb128(BCReaderState * s,int32_t * pval)34897 static int bc_get_sleb128(BCReaderState *s, int32_t *pval)
34898 {
34899     int ret;
34900     ret = get_sleb128(pval, s->ptr, s->buf_end);
34901     if (unlikely(ret < 0))
34902         return bc_read_error_end(s);
34903     s->ptr += ret;
34904     return 0;
34905 }
34906 
34907 /* XXX: used to read an `int` with a positive value */
bc_get_leb128_int(BCReaderState * s,int * pval)34908 static int bc_get_leb128_int(BCReaderState *s, int *pval)
34909 {
34910     return bc_get_leb128(s, (uint32_t *)pval);
34911 }
34912 
bc_get_leb128_u16(BCReaderState * s,uint16_t * pval)34913 static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval)
34914 {
34915     uint32_t val;
34916     if (bc_get_leb128(s, &val)) {
34917         *pval = 0;
34918         return -1;
34919     }
34920     *pval = val;
34921     return 0;
34922 }
34923 
bc_get_buf(BCReaderState * s,uint8_t * buf,uint32_t buf_len)34924 static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len)
34925 {
34926     if (buf_len != 0) {
34927         if (unlikely(!buf || s->buf_end - s->ptr < buf_len))
34928             return bc_read_error_end(s);
34929         memcpy(buf, s->ptr, buf_len);
34930         s->ptr += buf_len;
34931     }
34932     return 0;
34933 }
34934 
bc_idx_to_atom(BCReaderState * s,JSAtom * patom,uint32_t idx)34935 static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx)
34936 {
34937     JSAtom atom;
34938 
34939     if (__JS_AtomIsTaggedInt(idx)) {
34940         atom = idx;
34941     } else if (idx < s->first_atom) {
34942         atom = JS_DupAtom(s->ctx, idx);
34943     } else {
34944         idx -= s->first_atom;
34945         if (idx >= s->idx_to_atom_count) {
34946             JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)",
34947                                 (unsigned int)(s->ptr - s->buf_start));
34948             *patom = JS_ATOM_NULL;
34949             return s->error_state = -1;
34950         }
34951         atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]);
34952     }
34953     *patom = atom;
34954     return 0;
34955 }
34956 
bc_get_atom(BCReaderState * s,JSAtom * patom)34957 static int bc_get_atom(BCReaderState *s, JSAtom *patom)
34958 {
34959     uint32_t v;
34960     if (bc_get_leb128(s, &v))
34961         return -1;
34962     if (v & 1) {
34963         *patom = __JS_AtomFromUInt32(v >> 1);
34964         return 0;
34965     } else {
34966         return bc_idx_to_atom(s, patom, v >> 1);
34967     }
34968 }
34969 
JS_ReadString(BCReaderState * s)34970 static JSString *JS_ReadString(BCReaderState *s)
34971 {
34972     uint32_t len;
34973     size_t size;
34974     BOOL is_wide_char;
34975     JSString *p;
34976 
34977     if (bc_get_leb128(s, &len))
34978         return NULL;
34979     is_wide_char = len & 1;
34980     len >>= 1;
34981     p = js_alloc_string(s->ctx, len, is_wide_char);
34982     if (!p) {
34983         s->error_state = -1;
34984         return NULL;
34985     }
34986     size = (size_t)len << is_wide_char;
34987     if ((s->buf_end - s->ptr) < size) {
34988         bc_read_error_end(s);
34989         js_free_string(s->ctx->rt, p);
34990         return NULL;
34991     }
34992     memcpy(p->u.str8, s->ptr, size);
34993     s->ptr += size;
34994     if (!is_wide_char) {
34995         p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */
34996     }
34997 #ifdef DUMP_READ_OBJECT
34998     JS_DumpString(s->ctx->rt, p); printf("\n");
34999 #endif
35000     return p;
35001 }
35002 
bc_get_flags(uint32_t flags,int * pidx,int n)35003 static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n)
35004 {
35005     uint32_t val;
35006     /* XXX: this does not work for n == 32 */
35007     val = (flags >> *pidx) & ((1U << n) - 1);
35008     *pidx += n;
35009     return val;
35010 }
35011 
JS_ReadFunctionBytecode(BCReaderState * s,JSFunctionBytecode * b,int byte_code_offset,uint32_t bc_len)35012 static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,
35013                                    int byte_code_offset, uint32_t bc_len)
35014 {
35015     uint8_t *bc_buf;
35016     int pos, len, op;
35017     JSAtom atom;
35018     uint32_t idx;
35019 
35020     if (s->is_rom_data) {
35021         /* directly use the input buffer */
35022         if (unlikely(s->buf_end - s->ptr < bc_len))
35023             return bc_read_error_end(s);
35024         bc_buf = (uint8_t *)s->ptr;
35025         s->ptr += bc_len;
35026     } else {
35027         bc_buf = (void *)((uint8_t*)b + byte_code_offset);
35028         if (bc_get_buf(s, bc_buf, bc_len))
35029             return -1;
35030     }
35031     b->byte_code_buf = bc_buf;
35032 
35033     pos = 0;
35034     while (pos < bc_len) {
35035         op = bc_buf[pos];
35036         len = short_opcode_info(op).size;
35037         switch(short_opcode_info(op).fmt) {
35038         case OP_FMT_atom:
35039         case OP_FMT_atom_u8:
35040         case OP_FMT_atom_u16:
35041         case OP_FMT_atom_label_u8:
35042         case OP_FMT_atom_label_u16:
35043             idx = get_u32(bc_buf + pos + 1);
35044             if (s->is_rom_data) {
35045                 /* just increment the reference count of the atom */
35046                 JS_DupAtom(s->ctx, (JSAtom)idx);
35047             } else {
35048                 if (bc_idx_to_atom(s, &atom, idx)) {
35049                     /* Note: the atoms will be freed up to this position */
35050                     b->byte_code_len = pos;
35051                     return -1;
35052                 }
35053                 put_u32(bc_buf + pos + 1, atom);
35054 #ifdef DUMP_READ_OBJECT
35055                 bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n");
35056 #endif
35057             }
35058             break;
35059         default:
35060             break;
35061         }
35062         pos += len;
35063     }
35064     return 0;
35065 }
35066 
35067 #ifdef CONFIG_BIGNUM
JS_ReadBigNum(BCReaderState * s,int tag)35068 static JSValue JS_ReadBigNum(BCReaderState *s, int tag)
35069 {
35070     JSValue obj = JS_UNDEFINED;
35071     uint8_t v8;
35072     int32_t e;
35073     uint32_t len;
35074     limb_t l, i, n, j;
35075     JSBigFloat *p;
35076     limb_t v;
35077     bf_t *a;
35078     int bpos, d;
35079 
35080     p = js_new_bf(s->ctx);
35081     if (!p)
35082         goto fail;
35083     switch(tag) {
35084     case BC_TAG_BIG_INT:
35085         obj = JS_MKPTR(JS_TAG_BIG_INT, p);
35086         break;
35087     case BC_TAG_BIG_FLOAT:
35088         obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
35089         break;
35090     case BC_TAG_BIG_DECIMAL:
35091         obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
35092         break;
35093     default:
35094         abort();
35095     }
35096 
35097     /* sign + exponent */
35098     if (bc_get_sleb128(s, &e))
35099         goto fail;
35100 
35101     a = &p->num;
35102     a->sign = e & 1;
35103     e >>= 1;
35104     if (e == 0)
35105         a->expn = BF_EXP_ZERO;
35106     else if (e == 1)
35107         a->expn = BF_EXP_INF;
35108     else if (e == 2)
35109         a->expn = BF_EXP_NAN;
35110     else if (e >= 3)
35111         a->expn = e - 3;
35112     else
35113         a->expn = e;
35114 
35115     /* mantissa */
35116     if (a->expn != BF_EXP_ZERO &&
35117         a->expn != BF_EXP_INF &&
35118         a->expn != BF_EXP_NAN) {
35119         if (bc_get_leb128(s, &len))
35120             goto fail;
35121         bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);
35122         if (len == 0) {
35123             JS_ThrowInternalError(s->ctx, "invalid bignum length");
35124             goto fail;
35125         }
35126         if (tag != BC_TAG_BIG_DECIMAL)
35127             l = (len + sizeof(limb_t) - 1) / sizeof(limb_t);
35128         else
35129             l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS;
35130         if (bf_resize(a, l)) {
35131             JS_ThrowOutOfMemory(s->ctx);
35132             goto fail;
35133         }
35134         if (tag != BC_TAG_BIG_DECIMAL) {
35135             n = len & (sizeof(limb_t) - 1);
35136             if (n != 0) {
35137                 v = 0;
35138                 for(i = 0; i < n; i++) {
35139                     if (bc_get_u8(s, &v8))
35140                         goto fail;
35141                     v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8);
35142                 }
35143                 a->tab[0] = v;
35144                 i = 1;
35145             } else {
35146                 i = 0;
35147             }
35148             for(; i < l; i++) {
35149 #if LIMB_BITS == 32
35150                 if (bc_get_u32(s, &v))
35151                     goto fail;
35152 #ifdef WORDS_BIGENDIAN
35153                 v = bswap32(v);
35154 #endif
35155 #else
35156                 if (bc_get_u64(s, &v))
35157                     goto fail;
35158 #ifdef WORDS_BIGENDIAN
35159                 v = bswap64(v);
35160 #endif
35161 #endif
35162                 a->tab[i] = v;
35163             }
35164         } else {
35165             bpos = 0;
35166             for(i = 0; i < l; i++) {
35167                 if (i == 0 && (n = len % LIMB_DIGITS) != 0) {
35168                     j = LIMB_DIGITS - n;
35169                 } else {
35170                     j = 0;
35171                 }
35172                 v = 0;
35173                 for(; j < LIMB_DIGITS; j++) {
35174                     if (bpos == 0) {
35175                         if (bc_get_u8(s, &v8))
35176                             goto fail;
35177                         d = v8 & 0xf;
35178                         bpos = 1;
35179                     } else {
35180                         d = v8 >> 4;
35181                         bpos = 0;
35182                     }
35183                     if (d >= 10) {
35184                         JS_ThrowInternalError(s->ctx, "invalid digit");
35185                         goto fail;
35186                     }
35187                     v += mp_pow_dec[j] * d;
35188                 }
35189                 a->tab[i] = v;
35190             }
35191         }
35192     }
35193     bc_read_trace(s, "}\n");
35194     return obj;
35195  fail:
35196     JS_FreeValue(s->ctx, obj);
35197     return JS_EXCEPTION;
35198 }
35199 #endif /* CONFIG_BIGNUM */
35200 
35201 static JSValue JS_ReadObjectRec(BCReaderState *s);
35202 
BC_add_object_ref1(BCReaderState * s,JSObject * p)35203 static int BC_add_object_ref1(BCReaderState *s, JSObject *p)
35204 {
35205     if (s->allow_reference) {
35206         if (js_resize_array(s->ctx, (void *)&s->objects,
35207                             sizeof(s->objects[0]),
35208                             &s->objects_size, s->objects_count + 1))
35209             return -1;
35210         s->objects[s->objects_count++] = p;
35211     }
35212     return 0;
35213 }
35214 
BC_add_object_ref(BCReaderState * s,JSValueConst obj)35215 static int BC_add_object_ref(BCReaderState *s, JSValueConst obj)
35216 {
35217     return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj));
35218 }
35219 
JS_ReadFunctionTag(BCReaderState * s)35220 static JSValue JS_ReadFunctionTag(BCReaderState *s)
35221 {
35222     JSContext *ctx = s->ctx;
35223     JSFunctionBytecode bc, *b;
35224     JSValue obj = JS_UNDEFINED;
35225     uint16_t v16;
35226     uint8_t v8;
35227     int idx, i, local_count;
35228     int function_size, cpool_offset, byte_code_offset;
35229     int closure_var_offset, vardefs_offset;
35230 
35231     memset(&bc, 0, sizeof(bc));
35232     bc.header.ref_count = 1;
35233     //bc.gc_header.mark = 0;
35234 
35235     if (bc_get_u16(s, &v16))
35236         goto fail;
35237     idx = 0;
35238     bc.has_prototype = bc_get_flags(v16, &idx, 1);
35239     bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1);
35240     bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1);
35241     bc.need_home_object = bc_get_flags(v16, &idx, 1);
35242     bc.func_kind = bc_get_flags(v16, &idx, 2);
35243     bc.new_target_allowed = bc_get_flags(v16, &idx, 1);
35244     bc.super_call_allowed = bc_get_flags(v16, &idx, 1);
35245     bc.super_allowed = bc_get_flags(v16, &idx, 1);
35246     bc.arguments_allowed = bc_get_flags(v16, &idx, 1);
35247     bc.has_debug = bc_get_flags(v16, &idx, 1);
35248     bc.backtrace_barrier = bc_get_flags(v16, &idx, 1);
35249     bc.read_only_bytecode = s->is_rom_data;
35250     if (bc_get_u8(s, &v8))
35251         goto fail;
35252     bc.js_mode = v8;
35253     if (bc_get_atom(s, &bc.func_name))  //@ atom leak if failure
35254         goto fail;
35255     if (bc_get_leb128_u16(s, &bc.arg_count))
35256         goto fail;
35257     if (bc_get_leb128_u16(s, &bc.var_count))
35258         goto fail;
35259     if (bc_get_leb128_u16(s, &bc.defined_arg_count))
35260         goto fail;
35261     if (bc_get_leb128_u16(s, &bc.stack_size))
35262         goto fail;
35263     if (bc_get_leb128_int(s, &bc.closure_var_count))
35264         goto fail;
35265     if (bc_get_leb128_int(s, &bc.cpool_count))
35266         goto fail;
35267     if (bc_get_leb128_int(s, &bc.byte_code_len))
35268         goto fail;
35269     if (bc_get_leb128_int(s, &local_count))
35270         goto fail;
35271 
35272     if (bc.has_debug) {
35273         function_size = sizeof(*b);
35274     } else {
35275         function_size = offsetof(JSFunctionBytecode, debug);
35276     }
35277     cpool_offset = function_size;
35278     function_size += bc.cpool_count * sizeof(*bc.cpool);
35279     vardefs_offset = function_size;
35280     function_size += local_count * sizeof(*bc.vardefs);
35281     closure_var_offset = function_size;
35282     function_size += bc.closure_var_count * sizeof(*bc.closure_var);
35283     byte_code_offset = function_size;
35284     if (!bc.read_only_bytecode) {
35285         function_size += bc.byte_code_len;
35286     }
35287 
35288     b = js_mallocz(ctx, function_size);
35289     if (!b)
35290         return JS_EXCEPTION;
35291 
35292     memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));
35293     b->header.ref_count = 1;
35294     add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
35295 
35296     obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
35297 
35298 #ifdef DUMP_READ_OBJECT
35299     bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n");
35300 #endif
35301     bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n",
35302                   b->arg_count, b->var_count, b->defined_arg_count,
35303                   b->closure_var_count, b->cpool_count);
35304     bc_read_trace(s, "stack=%d bclen=%d locals=%d\n",
35305                   b->stack_size, b->byte_code_len, local_count);
35306 
35307     if (local_count != 0) {
35308         bc_read_trace(s, "vars {\n");
35309         b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
35310         for(i = 0; i < local_count; i++) {
35311             JSVarDef *vd = &b->vardefs[i];
35312             if (bc_get_atom(s, &vd->var_name))
35313                 goto fail;
35314             if (bc_get_leb128_int(s, &vd->scope_level))
35315                 goto fail;
35316             if (bc_get_leb128_int(s, &vd->scope_next))
35317                 goto fail;
35318             vd->scope_next--;
35319             if (bc_get_u8(s, &v8))
35320                 goto fail;
35321             idx = 0;
35322             vd->var_kind = bc_get_flags(v8, &idx, 4);
35323             vd->is_const = bc_get_flags(v8, &idx, 1);
35324             vd->is_lexical = bc_get_flags(v8, &idx, 1);
35325             vd->is_captured = bc_get_flags(v8, &idx, 1);
35326 #ifdef DUMP_READ_OBJECT
35327             bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");
35328 #endif
35329         }
35330         bc_read_trace(s, "}\n");
35331     }
35332     if (b->closure_var_count != 0) {
35333         bc_read_trace(s, "closure vars {\n");
35334         b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
35335         for(i = 0; i < b->closure_var_count; i++) {
35336             JSClosureVar *cv = &b->closure_var[i];
35337             int var_idx;
35338             if (bc_get_atom(s, &cv->var_name))
35339                 goto fail;
35340             if (bc_get_leb128_int(s, &var_idx))
35341                 goto fail;
35342             cv->var_idx = var_idx;
35343             if (bc_get_u8(s, &v8))
35344                 goto fail;
35345             idx = 0;
35346             cv->is_local = bc_get_flags(v8, &idx, 1);
35347             cv->is_arg = bc_get_flags(v8, &idx, 1);
35348             cv->is_const = bc_get_flags(v8, &idx, 1);
35349             cv->is_lexical = bc_get_flags(v8, &idx, 1);
35350             cv->var_kind = bc_get_flags(v8, &idx, 4);
35351 #ifdef DUMP_READ_OBJECT
35352             bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n");
35353 #endif
35354         }
35355         bc_read_trace(s, "}\n");
35356     }
35357     {
35358         bc_read_trace(s, "bytecode {\n");
35359         if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len))
35360             goto fail;
35361         bc_read_trace(s, "}\n");
35362     }
35363     if (b->has_debug) {
35364         /* read optional debug information */
35365         bc_read_trace(s, "debug {\n");
35366         if (bc_get_atom(s, &b->debug.filename))
35367             goto fail;
35368         if (bc_get_leb128_int(s, &b->debug.line_num))
35369             goto fail;
35370         if (bc_get_leb128_int(s, &b->debug.pc2line_len))
35371             goto fail;
35372         if (b->debug.pc2line_len) {
35373             b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len);
35374             if (!b->debug.pc2line_buf)
35375                 goto fail;
35376             if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))
35377                 goto fail;
35378         }
35379 #ifdef DUMP_READ_OBJECT
35380         bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");
35381 #endif
35382         bc_read_trace(s, "}\n");
35383     }
35384     if (b->cpool_count != 0) {
35385         bc_read_trace(s, "cpool {\n");
35386         b->cpool = (void *)((uint8_t*)b + cpool_offset);
35387         for(i = 0; i < b->cpool_count; i++) {
35388             JSValue val;
35389             val = JS_ReadObjectRec(s);
35390             if (JS_IsException(val))
35391                 goto fail;
35392             b->cpool[i] = val;
35393         }
35394         bc_read_trace(s, "}\n");
35395     }
35396     b->realm = JS_DupContext(ctx);
35397     return obj;
35398  fail:
35399     JS_FreeValue(ctx, obj);
35400     return JS_EXCEPTION;
35401 }
35402 
JS_ReadModule(BCReaderState * s)35403 static JSValue JS_ReadModule(BCReaderState *s)
35404 {
35405     JSContext *ctx = s->ctx;
35406     JSValue obj;
35407     JSModuleDef *m = NULL;
35408     JSAtom module_name;
35409     int i;
35410     uint8_t v8;
35411 
35412     if (bc_get_atom(s, &module_name))
35413         goto fail;
35414 #ifdef DUMP_READ_OBJECT
35415     bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n");
35416 #endif
35417     m = js_new_module_def(ctx, module_name);
35418     if (!m)
35419         goto fail;
35420     obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
35421     if (bc_get_leb128_int(s, &m->req_module_entries_count))
35422         goto fail;
35423     if (m->req_module_entries_count != 0) {
35424         m->req_module_entries_size = m->req_module_entries_count;
35425         m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size);
35426         if (!m->req_module_entries)
35427             goto fail;
35428         for(i = 0; i < m->req_module_entries_count; i++) {
35429             JSReqModuleEntry *rme = &m->req_module_entries[i];
35430             if (bc_get_atom(s, &rme->module_name))
35431                 goto fail;
35432         }
35433     }
35434 
35435     if (bc_get_leb128_int(s, &m->export_entries_count))
35436         goto fail;
35437     if (m->export_entries_count != 0) {
35438         m->export_entries_size = m->export_entries_count;
35439         m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size);
35440         if (!m->export_entries)
35441             goto fail;
35442         for(i = 0; i < m->export_entries_count; i++) {
35443             JSExportEntry *me = &m->export_entries[i];
35444             if (bc_get_u8(s, &v8))
35445                 goto fail;
35446             me->export_type = v8;
35447             if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
35448                 if (bc_get_leb128_int(s, &me->u.local.var_idx))
35449                     goto fail;
35450             } else {
35451                 if (bc_get_leb128_int(s, &me->u.req_module_idx))
35452                     goto fail;
35453                 if (bc_get_atom(s, &me->local_name))
35454                     goto fail;
35455             }
35456             if (bc_get_atom(s, &me->export_name))
35457                 goto fail;
35458         }
35459     }
35460 
35461     if (bc_get_leb128_int(s, &m->star_export_entries_count))
35462         goto fail;
35463     if (m->star_export_entries_count != 0) {
35464         m->star_export_entries_size = m->star_export_entries_count;
35465         m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size);
35466         if (!m->star_export_entries)
35467             goto fail;
35468         for(i = 0; i < m->star_export_entries_count; i++) {
35469             JSStarExportEntry *se = &m->star_export_entries[i];
35470             if (bc_get_leb128_int(s, &se->req_module_idx))
35471                 goto fail;
35472         }
35473     }
35474 
35475     if (bc_get_leb128_int(s, &m->import_entries_count))
35476         goto fail;
35477     if (m->import_entries_count != 0) {
35478         m->import_entries_size = m->import_entries_count;
35479         m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size);
35480         if (!m->import_entries)
35481             goto fail;
35482         for(i = 0; i < m->import_entries_count; i++) {
35483             JSImportEntry *mi = &m->import_entries[i];
35484             if (bc_get_leb128_int(s, &mi->var_idx))
35485                 goto fail;
35486             if (bc_get_atom(s, &mi->import_name))
35487                 goto fail;
35488             if (bc_get_leb128_int(s, &mi->req_module_idx))
35489                 goto fail;
35490         }
35491     }
35492 
35493     m->func_obj = JS_ReadObjectRec(s);
35494     if (JS_IsException(m->func_obj))
35495         goto fail;
35496     return obj;
35497  fail:
35498     if (m) {
35499         js_free_module_def(ctx, m);
35500     }
35501     return JS_EXCEPTION;
35502 }
35503 
JS_ReadObjectTag(BCReaderState * s)35504 static JSValue JS_ReadObjectTag(BCReaderState *s)
35505 {
35506     JSContext *ctx = s->ctx;
35507     JSValue obj;
35508     uint32_t prop_count, i;
35509     JSAtom atom;
35510     JSValue val;
35511     int ret;
35512 
35513     obj = JS_NewObject(ctx);
35514     if (BC_add_object_ref(s, obj))
35515         goto fail;
35516     if (bc_get_leb128(s, &prop_count))
35517         goto fail;
35518     for(i = 0; i < prop_count; i++) {
35519         if (bc_get_atom(s, &atom))
35520             goto fail;
35521 #ifdef DUMP_READ_OBJECT
35522         bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n");
35523 #endif
35524         val = JS_ReadObjectRec(s);
35525         if (JS_IsException(val)) {
35526             JS_FreeAtom(ctx, atom);
35527             goto fail;
35528         }
35529         ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E);
35530         JS_FreeAtom(ctx, atom);
35531         if (ret < 0)
35532             goto fail;
35533     }
35534     return obj;
35535  fail:
35536     JS_FreeValue(ctx, obj);
35537     return JS_EXCEPTION;
35538 }
35539 
JS_ReadArray(BCReaderState * s,int tag)35540 static JSValue JS_ReadArray(BCReaderState *s, int tag)
35541 {
35542     JSContext *ctx = s->ctx;
35543     JSValue obj;
35544     uint32_t len, i;
35545     JSValue val;
35546     int ret, prop_flags;
35547     BOOL is_template;
35548 
35549     obj = JS_NewArray(ctx);
35550     if (BC_add_object_ref(s, obj))
35551         goto fail;
35552     is_template = (tag == BC_TAG_TEMPLATE_OBJECT);
35553     if (bc_get_leb128(s, &len))
35554         goto fail;
35555     for(i = 0; i < len; i++) {
35556         val = JS_ReadObjectRec(s);
35557         if (JS_IsException(val))
35558             goto fail;
35559         if (is_template)
35560             prop_flags = JS_PROP_ENUMERABLE;
35561         else
35562             prop_flags = JS_PROP_C_W_E;
35563         ret = JS_DefinePropertyValueUint32(ctx, obj, i, val,
35564                                            prop_flags);
35565         if (ret < 0)
35566             goto fail;
35567     }
35568     if (is_template) {
35569         val = JS_ReadObjectRec(s);
35570         if (JS_IsException(val))
35571             goto fail;
35572         if (!JS_IsUndefined(val)) {
35573             ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0);
35574             if (ret < 0)
35575                 goto fail;
35576         }
35577         JS_PreventExtensions(ctx, obj);
35578     }
35579     return obj;
35580  fail:
35581     JS_FreeValue(ctx, obj);
35582     return JS_EXCEPTION;
35583 }
35584 
JS_ReadTypedArray(BCReaderState * s)35585 static JSValue JS_ReadTypedArray(BCReaderState *s)
35586 {
35587     JSContext *ctx = s->ctx;
35588     JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED;
35589     uint8_t array_tag;
35590     JSValueConst args[3];
35591     uint32_t offset, len, idx;
35592 
35593     if (bc_get_u8(s, &array_tag))
35594         return JS_EXCEPTION;
35595     if (array_tag >= JS_TYPED_ARRAY_COUNT)
35596         return JS_ThrowTypeError(ctx, "invalid typed array");
35597     if (bc_get_leb128(s, &len))
35598         return JS_EXCEPTION;
35599     if (bc_get_leb128(s, &offset))
35600         return JS_EXCEPTION;
35601     /* XXX: this hack could be avoided if the typed array could be
35602        created before the array buffer */
35603     idx = s->objects_count;
35604     if (BC_add_object_ref1(s, NULL))
35605         goto fail;
35606     array_buffer = JS_ReadObjectRec(s);
35607     if (JS_IsException(array_buffer))
35608         return JS_EXCEPTION;
35609     if (!js_get_array_buffer(ctx, array_buffer)) {
35610         JS_FreeValue(ctx, array_buffer);
35611         return JS_EXCEPTION;
35612     }
35613     args[0] = array_buffer;
35614     args[1] = JS_NewInt64(ctx, offset);
35615     args[2] = JS_NewInt64(ctx, len);
35616     obj = js_typed_array_constructor(ctx, JS_UNDEFINED,
35617                                      3, args,
35618                                      JS_CLASS_UINT8C_ARRAY + array_tag);
35619     if (JS_IsException(obj))
35620         goto fail;
35621     if (s->allow_reference) {
35622         s->objects[idx] = JS_VALUE_GET_OBJ(obj);
35623     }
35624     JS_FreeValue(ctx, array_buffer);
35625     return obj;
35626  fail:
35627     JS_FreeValue(ctx, array_buffer);
35628     JS_FreeValue(ctx, obj);
35629     return JS_EXCEPTION;
35630 }
35631 
JS_ReadArrayBuffer(BCReaderState * s)35632 static JSValue JS_ReadArrayBuffer(BCReaderState *s)
35633 {
35634     JSContext *ctx = s->ctx;
35635     uint32_t byte_length;
35636     JSValue obj;
35637 
35638     if (bc_get_leb128(s, &byte_length))
35639         return JS_EXCEPTION;
35640     if (unlikely(s->buf_end - s->ptr < byte_length)) {
35641         bc_read_error_end(s);
35642         return JS_EXCEPTION;
35643     }
35644     obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length);
35645     if (JS_IsException(obj))
35646         goto fail;
35647     if (BC_add_object_ref(s, obj))
35648         goto fail;
35649     s->ptr += byte_length;
35650     return obj;
35651  fail:
35652     JS_FreeValue(ctx, obj);
35653     return JS_EXCEPTION;
35654 }
35655 
JS_ReadSharedArrayBuffer(BCReaderState * s)35656 static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s)
35657 {
35658     JSContext *ctx = s->ctx;
35659     uint32_t byte_length;
35660     uint8_t *data_ptr;
35661     JSValue obj;
35662     uint64_t u64;
35663 
35664     if (bc_get_leb128(s, &byte_length))
35665         return JS_EXCEPTION;
35666     if (bc_get_u64(s, &u64))
35667         return JS_EXCEPTION;
35668     data_ptr = (uint8_t *)(uintptr_t)u64;
35669     /* the SharedArrayBuffer is cloned */
35670     obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length,
35671                                        JS_CLASS_SHARED_ARRAY_BUFFER,
35672                                        data_ptr,
35673                                        NULL, NULL, FALSE);
35674     if (JS_IsException(obj))
35675         goto fail;
35676     if (BC_add_object_ref(s, obj))
35677         goto fail;
35678     return obj;
35679  fail:
35680     JS_FreeValue(ctx, obj);
35681     return JS_EXCEPTION;
35682 }
35683 
JS_ReadDate(BCReaderState * s)35684 static JSValue JS_ReadDate(BCReaderState *s)
35685 {
35686     JSContext *ctx = s->ctx;
35687     JSValue val, obj = JS_UNDEFINED;
35688 
35689     val = JS_ReadObjectRec(s);
35690     if (JS_IsException(val))
35691         goto fail;
35692     if (!JS_IsNumber(val)) {
35693         JS_ThrowTypeError(ctx, "Number tag expected for date");
35694         goto fail;
35695     }
35696     obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE],
35697                                  JS_CLASS_DATE);
35698     if (JS_IsException(obj))
35699         goto fail;
35700     if (BC_add_object_ref(s, obj))
35701         goto fail;
35702     JS_SetObjectData(ctx, obj, val);
35703     return obj;
35704  fail:
35705     JS_FreeValue(ctx, val);
35706     JS_FreeValue(ctx, obj);
35707     return JS_EXCEPTION;
35708 }
35709 
JS_ReadObjectValue(BCReaderState * s)35710 static JSValue JS_ReadObjectValue(BCReaderState *s)
35711 {
35712     JSContext *ctx = s->ctx;
35713     JSValue val, obj = JS_UNDEFINED;
35714 
35715     val = JS_ReadObjectRec(s);
35716     if (JS_IsException(val))
35717         goto fail;
35718     obj = JS_ToObject(ctx, val);
35719     if (JS_IsException(obj))
35720         goto fail;
35721     if (BC_add_object_ref(s, obj))
35722         goto fail;
35723     JS_FreeValue(ctx, val);
35724     return obj;
35725  fail:
35726     JS_FreeValue(ctx, val);
35727     JS_FreeValue(ctx, obj);
35728     return JS_EXCEPTION;
35729 }
35730 
JS_ReadObjectRec(BCReaderState * s)35731 static JSValue JS_ReadObjectRec(BCReaderState *s)
35732 {
35733     JSContext *ctx = s->ctx;
35734     uint8_t tag;
35735     JSValue obj = JS_UNDEFINED;
35736 
35737     if (js_check_stack_overflow(ctx->rt, 0))
35738         return JS_ThrowStackOverflow(ctx);
35739 
35740     if (bc_get_u8(s, &tag))
35741         return JS_EXCEPTION;
35742 
35743     bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
35744 
35745     switch(tag) {
35746     case BC_TAG_NULL:
35747         obj = JS_NULL;
35748         break;
35749     case BC_TAG_UNDEFINED:
35750         obj = JS_UNDEFINED;
35751         break;
35752     case BC_TAG_BOOL_FALSE:
35753     case BC_TAG_BOOL_TRUE:
35754         obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE);
35755         break;
35756     case BC_TAG_INT32:
35757         {
35758             int32_t val;
35759             if (bc_get_sleb128(s, &val))
35760                 return JS_EXCEPTION;
35761             bc_read_trace(s, "%d\n", val);
35762             obj = JS_NewInt32(ctx, val);
35763         }
35764         break;
35765     case BC_TAG_FLOAT64:
35766         {
35767             JSFloat64Union u;
35768             if (bc_get_u64(s, &u.u64))
35769                 return JS_EXCEPTION;
35770             bc_read_trace(s, "%g\n", u.d);
35771             obj = __JS_NewFloat64(ctx, u.d);
35772         }
35773         break;
35774     case BC_TAG_STRING:
35775         {
35776             JSString *p;
35777             p = JS_ReadString(s);
35778             if (!p)
35779                 return JS_EXCEPTION;
35780             obj = JS_MKPTR(JS_TAG_STRING, p);
35781         }
35782         break;
35783     case BC_TAG_FUNCTION_BYTECODE:
35784         if (!s->allow_bytecode)
35785             goto invalid_tag;
35786         obj = JS_ReadFunctionTag(s);
35787         break;
35788     case BC_TAG_MODULE:
35789         if (!s->allow_bytecode)
35790             goto invalid_tag;
35791         obj = JS_ReadModule(s);
35792         break;
35793     case BC_TAG_OBJECT:
35794         obj = JS_ReadObjectTag(s);
35795         break;
35796     case BC_TAG_ARRAY:
35797     case BC_TAG_TEMPLATE_OBJECT:
35798         obj = JS_ReadArray(s, tag);
35799         break;
35800     case BC_TAG_TYPED_ARRAY:
35801         obj = JS_ReadTypedArray(s);
35802         break;
35803     case BC_TAG_ARRAY_BUFFER:
35804         obj = JS_ReadArrayBuffer(s);
35805         break;
35806     case BC_TAG_SHARED_ARRAY_BUFFER:
35807         if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup)
35808             goto invalid_tag;
35809         obj = JS_ReadSharedArrayBuffer(s);
35810         break;
35811     case BC_TAG_DATE:
35812         obj = JS_ReadDate(s);
35813         break;
35814     case BC_TAG_OBJECT_VALUE:
35815         obj = JS_ReadObjectValue(s);
35816         break;
35817 #ifdef CONFIG_BIGNUM
35818     case BC_TAG_BIG_INT:
35819     case BC_TAG_BIG_FLOAT:
35820     case BC_TAG_BIG_DECIMAL:
35821         obj = JS_ReadBigNum(s, tag);
35822         break;
35823 #endif
35824     case BC_TAG_OBJECT_REFERENCE:
35825         {
35826             uint32_t val;
35827             if (!s->allow_reference)
35828                 return JS_ThrowSyntaxError(ctx, "object references are not allowed");
35829             if (bc_get_leb128(s, &val))
35830                 return JS_EXCEPTION;
35831             bc_read_trace(s, "%u\n", val);
35832             if (val >= s->objects_count) {
35833                 return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)",
35834                                            val, s->objects_count);
35835             }
35836             obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val]));
35837         }
35838         break;
35839     default:
35840     invalid_tag:
35841         return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)",
35842                                    tag, (unsigned int)(s->ptr - s->buf_start));
35843     }
35844     bc_read_trace(s, "}\n");
35845     return obj;
35846 }
35847 
JS_ReadObjectAtoms(BCReaderState * s)35848 static int JS_ReadObjectAtoms(BCReaderState *s)
35849 {
35850     uint8_t v8;
35851     JSString *p;
35852     int i;
35853     JSAtom atom;
35854 
35855     if (bc_get_u8(s, &v8))
35856         return -1;
35857     /* XXX: could support byte swapped input */
35858     if (v8 != BC_VERSION) {
35859         JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)",
35860                             v8, BC_VERSION);
35861         return -1;
35862     }
35863     if (bc_get_leb128(s, &s->idx_to_atom_count))
35864         return -1;
35865 
35866     bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count);
35867 
35868     if (s->idx_to_atom_count != 0) {
35869         s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count *
35870                                     sizeof(s->idx_to_atom[0]));
35871         if (!s->idx_to_atom)
35872             return s->error_state = -1;
35873     }
35874     for(i = 0; i < s->idx_to_atom_count; i++) {
35875         p = JS_ReadString(s);
35876         if (!p)
35877             return -1;
35878         atom = JS_NewAtomStr(s->ctx, p);
35879         if (atom == JS_ATOM_NULL)
35880             return s->error_state = -1;
35881         s->idx_to_atom[i] = atom;
35882         if (s->is_rom_data && (atom != (i + s->first_atom)))
35883             s->is_rom_data = FALSE; /* atoms must be relocated */
35884     }
35885     bc_read_trace(s, "}\n");
35886     return 0;
35887 }
35888 
bc_reader_free(BCReaderState * s)35889 static void bc_reader_free(BCReaderState *s)
35890 {
35891     int i;
35892     if (s->idx_to_atom) {
35893         for(i = 0; i < s->idx_to_atom_count; i++) {
35894             JS_FreeAtom(s->ctx, s->idx_to_atom[i]);
35895         }
35896         js_free(s->ctx, s->idx_to_atom);
35897     }
35898     js_free(s->ctx, s->objects);
35899 }
35900 
JS_ReadObject(JSContext * ctx,const uint8_t * buf,size_t buf_len,int flags)35901 JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
35902                        int flags)
35903 {
35904     BCReaderState ss, *s = &ss;
35905     JSValue obj;
35906 
35907     ctx->binary_object_count += 1;
35908     ctx->binary_object_size += buf_len;
35909 
35910     memset(s, 0, sizeof(*s));
35911     s->ctx = ctx;
35912     s->buf_start = buf;
35913     s->buf_end = buf + buf_len;
35914     s->ptr = buf;
35915     s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0);
35916     s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0);
35917     s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0);
35918     s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0);
35919     if (s->allow_bytecode)
35920         s->first_atom = JS_ATOM_END;
35921     else
35922         s->first_atom = 1;
35923     if (JS_ReadObjectAtoms(s)) {
35924         obj = JS_EXCEPTION;
35925     } else {
35926         obj = JS_ReadObjectRec(s);
35927     }
35928     bc_reader_free(s);
35929     return obj;
35930 }
35931 
35932 /*******************************************************************/
35933 /* runtime functions & objects */
35934 
35935 static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val,
35936                                      int argc, JSValueConst *argv);
35937 static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val,
35938                                       int argc, JSValueConst *argv);
35939 static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val,
35940                                      int argc, JSValueConst *argv);
35941 
check_function(JSContext * ctx,JSValueConst obj)35942 static int check_function(JSContext *ctx, JSValueConst obj)
35943 {
35944     if (likely(JS_IsFunction(ctx, obj)))
35945         return 0;
35946     JS_ThrowTypeError(ctx, "not a function");
35947     return -1;
35948 }
35949 
check_exception_free(JSContext * ctx,JSValue obj)35950 static int check_exception_free(JSContext *ctx, JSValue obj)
35951 {
35952     JS_FreeValue(ctx, obj);
35953     return JS_IsException(obj);
35954 }
35955 
find_atom(JSContext * ctx,const char * name)35956 static JSAtom find_atom(JSContext *ctx, const char *name)
35957 {
35958     JSAtom atom;
35959     int len;
35960 
35961     if (*name == '[') {
35962         name++;
35963         len = strlen(name) - 1;
35964         /* We assume 8 bit non null strings, which is the case for these
35965            symbols */
35966         for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) {
35967             JSAtomStruct *p = ctx->rt->atom_array[atom];
35968             JSString *str = p;
35969             if (str->len == len && !memcmp(str->u.str8, name, len))
35970                 return JS_DupAtom(ctx, atom);
35971         }
35972         abort();
35973     } else {
35974         atom = JS_NewAtom(ctx, name);
35975     }
35976     return atom;
35977 }
35978 
JS_InstantiateFunctionListItem2(JSContext * ctx,JSObject * p,JSAtom atom,void * opaque)35979 static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
35980                                                JSAtom atom, void *opaque)
35981 {
35982     const JSCFunctionListEntry *e = opaque;
35983     JSValue val;
35984 
35985     switch(e->def_type) {
35986     case JS_DEF_CFUNC:
35987         val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
35988                                e->name, e->u.func.length, e->u.func.cproto, e->magic);
35989         break;
35990     case JS_DEF_PROP_STRING:
35991         val = JS_NewAtomString(ctx, e->u.str);
35992         break;
35993     case JS_DEF_OBJECT:
35994         val = JS_NewObject(ctx);
35995         JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);
35996         break;
35997     default:
35998         abort();
35999     }
36000     return val;
36001 }
36002 
JS_InstantiateFunctionListItem(JSContext * ctx,JSValueConst obj,JSAtom atom,const JSCFunctionListEntry * e)36003 static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj,
36004                                           JSAtom atom,
36005                                           const JSCFunctionListEntry *e)
36006 {
36007     JSValue val;
36008     int prop_flags = e->prop_flags;
36009 
36010     switch(e->def_type) {
36011     case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */
36012         {
36013             JSAtom atom1 = find_atom(ctx, e->u.alias.name);
36014             switch (e->u.alias.base) {
36015             case -1:
36016                 val = JS_GetProperty(ctx, obj, atom1);
36017                 break;
36018             case 0:
36019                 val = JS_GetProperty(ctx, ctx->global_obj, atom1);
36020                 break;
36021             case 1:
36022                 val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1);
36023                 break;
36024             default:
36025                 abort();
36026             }
36027             JS_FreeAtom(ctx, atom1);
36028             if (atom == JS_ATOM_Symbol_toPrimitive) {
36029                 /* Symbol.toPrimitive functions are not writable */
36030                 prop_flags = JS_PROP_CONFIGURABLE;
36031             } else if (atom == JS_ATOM_Symbol_hasInstance) {
36032                 /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
36033                 prop_flags = 0;
36034             }
36035         }
36036         break;
36037     case JS_DEF_CFUNC:
36038         if (atom == JS_ATOM_Symbol_toPrimitive) {
36039             /* Symbol.toPrimitive functions are not writable */
36040             prop_flags = JS_PROP_CONFIGURABLE;
36041         } else if (atom == JS_ATOM_Symbol_hasInstance) {
36042             /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
36043             prop_flags = 0;
36044         }
36045         JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
36046                                   (void *)e, prop_flags);
36047         return 0;
36048     case JS_DEF_CGETSET: /* XXX: use autoinit again ? */
36049     case JS_DEF_CGETSET_MAGIC:
36050         {
36051             JSValue getter, setter;
36052             char buf[64];
36053 
36054             getter = JS_UNDEFINED;
36055             if (e->u.getset.get.generic) {
36056                 snprintf(buf, sizeof(buf), "get %s", e->name);
36057                 getter = JS_NewCFunction2(ctx, e->u.getset.get.generic,
36058                                           buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter,
36059                                           e->magic);
36060             }
36061             setter = JS_UNDEFINED;
36062             if (e->u.getset.set.generic) {
36063                 snprintf(buf, sizeof(buf), "set %s", e->name);
36064                 setter = JS_NewCFunction2(ctx, e->u.getset.set.generic,
36065                                           buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter,
36066                                           e->magic);
36067             }
36068             JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags);
36069             return 0;
36070         }
36071         break;
36072     case JS_DEF_PROP_INT32:
36073         val = JS_NewInt32(ctx, e->u.i32);
36074         break;
36075     case JS_DEF_PROP_INT64:
36076         val = JS_NewInt64(ctx, e->u.i64);
36077         break;
36078     case JS_DEF_PROP_DOUBLE:
36079         val = __JS_NewFloat64(ctx, e->u.f64);
36080         break;
36081     case JS_DEF_PROP_UNDEFINED:
36082         val = JS_UNDEFINED;
36083         break;
36084     case JS_DEF_PROP_STRING:
36085     case JS_DEF_OBJECT:
36086         JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
36087                                   (void *)e, prop_flags);
36088         return 0;
36089     default:
36090         abort();
36091     }
36092     JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags);
36093     return 0;
36094 }
36095 
JS_SetPropertyFunctionList(JSContext * ctx,JSValueConst obj,const JSCFunctionListEntry * tab,int len)36096 void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,
36097                                 const JSCFunctionListEntry *tab, int len)
36098 {
36099     int i;
36100 
36101     for (i = 0; i < len; i++) {
36102         const JSCFunctionListEntry *e = &tab[i];
36103         JSAtom atom = find_atom(ctx, e->name);
36104         JS_InstantiateFunctionListItem(ctx, obj, atom, e);
36105         JS_FreeAtom(ctx, atom);
36106     }
36107 }
36108 
JS_AddModuleExportList(JSContext * ctx,JSModuleDef * m,const JSCFunctionListEntry * tab,int len)36109 int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,
36110                            const JSCFunctionListEntry *tab, int len)
36111 {
36112     int i;
36113     for(i = 0; i < len; i++) {
36114         if (JS_AddModuleExport(ctx, m, tab[i].name))
36115             return -1;
36116     }
36117     return 0;
36118 }
36119 
JS_SetModuleExportList(JSContext * ctx,JSModuleDef * m,const JSCFunctionListEntry * tab,int len)36120 int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
36121                            const JSCFunctionListEntry *tab, int len)
36122 {
36123     int i;
36124     JSValue val;
36125 
36126     for(i = 0; i < len; i++) {
36127         const JSCFunctionListEntry *e = &tab[i];
36128         switch(e->def_type) {
36129         case JS_DEF_CFUNC:
36130             val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
36131                                    e->name, e->u.func.length, e->u.func.cproto, e->magic);
36132             break;
36133         case JS_DEF_PROP_STRING:
36134             val = JS_NewString(ctx, e->u.str);
36135             break;
36136         case JS_DEF_PROP_INT32:
36137             val = JS_NewInt32(ctx, e->u.i32);
36138             break;
36139         case JS_DEF_PROP_INT64:
36140             val = JS_NewInt64(ctx, e->u.i64);
36141             break;
36142         case JS_DEF_PROP_DOUBLE:
36143             val = __JS_NewFloat64(ctx, e->u.f64);
36144             break;
36145         case JS_DEF_OBJECT:
36146             val = JS_NewObject(ctx);
36147             JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);
36148             break;
36149         default:
36150             abort();
36151         }
36152         if (JS_SetModuleExport(ctx, m, e->name, val))
36153             return -1;
36154     }
36155     return 0;
36156 }
36157 
36158 /* Note: 'func_obj' is not necessarily a constructor */
JS_SetConstructor2(JSContext * ctx,JSValueConst func_obj,JSValueConst proto,int proto_flags,int ctor_flags)36159 static void JS_SetConstructor2(JSContext *ctx,
36160                                JSValueConst func_obj,
36161                                JSValueConst proto,
36162                                int proto_flags, int ctor_flags)
36163 {
36164     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype,
36165                            JS_DupValue(ctx, proto), proto_flags);
36166     JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
36167                            JS_DupValue(ctx, func_obj),
36168                            ctor_flags);
36169     set_cycle_flag(ctx, func_obj);
36170     set_cycle_flag(ctx, proto);
36171 }
36172 
JS_SetConstructor(JSContext * ctx,JSValueConst func_obj,JSValueConst proto)36173 void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
36174                        JSValueConst proto)
36175 {
36176     JS_SetConstructor2(ctx, func_obj, proto,
36177                        0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
36178 }
36179 
JS_NewGlobalCConstructor2(JSContext * ctx,JSValue func_obj,const char * name,JSValueConst proto)36180 static void JS_NewGlobalCConstructor2(JSContext *ctx,
36181                                       JSValue func_obj,
36182                                       const char *name,
36183                                       JSValueConst proto)
36184 {
36185     JS_DefinePropertyValueStr(ctx, ctx->global_obj, name,
36186                            JS_DupValue(ctx, func_obj),
36187                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
36188     JS_SetConstructor(ctx, func_obj, proto);
36189     JS_FreeValue(ctx, func_obj);
36190 }
36191 
JS_NewGlobalCConstructor(JSContext * ctx,const char * name,JSCFunction * func,int length,JSValueConst proto)36192 static JSValueConst JS_NewGlobalCConstructor(JSContext *ctx, const char *name,
36193                                              JSCFunction *func, int length,
36194                                              JSValueConst proto)
36195 {
36196     JSValue func_obj;
36197     func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0);
36198     JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
36199     return func_obj;
36200 }
36201 
JS_NewGlobalCConstructorOnly(JSContext * ctx,const char * name,JSCFunction * func,int length,JSValueConst proto)36202 static JSValueConst JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name,
36203                                                  JSCFunction *func, int length,
36204                                                  JSValueConst proto)
36205 {
36206     JSValue func_obj;
36207     func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0);
36208     JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
36209     return func_obj;
36210 }
36211 
js_global_eval(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36212 static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val,
36213                               int argc, JSValueConst *argv)
36214 {
36215     return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1);
36216 }
36217 
js_global_isNaN(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36218 static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val,
36219                                int argc, JSValueConst *argv)
36220 {
36221     double d;
36222 
36223     /* XXX: does this work for bigfloat? */
36224     if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
36225         return JS_EXCEPTION;
36226     return JS_NewBool(ctx, isnan(d));
36227 }
36228 
js_global_isFinite(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36229 static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val,
36230                                   int argc, JSValueConst *argv)
36231 {
36232     BOOL res;
36233     double d;
36234     if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
36235         return JS_EXCEPTION;
36236     res = isfinite(d);
36237     return JS_NewBool(ctx, res);
36238 }
36239 
36240 /* Object class */
36241 
JS_ToObject(JSContext * ctx,JSValueConst val)36242 static JSValue JS_ToObject(JSContext *ctx, JSValueConst val)
36243 {
36244     int tag = JS_VALUE_GET_NORM_TAG(val);
36245     JSValue obj;
36246 
36247     switch(tag) {
36248     default:
36249     case JS_TAG_NULL:
36250     case JS_TAG_UNDEFINED:
36251         return JS_ThrowTypeError(ctx, "cannot convert to object");
36252     case JS_TAG_OBJECT:
36253     case JS_TAG_EXCEPTION:
36254         return JS_DupValue(ctx, val);
36255 #ifdef CONFIG_BIGNUM
36256     case JS_TAG_BIG_INT:
36257         obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);
36258         goto set_value;
36259     case JS_TAG_BIG_FLOAT:
36260         obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT);
36261         goto set_value;
36262     case JS_TAG_BIG_DECIMAL:
36263         obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL);
36264         goto set_value;
36265 #endif
36266     case JS_TAG_INT:
36267     case JS_TAG_FLOAT64:
36268         obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
36269         goto set_value;
36270     case JS_TAG_STRING:
36271         /* XXX: should call the string constructor */
36272         {
36273             JSString *p1 = JS_VALUE_GET_STRING(val);
36274             obj = JS_NewObjectClass(ctx, JS_CLASS_STRING);
36275             JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
36276         }
36277         goto set_value;
36278     case JS_TAG_BOOL:
36279         obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN);
36280         goto set_value;
36281     case JS_TAG_SYMBOL:
36282         obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL);
36283     set_value:
36284         if (!JS_IsException(obj))
36285             JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val));
36286         return obj;
36287     }
36288 }
36289 
JS_ToObjectFree(JSContext * ctx,JSValue val)36290 static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val)
36291 {
36292     JSValue obj = JS_ToObject(ctx, val);
36293     JS_FreeValue(ctx, val);
36294     return obj;
36295 }
36296 
js_obj_to_desc(JSContext * ctx,JSPropertyDescriptor * d,JSValueConst desc)36297 static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
36298                           JSValueConst desc)
36299 {
36300     JSValue val, getter, setter;
36301     int flags;
36302 
36303     if (!JS_IsObject(desc)) {
36304         JS_ThrowTypeErrorNotAnObject(ctx);
36305         return -1;
36306     }
36307     flags = 0;
36308     val = JS_UNDEFINED;
36309     getter = JS_UNDEFINED;
36310     setter = JS_UNDEFINED;
36311     if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) {
36312         JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable);
36313         if (JS_IsException(prop))
36314             goto fail;
36315         flags |= JS_PROP_HAS_CONFIGURABLE;
36316         if (JS_ToBoolFree(ctx, prop))
36317             flags |= JS_PROP_CONFIGURABLE;
36318     }
36319     if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) {
36320         JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable);
36321         if (JS_IsException(prop))
36322             goto fail;
36323         flags |= JS_PROP_HAS_WRITABLE;
36324         if (JS_ToBoolFree(ctx, prop))
36325             flags |= JS_PROP_WRITABLE;
36326     }
36327     if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) {
36328         JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable);
36329         if (JS_IsException(prop))
36330             goto fail;
36331         flags |= JS_PROP_HAS_ENUMERABLE;
36332         if (JS_ToBoolFree(ctx, prop))
36333             flags |= JS_PROP_ENUMERABLE;
36334     }
36335     if (JS_HasProperty(ctx, desc, JS_ATOM_value)) {
36336         flags |= JS_PROP_HAS_VALUE;
36337         val = JS_GetProperty(ctx, desc, JS_ATOM_value);
36338         if (JS_IsException(val))
36339             goto fail;
36340     }
36341     if (JS_HasProperty(ctx, desc, JS_ATOM_get)) {
36342         flags |= JS_PROP_HAS_GET;
36343         getter = JS_GetProperty(ctx, desc, JS_ATOM_get);
36344         if (JS_IsException(getter) ||
36345             !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) {
36346             JS_ThrowTypeError(ctx, "invalid getter");
36347             goto fail;
36348         }
36349     }
36350     if (JS_HasProperty(ctx, desc, JS_ATOM_set)) {
36351         flags |= JS_PROP_HAS_SET;
36352         setter = JS_GetProperty(ctx, desc, JS_ATOM_set);
36353         if (JS_IsException(setter) ||
36354             !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) {
36355             JS_ThrowTypeError(ctx, "invalid setter");
36356             goto fail;
36357         }
36358     }
36359     if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) &&
36360         (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) {
36361         JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable");
36362         goto fail;
36363     }
36364     d->flags = flags;
36365     d->value = val;
36366     d->getter = getter;
36367     d->setter = setter;
36368     return 0;
36369  fail:
36370     JS_FreeValue(ctx, val);
36371     JS_FreeValue(ctx, getter);
36372     JS_FreeValue(ctx, setter);
36373     return -1;
36374 }
36375 
JS_DefinePropertyDesc(JSContext * ctx,JSValueConst obj,JSAtom prop,JSValueConst desc,int flags)36376 static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
36377                                              JSAtom prop, JSValueConst desc,
36378                                              int flags)
36379 {
36380     JSPropertyDescriptor d;
36381     int ret;
36382 
36383     if (js_obj_to_desc(ctx, &d, desc) < 0)
36384         return -1;
36385 
36386     ret = JS_DefineProperty(ctx, obj, prop,
36387                             d.value, d.getter, d.setter, d.flags | flags);
36388     js_free_desc(ctx, &d);
36389     return ret;
36390 }
36391 
JS_ObjectDefineProperties(JSContext * ctx,JSValueConst obj,JSValueConst properties)36392 static __exception int JS_ObjectDefineProperties(JSContext *ctx,
36393                                                  JSValueConst obj,
36394                                                  JSValueConst properties)
36395 {
36396     JSValue props, desc;
36397     JSObject *p;
36398     JSPropertyEnum *atoms;
36399     uint32_t len, i;
36400     int ret = -1;
36401 
36402     if (!JS_IsObject(obj)) {
36403         JS_ThrowTypeErrorNotAnObject(ctx);
36404         return -1;
36405     }
36406     desc = JS_UNDEFINED;
36407     props = JS_ToObject(ctx, properties);
36408     if (JS_IsException(props))
36409         return -1;
36410     p = JS_VALUE_GET_OBJ(props);
36411     if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0)
36412         goto exception;
36413     for(i = 0; i < len; i++) {
36414         JS_FreeValue(ctx, desc);
36415         desc = JS_GetProperty(ctx, props, atoms[i].atom);
36416         if (JS_IsException(desc))
36417             goto exception;
36418         if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0)
36419             goto exception;
36420     }
36421     ret = 0;
36422 
36423 exception:
36424     js_free_prop_enum(ctx, atoms, len);
36425     JS_FreeValue(ctx, props);
36426     JS_FreeValue(ctx, desc);
36427     return ret;
36428 }
36429 
js_object_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)36430 static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target,
36431                                      int argc, JSValueConst *argv)
36432 {
36433     JSValue ret;
36434     if (!JS_IsUndefined(new_target) &&
36435         JS_VALUE_GET_OBJ(new_target) !=
36436         JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) {
36437         ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
36438     } else {
36439         int tag = JS_VALUE_GET_NORM_TAG(argv[0]);
36440         switch(tag) {
36441         case JS_TAG_NULL:
36442         case JS_TAG_UNDEFINED:
36443             ret = JS_NewObject(ctx);
36444             break;
36445         default:
36446             ret = JS_ToObject(ctx, argv[0]);
36447             break;
36448         }
36449     }
36450     return ret;
36451 }
36452 
js_object_create(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36453 static JSValue js_object_create(JSContext *ctx, JSValueConst this_val,
36454                                 int argc, JSValueConst *argv)
36455 {
36456     JSValueConst proto, props;
36457     JSValue obj;
36458 
36459     proto = argv[0];
36460     if (!JS_IsObject(proto) && !JS_IsNull(proto))
36461         return JS_ThrowTypeError(ctx, "not a prototype");
36462     obj = JS_NewObjectProto(ctx, proto);
36463     if (JS_IsException(obj))
36464         return JS_EXCEPTION;
36465     props = argv[1];
36466     if (!JS_IsUndefined(props)) {
36467         if (JS_ObjectDefineProperties(ctx, obj, props)) {
36468             JS_FreeValue(ctx, obj);
36469             return JS_EXCEPTION;
36470         }
36471     }
36472     return obj;
36473 }
36474 
js_object_getPrototypeOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)36475 static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val,
36476                                         int argc, JSValueConst *argv, int magic)
36477 {
36478     JSValueConst val;
36479 
36480     val = argv[0];
36481     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) {
36482         /* ES6 feature non compatible with ES5.1: primitive types are
36483            accepted */
36484         if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL ||
36485             JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED)
36486             return JS_ThrowTypeErrorNotAnObject(ctx);
36487     }
36488     return JS_GetPrototype(ctx, val);
36489 }
36490 
js_object_setPrototypeOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36491 static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
36492                                         int argc, JSValueConst *argv)
36493 {
36494     JSValueConst obj;
36495     obj = argv[0];
36496     if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0)
36497         return JS_EXCEPTION;
36498     return JS_DupValue(ctx, obj);
36499 }
36500 
36501 /* magic = 1 if called as Reflect.defineProperty */
js_object_defineProperty(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)36502 static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val,
36503                                         int argc, JSValueConst *argv, int magic)
36504 {
36505     JSValueConst obj, prop, desc;
36506     int ret, flags;
36507     JSAtom atom;
36508 
36509     obj = argv[0];
36510     prop = argv[1];
36511     desc = argv[2];
36512 
36513     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
36514         return JS_ThrowTypeErrorNotAnObject(ctx);
36515     atom = JS_ValueToAtom(ctx, prop);
36516     if (unlikely(atom == JS_ATOM_NULL))
36517         return JS_EXCEPTION;
36518     flags = 0;
36519     if (!magic)
36520         flags |= JS_PROP_THROW;
36521     ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags);
36522     JS_FreeAtom(ctx, atom);
36523     if (ret < 0) {
36524         return JS_EXCEPTION;
36525     } else if (magic) {
36526         return JS_NewBool(ctx, ret);
36527     } else {
36528         return JS_DupValue(ctx, obj);
36529     }
36530 }
36531 
js_object_defineProperties(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36532 static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val,
36533                                           int argc, JSValueConst *argv)
36534 {
36535     // defineProperties(obj, properties)
36536     JSValueConst obj = argv[0];
36537 
36538     if (JS_ObjectDefineProperties(ctx, obj, argv[1]))
36539         return JS_EXCEPTION;
36540     else
36541         return JS_DupValue(ctx, obj);
36542 }
36543 
36544 /* magic = 1 if called as __defineSetter__ */
js_object___defineGetter__(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)36545 static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val,
36546                                           int argc, JSValueConst *argv, int magic)
36547 {
36548     JSValue obj;
36549     JSValueConst prop, value, get, set;
36550     int ret, flags;
36551     JSAtom atom;
36552 
36553     prop = argv[0];
36554     value = argv[1];
36555 
36556     obj = JS_ToObject(ctx, this_val);
36557     if (JS_IsException(obj))
36558         return JS_EXCEPTION;
36559 
36560     if (check_function(ctx, value)) {
36561         JS_FreeValue(ctx, obj);
36562         return JS_EXCEPTION;
36563     }
36564     atom = JS_ValueToAtom(ctx, prop);
36565     if (unlikely(atom == JS_ATOM_NULL)) {
36566         JS_FreeValue(ctx, obj);
36567         return JS_EXCEPTION;
36568     }
36569     flags = JS_PROP_THROW |
36570         JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE |
36571         JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE;
36572     if (magic) {
36573         get = JS_UNDEFINED;
36574         set = value;
36575         flags |= JS_PROP_HAS_SET;
36576     } else {
36577         get = value;
36578         set = JS_UNDEFINED;
36579         flags |= JS_PROP_HAS_GET;
36580     }
36581     ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags);
36582     JS_FreeValue(ctx, obj);
36583     JS_FreeAtom(ctx, atom);
36584     if (ret < 0) {
36585         return JS_EXCEPTION;
36586     } else {
36587         return JS_UNDEFINED;
36588     }
36589 }
36590 
js_object_getOwnPropertyDescriptor(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)36591 static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val,
36592                                                   int argc, JSValueConst *argv, int magic)
36593 {
36594     JSValueConst prop;
36595     JSAtom atom;
36596     JSValue ret, obj;
36597     JSPropertyDescriptor desc;
36598     int res, flags;
36599 
36600     if (magic) {
36601         /* Reflect.getOwnPropertyDescriptor case */
36602         if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
36603             return JS_ThrowTypeErrorNotAnObject(ctx);
36604         obj = JS_DupValue(ctx, argv[0]);
36605     } else {
36606         obj = JS_ToObject(ctx, argv[0]);
36607         if (JS_IsException(obj))
36608             return obj;
36609     }
36610     prop = argv[1];
36611     atom = JS_ValueToAtom(ctx, prop);
36612     if (unlikely(atom == JS_ATOM_NULL))
36613         goto exception;
36614     ret = JS_UNDEFINED;
36615     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
36616         res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom);
36617         if (res < 0)
36618             goto exception;
36619         if (res) {
36620             ret = JS_NewObject(ctx);
36621             if (JS_IsException(ret))
36622                 goto exception1;
36623             flags = JS_PROP_C_W_E | JS_PROP_THROW;
36624             if (desc.flags & JS_PROP_GETSET) {
36625                 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0
36626                 ||  JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0)
36627                     goto exception1;
36628             } else {
36629                 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0
36630                 ||  JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
36631                                            JS_NewBool(ctx, (desc.flags & JS_PROP_WRITABLE) != 0), flags) < 0)
36632                     goto exception1;
36633             }
36634             if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
36635                                        JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0), flags) < 0
36636             ||  JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
36637                                        JS_NewBool(ctx, (desc.flags & JS_PROP_CONFIGURABLE) != 0), flags) < 0)
36638                 goto exception1;
36639             js_free_desc(ctx, &desc);
36640         }
36641     }
36642     JS_FreeAtom(ctx, atom);
36643     JS_FreeValue(ctx, obj);
36644     return ret;
36645 
36646 exception1:
36647     js_free_desc(ctx, &desc);
36648     JS_FreeValue(ctx, ret);
36649 exception:
36650     JS_FreeAtom(ctx, atom);
36651     JS_FreeValue(ctx, obj);
36652     return JS_EXCEPTION;
36653 }
36654 
js_object_getOwnPropertyDescriptors(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36655 static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val,
36656                                                    int argc, JSValueConst *argv)
36657 {
36658     //getOwnPropertyDescriptors(obj)
36659     JSValue obj, r;
36660     JSObject *p;
36661     JSPropertyEnum *props;
36662     uint32_t len, i;
36663 
36664     r = JS_UNDEFINED;
36665     obj = JS_ToObject(ctx, argv[0]);
36666     if (JS_IsException(obj))
36667         return JS_EXCEPTION;
36668 
36669     p = JS_VALUE_GET_OBJ(obj);
36670     if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p,
36671                                JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
36672         goto exception;
36673     r = JS_NewObject(ctx);
36674     if (JS_IsException(r))
36675         goto exception;
36676     for(i = 0; i < len; i++) {
36677         JSValue atomValue, desc;
36678         JSValueConst args[2];
36679 
36680         atomValue = JS_AtomToValue(ctx, props[i].atom);
36681         if (JS_IsException(atomValue))
36682             goto exception;
36683         args[0] = obj;
36684         args[1] = atomValue;
36685         desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0);
36686         JS_FreeValue(ctx, atomValue);
36687         if (JS_IsException(desc))
36688             goto exception;
36689         if (!JS_IsUndefined(desc)) {
36690             if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc,
36691                                        JS_PROP_C_W_E | JS_PROP_THROW) < 0)
36692                 goto exception;
36693         }
36694     }
36695     js_free_prop_enum(ctx, props, len);
36696     JS_FreeValue(ctx, obj);
36697     return r;
36698 
36699 exception:
36700     js_free_prop_enum(ctx, props, len);
36701     JS_FreeValue(ctx, obj);
36702     JS_FreeValue(ctx, r);
36703     return JS_EXCEPTION;
36704 }
36705 
JS_GetOwnPropertyNames2(JSContext * ctx,JSValueConst obj1,int flags,int kind)36706 static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1,
36707                                        int flags, int kind)
36708 {
36709     JSValue obj, r, val, key, value;
36710     JSObject *p;
36711     JSPropertyEnum *atoms;
36712     uint32_t len, i, j;
36713 
36714     r = JS_UNDEFINED;
36715     val = JS_UNDEFINED;
36716     obj = JS_ToObject(ctx, obj1);
36717     if (JS_IsException(obj))
36718         return JS_EXCEPTION;
36719     p = JS_VALUE_GET_OBJ(obj);
36720     if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY))
36721         goto exception;
36722     r = JS_NewArray(ctx);
36723     if (JS_IsException(r))
36724         goto exception;
36725     for(j = i = 0; i < len; i++) {
36726         JSAtom atom = atoms[i].atom;
36727         if (flags & JS_GPN_ENUM_ONLY) {
36728             JSPropertyDescriptor desc;
36729             int res;
36730 
36731             /* Check if property is still enumerable */
36732             res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
36733             if (res < 0)
36734                 goto exception;
36735             if (!res)
36736                 continue;
36737             js_free_desc(ctx, &desc);
36738             if (!(desc.flags & JS_PROP_ENUMERABLE))
36739                 continue;
36740         }
36741         switch(kind) {
36742         default:
36743         case JS_ITERATOR_KIND_KEY:
36744             val = JS_AtomToValue(ctx, atom);
36745             if (JS_IsException(val))
36746                 goto exception;
36747             break;
36748         case JS_ITERATOR_KIND_VALUE:
36749             val = JS_GetProperty(ctx, obj, atom);
36750             if (JS_IsException(val))
36751                 goto exception;
36752             break;
36753         case JS_ITERATOR_KIND_KEY_AND_VALUE:
36754             val = JS_NewArray(ctx);
36755             if (JS_IsException(val))
36756                 goto exception;
36757             key = JS_AtomToValue(ctx, atom);
36758             if (JS_IsException(key))
36759                 goto exception1;
36760             if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0)
36761                 goto exception1;
36762             value = JS_GetProperty(ctx, obj, atom);
36763             if (JS_IsException(value))
36764                 goto exception1;
36765             if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0)
36766                 goto exception1;
36767             break;
36768         }
36769         if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0)
36770             goto exception;
36771     }
36772     goto done;
36773 
36774 exception1:
36775     JS_FreeValue(ctx, val);
36776 exception:
36777     JS_FreeValue(ctx, r);
36778     r = JS_EXCEPTION;
36779 done:
36780     js_free_prop_enum(ctx, atoms, len);
36781     JS_FreeValue(ctx, obj);
36782     return r;
36783 }
36784 
js_object_getOwnPropertyNames(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36785 static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val,
36786                                              int argc, JSValueConst *argv)
36787 {
36788     return JS_GetOwnPropertyNames2(ctx, argv[0],
36789                                    JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY);
36790 }
36791 
js_object_getOwnPropertySymbols(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36792 static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val,
36793                                              int argc, JSValueConst *argv)
36794 {
36795     return JS_GetOwnPropertyNames2(ctx, argv[0],
36796                                    JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY);
36797 }
36798 
js_object_keys(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int kind)36799 static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val,
36800                               int argc, JSValueConst *argv, int kind)
36801 {
36802     return JS_GetOwnPropertyNames2(ctx, argv[0],
36803                                    JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind);
36804 }
36805 
js_object_isExtensible(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int reflect)36806 static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val,
36807                                       int argc, JSValueConst *argv, int reflect)
36808 {
36809     JSValueConst obj;
36810     int ret;
36811 
36812     obj = argv[0];
36813     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
36814         if (reflect)
36815             return JS_ThrowTypeErrorNotAnObject(ctx);
36816         else
36817             return JS_FALSE;
36818     }
36819     ret = JS_IsExtensible(ctx, obj);
36820     if (ret < 0)
36821         return JS_EXCEPTION;
36822     else
36823         return JS_NewBool(ctx, ret);
36824 }
36825 
js_object_preventExtensions(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int reflect)36826 static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val,
36827                                            int argc, JSValueConst *argv, int reflect)
36828 {
36829     JSValueConst obj;
36830     int ret;
36831 
36832     obj = argv[0];
36833     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
36834         if (reflect)
36835             return JS_ThrowTypeErrorNotAnObject(ctx);
36836         else
36837             return JS_DupValue(ctx, obj);
36838     }
36839     ret = JS_PreventExtensions(ctx, obj);
36840     if (ret < 0)
36841         return JS_EXCEPTION;
36842     if (reflect) {
36843         return JS_NewBool(ctx, ret);
36844     } else {
36845         if (!ret)
36846             return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
36847         return JS_DupValue(ctx, obj);
36848     }
36849 }
36850 
js_object_hasOwnProperty(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36851 static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,
36852                                         int argc, JSValueConst *argv)
36853 {
36854     JSValue obj;
36855     JSAtom atom;
36856     JSObject *p;
36857     BOOL ret;
36858 
36859     atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */
36860     if (unlikely(atom == JS_ATOM_NULL))
36861         return JS_EXCEPTION;
36862     obj = JS_ToObject(ctx, this_val);
36863     if (JS_IsException(obj)) {
36864         JS_FreeAtom(ctx, atom);
36865         return obj;
36866     }
36867     p = JS_VALUE_GET_OBJ(obj);
36868     ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
36869     JS_FreeAtom(ctx, atom);
36870     JS_FreeValue(ctx, obj);
36871     if (ret < 0)
36872         return JS_EXCEPTION;
36873     else
36874         return JS_NewBool(ctx, ret);
36875 }
36876 
js_object_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36877 static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val,
36878                                  int argc, JSValueConst *argv)
36879 {
36880     return JS_ToObject(ctx, this_val);
36881 }
36882 
js_object_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36883 static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val,
36884                                   int argc, JSValueConst *argv)
36885 {
36886     JSValue obj, tag;
36887     int is_array;
36888     JSAtom atom;
36889     JSObject *p;
36890 
36891     if (JS_IsNull(this_val)) {
36892         tag = JS_NewString(ctx, "Null");
36893     } else if (JS_IsUndefined(this_val)) {
36894         tag = JS_NewString(ctx, "Undefined");
36895     } else {
36896         obj = JS_ToObject(ctx, this_val);
36897         if (JS_IsException(obj))
36898             return obj;
36899         is_array = JS_IsArray(ctx, obj);
36900         if (is_array < 0) {
36901             JS_FreeValue(ctx, obj);
36902             return JS_EXCEPTION;
36903         }
36904         if (is_array) {
36905             atom = JS_ATOM_Array;
36906         } else if (JS_IsFunction(ctx, obj)) {
36907             atom = JS_ATOM_Function;
36908         } else {
36909             p = JS_VALUE_GET_OBJ(obj);
36910             switch(p->class_id) {
36911             case JS_CLASS_STRING:
36912             case JS_CLASS_ARGUMENTS:
36913             case JS_CLASS_MAPPED_ARGUMENTS:
36914             case JS_CLASS_ERROR:
36915             case JS_CLASS_BOOLEAN:
36916             case JS_CLASS_NUMBER:
36917             case JS_CLASS_DATE:
36918             case JS_CLASS_REGEXP:
36919                 atom = ctx->rt->class_array[p->class_id].class_name;
36920                 break;
36921             default:
36922                 atom = JS_ATOM_Object;
36923                 break;
36924             }
36925         }
36926         tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag);
36927         JS_FreeValue(ctx, obj);
36928         if (JS_IsException(tag))
36929             return JS_EXCEPTION;
36930         if (!JS_IsString(tag)) {
36931             JS_FreeValue(ctx, tag);
36932             tag = JS_AtomToString(ctx, atom);
36933         }
36934     }
36935     return JS_ConcatString3(ctx, "[object ", tag, "]");
36936 }
36937 
js_object_toLocaleString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36938 static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val,
36939                                         int argc, JSValueConst *argv)
36940 {
36941     return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL);
36942 }
36943 
js_object_assign(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)36944 static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val,
36945                                 int argc, JSValueConst *argv)
36946 {
36947     // Object.assign(obj, source1)
36948     JSValue obj, s;
36949     int i;
36950 
36951     s = JS_UNDEFINED;
36952     obj = JS_ToObject(ctx, argv[0]);
36953     if (JS_IsException(obj))
36954         goto exception;
36955     for (i = 1; i < argc; i++) {
36956         if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) {
36957             s = JS_ToObject(ctx, argv[i]);
36958             if (JS_IsException(s))
36959                 goto exception;
36960             if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE))
36961                 goto exception;
36962             JS_FreeValue(ctx, s);
36963         }
36964     }
36965     return obj;
36966 exception:
36967     JS_FreeValue(ctx, obj);
36968     JS_FreeValue(ctx, s);
36969     return JS_EXCEPTION;
36970 }
36971 
js_object_seal(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int freeze_flag)36972 static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val,
36973                               int argc, JSValueConst *argv, int freeze_flag)
36974 {
36975     JSValueConst obj = argv[0];
36976     JSObject *p;
36977     JSPropertyEnum *props;
36978     uint32_t len, i;
36979     int flags, desc_flags, res;
36980 
36981     if (!JS_IsObject(obj))
36982         return JS_DupValue(ctx, obj);
36983 
36984     res = JS_PreventExtensions(ctx, obj);
36985     if (res < 0)
36986         return JS_EXCEPTION;
36987     if (!res) {
36988         return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
36989     }
36990 
36991     p = JS_VALUE_GET_OBJ(obj);
36992     flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
36993     if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
36994         return JS_EXCEPTION;
36995 
36996     for(i = 0; i < len; i++) {
36997         JSPropertyDescriptor desc;
36998         JSAtom prop = props[i].atom;
36999 
37000         desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE;
37001         if (freeze_flag) {
37002             res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
37003             if (res < 0)
37004                 goto exception;
37005             if (res) {
37006                 if (desc.flags & JS_PROP_WRITABLE)
37007                     desc_flags |= JS_PROP_HAS_WRITABLE;
37008                 js_free_desc(ctx, &desc);
37009             }
37010         }
37011         if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED,
37012                               JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0)
37013             goto exception;
37014     }
37015     js_free_prop_enum(ctx, props, len);
37016     return JS_DupValue(ctx, obj);
37017 
37018  exception:
37019     js_free_prop_enum(ctx, props, len);
37020     return JS_EXCEPTION;
37021 }
37022 
js_object_isSealed(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int is_frozen)37023 static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val,
37024                                   int argc, JSValueConst *argv, int is_frozen)
37025 {
37026     JSValueConst obj = argv[0];
37027     JSObject *p;
37028     JSPropertyEnum *props;
37029     uint32_t len, i;
37030     int flags, res;
37031 
37032     if (!JS_IsObject(obj))
37033         return JS_TRUE;
37034 
37035     p = JS_VALUE_GET_OBJ(obj);
37036     flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
37037     if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
37038         return JS_EXCEPTION;
37039 
37040     for(i = 0; i < len; i++) {
37041         JSPropertyDescriptor desc;
37042         JSAtom prop = props[i].atom;
37043 
37044         res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
37045         if (res < 0)
37046             goto exception;
37047         if (res) {
37048             js_free_desc(ctx, &desc);
37049             if ((desc.flags & JS_PROP_CONFIGURABLE)
37050             ||  (is_frozen && (desc.flags & JS_PROP_WRITABLE))) {
37051                 res = FALSE;
37052                 goto done;
37053             }
37054         }
37055     }
37056     res = JS_IsExtensible(ctx, obj);
37057     if (res < 0)
37058         return JS_EXCEPTION;
37059     res ^= 1;
37060 done:
37061     js_free_prop_enum(ctx, props, len);
37062     return JS_NewBool(ctx, res);
37063 
37064 exception:
37065     js_free_prop_enum(ctx, props, len);
37066     return JS_EXCEPTION;
37067 }
37068 
js_object_fromEntries(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37069 static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val,
37070                                      int argc, JSValueConst *argv)
37071 {
37072     JSValue obj, iter, next_method = JS_UNDEFINED;
37073     JSValueConst iterable;
37074     BOOL done;
37075 
37076     /*  RequireObjectCoercible() not necessary because it is tested in
37077         JS_GetIterator() by JS_GetProperty() */
37078     iterable = argv[0];
37079 
37080     obj = JS_NewObject(ctx);
37081     if (JS_IsException(obj))
37082         return obj;
37083 
37084     iter = JS_GetIterator(ctx, iterable, FALSE);
37085     if (JS_IsException(iter))
37086         goto fail;
37087     next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
37088     if (JS_IsException(next_method))
37089         goto fail;
37090 
37091     for(;;) {
37092         JSValue key, value, item;
37093         item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
37094         if (JS_IsException(item))
37095             goto fail;
37096         if (done) {
37097             JS_FreeValue(ctx, item);
37098             break;
37099         }
37100 
37101         key = JS_UNDEFINED;
37102         value = JS_UNDEFINED;
37103         if (!JS_IsObject(item)) {
37104             JS_ThrowTypeErrorNotAnObject(ctx);
37105             goto fail1;
37106         }
37107         key = JS_GetPropertyUint32(ctx, item, 0);
37108         if (JS_IsException(key))
37109             goto fail1;
37110         value = JS_GetPropertyUint32(ctx, item, 1);
37111         if (JS_IsException(value)) {
37112             JS_FreeValue(ctx, key);
37113             goto fail1;
37114         }
37115         if (JS_DefinePropertyValueValue(ctx, obj, key, value,
37116                                         JS_PROP_C_W_E | JS_PROP_THROW) < 0) {
37117         fail1:
37118             JS_FreeValue(ctx, item);
37119             goto fail;
37120         }
37121         JS_FreeValue(ctx, item);
37122     }
37123     JS_FreeValue(ctx, next_method);
37124     JS_FreeValue(ctx, iter);
37125     return obj;
37126  fail:
37127     if (JS_IsObject(iter)) {
37128         /* close the iterator object, preserving pending exception */
37129         JS_IteratorClose(ctx, iter, TRUE);
37130     }
37131     JS_FreeValue(ctx, next_method);
37132     JS_FreeValue(ctx, iter);
37133     JS_FreeValue(ctx, obj);
37134     return JS_EXCEPTION;
37135 }
37136 
37137 #if 0
37138 /* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */
37139 static JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val,
37140                                           int argc, JSValueConst *argv)
37141 {
37142     int ret;
37143     ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]),
37144                                       JS_DupValue(ctx, argv[2]),
37145                                       JS_PROP_C_W_E | JS_PROP_THROW);
37146     if (ret < 0)
37147         return JS_EXCEPTION;
37148     else
37149         return JS_NewBool(ctx, ret);
37150 }
37151 
37152 static JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val,
37153                                     int argc, JSValueConst *argv)
37154 {
37155     return JS_ToObject(ctx, argv[0]);
37156 }
37157 
37158 static JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val,
37159                                        int argc, JSValueConst *argv)
37160 {
37161     int hint = HINT_NONE;
37162 
37163     if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT)
37164         hint = JS_VALUE_GET_INT(argv[1]);
37165 
37166     return JS_ToPrimitive(ctx, argv[0], hint);
37167 }
37168 #endif
37169 
37170 /* return an empty string if not an object */
js_object___getClass(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37171 static JSValue js_object___getClass(JSContext *ctx, JSValueConst this_val,
37172                                     int argc, JSValueConst *argv)
37173 {
37174     JSAtom atom;
37175     JSObject *p;
37176     uint32_t tag;
37177     int class_id;
37178 
37179     tag = JS_VALUE_GET_NORM_TAG(argv[0]);
37180     if (tag == JS_TAG_OBJECT) {
37181         p = JS_VALUE_GET_OBJ(argv[0]);
37182         class_id = p->class_id;
37183         if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0]))
37184             class_id = JS_CLASS_BYTECODE_FUNCTION;
37185         atom = ctx->rt->class_array[class_id].class_name;
37186     } else {
37187         atom = JS_ATOM_empty_string;
37188     }
37189     return JS_AtomToString(ctx, atom);
37190 }
37191 
js_object_is(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37192 static JSValue js_object_is(JSContext *ctx, JSValueConst this_val,
37193                             int argc, JSValueConst *argv)
37194 {
37195     return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1]));
37196 }
37197 
37198 #if 0
37199 static JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val,
37200                                          int argc, JSValueConst *argv)
37201 {
37202     return JS_GetObjectData(ctx, argv[0]);
37203 }
37204 
37205 static JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val,
37206                                          int argc, JSValueConst *argv)
37207 {
37208     if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1])))
37209         return JS_EXCEPTION;
37210     return JS_DupValue(ctx, argv[1]);
37211 }
37212 
37213 static JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val,
37214                                          int argc, JSValueConst *argv)
37215 {
37216     return JS_ToPropertyKey(ctx, argv[0]);
37217 }
37218 
37219 static JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val,
37220                                     int argc, JSValueConst *argv)
37221 {
37222     return JS_NewBool(ctx, JS_IsObject(argv[0]));
37223 }
37224 
37225 static JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val,
37226                                            int argc, JSValueConst *argv)
37227 {
37228     return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1]));
37229 }
37230 
37231 static JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val,
37232                                          int argc, JSValueConst *argv)
37233 {
37234     return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0]));
37235 }
37236 #endif
37237 
JS_SpeciesConstructor(JSContext * ctx,JSValueConst obj,JSValueConst defaultConstructor)37238 static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj,
37239                                      JSValueConst defaultConstructor)
37240 {
37241     JSValue ctor, species;
37242 
37243     if (!JS_IsObject(obj))
37244         return JS_ThrowTypeErrorNotAnObject(ctx);
37245     ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
37246     if (JS_IsException(ctor))
37247         return ctor;
37248     if (JS_IsUndefined(ctor))
37249         return JS_DupValue(ctx, defaultConstructor);
37250     if (!JS_IsObject(ctor)) {
37251         JS_FreeValue(ctx, ctor);
37252         return JS_ThrowTypeErrorNotAnObject(ctx);
37253     }
37254     species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
37255     JS_FreeValue(ctx, ctor);
37256     if (JS_IsException(species))
37257         return species;
37258     if (JS_IsUndefined(species) || JS_IsNull(species))
37259         return JS_DupValue(ctx, defaultConstructor);
37260     if (!JS_IsConstructor(ctx, species)) {
37261         JS_FreeValue(ctx, species);
37262         return JS_ThrowTypeError(ctx, "not a constructor");
37263     }
37264     return species;
37265 }
37266 
37267 #if 0
37268 static JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val,
37269                                               int argc, JSValueConst *argv)
37270 {
37271     return JS_SpeciesConstructor(ctx, argv[0], argv[1]);
37272 }
37273 #endif
37274 
js_object_get___proto__(JSContext * ctx,JSValueConst this_val)37275 static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val)
37276 {
37277     JSValue val, ret;
37278 
37279     val = JS_ToObject(ctx, this_val);
37280     if (JS_IsException(val))
37281         return val;
37282     ret = JS_GetPrototype(ctx, val);
37283     JS_FreeValue(ctx, val);
37284     return ret;
37285 }
37286 
js_object_set___proto__(JSContext * ctx,JSValueConst this_val,JSValueConst proto)37287 static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val,
37288                                        JSValueConst proto)
37289 {
37290     if (JS_IsUndefined(this_val) || JS_IsNull(this_val))
37291         return JS_ThrowTypeErrorNotAnObject(ctx);
37292     if (!JS_IsObject(proto) && !JS_IsNull(proto))
37293         return JS_UNDEFINED;
37294     if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0)
37295         return JS_EXCEPTION;
37296     else
37297         return JS_UNDEFINED;
37298 }
37299 
js_object_isPrototypeOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37300 static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val,
37301                                        int argc, JSValueConst *argv)
37302 {
37303     JSValue obj, v1;
37304     JSValueConst v;
37305     int res;
37306 
37307     v = argv[0];
37308     if (!JS_IsObject(v))
37309         return JS_FALSE;
37310     obj = JS_ToObject(ctx, this_val);
37311     if (JS_IsException(obj))
37312         return JS_EXCEPTION;
37313     v1 = JS_DupValue(ctx, v);
37314     for(;;) {
37315         v1 = JS_GetPrototypeFree(ctx, v1);
37316         if (JS_IsException(v1))
37317             goto exception;
37318         if (JS_IsNull(v1)) {
37319             res = FALSE;
37320             break;
37321         }
37322         if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) {
37323             res = TRUE;
37324             break;
37325         }
37326         /* avoid infinite loop (possible with proxies) */
37327         if (js_poll_interrupts(ctx))
37328             goto exception;
37329     }
37330     JS_FreeValue(ctx, v1);
37331     JS_FreeValue(ctx, obj);
37332     return JS_NewBool(ctx, res);
37333 
37334 exception:
37335     JS_FreeValue(ctx, v1);
37336     JS_FreeValue(ctx, obj);
37337     return JS_EXCEPTION;
37338 }
37339 
js_object_propertyIsEnumerable(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37340 static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val,
37341                                               int argc, JSValueConst *argv)
37342 {
37343     JSValue obj, res = JS_EXCEPTION;
37344     JSAtom prop = JS_ATOM_NULL;
37345     JSPropertyDescriptor desc;
37346     int has_prop;
37347 
37348     obj = JS_ToObject(ctx, this_val);
37349     if (JS_IsException(obj))
37350         goto exception;
37351     prop = JS_ValueToAtom(ctx, argv[0]);
37352     if (unlikely(prop == JS_ATOM_NULL))
37353         goto exception;
37354 
37355     has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
37356     if (has_prop < 0)
37357         goto exception;
37358     if (has_prop) {
37359         res = JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0);
37360         js_free_desc(ctx, &desc);
37361     } else {
37362         res = JS_FALSE;
37363     }
37364 
37365 exception:
37366     JS_FreeAtom(ctx, prop);
37367     JS_FreeValue(ctx, obj);
37368     return res;
37369 }
37370 
js_object___lookupGetter__(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int setter)37371 static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,
37372                                           int argc, JSValueConst *argv, int setter)
37373 {
37374     JSValue obj, res = JS_EXCEPTION;
37375     JSAtom prop = JS_ATOM_NULL;
37376     JSPropertyDescriptor desc;
37377     int has_prop;
37378 
37379     obj = JS_ToObject(ctx, this_val);
37380     if (JS_IsException(obj))
37381         goto exception;
37382     prop = JS_ValueToAtom(ctx, argv[0]);
37383     if (unlikely(prop == JS_ATOM_NULL))
37384         goto exception;
37385 
37386     for (;;) {
37387         has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
37388         if (has_prop < 0)
37389             goto exception;
37390         if (has_prop) {
37391             if (desc.flags & JS_PROP_GETSET)
37392                 res = JS_DupValue(ctx, setter ? desc.setter : desc.getter);
37393             else
37394                 res = JS_UNDEFINED;
37395             js_free_desc(ctx, &desc);
37396             break;
37397         }
37398         obj = JS_GetPrototypeFree(ctx, obj);
37399         if (JS_IsException(obj))
37400             goto exception;
37401         if (JS_IsNull(obj)) {
37402             res = JS_UNDEFINED;
37403             break;
37404         }
37405         /* avoid infinite loop (possible with proxies) */
37406         if (js_poll_interrupts(ctx))
37407             goto exception;
37408     }
37409 
37410 exception:
37411     JS_FreeAtom(ctx, prop);
37412     JS_FreeValue(ctx, obj);
37413     return res;
37414 }
37415 
37416 static const JSCFunctionListEntry js_object_funcs[] = {
37417     JS_CFUNC_DEF("create", 2, js_object_create ),
37418     JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ),
37419     JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ),
37420     JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ),
37421     JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ),
37422     JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ),
37423     JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ),
37424     JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ),
37425     JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ),
37426     JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ),
37427     JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ),
37428     JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ),
37429     JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ),
37430     JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ),
37431     JS_CFUNC_DEF("is", 2, js_object_is ),
37432     JS_CFUNC_DEF("assign", 2, js_object_assign ),
37433     JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ),
37434     JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ),
37435     JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ),
37436     JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ),
37437     JS_CFUNC_DEF("__getClass", 1, js_object___getClass ),
37438     //JS_CFUNC_DEF("__isObject", 1, js_object___isObject ),
37439     //JS_CFUNC_DEF("__isConstructor", 1, js_object___isConstructor ),
37440     //JS_CFUNC_DEF("__toObject", 1, js_object___toObject ),
37441     //JS_CFUNC_DEF("__setOwnProperty", 3, js_object___setOwnProperty ),
37442     //JS_CFUNC_DEF("__toPrimitive", 2, js_object___toPrimitive ),
37443     //JS_CFUNC_DEF("__toPropertyKey", 1, js_object___toPropertyKey ),
37444     //JS_CFUNC_DEF("__speciesConstructor", 2, js_object___speciesConstructor ),
37445     //JS_CFUNC_DEF("__isSameValueZero", 2, js_object___isSameValueZero ),
37446     //JS_CFUNC_DEF("__getObjectData", 1, js_object___getObjectData ),
37447     //JS_CFUNC_DEF("__setObjectData", 2, js_object___setObjectData ),
37448     JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ),
37449 };
37450 
37451 static const JSCFunctionListEntry js_object_proto_funcs[] = {
37452     JS_CFUNC_DEF("toString", 0, js_object_toString ),
37453     JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ),
37454     JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ),
37455     JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ),
37456     JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ),
37457     JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ),
37458     JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ),
37459     JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ),
37460     JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ),
37461     JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ),
37462     JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ),
37463 };
37464 
37465 /* Function class */
37466 
js_function_proto(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37467 static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val,
37468                                  int argc, JSValueConst *argv)
37469 {
37470     return JS_UNDEFINED;
37471 }
37472 
37473 /* 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)37474 static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,
37475                                        int argc, JSValueConst *argv, int magic)
37476 {
37477     JSFunctionKindEnum func_kind = magic;
37478     int i, n, ret;
37479     JSValue s, proto, obj = JS_UNDEFINED;
37480     StringBuffer b_s, *b = &b_s;
37481 
37482     string_buffer_init(ctx, b, 0);
37483     string_buffer_putc8(b, '(');
37484 
37485     if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) {
37486         string_buffer_puts8(b, "async ");
37487     }
37488     string_buffer_puts8(b, "function");
37489 
37490     if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) {
37491         string_buffer_putc8(b, '*');
37492     }
37493     string_buffer_puts8(b, " anonymous(");
37494 
37495     n = argc - 1;
37496     for(i = 0; i < n; i++) {
37497         if (i != 0) {
37498             string_buffer_putc8(b, ',');
37499         }
37500         if (string_buffer_concat_value(b, argv[i]))
37501             goto fail;
37502     }
37503     string_buffer_puts8(b, "\n) {\n");
37504     if (n >= 0) {
37505         if (string_buffer_concat_value(b, argv[n]))
37506             goto fail;
37507     }
37508     string_buffer_puts8(b, "\n})");
37509     s = string_buffer_end(b);
37510     if (JS_IsException(s))
37511         goto fail1;
37512 
37513     obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1);
37514     JS_FreeValue(ctx, s);
37515     if (JS_IsException(obj))
37516         goto fail1;
37517     if (!JS_IsUndefined(new_target)) {
37518         /* set the prototype */
37519         proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
37520         if (JS_IsException(proto))
37521             goto fail1;
37522         if (!JS_IsObject(proto)) {
37523             JSContext *realm;
37524             JS_FreeValue(ctx, proto);
37525             realm = JS_GetFunctionRealm(ctx, new_target);
37526             if (!realm)
37527                 goto fail1;
37528             proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]);
37529         }
37530         ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE);
37531         JS_FreeValue(ctx, proto);
37532         if (ret < 0)
37533             goto fail1;
37534     }
37535     return obj;
37536 
37537  fail:
37538     string_buffer_free(b);
37539  fail1:
37540     JS_FreeValue(ctx, obj);
37541     return JS_EXCEPTION;
37542 }
37543 
js_get_length32(JSContext * ctx,uint32_t * pres,JSValueConst obj)37544 static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
37545                                        JSValueConst obj)
37546 {
37547     JSValue len_val;
37548     len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
37549     if (JS_IsException(len_val)) {
37550         *pres = 0;
37551         return -1;
37552     }
37553     return JS_ToUint32Free(ctx, pres, len_val);
37554 }
37555 
js_get_length64(JSContext * ctx,int64_t * pres,JSValueConst obj)37556 int js_get_length64(JSContext *ctx, int64_t *pres, JSValueConst obj)
37557 {
37558     JSValue len_val;
37559     len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
37560     if (JS_IsException(len_val)) {
37561         *pres = 0;
37562         return -1;
37563     }
37564     return JS_ToLengthFree(ctx, pres, len_val);
37565 }
37566 
free_arg_list(JSContext * ctx,JSValue * tab,uint32_t len)37567 static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len)
37568 {
37569     uint32_t i;
37570     for(i = 0; i < len; i++) {
37571         JS_FreeValue(ctx, tab[i]);
37572     }
37573     js_free(ctx, tab);
37574 }
37575 
37576 /* XXX: should use ValueArray */
build_arg_list(JSContext * ctx,uint32_t * plen,JSValueConst array_arg)37577 static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
37578                                JSValueConst array_arg)
37579 {
37580     uint32_t len, i;
37581     JSValue *tab, ret;
37582     JSObject *p;
37583 
37584     if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) {
37585         JS_ThrowTypeError(ctx, "not a object");
37586         return NULL;
37587     }
37588     if (js_get_length32(ctx, &len, array_arg))
37589         return NULL;
37590     /* avoid allocating 0 bytes */
37591     tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len));
37592     if (!tab)
37593         return NULL;
37594     p = JS_VALUE_GET_OBJ(array_arg);
37595     if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) &&
37596         p->fast_array &&
37597         len == p->u.array.count) {
37598         for(i = 0; i < len; i++) {
37599             tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]);
37600         }
37601     } else {
37602         for(i = 0; i < len; i++) {
37603             ret = JS_GetPropertyUint32(ctx, array_arg, i);
37604             if (JS_IsException(ret)) {
37605                 free_arg_list(ctx, tab, i);
37606                 return NULL;
37607             }
37608             tab[i] = ret;
37609         }
37610     }
37611     *plen = len;
37612     return tab;
37613 }
37614 
37615 /* magic value: 0 = normal apply, 1 = apply for constructor, 2 =
37616    Reflect.apply */
js_function_apply(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)37617 static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
37618                                  int argc, JSValueConst *argv, int magic)
37619 {
37620     JSValueConst this_arg, array_arg;
37621     uint32_t len;
37622     JSValue *tab, ret;
37623 
37624     if (check_function(ctx, this_val))
37625         return JS_EXCEPTION;
37626     this_arg = argv[0];
37627     array_arg = argv[1];
37628     if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED ||
37629          JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) {
37630         return JS_Call(ctx, this_val, this_arg, 0, NULL);
37631     }
37632     tab = build_arg_list(ctx, &len, array_arg);
37633     if (!tab)
37634         return JS_EXCEPTION;
37635     if (magic & 1) {
37636         ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab);
37637     } else {
37638         ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab);
37639     }
37640     free_arg_list(ctx, tab, len);
37641     return ret;
37642 }
37643 
js_function_call(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37644 static JSValue js_function_call(JSContext *ctx, JSValueConst this_val,
37645                                 int argc, JSValueConst *argv)
37646 {
37647     if (argc <= 0) {
37648         return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL);
37649     } else {
37650         return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1);
37651     }
37652 }
37653 
js_function_bind(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37654 static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val,
37655                                 int argc, JSValueConst *argv)
37656 {
37657     JSBoundFunction *bf;
37658     JSValue func_obj, name1, len_val;
37659     JSObject *p;
37660     int arg_count, i, ret;
37661 
37662     if (check_function(ctx, this_val))
37663         return JS_EXCEPTION;
37664 
37665     func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
37666                                  JS_CLASS_BOUND_FUNCTION);
37667     if (JS_IsException(func_obj))
37668         return JS_EXCEPTION;
37669     p = JS_VALUE_GET_OBJ(func_obj);
37670     p->is_constructor = JS_IsConstructor(ctx, this_val);
37671     arg_count = max_int(0, argc - 1);
37672     bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue));
37673     if (!bf)
37674         goto exception;
37675     bf->func_obj = JS_DupValue(ctx, this_val);
37676     bf->this_val = JS_DupValue(ctx, argv[0]);
37677     bf->argc = arg_count;
37678     for(i = 0; i < arg_count; i++) {
37679         bf->argv[i] = JS_DupValue(ctx, argv[i + 1]);
37680     }
37681     p->u.bound_function = bf;
37682 
37683     /* XXX: the spec could be simpler by only using GetOwnProperty */
37684     ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length);
37685     if (ret < 0)
37686         goto exception;
37687     if (!ret) {
37688         len_val = JS_NewInt32(ctx, 0);
37689     } else {
37690         len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length);
37691         if (JS_IsException(len_val))
37692             goto exception;
37693         if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) {
37694             /* most common case */
37695             int len1 = JS_VALUE_GET_INT(len_val);
37696             if (len1 <= arg_count)
37697                 len1 = 0;
37698             else
37699                 len1 -= arg_count;
37700             len_val = JS_NewInt32(ctx, len1);
37701         } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) {
37702             double d = JS_VALUE_GET_FLOAT64(len_val);
37703             if (isnan(d)) {
37704                 d = 0.0;
37705             } else {
37706                 d = trunc(d);
37707                 if (d <= (double)arg_count)
37708                     d = 0.0;
37709                 else
37710                     d -= (double)arg_count; /* also converts -0 to +0 */
37711             }
37712             len_val = JS_NewFloat64(ctx, d);
37713         } else {
37714             JS_FreeValue(ctx, len_val);
37715             len_val = JS_NewInt32(ctx, 0);
37716         }
37717     }
37718     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,
37719                            len_val, JS_PROP_CONFIGURABLE);
37720 
37721     name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name);
37722     if (JS_IsException(name1))
37723         goto exception;
37724     if (!JS_IsString(name1)) {
37725         JS_FreeValue(ctx, name1);
37726         name1 = JS_AtomToString(ctx, JS_ATOM_empty_string);
37727     }
37728     name1 = JS_ConcatString3(ctx, "bound ", name1, "");
37729     if (JS_IsException(name1))
37730         goto exception;
37731     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1,
37732                            JS_PROP_CONFIGURABLE);
37733     return func_obj;
37734  exception:
37735     JS_FreeValue(ctx, func_obj);
37736     return JS_EXCEPTION;
37737 }
37738 
js_function_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37739 static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val,
37740                                     int argc, JSValueConst *argv)
37741 {
37742     JSObject *p;
37743     JSFunctionKindEnum func_kind = JS_FUNC_NORMAL;
37744 
37745     if (check_function(ctx, this_val))
37746         return JS_EXCEPTION;
37747 
37748     p = JS_VALUE_GET_OBJ(this_val);
37749     if (js_class_has_bytecode(p->class_id)) {
37750         JSFunctionBytecode *b = p->u.func.function_bytecode;
37751         if (b->has_debug && b->debug.source) {
37752             return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len);
37753         }
37754         func_kind = b->func_kind;
37755     }
37756     {
37757         JSValue name;
37758         const char *pref, *suff;
37759 
37760         switch(func_kind) {
37761         default:
37762         case JS_FUNC_NORMAL:
37763             pref = "function ";
37764             break;
37765         case JS_FUNC_GENERATOR:
37766             pref = "function *";
37767             break;
37768         case JS_FUNC_ASYNC:
37769             pref = "async function ";
37770             break;
37771         case JS_FUNC_ASYNC_GENERATOR:
37772             pref = "async function *";
37773             break;
37774         }
37775         suff = "() {\n    [native code]\n}";
37776         name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
37777         if (JS_IsUndefined(name))
37778             name = JS_AtomToString(ctx, JS_ATOM_empty_string);
37779         return JS_ConcatString3(ctx, pref, name, suff);
37780     }
37781 }
37782 
js_function_hasInstance(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37783 static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val,
37784                                        int argc, JSValueConst *argv)
37785 {
37786     int ret;
37787     ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val);
37788     if (ret < 0)
37789         return JS_EXCEPTION;
37790     else
37791         return JS_NewBool(ctx, ret);
37792 }
37793 
37794 static const JSCFunctionListEntry js_function_proto_funcs[] = {
37795     JS_CFUNC_DEF("call", 1, js_function_call ),
37796     JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ),
37797     JS_CFUNC_DEF("bind", 1, js_function_bind ),
37798     JS_CFUNC_DEF("toString", 0, js_function_toString ),
37799     JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ),
37800     JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ),
37801     JS_CGETSET_DEF("lineNumber", js_function_proto_lineNumber, NULL ),
37802 };
37803 
37804 /* Error class */
37805 
iterator_to_array(JSContext * ctx,JSValueConst items)37806 static JSValue iterator_to_array(JSContext *ctx, JSValueConst items)
37807 {
37808     JSValue iter, next_method = JS_UNDEFINED;
37809     JSValue v, r = JS_UNDEFINED;
37810     int64_t k;
37811     BOOL done;
37812 
37813     iter = JS_GetIterator(ctx, items, FALSE);
37814     if (JS_IsException(iter))
37815         goto exception;
37816     next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
37817     if (JS_IsException(next_method))
37818         goto exception;
37819     r = JS_NewArray(ctx);
37820     if (JS_IsException(r))
37821         goto exception;
37822     for (k = 0;; k++) {
37823         v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
37824         if (JS_IsException(v))
37825             goto exception_close;
37826         if (done)
37827             break;
37828         if (JS_DefinePropertyValueInt64(ctx, r, k, v,
37829                                         JS_PROP_C_W_E | JS_PROP_THROW) < 0)
37830             goto exception_close;
37831     }
37832  done:
37833     JS_FreeValue(ctx, next_method);
37834     JS_FreeValue(ctx, iter);
37835     return r;
37836  exception_close:
37837     JS_IteratorClose(ctx, iter, TRUE);
37838  exception:
37839     JS_FreeValue(ctx, r);
37840     r = JS_EXCEPTION;
37841     goto done;
37842 }
37843 
js_error_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv,int magic)37844 static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
37845                                     int argc, JSValueConst *argv, int magic)
37846 {
37847     JSValue obj, msg, proto;
37848     JSValueConst message;
37849 
37850     if (JS_IsUndefined(new_target))
37851         new_target = JS_GetActiveFunction(ctx);
37852     proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
37853     if (JS_IsException(proto))
37854         return proto;
37855     if (!JS_IsObject(proto)) {
37856         JSContext *realm;
37857         JSValueConst proto1;
37858 
37859         JS_FreeValue(ctx, proto);
37860         realm = JS_GetFunctionRealm(ctx, new_target);
37861         if (!realm)
37862             return JS_EXCEPTION;
37863         if (magic < 0) {
37864             proto1 = realm->class_proto[JS_CLASS_ERROR];
37865         } else {
37866             proto1 = realm->native_error_proto[magic];
37867         }
37868         proto = JS_DupValue(ctx, proto1);
37869     }
37870     obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR);
37871     JS_FreeValue(ctx, proto);
37872     if (JS_IsException(obj))
37873         return obj;
37874     if (magic == JS_AGGREGATE_ERROR) {
37875         message = argv[1];
37876     } else {
37877         message = argv[0];
37878     }
37879 
37880     if (!JS_IsUndefined(message)) {
37881         msg = JS_ToString(ctx, message);
37882         if (unlikely(JS_IsException(msg)))
37883             goto exception;
37884         JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg,
37885                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
37886     }
37887 
37888     if (magic == JS_AGGREGATE_ERROR) {
37889         JSValue error_list = iterator_to_array(ctx, argv[0]);
37890         if (JS_IsException(error_list))
37891             goto exception;
37892         JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list,
37893                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
37894     }
37895 
37896     /* skip the Error() function in the backtrace */
37897     build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
37898     return obj;
37899  exception:
37900     JS_FreeValue(ctx, obj);
37901     return JS_EXCEPTION;
37902 }
37903 
js_error_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37904 static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
37905                                  int argc, JSValueConst *argv)
37906 {
37907     JSValue name, msg;
37908 
37909     if (!JS_IsObject(this_val))
37910         return JS_ThrowTypeErrorNotAnObject(ctx);
37911     name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
37912     if (JS_IsUndefined(name))
37913         name = JS_AtomToString(ctx, JS_ATOM_Error);
37914     else
37915         name = JS_ToStringFree(ctx, name);
37916     if (JS_IsException(name))
37917         return JS_EXCEPTION;
37918 
37919     msg = JS_GetProperty(ctx, this_val, JS_ATOM_message);
37920     if (JS_IsUndefined(msg))
37921         msg = JS_AtomToString(ctx, JS_ATOM_empty_string);
37922     else
37923         msg = JS_ToStringFree(ctx, msg);
37924     if (JS_IsException(msg)) {
37925         JS_FreeValue(ctx, name);
37926         return JS_EXCEPTION;
37927     }
37928     if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg))
37929         name = JS_ConcatString3(ctx, "", name, ": ");
37930     return JS_ConcatString(ctx, name, msg);
37931 }
37932 
37933 static const JSCFunctionListEntry js_error_proto_funcs[] = {
37934     JS_CFUNC_DEF("toString", 0, js_error_toString ),
37935     JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
37936     JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
37937 };
37938 
37939 /* AggregateError */
37940 
37941 /* used by C code. */
js_aggregate_error_constructor(JSContext * ctx,JSValueConst errors)37942 static JSValue js_aggregate_error_constructor(JSContext *ctx,
37943                                               JSValueConst errors)
37944 {
37945     JSValue obj;
37946 
37947     obj = JS_NewObjectProtoClass(ctx,
37948                                  ctx->native_error_proto[JS_AGGREGATE_ERROR],
37949                                  JS_CLASS_ERROR);
37950     if (JS_IsException(obj))
37951         return obj;
37952     JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors),
37953                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
37954     return obj;
37955 }
37956 
37957 /* Array */
37958 
JS_CopySubArray(JSContext * ctx,JSValueConst obj,int64_t to_pos,int64_t from_pos,int64_t count,int dir)37959 static int JS_CopySubArray(JSContext *ctx,
37960                            JSValueConst obj, int64_t to_pos,
37961                            int64_t from_pos, int64_t count, int dir)
37962 {
37963     int64_t i, from, to;
37964     JSValue val;
37965     int fromPresent;
37966 
37967     /* XXX: should special case fast arrays */
37968     for (i = 0; i < count; i++) {
37969         if (dir < 0) {
37970             from = from_pos + count - i - 1;
37971             to = to_pos + count - i - 1;
37972         } else {
37973             from = from_pos + i;
37974             to = to_pos + i;
37975         }
37976         fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val);
37977         if (fromPresent < 0)
37978             goto exception;
37979 
37980         if (fromPresent) {
37981             if (JS_SetPropertyInt64(ctx, obj, to, val) < 0)
37982                 goto exception;
37983         } else {
37984             if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0)
37985                 goto exception;
37986         }
37987     }
37988     return 0;
37989 
37990  exception:
37991     return -1;
37992 }
37993 
js_array_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)37994 static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target,
37995                                     int argc, JSValueConst *argv)
37996 {
37997     JSValue obj;
37998     int i;
37999 
38000     obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY);
38001     if (JS_IsException(obj))
38002         return obj;
38003     if (argc == 1 && JS_IsNumber(argv[0])) {
38004         uint32_t len;
38005         if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE))
38006             goto fail;
38007         if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0)
38008             goto fail;
38009     } else {
38010         for(i = 0; i < argc; i++) {
38011             if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0)
38012                 goto fail;
38013         }
38014     }
38015     return obj;
38016 fail:
38017     JS_FreeValue(ctx, obj);
38018     return JS_EXCEPTION;
38019 }
38020 
js_array_from(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38021 static JSValue js_array_from(JSContext *ctx, JSValueConst this_val,
38022                              int argc, JSValueConst *argv)
38023 {
38024     // from(items, mapfn = void 0, this_arg = void 0)
38025     JSValueConst items = argv[0], mapfn, this_arg;
38026     JSValueConst args[2];
38027     JSValue stack[2];
38028     JSValue iter, r, v, v2, arrayLike;
38029     int64_t k, len;
38030     int done, mapping;
38031 
38032     mapping = FALSE;
38033     mapfn = JS_UNDEFINED;
38034     this_arg = JS_UNDEFINED;
38035     r = JS_UNDEFINED;
38036     arrayLike = JS_UNDEFINED;
38037     stack[0] = JS_UNDEFINED;
38038     stack[1] = JS_UNDEFINED;
38039 
38040     if (argc > 1) {
38041         mapfn = argv[1];
38042         if (!JS_IsUndefined(mapfn)) {
38043             if (check_function(ctx, mapfn))
38044                 goto exception;
38045             mapping = 1;
38046             if (argc > 2)
38047                 this_arg = argv[2];
38048         }
38049     }
38050     iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
38051     if (JS_IsException(iter))
38052         goto exception;
38053     if (!JS_IsUndefined(iter)) {
38054         JS_FreeValue(ctx, iter);
38055         if (JS_IsConstructor(ctx, this_val))
38056             r = JS_CallConstructor(ctx, this_val, 0, NULL);
38057         else
38058             r = JS_NewArray(ctx);
38059         if (JS_IsException(r))
38060             goto exception;
38061         stack[0] = JS_DupValue(ctx, items);
38062         if (js_for_of_start(ctx, &stack[1], FALSE))
38063             goto exception;
38064         for (k = 0;; k++) {
38065             v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);
38066             if (JS_IsException(v))
38067                 goto exception_close;
38068             if (done)
38069                 break;
38070             if (mapping) {
38071                 args[0] = v;
38072                 args[1] = JS_NewInt32(ctx, k);
38073                 v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
38074                 JS_FreeValue(ctx, v);
38075                 v = v2;
38076                 if (JS_IsException(v))
38077                     goto exception_close;
38078             }
38079             if (JS_DefinePropertyValueInt64(ctx, r, k, v,
38080                                             JS_PROP_C_W_E | JS_PROP_THROW) < 0)
38081                 goto exception_close;
38082         }
38083     } else {
38084         arrayLike = JS_ToObject(ctx, items);
38085         if (JS_IsException(arrayLike))
38086             goto exception;
38087         if (js_get_length64(ctx, &len, arrayLike) < 0)
38088             goto exception;
38089         v = JS_NewInt64(ctx, len);
38090         args[0] = v;
38091         if (JS_IsConstructor(ctx, this_val)) {
38092             r = JS_CallConstructor(ctx, this_val, 1, args);
38093         } else {
38094             r = js_array_constructor(ctx, JS_UNDEFINED, 1, args);
38095         }
38096         JS_FreeValue(ctx, v);
38097         if (JS_IsException(r))
38098             goto exception;
38099         for(k = 0; k < len; k++) {
38100             v = JS_GetPropertyInt64(ctx, arrayLike, k);
38101             if (JS_IsException(v))
38102                 goto exception;
38103             if (mapping) {
38104                 args[0] = v;
38105                 args[1] = JS_NewInt32(ctx, k);
38106                 v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
38107                 JS_FreeValue(ctx, v);
38108                 v = v2;
38109                 if (JS_IsException(v))
38110                     goto exception;
38111             }
38112             if (JS_DefinePropertyValueInt64(ctx, r, k, v,
38113                                             JS_PROP_C_W_E | JS_PROP_THROW) < 0)
38114                 goto exception;
38115         }
38116     }
38117     if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0)
38118         goto exception;
38119     goto done;
38120 
38121  exception_close:
38122     if (!JS_IsUndefined(stack[0]))
38123         JS_IteratorClose(ctx, stack[0], TRUE);
38124  exception:
38125     JS_FreeValue(ctx, r);
38126     r = JS_EXCEPTION;
38127  done:
38128     JS_FreeValue(ctx, arrayLike);
38129     JS_FreeValue(ctx, stack[0]);
38130     JS_FreeValue(ctx, stack[1]);
38131     return r;
38132 }
38133 
js_array_of(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38134 static JSValue js_array_of(JSContext *ctx, JSValueConst this_val,
38135                            int argc, JSValueConst *argv)
38136 {
38137     JSValue obj, args[1];
38138     int i;
38139 
38140     if (JS_IsConstructor(ctx, this_val)) {
38141         args[0] = JS_NewInt32(ctx, argc);
38142         obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args);
38143     } else {
38144         obj = JS_NewArray(ctx);
38145     }
38146     if (JS_IsException(obj))
38147         return JS_EXCEPTION;
38148     for(i = 0; i < argc; i++) {
38149         if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]),
38150                                         JS_PROP_THROW) < 0) {
38151             goto fail;
38152         }
38153     }
38154     if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) {
38155     fail:
38156         JS_FreeValue(ctx, obj);
38157         return JS_EXCEPTION;
38158     }
38159     return obj;
38160 }
38161 
js_array_isArray(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38162 static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val,
38163                                 int argc, JSValueConst *argv)
38164 {
38165     int ret;
38166     ret = JS_IsArray(ctx, argv[0]);
38167     if (ret < 0)
38168         return JS_EXCEPTION;
38169     else
38170         return JS_NewBool(ctx, ret);
38171 }
38172 
js_get_this(JSContext * ctx,JSValueConst this_val)38173 static JSValue js_get_this(JSContext *ctx,
38174                            JSValueConst this_val)
38175 {
38176     return JS_DupValue(ctx, this_val);
38177 }
38178 
JS_ArraySpeciesCreate(JSContext * ctx,JSValueConst obj,JSValueConst len_val)38179 static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj,
38180                                      JSValueConst len_val)
38181 {
38182     JSValue ctor, ret, species;
38183     int res;
38184     JSContext *realm;
38185 
38186     res = JS_IsArray(ctx, obj);
38187     if (res < 0)
38188         return JS_EXCEPTION;
38189     if (!res)
38190         return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
38191     ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
38192     if (JS_IsException(ctor))
38193         return ctor;
38194     if (JS_IsConstructor(ctx, ctor)) {
38195         /* legacy web compatibility */
38196         realm = JS_GetFunctionRealm(ctx, ctor);
38197         if (!realm) {
38198             JS_FreeValue(ctx, ctor);
38199             return JS_EXCEPTION;
38200         }
38201         if (realm != ctx &&
38202             js_same_value(ctx, ctor, realm->array_ctor)) {
38203             JS_FreeValue(ctx, ctor);
38204             ctor = JS_UNDEFINED;
38205         }
38206     }
38207     if (JS_IsObject(ctor)) {
38208         species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
38209         JS_FreeValue(ctx, ctor);
38210         if (JS_IsException(species))
38211             return species;
38212         ctor = species;
38213         if (JS_IsNull(ctor))
38214             ctor = JS_UNDEFINED;
38215     }
38216     if (JS_IsUndefined(ctor)) {
38217         return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
38218     } else {
38219         ret = JS_CallConstructor(ctx, ctor, 1, &len_val);
38220         JS_FreeValue(ctx, ctor);
38221         return ret;
38222     }
38223 }
38224 
38225 static const JSCFunctionListEntry js_array_funcs[] = {
38226     JS_CFUNC_DEF("isArray", 1, js_array_isArray ),
38227     JS_CFUNC_DEF("from", 1, js_array_from ),
38228     JS_CFUNC_DEF("of", 0, js_array_of ),
38229     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
38230 };
38231 
JS_isConcatSpreadable(JSContext * ctx,JSValueConst obj)38232 static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj)
38233 {
38234     JSValue val;
38235 
38236     if (!JS_IsObject(obj))
38237         return FALSE;
38238     val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable);
38239     if (JS_IsException(val))
38240         return -1;
38241     if (!JS_IsUndefined(val))
38242         return JS_ToBoolFree(ctx, val);
38243     return JS_IsArray(ctx, obj);
38244 }
38245 
js_array_concat(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38246 static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val,
38247                                int argc, JSValueConst *argv)
38248 {
38249     JSValue obj, arr, val;
38250     JSValueConst e;
38251     int64_t len, k, n;
38252     int i, res;
38253 
38254     arr = JS_UNDEFINED;
38255     obj = JS_ToObject(ctx, this_val);
38256     if (JS_IsException(obj))
38257         goto exception;
38258 
38259     arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
38260     if (JS_IsException(arr))
38261         goto exception;
38262     n = 0;
38263     for (i = -1; i < argc; i++) {
38264         if (i < 0)
38265             e = obj;
38266         else
38267             e = argv[i];
38268 
38269         res = JS_isConcatSpreadable(ctx, e);
38270         if (res < 0)
38271             goto exception;
38272         if (res) {
38273             if (js_get_length64(ctx, &len, e))
38274                 goto exception;
38275             if (n + len > MAX_SAFE_INTEGER) {
38276                 JS_ThrowTypeError(ctx, "Array loo long");
38277                 goto exception;
38278             }
38279             for (k = 0; k < len; k++, n++) {
38280                 res = JS_TryGetPropertyInt64(ctx, e, k, &val);
38281                 if (res < 0)
38282                     goto exception;
38283                 if (res) {
38284                     if (JS_DefinePropertyValueInt64(ctx, arr, n, val,
38285                                                     JS_PROP_C_W_E | JS_PROP_THROW) < 0)
38286                         goto exception;
38287                 }
38288             }
38289         } else {
38290             if (n >= MAX_SAFE_INTEGER) {
38291                 JS_ThrowTypeError(ctx, "Array loo long");
38292                 goto exception;
38293             }
38294             if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e),
38295                                             JS_PROP_C_W_E | JS_PROP_THROW) < 0)
38296                 goto exception;
38297             n++;
38298         }
38299     }
38300     if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
38301         goto exception;
38302 
38303     JS_FreeValue(ctx, obj);
38304     return arr;
38305 
38306 exception:
38307     JS_FreeValue(ctx, arr);
38308     JS_FreeValue(ctx, obj);
38309     return JS_EXCEPTION;
38310 }
38311 
38312 #define special_every    0
38313 #define special_some     1
38314 #define special_forEach  2
38315 #define special_map      3
38316 #define special_filter   4
38317 #define special_TA       8
38318 
38319 static int js_typed_array_get_length_internal(JSContext *ctx, JSValueConst obj);
38320 
38321 static JSValue js_typed_array___speciesCreate(JSContext *ctx,
38322                                               JSValueConst this_val,
38323                                               int argc, JSValueConst *argv);
38324 
js_array_every(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int special)38325 static JSValue js_array_every(JSContext *ctx, JSValueConst this_val,
38326                               int argc, JSValueConst *argv, int special)
38327 {
38328     JSValue obj, val, index_val, res, ret;
38329     JSValueConst args[3];
38330     JSValueConst func, this_arg;
38331     int64_t len, k, n;
38332     int present;
38333 
38334     ret = JS_UNDEFINED;
38335     val = JS_UNDEFINED;
38336     if (special & special_TA) {
38337         obj = JS_DupValue(ctx, this_val);
38338         len = js_typed_array_get_length_internal(ctx, obj);
38339         if (len < 0)
38340             goto exception;
38341     } else {
38342         obj = JS_ToObject(ctx, this_val);
38343         if (js_get_length64(ctx, &len, obj))
38344             goto exception;
38345     }
38346     func = argv[0];
38347     this_arg = JS_UNDEFINED;
38348     if (argc > 1)
38349         this_arg = argv[1];
38350 
38351     if (check_function(ctx, func))
38352         goto exception;
38353 
38354     switch (special) {
38355     case special_every:
38356     case special_every | special_TA:
38357         ret = JS_TRUE;
38358         break;
38359     case special_some:
38360     case special_some | special_TA:
38361         ret = JS_FALSE;
38362         break;
38363     case special_map:
38364         /* XXX: JS_ArraySpeciesCreate should take int64_t */
38365         ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len));
38366         if (JS_IsException(ret))
38367             goto exception;
38368         break;
38369     case special_filter:
38370         ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
38371         if (JS_IsException(ret))
38372             goto exception;
38373         break;
38374     case special_map | special_TA:
38375         args[0] = obj;
38376         args[1] = JS_NewInt32(ctx, len);
38377         ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
38378         if (JS_IsException(ret))
38379             goto exception;
38380         break;
38381     case special_filter | special_TA:
38382         ret = JS_NewArray(ctx);
38383         if (JS_IsException(ret))
38384             goto exception;
38385         break;
38386     }
38387     n = 0;
38388 
38389     for(k = 0; k < len; k++) {
38390         present = JS_TryGetPropertyInt64(ctx, obj, k, &val);
38391         if (present < 0)
38392             goto exception;
38393         if (present) {
38394             index_val = JS_NewInt64(ctx, k);
38395             if (JS_IsException(index_val))
38396                 goto exception;
38397             args[0] = val;
38398             args[1] = index_val;
38399             args[2] = obj;
38400             res = JS_Call(ctx, func, this_arg, 3, args);
38401             JS_FreeValue(ctx, index_val);
38402             if (JS_IsException(res))
38403                 goto exception;
38404             switch (special) {
38405             case special_every:
38406             case special_every | special_TA:
38407                 if (!JS_ToBoolFree(ctx, res)) {
38408                     ret = JS_FALSE;
38409                     goto done;
38410                 }
38411                 break;
38412             case special_some:
38413             case special_some | special_TA:
38414                 if (JS_ToBoolFree(ctx, res)) {
38415                     ret = JS_TRUE;
38416                     goto done;
38417                 }
38418                 break;
38419             case special_map:
38420                 if (JS_DefinePropertyValueInt64(ctx, ret, k, res,
38421                                                 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
38422                     goto exception;
38423                 break;
38424             case special_map | special_TA:
38425                 if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0)
38426                     goto exception;
38427                 break;
38428             case special_filter:
38429             case special_filter | special_TA:
38430                 if (JS_ToBoolFree(ctx, res)) {
38431                     if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val),
38432                                                     JS_PROP_C_W_E | JS_PROP_THROW) < 0)
38433                         goto exception;
38434                 }
38435                 break;
38436             default:
38437                 JS_FreeValue(ctx, res);
38438                 break;
38439             }
38440             JS_FreeValue(ctx, val);
38441             val = JS_UNDEFINED;
38442         }
38443     }
38444 done:
38445     if (special == (special_filter | special_TA)) {
38446         JSValue arr;
38447         args[0] = obj;
38448         args[1] = JS_NewInt32(ctx, n);
38449         arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
38450         if (JS_IsException(arr))
38451             goto exception;
38452         args[0] = ret;
38453         res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args);
38454         if (check_exception_free(ctx, res))
38455             goto exception;
38456         JS_FreeValue(ctx, ret);
38457         ret = arr;
38458     }
38459     JS_FreeValue(ctx, val);
38460     JS_FreeValue(ctx, obj);
38461     return ret;
38462 
38463 exception:
38464     JS_FreeValue(ctx, ret);
38465     JS_FreeValue(ctx, val);
38466     JS_FreeValue(ctx, obj);
38467     return JS_EXCEPTION;
38468 }
38469 
38470 #define special_reduce       0
38471 #define special_reduceRight  1
38472 
js_array_reduce(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int special)38473 static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val,
38474                                int argc, JSValueConst *argv, int special)
38475 {
38476     JSValue obj, val, index_val, acc, acc1;
38477     JSValueConst args[4];
38478     JSValueConst func;
38479     int64_t len, k, k1;
38480     int present;
38481 
38482     acc = JS_UNDEFINED;
38483     val = JS_UNDEFINED;
38484     if (special & special_TA) {
38485         obj = JS_DupValue(ctx, this_val);
38486         len = js_typed_array_get_length_internal(ctx, obj);
38487         if (len < 0)
38488             goto exception;
38489     } else {
38490         obj = JS_ToObject(ctx, this_val);
38491         if (js_get_length64(ctx, &len, obj))
38492             goto exception;
38493     }
38494     func = argv[0];
38495 
38496     if (check_function(ctx, func))
38497         goto exception;
38498 
38499     k = 0;
38500     if (argc > 1) {
38501         acc = JS_DupValue(ctx, argv[1]);
38502     } else {
38503         for(;;) {
38504             if (k >= len) {
38505                 JS_ThrowTypeError(ctx, "empty array");
38506                 goto exception;
38507             }
38508             k1 = (special & special_reduceRight) ? len - k - 1 : k;
38509             k++;
38510             present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc);
38511             if (present < 0)
38512                 goto exception;
38513             if (present)
38514                 break;
38515         }
38516     }
38517     for (; k < len; k++) {
38518         k1 = (special & special_reduceRight) ? len - k - 1 : k;
38519         present = JS_TryGetPropertyInt64(ctx, obj, k1, &val);
38520         if (present < 0)
38521             goto exception;
38522         if (present) {
38523             index_val = JS_NewInt64(ctx, k1);
38524             if (JS_IsException(index_val))
38525                 goto exception;
38526             args[0] = acc;
38527             args[1] = val;
38528             args[2] = index_val;
38529             args[3] = obj;
38530             acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args);
38531             JS_FreeValue(ctx, index_val);
38532             JS_FreeValue(ctx, val);
38533             val = JS_UNDEFINED;
38534             if (JS_IsException(acc1))
38535                 goto exception;
38536             JS_FreeValue(ctx, acc);
38537             acc = acc1;
38538         }
38539     }
38540     JS_FreeValue(ctx, obj);
38541     return acc;
38542 
38543 exception:
38544     JS_FreeValue(ctx, acc);
38545     JS_FreeValue(ctx, val);
38546     JS_FreeValue(ctx, obj);
38547     return JS_EXCEPTION;
38548 }
38549 
js_array_fill(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38550 static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val,
38551                              int argc, JSValueConst *argv)
38552 {
38553     JSValue obj;
38554     int64_t len, start, end;
38555 
38556     obj = JS_ToObject(ctx, this_val);
38557     if (js_get_length64(ctx, &len, obj))
38558         goto exception;
38559 
38560     start = 0;
38561     if (argc > 1 && !JS_IsUndefined(argv[1])) {
38562         if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len))
38563             goto exception;
38564     }
38565 
38566     end = len;
38567     if (argc > 2 && !JS_IsUndefined(argv[2])) {
38568         if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len))
38569             goto exception;
38570     }
38571 
38572     /* XXX: should special case fast arrays */
38573     while (start < end) {
38574         if (JS_SetPropertyInt64(ctx, obj, start,
38575                                 JS_DupValue(ctx, argv[0])) < 0)
38576             goto exception;
38577         start++;
38578     }
38579     return obj;
38580 
38581  exception:
38582     JS_FreeValue(ctx, obj);
38583     return JS_EXCEPTION;
38584 }
38585 
js_array_includes(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38586 static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val,
38587                                  int argc, JSValueConst *argv)
38588 {
38589     JSValue obj, val;
38590     int64_t len, n, res;
38591     JSValue *arrp;
38592     uint32_t count;
38593 
38594     obj = JS_ToObject(ctx, this_val);
38595     if (js_get_length64(ctx, &len, obj))
38596         goto exception;
38597 
38598     res = FALSE;
38599     if (len > 0) {
38600         n = 0;
38601         if (argc > 1) {
38602             if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
38603                 goto exception;
38604         }
38605         if (js_get_fast_array(ctx, obj, &arrp, &count)) {
38606             for (; n < count; n++) {
38607                 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
38608                                   JS_DupValue(ctx, arrp[n]),
38609                                   JS_EQ_SAME_VALUE_ZERO)) {
38610                     res = TRUE;
38611                     goto done;
38612                 }
38613             }
38614         }
38615         for (; n < len; n++) {
38616             val = JS_GetPropertyInt64(ctx, obj, n);
38617             if (JS_IsException(val))
38618                 goto exception;
38619             if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val,
38620                               JS_EQ_SAME_VALUE_ZERO)) {
38621                 res = TRUE;
38622                 break;
38623             }
38624         }
38625     }
38626  done:
38627     JS_FreeValue(ctx, obj);
38628     return JS_NewBool(ctx, res);
38629 
38630  exception:
38631     JS_FreeValue(ctx, obj);
38632     return JS_EXCEPTION;
38633 }
38634 
js_array_indexOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38635 static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val,
38636                                 int argc, JSValueConst *argv)
38637 {
38638     JSValue obj, val;
38639     int64_t len, n, res;
38640     JSValue *arrp;
38641     uint32_t count;
38642 
38643     obj = JS_ToObject(ctx, this_val);
38644     if (js_get_length64(ctx, &len, obj))
38645         goto exception;
38646 
38647     res = -1;
38648     if (len > 0) {
38649         n = 0;
38650         if (argc > 1) {
38651             if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
38652                 goto exception;
38653         }
38654         if (js_get_fast_array(ctx, obj, &arrp, &count)) {
38655             for (; n < count; n++) {
38656                 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
38657                                   JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) {
38658                     res = n;
38659                     goto done;
38660                 }
38661             }
38662         }
38663         for (; n < len; n++) {
38664             int present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
38665             if (present < 0)
38666                 goto exception;
38667             if (present) {
38668                 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
38669                     res = n;
38670                     break;
38671                 }
38672             }
38673         }
38674     }
38675  done:
38676     JS_FreeValue(ctx, obj);
38677     return JS_NewInt64(ctx, res);
38678 
38679  exception:
38680     JS_FreeValue(ctx, obj);
38681     return JS_EXCEPTION;
38682 }
38683 
js_array_lastIndexOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38684 static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val,
38685                                     int argc, JSValueConst *argv)
38686 {
38687     JSValue obj, val;
38688     int64_t len, n, res;
38689     int present;
38690 
38691     obj = JS_ToObject(ctx, this_val);
38692     if (js_get_length64(ctx, &len, obj))
38693         goto exception;
38694 
38695     res = -1;
38696     if (len > 0) {
38697         n = len - 1;
38698         if (argc > 1) {
38699             if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len))
38700                 goto exception;
38701         }
38702         /* XXX: should special case fast arrays */
38703         for (; n >= 0; n--) {
38704             present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
38705             if (present < 0)
38706                 goto exception;
38707             if (present) {
38708                 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
38709                     res = n;
38710                     break;
38711                 }
38712             }
38713         }
38714     }
38715     JS_FreeValue(ctx, obj);
38716     return JS_NewInt64(ctx, res);
38717 
38718  exception:
38719     JS_FreeValue(ctx, obj);
38720     return JS_EXCEPTION;
38721 }
38722 
js_array_find(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int findIndex)38723 static JSValue js_array_find(JSContext *ctx, JSValueConst this_val,
38724                              int argc, JSValueConst *argv, int findIndex)
38725 {
38726     JSValueConst func, this_arg;
38727     JSValueConst args[3];
38728     JSValue obj, val, index_val, res;
38729     int64_t len, k;
38730 
38731     index_val = JS_UNDEFINED;
38732     val = JS_UNDEFINED;
38733     obj = JS_ToObject(ctx, this_val);
38734     if (js_get_length64(ctx, &len, obj))
38735         goto exception;
38736 
38737     func = argv[0];
38738     if (check_function(ctx, func))
38739         goto exception;
38740 
38741     this_arg = JS_UNDEFINED;
38742     if (argc > 1)
38743         this_arg = argv[1];
38744 
38745     for(k = 0; k < len; k++) {
38746         index_val = JS_NewInt64(ctx, k);
38747         if (JS_IsException(index_val))
38748             goto exception;
38749         val = JS_GetPropertyValue(ctx, obj, index_val);
38750         if (JS_IsException(val))
38751             goto exception;
38752         args[0] = val;
38753         args[1] = index_val;
38754         args[2] = this_val;
38755         res = JS_Call(ctx, func, this_arg, 3, args);
38756         if (JS_IsException(res))
38757             goto exception;
38758         if (JS_ToBoolFree(ctx, res)) {
38759             if (findIndex) {
38760                 JS_FreeValue(ctx, val);
38761                 JS_FreeValue(ctx, obj);
38762                 return index_val;
38763             } else {
38764                 JS_FreeValue(ctx, index_val);
38765                 JS_FreeValue(ctx, obj);
38766                 return val;
38767             }
38768         }
38769         JS_FreeValue(ctx, val);
38770         JS_FreeValue(ctx, index_val);
38771     }
38772     JS_FreeValue(ctx, obj);
38773     if (findIndex)
38774         return JS_NewInt32(ctx, -1);
38775     else
38776         return JS_UNDEFINED;
38777 
38778 exception:
38779     JS_FreeValue(ctx, index_val);
38780     JS_FreeValue(ctx, val);
38781     JS_FreeValue(ctx, obj);
38782     return JS_EXCEPTION;
38783 }
38784 
js_array_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38785 static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val,
38786                                  int argc, JSValueConst *argv)
38787 {
38788     JSValue obj, method, ret;
38789 
38790     obj = JS_ToObject(ctx, this_val);
38791     if (JS_IsException(obj))
38792         return JS_EXCEPTION;
38793     method = JS_GetProperty(ctx, obj, JS_ATOM_join);
38794     if (JS_IsException(method)) {
38795         ret = JS_EXCEPTION;
38796     } else
38797     if (!JS_IsFunction(ctx, method)) {
38798         /* Use intrinsic Object.prototype.toString */
38799         JS_FreeValue(ctx, method);
38800         ret = js_object_toString(ctx, obj, 0, NULL);
38801     } else {
38802         ret = JS_CallFree(ctx, method, obj, 0, NULL);
38803     }
38804     JS_FreeValue(ctx, obj);
38805     return ret;
38806 }
38807 
js_array_join(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int toLocaleString)38808 static JSValue js_array_join(JSContext *ctx, JSValueConst this_val,
38809                              int argc, JSValueConst *argv, int toLocaleString)
38810 {
38811     JSValue obj, sep = JS_UNDEFINED, el;
38812     StringBuffer b_s, *b = &b_s;
38813     JSString *p = NULL;
38814     int64_t i, n;
38815     int c;
38816 
38817     obj = JS_ToObject(ctx, this_val);
38818     if (js_get_length64(ctx, &n, obj))
38819         goto exception;
38820 
38821     c = ',';    /* default separator */
38822     if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
38823         sep = JS_ToString(ctx, argv[0]);
38824         if (JS_IsException(sep))
38825             goto exception;
38826         p = JS_VALUE_GET_STRING(sep);
38827         if (p->len == 1 && !p->is_wide_char)
38828             c = p->u.str8[0];
38829         else
38830             c = -1;
38831     }
38832     string_buffer_init(ctx, b, 0);
38833 
38834     for(i = 0; i < n; i++) {
38835         if (i > 0) {
38836             if (c >= 0) {
38837                 string_buffer_putc8(b, c);
38838             } else {
38839                 string_buffer_concat(b, p, 0, p->len);
38840             }
38841         }
38842         el = JS_GetPropertyUint32(ctx, obj, i);
38843         if (JS_IsException(el))
38844             goto fail;
38845         if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
38846             if (toLocaleString) {
38847                 el = JS_ToLocaleStringFree(ctx, el);
38848             }
38849             if (string_buffer_concat_value_free(b, el))
38850                 goto fail;
38851         }
38852     }
38853     JS_FreeValue(ctx, sep);
38854     JS_FreeValue(ctx, obj);
38855     return string_buffer_end(b);
38856 
38857 fail:
38858     string_buffer_free(b);
38859     JS_FreeValue(ctx, sep);
38860 exception:
38861     JS_FreeValue(ctx, obj);
38862     return JS_EXCEPTION;
38863 }
38864 
js_array_pop(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int shift)38865 static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val,
38866                             int argc, JSValueConst *argv, int shift)
38867 {
38868     JSValue obj, res = JS_UNDEFINED;
38869     int64_t len, newLen;
38870     JSValue *arrp;
38871     uint32_t count32;
38872 
38873     obj = JS_ToObject(ctx, this_val);
38874     if (js_get_length64(ctx, &len, obj))
38875         goto exception;
38876     newLen = 0;
38877     if (len > 0) {
38878         newLen = len - 1;
38879         /* Special case fast arrays */
38880         if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
38881             JSObject *p = JS_VALUE_GET_OBJ(obj);
38882             if (shift) {
38883                 res = arrp[0];
38884                 memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp));
38885                 p->u.array.count--;
38886             } else {
38887                 res = arrp[count32 - 1];
38888                 p->u.array.count--;
38889             }
38890         } else {
38891             if (shift) {
38892                 res = JS_GetPropertyInt64(ctx, obj, 0);
38893                 if (JS_IsException(res))
38894                     goto exception;
38895                 if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1))
38896                     goto exception;
38897             } else {
38898                 res = JS_GetPropertyInt64(ctx, obj, newLen);
38899                 if (JS_IsException(res))
38900                     goto exception;
38901             }
38902             if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0)
38903                 goto exception;
38904         }
38905     }
38906     if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
38907         goto exception;
38908 
38909     JS_FreeValue(ctx, obj);
38910     return res;
38911 
38912  exception:
38913     JS_FreeValue(ctx, res);
38914     JS_FreeValue(ctx, obj);
38915     return JS_EXCEPTION;
38916 }
38917 
js_array_push(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int unshift)38918 static JSValue js_array_push(JSContext *ctx, JSValueConst this_val,
38919                              int argc, JSValueConst *argv, int unshift)
38920 {
38921     JSValue obj;
38922     int i;
38923     int64_t len, from, newLen;
38924 
38925     obj = JS_ToObject(ctx, this_val);
38926     if (js_get_length64(ctx, &len, obj))
38927         goto exception;
38928     newLen = len + argc;
38929     if (newLen > MAX_SAFE_INTEGER) {
38930         JS_ThrowTypeError(ctx, "Array loo long");
38931         goto exception;
38932     }
38933     from = len;
38934     if (unshift && argc > 0) {
38935         if (JS_CopySubArray(ctx, obj, argc, 0, len, -1))
38936             goto exception;
38937         from = 0;
38938     }
38939     for(i = 0; i < argc; i++) {
38940         if (JS_SetPropertyInt64(ctx, obj, from + i,
38941                                 JS_DupValue(ctx, argv[i])) < 0)
38942             goto exception;
38943     }
38944     if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
38945         goto exception;
38946 
38947     JS_FreeValue(ctx, obj);
38948     return JS_NewInt64(ctx, newLen);
38949 
38950  exception:
38951     JS_FreeValue(ctx, obj);
38952     return JS_EXCEPTION;
38953 }
38954 
js_array_reverse(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)38955 static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val,
38956                                 int argc, JSValueConst *argv)
38957 {
38958     JSValue obj, lval, hval;
38959     JSValue *arrp;
38960     int64_t len, l, h;
38961     int l_present, h_present;
38962     uint32_t count32;
38963 
38964     lval = JS_UNDEFINED;
38965     obj = JS_ToObject(ctx, this_val);
38966     if (js_get_length64(ctx, &len, obj))
38967         goto exception;
38968 
38969     /* Special case fast arrays */
38970     if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
38971         uint32_t ll, hh;
38972 
38973         if (count32 > 1) {
38974             for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) {
38975                 lval = arrp[ll];
38976                 arrp[ll] = arrp[hh];
38977                 arrp[hh] = lval;
38978             }
38979         }
38980         return obj;
38981     }
38982 
38983     for (l = 0, h = len - 1; l < h; l++, h--) {
38984         l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval);
38985         if (l_present < 0)
38986             goto exception;
38987         h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval);
38988         if (h_present < 0)
38989             goto exception;
38990         if (h_present) {
38991             if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0)
38992                 goto exception;
38993 
38994             if (l_present) {
38995                 if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
38996                     lval = JS_UNDEFINED;
38997                     goto exception;
38998                 }
38999                 lval = JS_UNDEFINED;
39000             } else {
39001                 if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0)
39002                     goto exception;
39003             }
39004         } else {
39005             if (l_present) {
39006                 if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0)
39007                     goto exception;
39008                 if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
39009                     lval = JS_UNDEFINED;
39010                     goto exception;
39011                 }
39012                 lval = JS_UNDEFINED;
39013             }
39014         }
39015     }
39016     return obj;
39017 
39018  exception:
39019     JS_FreeValue(ctx, lval);
39020     JS_FreeValue(ctx, obj);
39021     return JS_EXCEPTION;
39022 }
39023 
js_array_slice(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int splice)39024 static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,
39025                               int argc, JSValueConst *argv, int splice)
39026 {
39027     JSValue obj, arr, val, len_val;
39028     int64_t len, start, k, final, n, count, del_count, new_len;
39029     int kPresent;
39030     JSValue *arrp;
39031     uint32_t count32, i, item_count;
39032 
39033     arr = JS_UNDEFINED;
39034     obj = JS_ToObject(ctx, this_val);
39035     if (js_get_length64(ctx, &len, obj))
39036         goto exception;
39037 
39038     if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
39039         goto exception;
39040 
39041     if (splice) {
39042         if (argc == 0) {
39043             item_count = 0;
39044             del_count = 0;
39045         } else
39046         if (argc == 1) {
39047             item_count = 0;
39048             del_count = len - start;
39049         } else {
39050             item_count = argc - 2;
39051             if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0))
39052                 goto exception;
39053         }
39054         if (len + item_count - del_count > MAX_SAFE_INTEGER) {
39055             JS_ThrowTypeError(ctx, "Array loo long");
39056             goto exception;
39057         }
39058         count = del_count;
39059     } else {
39060         item_count = 0; /* avoid warning */
39061         final = len;
39062         if (!JS_IsUndefined(argv[1])) {
39063             if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len))
39064                 goto exception;
39065         }
39066         count = max_int64(final - start, 0);
39067     }
39068     len_val = JS_NewInt64(ctx, count);
39069     arr = JS_ArraySpeciesCreate(ctx, obj, len_val);
39070     JS_FreeValue(ctx, len_val);
39071     if (JS_IsException(arr))
39072         goto exception;
39073 
39074     k = start;
39075     final = start + count;
39076     n = 0;
39077     /* The fast array test on arr ensures that
39078        JS_CreateDataPropertyUint32() won't modify obj in case arr is
39079        an exotic object */
39080     /* Special case fast arrays */
39081     if (js_get_fast_array(ctx, obj, &arrp, &count32) &&
39082         js_is_fast_array(ctx, arr)) {
39083         /* XXX: should share code with fast array constructor */
39084         for (; k < final && k < count32; k++, n++) {
39085             if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0)
39086                 goto exception;
39087         }
39088     }
39089     /* Copy the remaining elements if any (handle case of inherited properties) */
39090     for (; k < final; k++, n++) {
39091         kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val);
39092         if (kPresent < 0)
39093             goto exception;
39094         if (kPresent) {
39095             if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0)
39096                 goto exception;
39097         }
39098     }
39099     if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
39100         goto exception;
39101 
39102     if (splice) {
39103         new_len = len + item_count - del_count;
39104         if (item_count != del_count) {
39105             if (JS_CopySubArray(ctx, obj, start + item_count,
39106                                 start + del_count, len - (start + del_count),
39107                                 item_count <= del_count ? +1 : -1) < 0)
39108                 goto exception;
39109 
39110             for (k = len; k-- > new_len; ) {
39111                 if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0)
39112                     goto exception;
39113             }
39114         }
39115         for (i = 0; i < item_count; i++) {
39116             if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0)
39117                 goto exception;
39118         }
39119         if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0)
39120             goto exception;
39121     }
39122     JS_FreeValue(ctx, obj);
39123     return arr;
39124 
39125  exception:
39126     JS_FreeValue(ctx, obj);
39127     JS_FreeValue(ctx, arr);
39128     return JS_EXCEPTION;
39129 }
39130 
js_array_copyWithin(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39131 static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val,
39132                                    int argc, JSValueConst *argv)
39133 {
39134     JSValue obj;
39135     int64_t len, from, to, final, count;
39136 
39137     obj = JS_ToObject(ctx, this_val);
39138     if (js_get_length64(ctx, &len, obj))
39139         goto exception;
39140 
39141     if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len))
39142         goto exception;
39143 
39144     if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len))
39145         goto exception;
39146 
39147     final = len;
39148     if (argc > 2 && !JS_IsUndefined(argv[2])) {
39149         if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len))
39150             goto exception;
39151     }
39152 
39153     count = min_int64(final - from, len - to);
39154 
39155     if (JS_CopySubArray(ctx, obj, to, from, count,
39156                         (from < to && to < from + count) ? -1 : +1))
39157         goto exception;
39158 
39159     return obj;
39160 
39161  exception:
39162     JS_FreeValue(ctx, obj);
39163     return JS_EXCEPTION;
39164 }
39165 
JS_FlattenIntoArray(JSContext * ctx,JSValueConst target,JSValueConst source,int64_t sourceLen,int64_t targetIndex,int depth,JSValueConst mapperFunction,JSValueConst thisArg)39166 static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target,
39167                                    JSValueConst source, int64_t sourceLen,
39168                                    int64_t targetIndex, int depth,
39169                                    JSValueConst mapperFunction,
39170                                    JSValueConst thisArg)
39171 {
39172     JSValue element;
39173     int64_t sourceIndex, elementLen;
39174     int present, is_array;
39175 
39176     for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) {
39177         present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element);
39178         if (present < 0)
39179             return -1;
39180         if (!present)
39181             continue;
39182         if (!JS_IsUndefined(mapperFunction)) {
39183             JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source };
39184             element = JS_Call(ctx, mapperFunction, thisArg, 3, args);
39185             JS_FreeValue(ctx, (JSValue)args[0]);
39186             JS_FreeValue(ctx, (JSValue)args[1]);
39187             if (JS_IsException(element))
39188                 return -1;
39189         }
39190         if (depth > 0) {
39191             is_array = JS_IsArray(ctx, element);
39192             if (is_array < 0)
39193                 goto fail;
39194             if (is_array) {
39195                 if (js_get_length64(ctx, &elementLen, element) < 0)
39196                     goto fail;
39197                 targetIndex = JS_FlattenIntoArray(ctx, target, element,
39198                                                   elementLen, targetIndex,
39199                                                   depth - 1,
39200                                                   JS_UNDEFINED, JS_UNDEFINED);
39201                 if (targetIndex < 0)
39202                     goto fail;
39203                 JS_FreeValue(ctx, element);
39204                 continue;
39205             }
39206         }
39207         if (targetIndex >= MAX_SAFE_INTEGER) {
39208             JS_ThrowTypeError(ctx, "Array too long");
39209             goto fail;
39210         }
39211         if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element,
39212                                         JS_PROP_C_W_E | JS_PROP_THROW) < 0)
39213             return -1;
39214         targetIndex++;
39215     }
39216     return targetIndex;
39217 
39218 fail:
39219     JS_FreeValue(ctx, element);
39220     return -1;
39221 }
39222 
js_array_flatten(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int map)39223 static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val,
39224                                 int argc, JSValueConst *argv, int map)
39225 {
39226     JSValue obj, arr;
39227     JSValueConst mapperFunction, thisArg;
39228     int64_t sourceLen;
39229     int depthNum;
39230 
39231     arr = JS_UNDEFINED;
39232     obj = JS_ToObject(ctx, this_val);
39233     if (js_get_length64(ctx, &sourceLen, obj))
39234         goto exception;
39235 
39236     depthNum = 1;
39237     mapperFunction = JS_UNDEFINED;
39238     thisArg = JS_UNDEFINED;
39239     if (map) {
39240         mapperFunction = argv[0];
39241         if (argc > 1) {
39242             thisArg = argv[1];
39243         }
39244         if (check_function(ctx, mapperFunction))
39245             goto exception;
39246     } else {
39247         if (argc > 0 && !JS_IsUndefined(argv[0])) {
39248             if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0)
39249                 goto exception;
39250         }
39251     }
39252     arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
39253     if (JS_IsException(arr))
39254         goto exception;
39255     if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum,
39256                             mapperFunction, thisArg) < 0)
39257         goto exception;
39258     JS_FreeValue(ctx, obj);
39259     return arr;
39260 
39261 exception:
39262     JS_FreeValue(ctx, obj);
39263     JS_FreeValue(ctx, arr);
39264     return JS_EXCEPTION;
39265 }
39266 
39267 /* Array sort */
39268 
39269 typedef struct ValueSlot {
39270     JSValue val;
39271     JSString *str;
39272     int64_t pos;
39273 } ValueSlot;
39274 
39275 struct array_sort_context {
39276     JSContext *ctx;
39277     int exception;
39278     int has_method;
39279     JSValueConst method;
39280 };
39281 
js_array_cmp_generic(const void * a,const void * b,void * opaque)39282 static int js_array_cmp_generic(const void *a, const void *b, void *opaque) {
39283     struct array_sort_context *psc = opaque;
39284     JSContext *ctx = psc->ctx;
39285     JSValueConst argv[2];
39286     JSValue res;
39287     ValueSlot *ap = (ValueSlot *)(void *)a;
39288     ValueSlot *bp = (ValueSlot *)(void *)b;
39289     int cmp;
39290 
39291     if (psc->exception)
39292         return 0;
39293 
39294     if (psc->has_method) {
39295         /* custom sort function is specified as returning 0 for identical
39296          * objects: avoid method call overhead.
39297          */
39298         if (!memcmp(&ap->val, &bp->val, sizeof(ap->val)))
39299             goto cmp_same;
39300         argv[0] = ap->val;
39301         argv[1] = bp->val;
39302         res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv);
39303         if (JS_IsException(res))
39304             goto exception;
39305         if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
39306             int val = JS_VALUE_GET_INT(res);
39307             cmp = (val > 0) - (val < 0);
39308         } else {
39309             double val;
39310             if (JS_ToFloat64Free(ctx, &val, res) < 0)
39311                 goto exception;
39312             cmp = (val > 0) - (val < 0);
39313         }
39314     } else {
39315         /* Not supposed to bypass ToString even for identical objects as
39316          * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js
39317          */
39318         if (!ap->str) {
39319             JSValue str = JS_ToString(ctx, ap->val);
39320             if (JS_IsException(str))
39321                 goto exception;
39322             ap->str = JS_VALUE_GET_STRING(str);
39323         }
39324         if (!bp->str) {
39325             JSValue str = JS_ToString(ctx, bp->val);
39326             if (JS_IsException(str))
39327                 goto exception;
39328             bp->str = JS_VALUE_GET_STRING(str);
39329         }
39330         cmp = js_string_compare(ctx, ap->str, bp->str);
39331     }
39332     if (cmp != 0)
39333         return cmp;
39334 cmp_same:
39335     /* make sort stable: compare array offsets */
39336     return (ap->pos > bp->pos) - (ap->pos < bp->pos);
39337 
39338 exception:
39339     psc->exception = 1;
39340     return 0;
39341 }
39342 
js_array_sort(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39343 static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val,
39344                              int argc, JSValueConst *argv)
39345 {
39346     struct array_sort_context asc = { ctx, 0, 0, argv[0] };
39347     JSValue obj = JS_UNDEFINED;
39348     ValueSlot *array = NULL;
39349     size_t array_size = 0, pos = 0, n = 0;
39350     int64_t i, len, undefined_count = 0;
39351     int present;
39352 
39353     if (!JS_IsUndefined(asc.method)) {
39354         if (check_function(ctx, asc.method))
39355             goto exception;
39356         asc.has_method = 1;
39357     }
39358     obj = JS_ToObject(ctx, this_val);
39359     if (js_get_length64(ctx, &len, obj))
39360         goto exception;
39361 
39362     /* XXX: should special case fast arrays */
39363     for (i = 0; i < len; i++) {
39364         if (pos >= array_size) {
39365             size_t new_size, slack;
39366             ValueSlot *new_array;
39367             new_size = (array_size + (array_size >> 1) + 31) & ~15;
39368             new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack);
39369             if (new_array == NULL)
39370                 goto exception;
39371             new_size += slack / sizeof(*new_array);
39372             array = new_array;
39373             array_size = new_size;
39374         }
39375         present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val);
39376         if (present < 0)
39377             goto exception;
39378         if (present == 0)
39379             continue;
39380         if (JS_IsUndefined(array[pos].val)) {
39381             undefined_count++;
39382             continue;
39383         }
39384         array[pos].str = NULL;
39385         array[pos].pos = i;
39386         pos++;
39387     }
39388     rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc);
39389     if (asc.exception)
39390         goto exception;
39391 
39392     /* XXX: should special case fast arrays */
39393     while (n < pos) {
39394         if (array[n].str)
39395             JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
39396         if (array[n].pos == n) {
39397             JS_FreeValue(ctx, array[n].val);
39398         } else {
39399             if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) {
39400                 n++;
39401                 goto exception;
39402             }
39403         }
39404         n++;
39405     }
39406     js_free(ctx, array);
39407     for (i = n; undefined_count-- > 0; i++) {
39408         if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0)
39409             goto fail;
39410     }
39411     for (; i < len; i++) {
39412         if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0)
39413             goto fail;
39414     }
39415     return obj;
39416 
39417 exception:
39418     for (; n < pos; n++) {
39419         JS_FreeValue(ctx, array[n].val);
39420         if (array[n].str)
39421             JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
39422     }
39423     js_free(ctx, array);
39424 fail:
39425     JS_FreeValue(ctx, obj);
39426     return JS_EXCEPTION;
39427 }
39428 
39429 typedef struct JSArrayIteratorData {
39430     JSValue obj;
39431     JSIteratorKindEnum kind;
39432     uint32_t idx;
39433 } JSArrayIteratorData;
39434 
js_array_iterator_finalizer(JSRuntime * rt,JSValue val)39435 static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val)
39436 {
39437     JSObject *p = JS_VALUE_GET_OBJ(val);
39438     JSArrayIteratorData *it = p->u.array_iterator_data;
39439     if (it) {
39440         JS_FreeValueRT(rt, it->obj);
39441         js_free_rt(rt, it);
39442     }
39443 }
39444 
js_array_iterator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)39445 static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
39446                                    JS_MarkFunc *mark_func)
39447 {
39448     JSObject *p = JS_VALUE_GET_OBJ(val);
39449     JSArrayIteratorData *it = p->u.array_iterator_data;
39450     if (it) {
39451         JS_MarkValue(rt, it->obj, mark_func);
39452     }
39453 }
39454 
js_create_array(JSContext * ctx,int len,JSValueConst * tab)39455 static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab)
39456 {
39457     JSValue obj;
39458     int i;
39459 
39460     obj = JS_NewArray(ctx);
39461     if (JS_IsException(obj))
39462         return JS_EXCEPTION;
39463     for(i = 0; i < len; i++) {
39464         if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) {
39465             JS_FreeValue(ctx, obj);
39466             return JS_EXCEPTION;
39467         }
39468     }
39469     return obj;
39470 }
39471 
js_create_array_iterator(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)39472 static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
39473                                         int argc, JSValueConst *argv, int magic)
39474 {
39475     JSValue enum_obj, arr;
39476     JSArrayIteratorData *it;
39477     JSIteratorKindEnum kind;
39478     int class_id;
39479 
39480     kind = magic & 3;
39481     if (magic & 4) {
39482         /* string iterator case */
39483         arr = JS_ToStringCheckObject(ctx, this_val);
39484         class_id = JS_CLASS_STRING_ITERATOR;
39485     } else {
39486         arr = JS_ToObject(ctx, this_val);
39487         class_id = JS_CLASS_ARRAY_ITERATOR;
39488     }
39489     if (JS_IsException(arr))
39490         goto fail;
39491     enum_obj = JS_NewObjectClass(ctx, class_id);
39492     if (JS_IsException(enum_obj))
39493         goto fail;
39494     it = js_malloc(ctx, sizeof(*it));
39495     if (!it)
39496         goto fail1;
39497     it->obj = arr;
39498     it->kind = kind;
39499     it->idx = 0;
39500     JS_SetOpaque(enum_obj, it);
39501     return enum_obj;
39502  fail1:
39503     JS_FreeValue(ctx, enum_obj);
39504  fail:
39505     JS_FreeValue(ctx, arr);
39506     return JS_EXCEPTION;
39507 }
39508 
js_array_iterator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,BOOL * pdone,int magic)39509 static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
39510                                       int argc, JSValueConst *argv,
39511                                       BOOL *pdone, int magic)
39512 {
39513     JSArrayIteratorData *it;
39514     uint32_t len, idx;
39515     JSValue val, obj;
39516     JSObject *p;
39517 
39518     it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR);
39519     if (!it)
39520         goto fail1;
39521     if (JS_IsUndefined(it->obj))
39522         goto done;
39523     p = JS_VALUE_GET_OBJ(it->obj);
39524     if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
39525         p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
39526         if (typed_array_is_detached(ctx, p)) {
39527             JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
39528             goto fail1;
39529         }
39530         len = p->u.array.count;
39531     } else {
39532         if (js_get_length32(ctx, &len, it->obj)) {
39533         fail1:
39534             *pdone = FALSE;
39535             return JS_EXCEPTION;
39536         }
39537     }
39538     idx = it->idx;
39539     if (idx >= len) {
39540         JS_FreeValue(ctx, it->obj);
39541         it->obj = JS_UNDEFINED;
39542     done:
39543         *pdone = TRUE;
39544         return JS_UNDEFINED;
39545     }
39546     it->idx = idx + 1;
39547     *pdone = FALSE;
39548     if (it->kind == JS_ITERATOR_KIND_KEY) {
39549         return JS_NewUint32(ctx, idx);
39550     } else {
39551         val = JS_GetPropertyUint32(ctx, it->obj, idx);
39552         if (JS_IsException(val))
39553             return JS_EXCEPTION;
39554         if (it->kind == JS_ITERATOR_KIND_VALUE) {
39555             return val;
39556         } else {
39557             JSValueConst args[2];
39558             JSValue num;
39559             num = JS_NewUint32(ctx, idx);
39560             args[0] = num;
39561             args[1] = val;
39562             obj = js_create_array(ctx, 2, args);
39563             JS_FreeValue(ctx, val);
39564             JS_FreeValue(ctx, num);
39565             return obj;
39566         }
39567     }
39568 }
39569 
js_iterator_proto_iterator(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39570 static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val,
39571                                           int argc, JSValueConst *argv)
39572 {
39573     return JS_DupValue(ctx, this_val);
39574 }
39575 
39576 static const JSCFunctionListEntry js_iterator_proto_funcs[] = {
39577     JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ),
39578 };
39579 
39580 static const JSCFunctionListEntry js_array_proto_funcs[] = {
39581     JS_CFUNC_DEF("concat", 1, js_array_concat ),
39582     JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ),
39583     JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ),
39584     JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ),
39585     JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ),
39586     JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ),
39587     JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ),
39588     JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ),
39589     JS_CFUNC_DEF("fill", 1, js_array_fill ),
39590     JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, 0 ),
39591     JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, 1 ),
39592     JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ),
39593     JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ),
39594     JS_CFUNC_DEF("includes", 1, js_array_includes ),
39595     JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ),
39596     JS_CFUNC_DEF("toString", 0, js_array_toString ),
39597     JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ),
39598     JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ),
39599     JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ),
39600     JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ),
39601     JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ),
39602     JS_CFUNC_DEF("reverse", 0, js_array_reverse ),
39603     JS_CFUNC_DEF("sort", 1, js_array_sort ),
39604     JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ),
39605     JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ),
39606     JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ),
39607     JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ),
39608     JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ),
39609     JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ),
39610     JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
39611     JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ),
39612     JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
39613 };
39614 
39615 static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = {
39616     JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ),
39617     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ),
39618 };
39619 
39620 /* Number */
39621 
js_number_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)39622 static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,
39623                                      int argc, JSValueConst *argv)
39624 {
39625     JSValue val, obj;
39626     if (argc == 0) {
39627         val = JS_NewInt32(ctx, 0);
39628     } else {
39629         val = JS_ToNumeric(ctx, argv[0]);
39630         if (JS_IsException(val))
39631             return val;
39632         switch(JS_VALUE_GET_TAG(val)) {
39633 #ifdef CONFIG_BIGNUM
39634         case JS_TAG_BIG_INT:
39635         case JS_TAG_BIG_FLOAT:
39636             {
39637                 JSBigFloat *p = JS_VALUE_GET_PTR(val);
39638                 double d;
39639                 bf_get_float64(&p->num, &d, BF_RNDN);
39640                 JS_FreeValue(ctx, val);
39641                 val = __JS_NewFloat64(ctx, d);
39642             }
39643             break;
39644         case JS_TAG_BIG_DECIMAL:
39645             val = JS_ToStringFree(ctx, val);
39646             if (JS_IsException(val))
39647                 return val;
39648             val = JS_ToNumberFree(ctx, val);
39649             if (JS_IsException(val))
39650                 return val;
39651             break;
39652 #endif
39653         default:
39654             break;
39655         }
39656     }
39657     if (!JS_IsUndefined(new_target)) {
39658         obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER);
39659         if (!JS_IsException(obj))
39660             JS_SetObjectData(ctx, obj, val);
39661         return obj;
39662     } else {
39663         return val;
39664     }
39665 }
39666 
39667 #if 0
39668 static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val,
39669                                      int argc, JSValueConst *argv)
39670 {
39671     return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0]));
39672 }
39673 
39674 static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val,
39675                                     int argc, JSValueConst *argv)
39676 {
39677     int64_t v;
39678     if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0])))
39679         return JS_EXCEPTION;
39680     return JS_NewInt64(ctx, v);
39681 }
39682 #endif
39683 
js_number_isNaN(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39684 static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val,
39685                                int argc, JSValueConst *argv)
39686 {
39687     if (!JS_IsNumber(argv[0]))
39688         return JS_FALSE;
39689     return js_global_isNaN(ctx, this_val, argc, argv);
39690 }
39691 
js_number_isFinite(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39692 static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val,
39693                                   int argc, JSValueConst *argv)
39694 {
39695     if (!JS_IsNumber(argv[0]))
39696         return JS_FALSE;
39697     return js_global_isFinite(ctx, this_val, argc, argv);
39698 }
39699 
js_number_isInteger(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39700 static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val,
39701                                    int argc, JSValueConst *argv)
39702 {
39703     int ret;
39704     ret = JS_NumberIsInteger(ctx, argv[0]);
39705     if (ret < 0)
39706         return JS_EXCEPTION;
39707     else
39708         return JS_NewBool(ctx, ret);
39709 }
39710 
js_number_isSafeInteger(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39711 static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val,
39712                                        int argc, JSValueConst *argv)
39713 {
39714     double d;
39715     if (!JS_IsNumber(argv[0]))
39716         return JS_FALSE;
39717     if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
39718         return JS_EXCEPTION;
39719     return JS_NewBool(ctx, is_safe_integer(d));
39720 }
39721 
39722 static const JSCFunctionListEntry js_number_funcs[] = {
39723     /* global ParseInt and parseFloat should be defined already or delayed */
39724     JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ),
39725     JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ),
39726     JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ),
39727     JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ),
39728     JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ),
39729     JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ),
39730     JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ),
39731     JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ),
39732     JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
39733     JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ),
39734     JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ),
39735     JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */
39736     JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */
39737     JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */
39738     //JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ),
39739     //JS_CFUNC_DEF("__toLength", 1, js_number___toLength ),
39740 };
39741 
js_thisNumberValue(JSContext * ctx,JSValueConst this_val)39742 static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val)
39743 {
39744     if (JS_IsNumber(this_val))
39745         return JS_DupValue(ctx, this_val);
39746 
39747     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
39748         JSObject *p = JS_VALUE_GET_OBJ(this_val);
39749         if (p->class_id == JS_CLASS_NUMBER) {
39750             if (JS_IsNumber(p->u.object_data))
39751                 return JS_DupValue(ctx, p->u.object_data);
39752         }
39753     }
39754     return JS_ThrowTypeError(ctx, "not a number");
39755 }
39756 
js_number_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39757 static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val,
39758                                  int argc, JSValueConst *argv)
39759 {
39760     return js_thisNumberValue(ctx, this_val);
39761 }
39762 
js_get_radix(JSContext * ctx,JSValueConst val)39763 static int js_get_radix(JSContext *ctx, JSValueConst val)
39764 {
39765     int radix;
39766     if (JS_ToInt32Sat(ctx, &radix, val))
39767         return -1;
39768     if (radix < 2 || radix > 36) {
39769         JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
39770         return -1;
39771     }
39772     return radix;
39773 }
39774 
js_number_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)39775 static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,
39776                                   int argc, JSValueConst *argv, int magic)
39777 {
39778     JSValue val;
39779     int base;
39780     double d;
39781 
39782     val = js_thisNumberValue(ctx, this_val);
39783     if (JS_IsException(val))
39784         return val;
39785     if (magic || JS_IsUndefined(argv[0])) {
39786         base = 10;
39787     } else {
39788         base = js_get_radix(ctx, argv[0]);
39789         if (base < 0)
39790             goto fail;
39791     }
39792     if (JS_ToFloat64Free(ctx, &d, val))
39793         return JS_EXCEPTION;
39794     return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT);
39795  fail:
39796     JS_FreeValue(ctx, val);
39797     return JS_EXCEPTION;
39798 }
39799 
js_number_toFixed(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39800 static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val,
39801                                  int argc, JSValueConst *argv)
39802 {
39803     JSValue val;
39804     int f;
39805     double d;
39806 
39807     val = js_thisNumberValue(ctx, this_val);
39808     if (JS_IsException(val))
39809         return val;
39810     if (JS_ToFloat64Free(ctx, &d, val))
39811         return JS_EXCEPTION;
39812     if (JS_ToInt32Sat(ctx, &f, argv[0]))
39813         return JS_EXCEPTION;
39814     if (f < 0 || f > 100)
39815         return JS_ThrowRangeError(ctx, "invalid number of digits");
39816     if (fabs(d) >= 1e21) {
39817         return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
39818     } else {
39819         return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT);
39820     }
39821 }
39822 
js_number_toExponential(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39823 static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val,
39824                                        int argc, JSValueConst *argv)
39825 {
39826     JSValue val;
39827     int f, flags;
39828     double d;
39829 
39830     val = js_thisNumberValue(ctx, this_val);
39831     if (JS_IsException(val))
39832         return val;
39833     if (JS_ToFloat64Free(ctx, &d, val))
39834         return JS_EXCEPTION;
39835     if (JS_ToInt32Sat(ctx, &f, argv[0]))
39836         return JS_EXCEPTION;
39837     if (!isfinite(d)) {
39838         return JS_ToStringFree(ctx,  __JS_NewFloat64(ctx, d));
39839     }
39840     if (JS_IsUndefined(argv[0])) {
39841         flags = 0;
39842         f = 0;
39843     } else {
39844         if (f < 0 || f > 100)
39845             return JS_ThrowRangeError(ctx, "invalid number of digits");
39846         f++;
39847         flags = JS_DTOA_FIXED_FORMAT;
39848     }
39849     return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP);
39850 }
39851 
js_number_toPrecision(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39852 static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val,
39853                                      int argc, JSValueConst *argv)
39854 {
39855     JSValue val;
39856     int p;
39857     double d;
39858 
39859     val = js_thisNumberValue(ctx, this_val);
39860     if (JS_IsException(val))
39861         return val;
39862     if (JS_ToFloat64Free(ctx, &d, val))
39863         return JS_EXCEPTION;
39864     if (JS_IsUndefined(argv[0]))
39865         goto to_string;
39866     if (JS_ToInt32Sat(ctx, &p, argv[0]))
39867         return JS_EXCEPTION;
39868     if (!isfinite(d)) {
39869     to_string:
39870         return JS_ToStringFree(ctx,  __JS_NewFloat64(ctx, d));
39871     }
39872     if (p < 1 || p > 100)
39873         return JS_ThrowRangeError(ctx, "invalid number of digits");
39874     return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT);
39875 }
39876 
39877 static const JSCFunctionListEntry js_number_proto_funcs[] = {
39878     JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ),
39879     JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ),
39880     JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ),
39881     JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ),
39882     JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ),
39883     JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ),
39884 };
39885 
js_parseInt(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39886 static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val,
39887                            int argc, JSValueConst *argv)
39888 {
39889     const char *str, *p;
39890     int radix, flags;
39891     JSValue ret;
39892 
39893     str = JS_ToCString(ctx, argv[0]);
39894     if (!str)
39895         return JS_EXCEPTION;
39896     if (JS_ToInt32(ctx, &radix, argv[1])) {
39897         JS_FreeCString(ctx, str);
39898         return JS_EXCEPTION;
39899     }
39900     if (radix != 0 && (radix < 2 || radix > 36)) {
39901         ret = JS_NAN;
39902     } else {
39903         p = str;
39904         p += skip_spaces(p);
39905         flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN;
39906         ret = js_atof(ctx, p, NULL, radix, flags);
39907     }
39908     JS_FreeCString(ctx, str);
39909     return ret;
39910 }
39911 
js_parseFloat(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39912 static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val,
39913                              int argc, JSValueConst *argv)
39914 {
39915     const char *str, *p;
39916     JSValue ret;
39917 
39918     str = JS_ToCString(ctx, argv[0]);
39919     if (!str)
39920         return JS_EXCEPTION;
39921     p = str;
39922     p += skip_spaces(p);
39923     ret = js_atof(ctx, p, NULL, 10, 0);
39924     JS_FreeCString(ctx, str);
39925     return ret;
39926 }
39927 
39928 /* Boolean */
js_boolean_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)39929 static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target,
39930                                      int argc, JSValueConst *argv)
39931 {
39932     JSValue val, obj;
39933     val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0]));
39934     if (!JS_IsUndefined(new_target)) {
39935         obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN);
39936         if (!JS_IsException(obj))
39937             JS_SetObjectData(ctx, obj, val);
39938         return obj;
39939     } else {
39940         return val;
39941     }
39942 }
39943 
js_thisBooleanValue(JSContext * ctx,JSValueConst this_val)39944 static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val)
39945 {
39946     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL)
39947         return JS_DupValue(ctx, this_val);
39948 
39949     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
39950         JSObject *p = JS_VALUE_GET_OBJ(this_val);
39951         if (p->class_id == JS_CLASS_BOOLEAN) {
39952             if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL)
39953                 return p->u.object_data;
39954         }
39955     }
39956     return JS_ThrowTypeError(ctx, "not a boolean");
39957 }
39958 
js_boolean_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39959 static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val,
39960                                    int argc, JSValueConst *argv)
39961 {
39962     JSValue val = js_thisBooleanValue(ctx, this_val);
39963     if (JS_IsException(val))
39964         return val;
39965     return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
39966                        JS_ATOM_true : JS_ATOM_false);
39967 }
39968 
js_boolean_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)39969 static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val,
39970                                   int argc, JSValueConst *argv)
39971 {
39972     return js_thisBooleanValue(ctx, this_val);
39973 }
39974 
39975 static const JSCFunctionListEntry js_boolean_proto_funcs[] = {
39976     JS_CFUNC_DEF("toString", 0, js_boolean_toString ),
39977     JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ),
39978 };
39979 
39980 /* String */
39981 
js_string_get_own_property(JSContext * ctx,JSPropertyDescriptor * desc,JSValueConst obj,JSAtom prop)39982 static int js_string_get_own_property(JSContext *ctx,
39983                                       JSPropertyDescriptor *desc,
39984                                       JSValueConst obj, JSAtom prop)
39985 {
39986     JSObject *p;
39987     JSString *p1;
39988     uint32_t idx, ch;
39989 
39990     /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
39991     if (__JS_AtomIsTaggedInt(prop)) {
39992         p = JS_VALUE_GET_OBJ(obj);
39993         if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
39994             p1 = JS_VALUE_GET_STRING(p->u.object_data);
39995             idx = __JS_AtomToUInt32(prop);
39996             if (idx < p1->len) {
39997                 if (desc) {
39998                     if (p1->is_wide_char)
39999                         ch = p1->u.str16[idx];
40000                     else
40001                         ch = p1->u.str8[idx];
40002                     desc->flags = JS_PROP_ENUMERABLE;
40003                     desc->value = js_new_string_char(ctx, ch);
40004                     desc->getter = JS_UNDEFINED;
40005                     desc->setter = JS_UNDEFINED;
40006                 }
40007                 return TRUE;
40008             }
40009         }
40010     }
40011     return FALSE;
40012 }
40013 
js_string_define_own_property(JSContext * ctx,JSValueConst this_obj,JSAtom prop,JSValueConst val,JSValueConst getter,JSValueConst setter,int flags)40014 static int js_string_define_own_property(JSContext *ctx,
40015                                          JSValueConst this_obj,
40016                                          JSAtom prop, JSValueConst val,
40017                                          JSValueConst getter,
40018                                          JSValueConst setter, int flags)
40019 {
40020     uint32_t idx;
40021     JSObject *p;
40022     JSString *p1, *p2;
40023 
40024     if (__JS_AtomIsTaggedInt(prop)) {
40025         idx = __JS_AtomToUInt32(prop);
40026         p = JS_VALUE_GET_OBJ(this_obj);
40027         if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING)
40028             goto def;
40029         p1 = JS_VALUE_GET_STRING(p->u.object_data);
40030         if (idx >= p1->len)
40031             goto def;
40032         if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags))
40033             goto fail;
40034         /* check that the same value is configured */
40035         if (flags & JS_PROP_HAS_VALUE) {
40036             if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
40037                 goto fail;
40038             p2 = JS_VALUE_GET_STRING(val);
40039             if (p2->len != 1)
40040                 goto fail;
40041             if (string_get(p1, idx) != string_get(p2, 0)) {
40042             fail:
40043                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
40044             }
40045         }
40046         return TRUE;
40047     } else {
40048     def:
40049         return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
40050                                  flags | JS_PROP_NO_EXOTIC);
40051     }
40052 }
40053 
js_string_delete_property(JSContext * ctx,JSValueConst obj,JSAtom prop)40054 static int js_string_delete_property(JSContext *ctx,
40055                                      JSValueConst obj, JSAtom prop)
40056 {
40057     uint32_t idx;
40058 
40059     if (__JS_AtomIsTaggedInt(prop)) {
40060         idx = __JS_AtomToUInt32(prop);
40061         if (idx < js_string_obj_get_length(ctx, obj)) {
40062             return FALSE;
40063         }
40064     }
40065     return TRUE;
40066 }
40067 
40068 static const JSClassExoticMethods js_string_exotic_methods = {
40069     .get_own_property = js_string_get_own_property,
40070     .define_own_property = js_string_define_own_property,
40071     .delete_property = js_string_delete_property,
40072 };
40073 
js_string_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)40074 static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target,
40075                                      int argc, JSValueConst *argv)
40076 {
40077     JSValue val, obj;
40078     if (argc == 0) {
40079         val = JS_AtomToString(ctx, JS_ATOM_empty_string);
40080     } else {
40081         if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) {
40082             JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]);
40083             val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")");
40084         } else {
40085             val = JS_ToString(ctx, argv[0]);
40086         }
40087         if (JS_IsException(val))
40088             return val;
40089     }
40090     if (!JS_IsUndefined(new_target)) {
40091         JSString *p1 = JS_VALUE_GET_STRING(val);
40092 
40093         obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING);
40094         if (!JS_IsException(obj)) {
40095             JS_SetObjectData(ctx, obj, val);
40096             JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
40097         }
40098         return obj;
40099     } else {
40100         return val;
40101     }
40102 }
40103 
js_thisStringValue(JSContext * ctx,JSValueConst this_val)40104 static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val)
40105 {
40106     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING)
40107         return JS_DupValue(ctx, this_val);
40108 
40109     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
40110         JSObject *p = JS_VALUE_GET_OBJ(this_val);
40111         if (p->class_id == JS_CLASS_STRING) {
40112             if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING)
40113                 return JS_DupValue(ctx, p->u.object_data);
40114         }
40115     }
40116     return JS_ThrowTypeError(ctx, "not a string");
40117 }
40118 
js_string_fromCharCode(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40119 static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val,
40120                                       int argc, JSValueConst *argv)
40121 {
40122     int i;
40123     StringBuffer b_s, *b = &b_s;
40124 
40125     string_buffer_init(ctx, b, argc);
40126 
40127     for(i = 0; i < argc; i++) {
40128         int32_t c;
40129         if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) {
40130             string_buffer_free(b);
40131             return JS_EXCEPTION;
40132         }
40133     }
40134     return string_buffer_end(b);
40135 }
40136 
js_string_fromCodePoint(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40137 static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val,
40138                                        int argc, JSValueConst *argv)
40139 {
40140     double d;
40141     int i, c;
40142     StringBuffer b_s, *b = &b_s;
40143 
40144     /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */
40145 
40146     if (string_buffer_init(ctx, b, argc))
40147         goto fail;
40148     for(i = 0; i < argc; i++) {
40149         if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) {
40150             c = JS_VALUE_GET_INT(argv[i]);
40151             if (c < 0 || c > 0x10ffff)
40152                 goto range_error;
40153         } else {
40154             if (JS_ToFloat64(ctx, &d, argv[i]))
40155                 goto fail;
40156             if (d < 0 || d > 0x10ffff || (c = (int)d) != d)
40157                 goto range_error;
40158         }
40159         if (string_buffer_putc(b, c))
40160             goto fail;
40161     }
40162     return string_buffer_end(b);
40163 
40164  range_error:
40165     JS_ThrowRangeError(ctx, "invalid code point");
40166  fail:
40167     string_buffer_free(b);
40168     return JS_EXCEPTION;
40169 }
40170 
js_string_raw(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40171 static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val,
40172                              int argc, JSValueConst *argv)
40173 {
40174     // raw(temp,...a)
40175     JSValue cooked, val, raw;
40176     StringBuffer b_s, *b = &b_s;
40177     int64_t i, n;
40178 
40179     string_buffer_init(ctx, b, 0);
40180     raw = JS_UNDEFINED;
40181     cooked = JS_ToObject(ctx, argv[0]);
40182     if (JS_IsException(cooked))
40183         goto exception;
40184     raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw));
40185     if (JS_IsException(raw))
40186         goto exception;
40187     if (js_get_length64(ctx, &n, raw) < 0)
40188         goto exception;
40189 
40190     for (i = 0; i < n; i++) {
40191         val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i));
40192         if (JS_IsException(val))
40193             goto exception;
40194         string_buffer_concat_value_free(b, val);
40195         if (i < n - 1 && i + 1 < argc) {
40196             if (string_buffer_concat_value(b, argv[i + 1]))
40197                 goto exception;
40198         }
40199     }
40200     JS_FreeValue(ctx, cooked);
40201     JS_FreeValue(ctx, raw);
40202     return string_buffer_end(b);
40203 
40204 exception:
40205     JS_FreeValue(ctx, cooked);
40206     JS_FreeValue(ctx, raw);
40207     string_buffer_free(b);
40208     return JS_EXCEPTION;
40209 }
40210 
40211 /* only used in test262 */
js_string_codePointRange(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40212 JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
40213                                  int argc, JSValueConst *argv)
40214 {
40215     uint32_t start, end, i, n;
40216     StringBuffer b_s, *b = &b_s;
40217 
40218     if (JS_ToUint32(ctx, &start, argv[0]) ||
40219         JS_ToUint32(ctx, &end, argv[1]))
40220         return JS_EXCEPTION;
40221     end = min_uint32(end, 0x10ffff + 1);
40222 
40223     if (start > end) {
40224         start = end;
40225     }
40226     n = end - start;
40227     if (end > 0x10000) {
40228         n += end - max_uint32(start, 0x10000);
40229     }
40230     if (string_buffer_init2(ctx, b, n, end >= 0x100))
40231         return JS_EXCEPTION;
40232     for(i = start; i < end; i++) {
40233         string_buffer_putc(b, i);
40234     }
40235     return string_buffer_end(b);
40236 }
40237 
40238 #if 0
40239 static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val,
40240                                    int argc, JSValueConst *argv)
40241 {
40242     int c;
40243     if (JS_ToInt32(ctx, &c, argv[0]))
40244         return JS_EXCEPTION;
40245     return JS_NewBool(ctx, lre_is_space(c));
40246 }
40247 #endif
40248 
js_string_charCodeAt(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40249 static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val,
40250                                      int argc, JSValueConst *argv)
40251 {
40252     JSValue val, ret;
40253     JSString *p;
40254     int idx, c;
40255 
40256     val = JS_ToStringCheckObject(ctx, this_val);
40257     if (JS_IsException(val))
40258         return val;
40259     p = JS_VALUE_GET_STRING(val);
40260     if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
40261         JS_FreeValue(ctx, val);
40262         return JS_EXCEPTION;
40263     }
40264     if (idx < 0 || idx >= p->len) {
40265         ret = JS_NAN;
40266     } else {
40267         if (p->is_wide_char)
40268             c = p->u.str16[idx];
40269         else
40270             c = p->u.str8[idx];
40271         ret = JS_NewInt32(ctx, c);
40272     }
40273     JS_FreeValue(ctx, val);
40274     return ret;
40275 }
40276 
js_string_charAt(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40277 static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val,
40278                                 int argc, JSValueConst *argv)
40279 {
40280     JSValue val, ret;
40281     JSString *p;
40282     int idx, c;
40283 
40284     val = JS_ToStringCheckObject(ctx, this_val);
40285     if (JS_IsException(val))
40286         return val;
40287     p = JS_VALUE_GET_STRING(val);
40288     if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
40289         JS_FreeValue(ctx, val);
40290         return JS_EXCEPTION;
40291     }
40292     if (idx < 0 || idx >= p->len) {
40293         ret = js_new_string8(ctx, NULL, 0);
40294     } else {
40295         if (p->is_wide_char)
40296             c = p->u.str16[idx];
40297         else
40298             c = p->u.str8[idx];
40299         ret = js_new_string_char(ctx, c);
40300     }
40301     JS_FreeValue(ctx, val);
40302     return ret;
40303 }
40304 
js_string_codePointAt(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40305 static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val,
40306                                      int argc, JSValueConst *argv)
40307 {
40308     JSValue val, ret;
40309     JSString *p;
40310     int idx, c;
40311 
40312     val = JS_ToStringCheckObject(ctx, this_val);
40313     if (JS_IsException(val))
40314         return val;
40315     p = JS_VALUE_GET_STRING(val);
40316     if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
40317         JS_FreeValue(ctx, val);
40318         return JS_EXCEPTION;
40319     }
40320     if (idx < 0 || idx >= p->len) {
40321         ret = JS_UNDEFINED;
40322     } else {
40323         c = string_getc(p, &idx);
40324         ret = JS_NewInt32(ctx, c);
40325     }
40326     JS_FreeValue(ctx, val);
40327     return ret;
40328 }
40329 
js_string_concat(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40330 static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val,
40331                                 int argc, JSValueConst *argv)
40332 {
40333     JSValue r;
40334     int i;
40335 
40336     /* XXX: Use more efficient method */
40337     /* XXX: This method is OK if r has a single refcount */
40338     /* XXX: should use string_buffer? */
40339     r = JS_ToStringCheckObject(ctx, this_val);
40340     for (i = 0; i < argc; i++) {
40341         if (JS_IsException(r))
40342             break;
40343         r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i]));
40344     }
40345     return r;
40346 }
40347 
string_cmp(JSString * p1,JSString * p2,int x1,int x2,int len)40348 static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len)
40349 {
40350     int i, c1, c2;
40351     for (i = 0; i < len; i++) {
40352         if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i)))
40353             return c1 - c2;
40354     }
40355     return 0;
40356 }
40357 
string_indexof_char(JSString * p,int c,int from)40358 static int string_indexof_char(JSString *p, int c, int from)
40359 {
40360     /* assuming 0 <= from <= p->len */
40361     int i, len = p->len;
40362     if (p->is_wide_char) {
40363         for (i = from; i < len; i++) {
40364             if (p->u.str16[i] == c)
40365                 return i;
40366         }
40367     } else {
40368         if ((c & ~0xff) == 0) {
40369             for (i = from; i < len; i++) {
40370                 if (p->u.str8[i] == (uint8_t)c)
40371                     return i;
40372             }
40373         }
40374     }
40375     return -1;
40376 }
40377 
string_indexof(JSString * p1,JSString * p2,int from)40378 static int string_indexof(JSString *p1, JSString *p2, int from)
40379 {
40380     /* assuming 0 <= from <= p1->len */
40381     int c, i, j, len1 = p1->len, len2 = p2->len;
40382     if (len2 == 0)
40383         return from;
40384     for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) {
40385         j = string_indexof_char(p1, c, i);
40386         if (j < 0 || j + len2 > len1)
40387             break;
40388         if (!string_cmp(p1, p2, j + 1, 1, len2 - 1))
40389             return j;
40390     }
40391     return -1;
40392 }
40393 
string_advance_index(JSString * p,int64_t index,BOOL unicode)40394 static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode)
40395 {
40396     if (!unicode || index >= p->len || !p->is_wide_char) {
40397         index++;
40398     } else {
40399         int index32 = (int)index;
40400         string_getc(p, &index32);
40401         index = index32;
40402     }
40403     return index;
40404 }
40405 
js_string_indexOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int lastIndexOf)40406 static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val,
40407                                  int argc, JSValueConst *argv, int lastIndexOf)
40408 {
40409     JSValue str, v;
40410     int i, len, v_len, pos, start, stop, ret, inc;
40411     JSString *p;
40412     JSString *p1;
40413 
40414     str = JS_ToStringCheckObject(ctx, this_val);
40415     if (JS_IsException(str))
40416         return str;
40417     v = JS_ToString(ctx, argv[0]);
40418     if (JS_IsException(v))
40419         goto fail;
40420     p = JS_VALUE_GET_STRING(str);
40421     p1 = JS_VALUE_GET_STRING(v);
40422     len = p->len;
40423     v_len = p1->len;
40424     if (lastIndexOf) {
40425         pos = len - v_len;
40426         if (argc > 1) {
40427             double d;
40428             if (JS_ToFloat64(ctx, &d, argv[1]))
40429                 goto fail;
40430             if (!isnan(d)) {
40431                 if (d <= 0)
40432                     pos = 0;
40433                 else if (d < pos)
40434                     pos = d;
40435             }
40436         }
40437         start = pos;
40438         stop = 0;
40439         inc = -1;
40440     } else {
40441         pos = 0;
40442         if (argc > 1) {
40443             if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
40444                 goto fail;
40445         }
40446         start = pos;
40447         stop = len - v_len;
40448         inc = 1;
40449     }
40450     ret = -1;
40451     if (len >= v_len && inc * (stop - start) >= 0) {
40452         for (i = start;; i += inc) {
40453             if (!string_cmp(p, p1, i, 0, v_len)) {
40454                 ret = i;
40455                 break;
40456             }
40457             if (i == stop)
40458                 break;
40459         }
40460     }
40461     JS_FreeValue(ctx, str);
40462     JS_FreeValue(ctx, v);
40463     return JS_NewInt32(ctx, ret);
40464 
40465 fail:
40466     JS_FreeValue(ctx, str);
40467     JS_FreeValue(ctx, v);
40468     return JS_EXCEPTION;
40469 }
40470 
40471 /* return < 0 if exception or TRUE/FALSE */
40472 static int js_is_regexp(JSContext *ctx, JSValueConst obj);
40473 
js_string_includes(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)40474 static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val,
40475                                   int argc, JSValueConst *argv, int magic)
40476 {
40477     JSValue str, v = JS_UNDEFINED;
40478     int i, len, v_len, pos, start, stop, ret;
40479     JSString *p;
40480     JSString *p1;
40481 
40482     str = JS_ToStringCheckObject(ctx, this_val);
40483     if (JS_IsException(str))
40484         return str;
40485     ret = js_is_regexp(ctx, argv[0]);
40486     if (ret) {
40487         if (ret > 0)
40488             JS_ThrowTypeError(ctx, "regex not supported");
40489         goto fail;
40490     }
40491     v = JS_ToString(ctx, argv[0]);
40492     if (JS_IsException(v))
40493         goto fail;
40494     p = JS_VALUE_GET_STRING(str);
40495     p1 = JS_VALUE_GET_STRING(v);
40496     len = p->len;
40497     v_len = p1->len;
40498     pos = (magic == 2) ? len : 0;
40499     if (argc > 1 && !JS_IsUndefined(argv[1])) {
40500         if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
40501             goto fail;
40502     }
40503     len -= v_len;
40504     ret = 0;
40505     if (magic == 0) {
40506         start = pos;
40507         stop = len;
40508     } else {
40509         if (magic == 1) {
40510             if (pos > len)
40511                 goto done;
40512         } else {
40513             pos -= v_len;
40514         }
40515         start = stop = pos;
40516     }
40517     if (start >= 0 && start <= stop) {
40518         for (i = start;; i++) {
40519             if (!string_cmp(p, p1, i, 0, v_len)) {
40520                 ret = 1;
40521                 break;
40522             }
40523             if (i == stop)
40524                 break;
40525         }
40526     }
40527  done:
40528     JS_FreeValue(ctx, str);
40529     JS_FreeValue(ctx, v);
40530     return JS_NewBool(ctx, ret);
40531 
40532 fail:
40533     JS_FreeValue(ctx, str);
40534     JS_FreeValue(ctx, v);
40535     return JS_EXCEPTION;
40536 }
40537 
check_regexp_g_flag(JSContext * ctx,JSValueConst regexp)40538 static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp)
40539 {
40540     int ret;
40541     JSValue flags;
40542 
40543     ret = js_is_regexp(ctx, regexp);
40544     if (ret < 0)
40545         return -1;
40546     if (ret) {
40547         flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags);
40548         if (JS_IsException(flags))
40549             return -1;
40550         if (JS_IsUndefined(flags) || JS_IsNull(flags)) {
40551             JS_ThrowTypeError(ctx, "cannot convert to object");
40552             return -1;
40553         }
40554         flags = JS_ToStringFree(ctx, flags);
40555         if (JS_IsException(flags))
40556             return -1;
40557         ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0);
40558         JS_FreeValue(ctx, flags);
40559         if (ret < 0) {
40560             JS_ThrowTypeError(ctx, "regexp must have the 'g' flag");
40561             return -1;
40562         }
40563     }
40564     return 0;
40565 }
40566 
js_string_match(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int atom)40567 static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,
40568                                int argc, JSValueConst *argv, int atom)
40569 {
40570     // match(rx), search(rx), matchAll(rx)
40571     // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll
40572     JSValueConst O = this_val, regexp = argv[0], args[2];
40573     JSValue matcher, S, rx, result, str;
40574     int args_len;
40575 
40576     if (JS_IsUndefined(O) || JS_IsNull(O))
40577         return JS_ThrowTypeError(ctx, "cannot convert to object");
40578 
40579     if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) {
40580         matcher = JS_GetProperty(ctx, regexp, atom);
40581         if (JS_IsException(matcher))
40582             return JS_EXCEPTION;
40583         if (atom == JS_ATOM_Symbol_matchAll) {
40584             if (check_regexp_g_flag(ctx, regexp) < 0) {
40585                 JS_FreeValue(ctx, matcher);
40586                 return JS_EXCEPTION;
40587             }
40588         }
40589         if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) {
40590             return JS_CallFree(ctx, matcher, regexp, 1, &O);
40591         }
40592     }
40593     S = JS_ToString(ctx, O);
40594     if (JS_IsException(S))
40595         return JS_EXCEPTION;
40596     args_len = 1;
40597     args[0] = regexp;
40598     str = JS_UNDEFINED;
40599     if (atom == JS_ATOM_Symbol_matchAll) {
40600         str = JS_NewString(ctx, "g");
40601         if (JS_IsException(str))
40602             goto fail;
40603         args[args_len++] = (JSValueConst)str;
40604     }
40605     rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args);
40606     JS_FreeValue(ctx, str);
40607     if (JS_IsException(rx)) {
40608     fail:
40609         JS_FreeValue(ctx, S);
40610         return JS_EXCEPTION;
40611     }
40612     result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S);
40613     JS_FreeValue(ctx, S);
40614     return result;
40615 }
40616 
js_string___GetSubstitution(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40617 static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val,
40618                                            int argc, JSValueConst *argv)
40619 {
40620     // GetSubstitution(matched, str, position, captures, namedCaptures, rep)
40621     JSValueConst matched, str, captures, namedCaptures, rep;
40622     JSValue capture, name, s;
40623     uint32_t position, len, matched_len, captures_len;
40624     int i, j, j0, k, k1;
40625     int c, c1;
40626     StringBuffer b_s, *b = &b_s;
40627     JSString *sp, *rp;
40628 
40629     matched = argv[0];
40630     str = argv[1];
40631     captures = argv[3];
40632     namedCaptures = argv[4];
40633     rep = argv[5];
40634 
40635     if (!JS_IsString(rep) || !JS_IsString(str))
40636         return JS_ThrowTypeError(ctx, "not a string");
40637 
40638     sp = JS_VALUE_GET_STRING(str);
40639     rp = JS_VALUE_GET_STRING(rep);
40640 
40641     string_buffer_init(ctx, b, 0);
40642 
40643     captures_len = 0;
40644     if (!JS_IsUndefined(captures)) {
40645         if (js_get_length32(ctx, &captures_len, captures))
40646             goto exception;
40647     }
40648     if (js_get_length32(ctx, &matched_len, matched))
40649         goto exception;
40650     if (JS_ToUint32(ctx, &position, argv[2]) < 0)
40651         goto exception;
40652 
40653     len = rp->len;
40654     i = 0;
40655     for(;;) {
40656         j = string_indexof_char(rp, '$', i);
40657         if (j < 0 || j + 1 >= len)
40658             break;
40659         string_buffer_concat(b, rp, i, j);
40660         j0 = j++;
40661         c = string_get(rp, j++);
40662         if (c == '$') {
40663             string_buffer_putc8(b, '$');
40664         } else if (c == '&') {
40665             if (string_buffer_concat_value(b, matched))
40666                 goto exception;
40667         } else if (c == '`') {
40668             string_buffer_concat(b, sp, 0, position);
40669         } else if (c == '\'') {
40670             string_buffer_concat(b, sp, position + matched_len, sp->len);
40671         } else if (c >= '0' && c <= '9') {
40672             k = c - '0';
40673             if (j < len) {
40674                 c1 = string_get(rp, j);
40675                 if (c1 >= '0' && c1 <= '9') {
40676                     /* This behavior is specified in ES6 and refined in ECMA 2019 */
40677                     /* ECMA 2019 does not have the extra test, but
40678                        Test262 S15.5.4.11_A3_T1..3 require this behavior */
40679                     k1 = k * 10 + c1 - '0';
40680                     if (k1 >= 1 && k1 < captures_len) {
40681                         k = k1;
40682                         j++;
40683                     }
40684                 }
40685             }
40686             if (k >= 1 && k < captures_len) {
40687                 s = JS_GetPropertyInt64(ctx, captures, k);
40688                 if (JS_IsException(s))
40689                     goto exception;
40690                 if (!JS_IsUndefined(s)) {
40691                     if (string_buffer_concat_value_free(b, s))
40692                         goto exception;
40693                 }
40694             } else {
40695                 goto norep;
40696             }
40697         } else if (c == '<' && !JS_IsUndefined(namedCaptures)) {
40698             k = string_indexof_char(rp, '>', j);
40699             if (k < 0)
40700                 goto norep;
40701             name = js_sub_string(ctx, rp, j, k);
40702             if (JS_IsException(name))
40703                 goto exception;
40704             capture = JS_GetPropertyValue(ctx, namedCaptures, name);
40705             if (JS_IsException(capture))
40706                 goto exception;
40707             if (!JS_IsUndefined(capture)) {
40708                 if (string_buffer_concat_value_free(b, capture))
40709                     goto exception;
40710             }
40711             j = k + 1;
40712         } else {
40713         norep:
40714             string_buffer_concat(b, rp, j0, j);
40715         }
40716         i = j;
40717     }
40718     string_buffer_concat(b, rp, i, rp->len);
40719     return string_buffer_end(b);
40720 exception:
40721     string_buffer_free(b);
40722     return JS_EXCEPTION;
40723 }
40724 
js_string_replace(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int is_replaceAll)40725 static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val,
40726                                  int argc, JSValueConst *argv,
40727                                  int is_replaceAll)
40728 {
40729     // replace(rx, rep)
40730     JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1];
40731     JSValueConst args[6];
40732     JSValue str, search_str, replaceValue_str, repl_str;
40733     JSString *sp, *searchp;
40734     StringBuffer b_s, *b = &b_s;
40735     int pos, functionalReplace, endOfLastMatch;
40736     BOOL is_first;
40737 
40738     if (JS_IsUndefined(O) || JS_IsNull(O))
40739         return JS_ThrowTypeError(ctx, "cannot convert to object");
40740 
40741     search_str = JS_UNDEFINED;
40742     replaceValue_str = JS_UNDEFINED;
40743     repl_str = JS_UNDEFINED;
40744 
40745     if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) {
40746         JSValue replacer;
40747         if (is_replaceAll) {
40748             if (check_regexp_g_flag(ctx, searchValue) < 0)
40749                 return JS_EXCEPTION;
40750         }
40751         replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace);
40752         if (JS_IsException(replacer))
40753             return JS_EXCEPTION;
40754         if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) {
40755             args[0] = O;
40756             args[1] = replaceValue;
40757             return JS_CallFree(ctx, replacer, searchValue, 2, args);
40758         }
40759     }
40760     string_buffer_init(ctx, b, 0);
40761 
40762     str = JS_ToString(ctx, O);
40763     if (JS_IsException(str))
40764         goto exception;
40765     search_str = JS_ToString(ctx, searchValue);
40766     if (JS_IsException(search_str))
40767         goto exception;
40768     functionalReplace = JS_IsFunction(ctx, replaceValue);
40769     if (!functionalReplace) {
40770         replaceValue_str = JS_ToString(ctx, replaceValue);
40771         if (JS_IsException(replaceValue_str))
40772             goto exception;
40773     }
40774 
40775     sp = JS_VALUE_GET_STRING(str);
40776     searchp = JS_VALUE_GET_STRING(search_str);
40777     endOfLastMatch = 0;
40778     is_first = TRUE;
40779     for(;;) {
40780         if (unlikely(searchp->len == 0)) {
40781             if (is_first)
40782                 pos = 0;
40783             else if (endOfLastMatch >= sp->len)
40784                 pos = -1;
40785             else
40786                 pos = endOfLastMatch + 1;
40787         } else {
40788             pos = string_indexof(sp, searchp, endOfLastMatch);
40789         }
40790         if (pos < 0) {
40791             if (is_first) {
40792                 string_buffer_free(b);
40793                 JS_FreeValue(ctx, search_str);
40794                 JS_FreeValue(ctx, replaceValue_str);
40795                 return str;
40796             } else {
40797                 break;
40798             }
40799         }
40800         if (functionalReplace) {
40801             args[0] = search_str;
40802             args[1] = JS_NewInt32(ctx, pos);
40803             args[2] = str;
40804             repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args));
40805         } else {
40806             args[0] = search_str;
40807             args[1] = str;
40808             args[2] = JS_NewInt32(ctx, pos);
40809             args[3] = JS_UNDEFINED;
40810             args[4] = JS_UNDEFINED;
40811             args[5] = replaceValue_str;
40812             repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
40813         }
40814         if (JS_IsException(repl_str))
40815             goto exception;
40816 
40817         string_buffer_concat(b, sp, endOfLastMatch, pos);
40818         string_buffer_concat_value_free(b, repl_str);
40819         endOfLastMatch = pos + searchp->len;
40820         is_first = FALSE;
40821         if (!is_replaceAll)
40822             break;
40823     }
40824     string_buffer_concat(b, sp, endOfLastMatch, sp->len);
40825     JS_FreeValue(ctx, search_str);
40826     JS_FreeValue(ctx, replaceValue_str);
40827     JS_FreeValue(ctx, str);
40828     return string_buffer_end(b);
40829 
40830 exception:
40831     string_buffer_free(b);
40832     JS_FreeValue(ctx, search_str);
40833     JS_FreeValue(ctx, replaceValue_str);
40834     JS_FreeValue(ctx, str);
40835     return JS_EXCEPTION;
40836 }
40837 
js_string_split(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40838 static JSValue js_string_split(JSContext *ctx, JSValueConst this_val,
40839                                int argc, JSValueConst *argv)
40840 {
40841     // split(sep, limit)
40842     JSValueConst O = this_val, separator = argv[0], limit = argv[1];
40843     JSValueConst args[2];
40844     JSValue S, A, R, T;
40845     uint32_t lim, lengthA;
40846     int64_t p, q, s, r, e;
40847     JSString *sp, *rp;
40848 
40849     if (JS_IsUndefined(O) || JS_IsNull(O))
40850         return JS_ThrowTypeError(ctx, "cannot convert to object");
40851 
40852     S = JS_UNDEFINED;
40853     A = JS_UNDEFINED;
40854     R = JS_UNDEFINED;
40855 
40856     if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) {
40857         JSValue splitter;
40858         splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split);
40859         if (JS_IsException(splitter))
40860             return JS_EXCEPTION;
40861         if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) {
40862             args[0] = O;
40863             args[1] = limit;
40864             return JS_CallFree(ctx, splitter, separator, 2, args);
40865         }
40866     }
40867     S = JS_ToString(ctx, O);
40868     if (JS_IsException(S))
40869         goto exception;
40870     A = JS_NewArray(ctx);
40871     if (JS_IsException(A))
40872         goto exception;
40873     lengthA = 0;
40874     if (JS_IsUndefined(limit)) {
40875         lim = 0xffffffff;
40876     } else {
40877         if (JS_ToUint32(ctx, &lim, limit) < 0)
40878             goto exception;
40879     }
40880     sp = JS_VALUE_GET_STRING(S);
40881     s = sp->len;
40882     R = JS_ToString(ctx, separator);
40883     if (JS_IsException(R))
40884         goto exception;
40885     rp = JS_VALUE_GET_STRING(R);
40886     r = rp->len;
40887     p = 0;
40888     if (lim == 0)
40889         goto done;
40890     if (JS_IsUndefined(separator))
40891         goto add_tail;
40892     if (s == 0) {
40893         if (r != 0)
40894             goto add_tail;
40895         goto done;
40896     }
40897     q = p;
40898     for (q = p; (q += !r) <= s - r - !r; q = p = e + r) {
40899         e = string_indexof(sp, rp, q);
40900         if (e < 0)
40901             break;
40902         T = js_sub_string(ctx, sp, p, e);
40903         if (JS_IsException(T))
40904             goto exception;
40905         if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0)
40906             goto exception;
40907         if (lengthA == lim)
40908             goto done;
40909     }
40910 add_tail:
40911     T = js_sub_string(ctx, sp, p, s);
40912     if (JS_IsException(T))
40913         goto exception;
40914     if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0)
40915         goto exception;
40916 done:
40917     JS_FreeValue(ctx, S);
40918     JS_FreeValue(ctx, R);
40919     return A;
40920 
40921 exception:
40922     JS_FreeValue(ctx, A);
40923     JS_FreeValue(ctx, S);
40924     JS_FreeValue(ctx, R);
40925     return JS_EXCEPTION;
40926 }
40927 
js_string_substring(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40928 static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val,
40929                                    int argc, JSValueConst *argv)
40930 {
40931     JSValue str, ret;
40932     int a, b, start, end;
40933     JSString *p;
40934 
40935     str = JS_ToStringCheckObject(ctx, this_val);
40936     if (JS_IsException(str))
40937         return str;
40938     p = JS_VALUE_GET_STRING(str);
40939     if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) {
40940         JS_FreeValue(ctx, str);
40941         return JS_EXCEPTION;
40942     }
40943     b = p->len;
40944     if (!JS_IsUndefined(argv[1])) {
40945         if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) {
40946             JS_FreeValue(ctx, str);
40947             return JS_EXCEPTION;
40948         }
40949     }
40950     if (a < b) {
40951         start = a;
40952         end = b;
40953     } else {
40954         start = b;
40955         end = a;
40956     }
40957     ret = js_sub_string(ctx, p, start, end);
40958     JS_FreeValue(ctx, str);
40959     return ret;
40960 }
40961 
js_string_substr(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40962 static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val,
40963                                 int argc, JSValueConst *argv)
40964 {
40965     JSValue str, ret;
40966     int a, len, n;
40967     JSString *p;
40968 
40969     str = JS_ToStringCheckObject(ctx, this_val);
40970     if (JS_IsException(str))
40971         return str;
40972     p = JS_VALUE_GET_STRING(str);
40973     len = p->len;
40974     if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) {
40975         JS_FreeValue(ctx, str);
40976         return JS_EXCEPTION;
40977     }
40978     n = len - a;
40979     if (!JS_IsUndefined(argv[1])) {
40980         if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) {
40981             JS_FreeValue(ctx, str);
40982             return JS_EXCEPTION;
40983         }
40984     }
40985     ret = js_sub_string(ctx, p, a, a + n);
40986     JS_FreeValue(ctx, str);
40987     return ret;
40988 }
40989 
js_string_slice(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)40990 static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val,
40991                                int argc, JSValueConst *argv)
40992 {
40993     JSValue str, ret;
40994     int len, start, end;
40995     JSString *p;
40996 
40997     str = JS_ToStringCheckObject(ctx, this_val);
40998     if (JS_IsException(str))
40999         return str;
41000     p = JS_VALUE_GET_STRING(str);
41001     len = p->len;
41002     if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) {
41003         JS_FreeValue(ctx, str);
41004         return JS_EXCEPTION;
41005     }
41006     end = len;
41007     if (!JS_IsUndefined(argv[1])) {
41008         if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) {
41009             JS_FreeValue(ctx, str);
41010             return JS_EXCEPTION;
41011         }
41012     }
41013     ret = js_sub_string(ctx, p, start, max_int(end, start));
41014     JS_FreeValue(ctx, str);
41015     return ret;
41016 }
41017 
js_string_pad(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int padEnd)41018 static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val,
41019                              int argc, JSValueConst *argv, int padEnd)
41020 {
41021     JSValue str, v = JS_UNDEFINED;
41022     StringBuffer b_s, *b = &b_s;
41023     JSString *p, *p1 = NULL;
41024     int n, len, c = ' ';
41025 
41026     str = JS_ToStringCheckObject(ctx, this_val);
41027     if (JS_IsException(str))
41028         goto fail1;
41029     if (JS_ToInt32Sat(ctx, &n, argv[0]))
41030         goto fail2;
41031     p = JS_VALUE_GET_STRING(str);
41032     len = p->len;
41033     if (len >= n)
41034         return str;
41035     if (argc > 1 && !JS_IsUndefined(argv[1])) {
41036         v = JS_ToString(ctx, argv[1]);
41037         if (JS_IsException(v))
41038             goto fail2;
41039         p1 = JS_VALUE_GET_STRING(v);
41040         if (p1->len == 0) {
41041             JS_FreeValue(ctx, v);
41042             return str;
41043         }
41044         if (p1->len == 1) {
41045             c = string_get(p1, 0);
41046             p1 = NULL;
41047         }
41048     }
41049     if (n > JS_STRING_LEN_MAX) {
41050         JS_ThrowInternalError(ctx, "string too long");
41051         goto fail2;
41052     }
41053     if (string_buffer_init(ctx, b, n))
41054         goto fail3;
41055     n -= len;
41056     if (padEnd) {
41057         if (string_buffer_concat(b, p, 0, len))
41058             goto fail;
41059     }
41060     if (p1) {
41061         while (n > 0) {
41062             int chunk = min_int(n, p1->len);
41063             if (string_buffer_concat(b, p1, 0, chunk))
41064                 goto fail;
41065             n -= chunk;
41066         }
41067     } else {
41068         if (string_buffer_fill(b, c, n))
41069             goto fail;
41070     }
41071     if (!padEnd) {
41072         if (string_buffer_concat(b, p, 0, len))
41073             goto fail;
41074     }
41075     JS_FreeValue(ctx, v);
41076     JS_FreeValue(ctx, str);
41077     return string_buffer_end(b);
41078 
41079 fail:
41080     string_buffer_free(b);
41081 fail3:
41082     JS_FreeValue(ctx, v);
41083 fail2:
41084     JS_FreeValue(ctx, str);
41085 fail1:
41086     return JS_EXCEPTION;
41087 }
41088 
js_string_repeat(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41089 static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val,
41090                                 int argc, JSValueConst *argv)
41091 {
41092     JSValue str;
41093     StringBuffer b_s, *b = &b_s;
41094     JSString *p;
41095     int64_t val;
41096     int n, len;
41097 
41098     str = JS_ToStringCheckObject(ctx, this_val);
41099     if (JS_IsException(str))
41100         goto fail;
41101     if (JS_ToInt64Sat(ctx, &val, argv[0]))
41102         goto fail;
41103     if (val < 0 || val > 2147483647) {
41104         JS_ThrowRangeError(ctx, "invalid repeat count");
41105         goto fail;
41106     }
41107     n = val;
41108     p = JS_VALUE_GET_STRING(str);
41109     len = p->len;
41110     if (len == 0 || n == 1)
41111         return str;
41112     if (val * len > JS_STRING_LEN_MAX) {
41113         JS_ThrowInternalError(ctx, "string too long");
41114         goto fail;
41115     }
41116     if (string_buffer_init2(ctx, b, n * len, p->is_wide_char))
41117         goto fail;
41118     if (len == 1) {
41119         string_buffer_fill(b, string_get(p, 0), n);
41120     } else {
41121         while (n-- > 0) {
41122             string_buffer_concat(b, p, 0, len);
41123         }
41124     }
41125     JS_FreeValue(ctx, str);
41126     return string_buffer_end(b);
41127 
41128 fail:
41129     JS_FreeValue(ctx, str);
41130     return JS_EXCEPTION;
41131 }
41132 
js_string_trim(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)41133 static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val,
41134                               int argc, JSValueConst *argv, int magic)
41135 {
41136     JSValue str, ret;
41137     int a, b, len;
41138     JSString *p;
41139 
41140     str = JS_ToStringCheckObject(ctx, this_val);
41141     if (JS_IsException(str))
41142         return str;
41143     p = JS_VALUE_GET_STRING(str);
41144     a = 0;
41145     b = len = p->len;
41146     if (magic & 1) {
41147         while (a < len && lre_is_space(string_get(p, a)))
41148             a++;
41149     }
41150     if (magic & 2) {
41151         while (b > a && lre_is_space(string_get(p, b - 1)))
41152             b--;
41153     }
41154     ret = js_sub_string(ctx, p, a, b);
41155     JS_FreeValue(ctx, str);
41156     return ret;
41157 }
41158 
js_string___quote(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41159 static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val,
41160                                  int argc, JSValueConst *argv)
41161 {
41162     return JS_ToQuotedString(ctx, this_val);
41163 }
41164 
41165 /* return 0 if before the first char */
string_prevc(JSString * p,int * pidx)41166 static int string_prevc(JSString *p, int *pidx)
41167 {
41168     int idx, c, c1;
41169 
41170     idx = *pidx;
41171     if (idx <= 0)
41172         return 0;
41173     idx--;
41174     if (p->is_wide_char) {
41175         c = p->u.str16[idx];
41176         if (c >= 0xdc00 && c < 0xe000 && idx > 0) {
41177             c1 = p->u.str16[idx - 1];
41178             if (c1 >= 0xd800 && c1 <= 0xdc00) {
41179                 c = (((c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000;
41180                 idx--;
41181             }
41182         }
41183     } else {
41184         c = p->u.str8[idx];
41185     }
41186     *pidx = idx;
41187     return c;
41188 }
41189 
test_final_sigma(JSString * p,int sigma_pos)41190 static BOOL test_final_sigma(JSString *p, int sigma_pos)
41191 {
41192     int k, c1;
41193 
41194     /* before C: skip case ignorable chars and check there is
41195        a cased letter */
41196     k = sigma_pos;
41197     for(;;) {
41198         c1 = string_prevc(p, &k);
41199         if (!lre_is_case_ignorable(c1))
41200             break;
41201     }
41202     if (!lre_is_cased(c1))
41203         return FALSE;
41204 
41205     /* after C: skip case ignorable chars and check there is
41206        no cased letter */
41207     k = sigma_pos + 1;
41208     for(;;) {
41209         if (k >= p->len)
41210             return TRUE;
41211         c1 = string_getc(p, &k);
41212         if (!lre_is_case_ignorable(c1))
41213             break;
41214     }
41215     return !lre_is_cased(c1);
41216 }
41217 
js_string_localeCompare(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41218 static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
41219                                        int argc, JSValueConst *argv)
41220 {
41221     JSValue a, b;
41222     int cmp;
41223 
41224     a = JS_ToStringCheckObject(ctx, this_val);
41225     if (JS_IsException(a))
41226         return JS_EXCEPTION;
41227     b = JS_ToString(ctx, argv[0]);
41228     if (JS_IsException(b)) {
41229         JS_FreeValue(ctx, a);
41230         return JS_EXCEPTION;
41231     }
41232     cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b));
41233     JS_FreeValue(ctx, a);
41234     JS_FreeValue(ctx, b);
41235     return JS_NewInt32(ctx, cmp);
41236 }
41237 
js_string_toLowerCase(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int to_lower)41238 static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val,
41239                                      int argc, JSValueConst *argv, int to_lower)
41240 {
41241     JSValue val;
41242     StringBuffer b_s, *b = &b_s;
41243     JSString *p;
41244     int i, c, j, l;
41245     uint32_t res[LRE_CC_RES_LEN_MAX];
41246 
41247     val = JS_ToStringCheckObject(ctx, this_val);
41248     if (JS_IsException(val))
41249         return val;
41250     p = JS_VALUE_GET_STRING(val);
41251     if (p->len == 0)
41252         return val;
41253     if (string_buffer_init(ctx, b, p->len))
41254         goto fail;
41255     for(i = 0; i < p->len;) {
41256         c = string_getc(p, &i);
41257         if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) {
41258             res[0] = 0x3c2; /* final sigma */
41259             l = 1;
41260         } else {
41261             l = lre_case_conv(res, c, to_lower);
41262         }
41263         for(j = 0; j < l; j++) {
41264             if (string_buffer_putc(b, res[j]))
41265                 goto fail;
41266         }
41267     }
41268     JS_FreeValue(ctx, val);
41269     return string_buffer_end(b);
41270  fail:
41271     JS_FreeValue(ctx, val);
41272     string_buffer_free(b);
41273     return JS_EXCEPTION;
41274 }
41275 
41276 #ifdef CONFIG_ALL_UNICODE
41277 
41278 /* return (-1, NULL) if exception, otherwise (len, buf) */
JS_ToUTF32String(JSContext * ctx,uint32_t ** pbuf,JSValueConst val1)41279 static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1)
41280 {
41281     JSValue val;
41282     JSString *p;
41283     uint32_t *buf;
41284     int i, j, len;
41285 
41286     val = JS_ToString(ctx, val1);
41287     if (JS_IsException(val))
41288         return -1;
41289     p = JS_VALUE_GET_STRING(val);
41290     len = p->len;
41291     /* UTF32 buffer length is len minus the number of correct surrogates pairs */
41292     buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1));
41293     if (!buf) {
41294         JS_FreeValue(ctx, val);
41295         goto fail;
41296     }
41297     for(i = j = 0; i < len;)
41298         buf[j++] = string_getc(p, &i);
41299     JS_FreeValue(ctx, val);
41300     *pbuf = buf;
41301     return j;
41302  fail:
41303     *pbuf = NULL;
41304     return -1;
41305 }
41306 
JS_NewUTF32String(JSContext * ctx,const uint32_t * buf,int len)41307 static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len)
41308 {
41309     int i;
41310     StringBuffer b_s, *b = &b_s;
41311     if (string_buffer_init(ctx, b, len))
41312         return JS_EXCEPTION;
41313     for(i = 0; i < len; i++) {
41314         if (string_buffer_putc(b, buf[i]))
41315             goto fail;
41316     }
41317     return string_buffer_end(b);
41318  fail:
41319     string_buffer_free(b);
41320     return JS_EXCEPTION;
41321 }
41322 
js_string_normalize(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41323 static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val,
41324                                    int argc, JSValueConst *argv)
41325 {
41326     const char *form, *p;
41327     size_t form_len;
41328     int is_compat, buf_len, out_len;
41329     UnicodeNormalizationEnum n_type;
41330     JSValue val;
41331     uint32_t *buf, *out_buf;
41332 
41333     val = JS_ToStringCheckObject(ctx, this_val);
41334     if (JS_IsException(val))
41335         return val;
41336     buf_len = JS_ToUTF32String(ctx, &buf, val);
41337     JS_FreeValue(ctx, val);
41338     if (buf_len < 0)
41339         return JS_EXCEPTION;
41340 
41341     if (argc == 0 || JS_IsUndefined(argv[0])) {
41342         n_type = UNICODE_NFC;
41343     } else {
41344         form = JS_ToCStringLen(ctx, &form_len, argv[0]);
41345         if (!form)
41346             goto fail1;
41347         p = form;
41348         if (p[0] != 'N' || p[1] != 'F')
41349             goto bad_form;
41350         p += 2;
41351         is_compat = FALSE;
41352         if (*p == 'K') {
41353             is_compat = TRUE;
41354             p++;
41355         }
41356         if (*p == 'C' || *p == 'D') {
41357             n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C');
41358             if ((p + 1 - form) != form_len)
41359                 goto bad_form;
41360         } else {
41361         bad_form:
41362             JS_FreeCString(ctx, form);
41363             JS_ThrowRangeError(ctx, "bad normalization form");
41364         fail1:
41365             js_free(ctx, buf);
41366             return JS_EXCEPTION;
41367         }
41368         JS_FreeCString(ctx, form);
41369     }
41370 
41371     out_len = unicode_normalize(&out_buf, buf, buf_len, n_type,
41372                                 ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
41373     js_free(ctx, buf);
41374     if (out_len < 0)
41375         return JS_EXCEPTION;
41376     val = JS_NewUTF32String(ctx, out_buf, out_len);
41377     js_free(ctx, out_buf);
41378     return val;
41379 }
41380 #endif /* CONFIG_ALL_UNICODE */
41381 
41382 /* also used for String.prototype.valueOf */
js_string_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41383 static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val,
41384                                   int argc, JSValueConst *argv)
41385 {
41386     return js_thisStringValue(ctx, this_val);
41387 }
41388 
41389 #if 0
41390 static JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val,
41391                                                int argc, JSValueConst *argv)
41392 {
41393     return JS_ToStringCheckObject(ctx, argv[0]);
41394 }
41395 
41396 static JSValue js_string___toString(JSContext *ctx, JSValueConst this_val,
41397                                     int argc, JSValueConst *argv)
41398 {
41399     return JS_ToString(ctx, argv[0]);
41400 }
41401 
41402 static JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst
41403                                               this_val,
41404                                               int argc, JSValueConst *argv)
41405 {
41406     JSValue str;
41407     int idx;
41408     BOOL is_unicode;
41409     JSString *p;
41410 
41411     str = JS_ToString(ctx, argv[0]);
41412     if (JS_IsException(str))
41413         return str;
41414     if (JS_ToInt32Sat(ctx, &idx, argv[1])) {
41415         JS_FreeValue(ctx, str);
41416         return JS_EXCEPTION;
41417     }
41418     is_unicode = JS_ToBool(ctx, argv[2]);
41419     p = JS_VALUE_GET_STRING(str);
41420     if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) {
41421         idx++;
41422     } else {
41423         string_getc(p, &idx);
41424     }
41425     JS_FreeValue(ctx, str);
41426     return JS_NewInt32(ctx, idx);
41427 }
41428 #endif
41429 
41430 /* String Iterator */
41431 
js_string_iterator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,BOOL * pdone,int magic)41432 static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val,
41433                                        int argc, JSValueConst *argv,
41434                                        BOOL *pdone, int magic)
41435 {
41436     JSArrayIteratorData *it;
41437     uint32_t idx, c, start;
41438     JSString *p;
41439 
41440     it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR);
41441     if (!it) {
41442         *pdone = FALSE;
41443         return JS_EXCEPTION;
41444     }
41445     if (JS_IsUndefined(it->obj))
41446         goto done;
41447     p = JS_VALUE_GET_STRING(it->obj);
41448     idx = it->idx;
41449     if (idx >= p->len) {
41450         JS_FreeValue(ctx, it->obj);
41451         it->obj = JS_UNDEFINED;
41452     done:
41453         *pdone = TRUE;
41454         return JS_UNDEFINED;
41455     }
41456 
41457     start = idx;
41458     c = string_getc(p, (int *)&idx);
41459     it->idx = idx;
41460     *pdone = FALSE;
41461     if (c <= 0xffff) {
41462         return js_new_string_char(ctx, c);
41463     } else {
41464         return js_new_string16(ctx, p->u.str16 + start, 2);
41465     }
41466 }
41467 
41468 /* ES6 Annex B 2.3.2 etc. */
41469 enum {
41470     magic_string_anchor,
41471     magic_string_big,
41472     magic_string_blink,
41473     magic_string_bold,
41474     magic_string_fixed,
41475     magic_string_fontcolor,
41476     magic_string_fontsize,
41477     magic_string_italics,
41478     magic_string_link,
41479     magic_string_small,
41480     magic_string_strike,
41481     magic_string_sub,
41482     magic_string_sup,
41483 };
41484 
js_string_CreateHTML(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)41485 static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val,
41486                                     int argc, JSValueConst *argv, int magic)
41487 {
41488     JSValue str;
41489     const JSString *p;
41490     StringBuffer b_s, *b = &b_s;
41491     static struct { const char *tag, *attr; } const defs[] = {
41492         { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL },
41493         { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL },
41494         { "a", "href" }, { "small", NULL }, { "strike", NULL },
41495         { "sub", NULL }, { "sup", NULL },
41496     };
41497 
41498     str = JS_ToStringCheckObject(ctx, this_val);
41499     if (JS_IsException(str))
41500         return JS_EXCEPTION;
41501     string_buffer_init(ctx, b, 7);
41502     string_buffer_putc8(b, '<');
41503     string_buffer_puts8(b, defs[magic].tag);
41504     if (defs[magic].attr) {
41505         // r += " " + attr + "=\"" + value + "\"";
41506         JSValue value;
41507         int i;
41508 
41509         string_buffer_putc8(b, ' ');
41510         string_buffer_puts8(b, defs[magic].attr);
41511         string_buffer_puts8(b, "=\"");
41512         value = JS_ToStringCheckObject(ctx, argv[0]);
41513         if (JS_IsException(value)) {
41514             JS_FreeValue(ctx, str);
41515             string_buffer_free(b);
41516             return JS_EXCEPTION;
41517         }
41518         p = JS_VALUE_GET_STRING(value);
41519         for (i = 0; i < p->len; i++) {
41520             int c = string_get(p, i);
41521             if (c == '"') {
41522                 string_buffer_puts8(b, "&quot;");
41523             } else {
41524                 string_buffer_putc16(b, c);
41525             }
41526         }
41527         JS_FreeValue(ctx, value);
41528         string_buffer_putc8(b, '\"');
41529     }
41530     // return r + ">" + str + "</" + tag + ">";
41531     string_buffer_putc8(b, '>');
41532     string_buffer_concat_value_free(b, str);
41533     string_buffer_puts8(b, "</");
41534     string_buffer_puts8(b, defs[magic].tag);
41535     string_buffer_putc8(b, '>');
41536     return string_buffer_end(b);
41537 }
41538 
41539 static const JSCFunctionListEntry js_string_funcs[] = {
41540     JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ),
41541     JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ),
41542     JS_CFUNC_DEF("raw", 1, js_string_raw ),
41543     //JS_CFUNC_DEF("__toString", 1, js_string___toString ),
41544     //JS_CFUNC_DEF("__isSpace", 1, js_string___isSpace ),
41545     //JS_CFUNC_DEF("__toStringCheckObject", 1, js_string___toStringCheckObject ),
41546     //JS_CFUNC_DEF("__advanceStringIndex", 3, js_string___advanceStringIndex ),
41547     //JS_CFUNC_DEF("__GetSubstitution", 6, js_string___GetSubstitution ),
41548 };
41549 
41550 static const JSCFunctionListEntry js_string_proto_funcs[] = {
41551     JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ),
41552     JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ),
41553     JS_CFUNC_DEF("charAt", 1, js_string_charAt ),
41554     JS_CFUNC_DEF("concat", 1, js_string_concat ),
41555     JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ),
41556     JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),
41557     JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),
41558     JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ),
41559     JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ),
41560     JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ),
41561     JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ),
41562     JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ),
41563     JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ),
41564     JS_CFUNC_DEF("split", 2, js_string_split ),
41565     JS_CFUNC_DEF("substring", 2, js_string_substring ),
41566     JS_CFUNC_DEF("substr", 2, js_string_substr ),
41567     JS_CFUNC_DEF("slice", 2, js_string_slice ),
41568     JS_CFUNC_DEF("repeat", 1, js_string_repeat ),
41569     JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),
41570     JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),
41571     JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ),
41572     JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ),
41573     JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ),
41574     JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ),
41575     JS_ALIAS_DEF("trimRight", "trimEnd" ),
41576     JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ),
41577     JS_ALIAS_DEF("trimLeft", "trimStart" ),
41578     JS_CFUNC_DEF("toString", 0, js_string_toString ),
41579     JS_CFUNC_DEF("valueOf", 0, js_string_toString ),
41580     JS_CFUNC_DEF("__quote", 1, js_string___quote ),
41581     JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ),
41582     JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),
41583     JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),
41584     JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ),
41585     JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ),
41586     JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),
41587     /* ES6 Annex B 2.3.2 etc. */
41588     JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ),
41589     JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ),
41590     JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ),
41591     JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ),
41592     JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ),
41593     JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ),
41594     JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ),
41595     JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ),
41596     JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ),
41597     JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ),
41598     JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ),
41599     JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ),
41600     JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ),
41601 };
41602 
41603 static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = {
41604     JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ),
41605     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ),
41606 };
41607 
41608 #ifdef CONFIG_ALL_UNICODE
41609 static const JSCFunctionListEntry js_string_proto_normalize[] = {
41610     JS_CFUNC_DEF("normalize", 0, js_string_normalize ),
41611 };
41612 #endif
41613 
JS_AddIntrinsicStringNormalize(JSContext * ctx)41614 void JS_AddIntrinsicStringNormalize(JSContext *ctx)
41615 {
41616 #ifdef CONFIG_ALL_UNICODE
41617     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize,
41618                                countof(js_string_proto_normalize));
41619 #endif
41620 }
41621 
41622 /* Math */
41623 
41624 /* precondition: a and b are not NaN */
js_fmin(double a,double b)41625 static double js_fmin(double a, double b)
41626 {
41627     if (a == 0 && b == 0) {
41628         JSFloat64Union a1, b1;
41629         a1.d = a;
41630         b1.d = b;
41631         a1.u64 |= b1.u64;
41632         return a1.d;
41633     } else {
41634         return fmin(a, b);
41635     }
41636 }
41637 
41638 /* precondition: a and b are not NaN */
js_fmax(double a,double b)41639 static double js_fmax(double a, double b)
41640 {
41641     if (a == 0 && b == 0) {
41642         JSFloat64Union a1, b1;
41643         a1.d = a;
41644         b1.d = b;
41645         a1.u64 &= b1.u64;
41646         return a1.d;
41647     } else {
41648         return fmax(a, b);
41649     }
41650 }
41651 
js_math_min_max(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)41652 static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
41653                                int argc, JSValueConst *argv, int magic)
41654 {
41655     BOOL is_max = magic;
41656     double r, a;
41657     int i;
41658     uint32_t tag;
41659 
41660     if (unlikely(argc == 0)) {
41661         return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);
41662     }
41663 
41664     tag = JS_VALUE_GET_TAG(argv[0]);
41665     if (tag == JS_TAG_INT) {
41666         int a1, r1 = JS_VALUE_GET_INT(argv[0]);
41667         for(i = 1; i < argc; i++) {
41668             tag = JS_VALUE_GET_TAG(argv[i]);
41669             if (tag != JS_TAG_INT) {
41670                 r = r1;
41671                 goto generic_case;
41672             }
41673             a1 = JS_VALUE_GET_INT(argv[i]);
41674             if (is_max)
41675                 r1 = max_int(r1, a1);
41676             else
41677                 r1 = min_int(r1, a1);
41678 
41679         }
41680         return JS_NewInt32(ctx, r1);
41681     } else {
41682         if (JS_ToFloat64(ctx, &r, argv[0]))
41683             return JS_EXCEPTION;
41684         i = 1;
41685     generic_case:
41686         while (i < argc) {
41687             if (JS_ToFloat64(ctx, &a, argv[i]))
41688                 return JS_EXCEPTION;
41689             if (!isnan(r)) {
41690                 if (isnan(a)) {
41691                     r = a;
41692                 } else {
41693                     if (is_max)
41694                         r = js_fmax(r, a);
41695                     else
41696                         r = js_fmin(r, a);
41697                 }
41698             }
41699             i++;
41700         }
41701         return JS_NewFloat64(ctx, r);
41702     }
41703 }
41704 
js_math_sign(double a)41705 static double js_math_sign(double a)
41706 {
41707     if (isnan(a) || a == 0.0)
41708         return a;
41709     if (a < 0)
41710         return -1;
41711     else
41712         return 1;
41713 }
41714 
js_math_round(double a)41715 static double js_math_round(double a)
41716 {
41717     JSFloat64Union u;
41718     uint64_t frac_mask, one;
41719     unsigned int e, s;
41720 
41721     u.d = a;
41722     e = (u.u64 >> 52) & 0x7ff;
41723     if (e < 1023) {
41724         /* abs(a) < 1 */
41725         if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) {
41726             /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */
41727             u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52);
41728         } else {
41729             /* return +/-0.0 */
41730             u.u64 &= (uint64_t)1 << 63;
41731         }
41732     } else if (e < (1023 + 52)) {
41733         s = u.u64 >> 63;
41734         one = (uint64_t)1 << (52 - (e - 1023));
41735         frac_mask = one - 1;
41736         u.u64 += (one >> 1) - s;
41737         u.u64 &= ~frac_mask; /* truncate to an integer */
41738     }
41739     /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */
41740     return u.d;
41741 }
41742 
js_math_hypot(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41743 static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,
41744                              int argc, JSValueConst *argv)
41745 {
41746     double r, a;
41747     int i;
41748 
41749     r = 0;
41750     if (argc > 0) {
41751         if (JS_ToFloat64(ctx, &r, argv[0]))
41752             return JS_EXCEPTION;
41753         if (argc == 1) {
41754             r = fabs(r);
41755         } else {
41756             /* use the built-in function to minimize precision loss */
41757             for (i = 1; i < argc; i++) {
41758                 if (JS_ToFloat64(ctx, &a, argv[i]))
41759                     return JS_EXCEPTION;
41760                 r = hypot(r, a);
41761             }
41762         }
41763     }
41764     return JS_NewFloat64(ctx, r);
41765 }
41766 
js_math_fround(double a)41767 static double js_math_fround(double a)
41768 {
41769     return (float)a;
41770 }
41771 
js_math_imul(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41772 static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val,
41773                             int argc, JSValueConst *argv)
41774 {
41775     int a, b;
41776 
41777     if (JS_ToInt32(ctx, &a, argv[0]))
41778         return JS_EXCEPTION;
41779     if (JS_ToInt32(ctx, &b, argv[1]))
41780         return JS_EXCEPTION;
41781     /* purposely ignoring overflow */
41782     return JS_NewInt32(ctx, a * b);
41783 }
41784 
js_math_clz32(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41785 static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,
41786                              int argc, JSValueConst *argv)
41787 {
41788     uint32_t a, r;
41789 
41790     if (JS_ToUint32(ctx, &a, argv[0]))
41791         return JS_EXCEPTION;
41792     if (a == 0)
41793         r = 32;
41794     else
41795         r = clz32(a);
41796     return JS_NewInt32(ctx, r);
41797 }
41798 
41799 /* xorshift* random number generator by Marsaglia */
xorshift64star(uint64_t * pstate)41800 static uint64_t xorshift64star(uint64_t *pstate)
41801 {
41802     uint64_t x;
41803     x = *pstate;
41804     x ^= x >> 12;
41805     x ^= x << 25;
41806     x ^= x >> 27;
41807     *pstate = x;
41808     return x * 0x2545F4914F6CDD1D;
41809 }
41810 
js_random_init(JSContext * ctx)41811 static void js_random_init(JSContext *ctx)
41812 {
41813     struct timeval tv;
41814     gettimeofday(&tv, NULL);
41815     ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
41816     /* the state must be non zero */
41817     if (ctx->random_state == 0)
41818         ctx->random_state = 1;
41819 }
41820 
js_math_random(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41821 static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,
41822                               int argc, JSValueConst *argv)
41823 {
41824     JSFloat64Union u;
41825     uint64_t v;
41826 
41827     v = xorshift64star(&ctx->random_state);
41828     /* 1.0 <= u.d < 2 */
41829     u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12);
41830     return __JS_NewFloat64(ctx, u.d - 1.0);
41831 }
41832 
41833 static const JSCFunctionListEntry js_math_funcs[] = {
41834     JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),
41835     JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),
41836     JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),
41837     JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ),
41838     JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ),
41839     JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),
41840     JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ),
41841 
41842     JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ),
41843     JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ),
41844     JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ),
41845     JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ),
41846     JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ),
41847     JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ),
41848     JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ),
41849     JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ),
41850     JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ),
41851     JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ),
41852     /* ES6 */
41853     JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ),
41854     JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ),
41855     JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ),
41856     JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ),
41857     JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ),
41858     JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ),
41859     JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ),
41860     JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ),
41861     JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ),
41862     JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ),
41863     JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ),
41864     JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ),
41865     JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),
41866     JS_CFUNC_DEF("hypot", 2, js_math_hypot ),
41867     JS_CFUNC_DEF("random", 0, js_math_random ),
41868     JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),
41869     JS_CFUNC_DEF("imul", 2, js_math_imul ),
41870     JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),
41871     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ),
41872     JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ),
41873     JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ),
41874     JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ),
41875     JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ),
41876     JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ),
41877     JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ),
41878     JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ),
41879     JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ),
41880 };
41881 
41882 static const JSCFunctionListEntry js_math_obj[] = {
41883     JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
41884 };
41885 
41886 /* Date */
41887 
41888 #if 0
41889 /* OS dependent: return the UTC time in ms since 1970. */
41890 static JSValue js___date_now(JSContext *ctx, JSValueConst this_val,
41891                              int argc, JSValueConst *argv)
41892 {
41893     int64_t d;
41894     struct timeval tv;
41895     gettimeofday(&tv, NULL);
41896     d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
41897     return JS_NewInt64(ctx, d);
41898 }
41899 #endif
41900 
41901 /* OS dependent: return the UTC time in microseconds since 1970. */
js___date_clock(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)41902 static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val,
41903                                int argc, JSValueConst *argv)
41904 {
41905     int64_t d;
41906     struct timeval tv;
41907     gettimeofday(&tv, NULL);
41908     d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
41909     return JS_NewInt64(ctx, d);
41910 }
41911 
41912 /* OS dependent. d = argv[0] is in ms from 1970. Return the difference
41913    between local time and UTC time 'd' in minutes */
getTimezoneOffset(int64_t time)41914 static int getTimezoneOffset(int64_t time) {
41915 #if defined(_WIN32)
41916     /* XXX: TODO */
41917     return 0;
41918 #else
41919     time_t ti;
41920     struct tm tm;
41921 
41922     time /= 1000; /* convert to seconds */
41923     if (sizeof(time_t) == 4) {
41924         /* on 32-bit systems, we need to clamp the time value to the
41925            range of `time_t`. This is better than truncating values to
41926            32 bits and hopefully provides the same result as 64-bit
41927            implementation of localtime_r.
41928          */
41929         if ((time_t)-1 < 0) {
41930             if (time < INT32_MIN) {
41931                 time = INT32_MIN;
41932             } else if (time > INT32_MAX) {
41933                 time = INT32_MAX;
41934             }
41935         } else {
41936             if (time < 0) {
41937                 time = 0;
41938             } else if (time > UINT32_MAX) {
41939                 time = UINT32_MAX;
41940             }
41941         }
41942     }
41943     ti = time;
41944     localtime_r(&ti, &tm);
41945     return -tm.tm_gmtoff / 60;
41946 #endif
41947 }
41948 
41949 #if 0
41950 static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
41951                                            int argc, JSValueConst *argv)
41952 {
41953     double dd;
41954 
41955     if (JS_ToFloat64(ctx, &dd, argv[0]))
41956         return JS_EXCEPTION;
41957     if (isnan(dd))
41958         return __JS_NewFloat64(ctx, dd);
41959     else
41960         return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd));
41961 }
41962 
41963 static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor,
41964                                           JSValueConst def_proto)
41965 {
41966     JSValue proto;
41967     proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
41968     if (JS_IsException(proto))
41969         return proto;
41970     if (!JS_IsObject(proto)) {
41971         JS_FreeValue(ctx, proto);
41972         proto = JS_DupValue(ctx, def_proto);
41973     }
41974     return proto;
41975 }
41976 
41977 /* create a new date object */
41978 static JSValue js___date_create(JSContext *ctx, JSValueConst this_val,
41979                                 int argc, JSValueConst *argv)
41980 {
41981     JSValue obj, proto;
41982     proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]);
41983     if (JS_IsException(proto))
41984         return proto;
41985     obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE);
41986     JS_FreeValue(ctx, proto);
41987     if (!JS_IsException(obj))
41988         JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2]));
41989     return obj;
41990 }
41991 #endif
41992 
41993 /* RegExp */
41994 
js_regexp_finalizer(JSRuntime * rt,JSValue val)41995 static void js_regexp_finalizer(JSRuntime *rt, JSValue val)
41996 {
41997     JSObject *p = JS_VALUE_GET_OBJ(val);
41998     JSRegExp *re = &p->u.regexp;
41999     JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode));
42000     JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern));
42001 }
42002 
42003 /* create a string containing the RegExp bytecode */
js_compile_regexp(JSContext * ctx,JSValueConst pattern,JSValueConst flags)42004 static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
42005                                  JSValueConst flags)
42006 {
42007     const char *str;
42008     int re_flags, mask;
42009     uint8_t *re_bytecode_buf;
42010     size_t i, len;
42011     int re_bytecode_len;
42012     JSValue ret;
42013     char error_msg[64];
42014 
42015     re_flags = 0;
42016     if (!JS_IsUndefined(flags)) {
42017         str = JS_ToCStringLen(ctx, &len, flags);
42018         if (!str)
42019             return JS_EXCEPTION;
42020         /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */
42021         for (i = 0; i < len; i++) {
42022             switch(str[i]) {
42023             case 'g':
42024                 mask = LRE_FLAG_GLOBAL;
42025                 break;
42026             case 'i':
42027                 mask = LRE_FLAG_IGNORECASE;
42028                 break;
42029             case 'm':
42030                 mask = LRE_FLAG_MULTILINE;
42031                 break;
42032             case 's':
42033                 mask = LRE_FLAG_DOTALL;
42034                 break;
42035             case 'u':
42036                 mask = LRE_FLAG_UTF16;
42037                 break;
42038             case 'y':
42039                 mask = LRE_FLAG_STICKY;
42040                 break;
42041             default:
42042                 goto bad_flags;
42043             }
42044             if ((re_flags & mask) != 0) {
42045             bad_flags:
42046                 JS_FreeCString(ctx, str);
42047                 return JS_ThrowSyntaxError(ctx, "invalid regular expression flags");
42048             }
42049             re_flags |= mask;
42050         }
42051         JS_FreeCString(ctx, str);
42052     }
42053 
42054     str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UTF16));
42055     if (!str)
42056         return JS_EXCEPTION;
42057     re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg,
42058                                   sizeof(error_msg), str, len, re_flags, ctx);
42059     JS_FreeCString(ctx, str);
42060     if (!re_bytecode_buf) {
42061         JS_ThrowSyntaxError(ctx, "%s", error_msg);
42062         return JS_EXCEPTION;
42063     }
42064 
42065     ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len);
42066     js_free(ctx, re_bytecode_buf);
42067     return ret;
42068 }
42069 
42070 /* create a RegExp object from a string containing the RegExp bytecode
42071    and the source pattern */
js_regexp_constructor_internal(JSContext * ctx,JSValueConst ctor,JSValue pattern,JSValue bc)42072 static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
42073                                               JSValue pattern, JSValue bc)
42074 {
42075     JSValue obj;
42076     JSObject *p;
42077     JSRegExp *re;
42078 
42079     /* sanity check */
42080     if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING ||
42081         JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) {
42082         JS_ThrowTypeError(ctx, "string expected");
42083     fail:
42084         JS_FreeValue(ctx, bc);
42085         JS_FreeValue(ctx, pattern);
42086         return JS_EXCEPTION;
42087     }
42088 
42089     obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP);
42090     if (JS_IsException(obj))
42091         goto fail;
42092     p = JS_VALUE_GET_OBJ(obj);
42093     re = &p->u.regexp;
42094     re->pattern = JS_VALUE_GET_STRING(pattern);
42095     re->bytecode = JS_VALUE_GET_STRING(bc);
42096     JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0),
42097                            JS_PROP_WRITABLE);
42098     return obj;
42099 }
42100 
js_get_regexp(JSContext * ctx,JSValueConst obj,BOOL throw_error)42101 static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error)
42102 {
42103     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
42104         JSObject *p = JS_VALUE_GET_OBJ(obj);
42105         if (p->class_id == JS_CLASS_REGEXP)
42106             return &p->u.regexp;
42107     }
42108     if (throw_error) {
42109         JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
42110     }
42111     return NULL;
42112 }
42113 
42114 /* return < 0 if exception or TRUE/FALSE */
js_is_regexp(JSContext * ctx,JSValueConst obj)42115 static int js_is_regexp(JSContext *ctx, JSValueConst obj)
42116 {
42117     JSValue m;
42118 
42119     if (!JS_IsObject(obj))
42120         return FALSE;
42121     m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match);
42122     if (JS_IsException(m))
42123         return -1;
42124     if (!JS_IsUndefined(m))
42125         return JS_ToBoolFree(ctx, m);
42126     return js_get_regexp(ctx, obj, FALSE) != NULL;
42127 }
42128 
js_regexp_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)42129 static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target,
42130                                      int argc, JSValueConst *argv)
42131 {
42132     JSValue pattern, flags, bc, val;
42133     JSValueConst pat, flags1;
42134     JSRegExp *re;
42135     int pat_is_regexp;
42136 
42137     pat = argv[0];
42138     flags1 = argv[1];
42139     pat_is_regexp = js_is_regexp(ctx, pat);
42140     if (pat_is_regexp < 0)
42141         return JS_EXCEPTION;
42142     if (JS_IsUndefined(new_target)) {
42143         /* called as a function */
42144         new_target = JS_GetActiveFunction(ctx);
42145         if (pat_is_regexp && JS_IsUndefined(flags1)) {
42146             JSValue ctor;
42147             BOOL res;
42148             ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor);
42149             if (JS_IsException(ctor))
42150                 return ctor;
42151             res = js_same_value(ctx, ctor, new_target);
42152             JS_FreeValue(ctx, ctor);
42153             if (res)
42154                 return JS_DupValue(ctx, pat);
42155         }
42156     }
42157     re = js_get_regexp(ctx, pat, FALSE);
42158     if (re) {
42159         pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
42160         if (JS_IsUndefined(flags1)) {
42161             bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
42162             goto no_compilation;
42163         } else {
42164             flags = JS_ToString(ctx, flags1);
42165             if (JS_IsException(flags))
42166                 goto fail;
42167         }
42168     } else {
42169         flags = JS_UNDEFINED;
42170         if (pat_is_regexp) {
42171             pattern = JS_GetProperty(ctx, pat, JS_ATOM_source);
42172             if (JS_IsException(pattern))
42173                 goto fail;
42174             if (JS_IsUndefined(flags1)) {
42175                 flags = JS_GetProperty(ctx, pat, JS_ATOM_flags);
42176                 if (JS_IsException(flags))
42177                     goto fail;
42178             } else {
42179                 flags = JS_DupValue(ctx, flags1);
42180             }
42181         } else {
42182             pattern = JS_DupValue(ctx, pat);
42183             flags = JS_DupValue(ctx, flags1);
42184         }
42185         if (JS_IsUndefined(pattern)) {
42186             pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
42187         } else {
42188             val = pattern;
42189             pattern = JS_ToString(ctx, val);
42190             JS_FreeValue(ctx, val);
42191             if (JS_IsException(pattern))
42192                 goto fail;
42193         }
42194     }
42195     bc = js_compile_regexp(ctx, pattern, flags);
42196     if (JS_IsException(bc))
42197         goto fail;
42198     JS_FreeValue(ctx, flags);
42199  no_compilation:
42200     return js_regexp_constructor_internal(ctx, new_target, pattern, bc);
42201  fail:
42202     JS_FreeValue(ctx, pattern);
42203     JS_FreeValue(ctx, flags);
42204     return JS_EXCEPTION;
42205 }
42206 
js_regexp_compile(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)42207 static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val,
42208                                  int argc, JSValueConst *argv)
42209 {
42210     JSRegExp *re1, *re;
42211     JSValueConst pattern1, flags1;
42212     JSValue bc, pattern;
42213 
42214     re = js_get_regexp(ctx, this_val, TRUE);
42215     if (!re)
42216         return JS_EXCEPTION;
42217     pattern1 = argv[0];
42218     flags1 = argv[1];
42219     re1 = js_get_regexp(ctx, pattern1, FALSE);
42220     if (re1) {
42221         if (!JS_IsUndefined(flags1))
42222             return JS_ThrowTypeError(ctx, "flags must be undefined");
42223         pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern));
42224         bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode));
42225     } else {
42226         bc = JS_UNDEFINED;
42227         if (JS_IsUndefined(pattern1))
42228             pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
42229         else
42230             pattern = JS_ToString(ctx, pattern1);
42231         if (JS_IsException(pattern))
42232             goto fail;
42233         bc = js_compile_regexp(ctx, pattern, flags1);
42234         if (JS_IsException(bc))
42235             goto fail;
42236     }
42237     JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
42238     JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
42239     re->pattern = JS_VALUE_GET_STRING(pattern);
42240     re->bytecode = JS_VALUE_GET_STRING(bc);
42241     if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
42242                        JS_NewInt32(ctx, 0)) < 0)
42243         return JS_EXCEPTION;
42244     return JS_DupValue(ctx, this_val);
42245  fail:
42246     JS_FreeValue(ctx, pattern);
42247     JS_FreeValue(ctx, bc);
42248     return JS_EXCEPTION;
42249 }
42250 
42251 #if 0
42252 static JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val)
42253 {
42254     JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
42255     if (!re)
42256         return JS_EXCEPTION;
42257     return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
42258 }
42259 
42260 static JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val)
42261 {
42262     JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
42263     int flags;
42264 
42265     if (!re)
42266         return JS_EXCEPTION;
42267     flags = lre_get_flags(re->bytecode->u.str8);
42268     return JS_NewInt32(ctx, flags);
42269 }
42270 #endif
42271 
js_regexp_get_source(JSContext * ctx,JSValueConst this_val)42272 static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val)
42273 {
42274     JSRegExp *re;
42275     JSString *p;
42276     StringBuffer b_s, *b = &b_s;
42277     int i, n, c, c2, bra;
42278 
42279     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
42280         return JS_ThrowTypeErrorNotAnObject(ctx);
42281 
42282     if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
42283         goto empty_regex;
42284 
42285     re = js_get_regexp(ctx, this_val, TRUE);
42286     if (!re)
42287         return JS_EXCEPTION;
42288 
42289     p = re->pattern;
42290 
42291     if (p->len == 0) {
42292     empty_regex:
42293         return JS_NewString(ctx, "(?:)");
42294     }
42295     string_buffer_init2(ctx, b, p->len, p->is_wide_char);
42296 
42297     /* Escape '/' and newline sequences as needed */
42298     bra = 0;
42299     for (i = 0, n = p->len; i < n;) {
42300         c2 = -1;
42301         switch (c = string_get(p, i++)) {
42302         case '\\':
42303             if (i < n)
42304                 c2 = string_get(p, i++);
42305             break;
42306         case ']':
42307             bra = 0;
42308             break;
42309         case '[':
42310             if (!bra) {
42311                 if (i < n && string_get(p, i) == ']')
42312                     c2 = string_get(p, i++);
42313                 bra = 1;
42314             }
42315             break;
42316         case '\n':
42317             c = '\\';
42318             c2 = 'n';
42319             break;
42320         case '\r':
42321             c = '\\';
42322             c2 = 'r';
42323             break;
42324         case '/':
42325             if (!bra) {
42326                 c = '\\';
42327                 c2 = '/';
42328             }
42329             break;
42330         }
42331         string_buffer_putc16(b, c);
42332         if (c2 >= 0)
42333             string_buffer_putc16(b, c2);
42334     }
42335     return string_buffer_end(b);
42336 }
42337 
js_regexp_get_flag(JSContext * ctx,JSValueConst this_val,int mask)42338 static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask)
42339 {
42340     JSRegExp *re;
42341     int flags;
42342 
42343     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
42344         return JS_ThrowTypeErrorNotAnObject(ctx);
42345 
42346     re = js_get_regexp(ctx, this_val, FALSE);
42347     if (!re) {
42348         if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
42349             return JS_UNDEFINED;
42350         else
42351             return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
42352     }
42353 
42354     flags = lre_get_flags(re->bytecode->u.str8);
42355     return JS_NewBool(ctx, (flags & mask) != 0);
42356 }
42357 
js_regexp_get_flags(JSContext * ctx,JSValueConst this_val)42358 static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val)
42359 {
42360     char str[8], *p = str;
42361     int res;
42362 
42363     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
42364         return JS_ThrowTypeErrorNotAnObject(ctx);
42365 
42366     res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global));
42367     if (res < 0)
42368         goto exception;
42369     if (res)
42370         *p++ = 'g';
42371     res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase"));
42372     if (res < 0)
42373         goto exception;
42374     if (res)
42375         *p++ = 'i';
42376     res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline"));
42377     if (res < 0)
42378         goto exception;
42379     if (res)
42380         *p++ = 'm';
42381     res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll"));
42382     if (res < 0)
42383         goto exception;
42384     if (res)
42385         *p++ = 's';
42386     res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode));
42387     if (res < 0)
42388         goto exception;
42389     if (res)
42390         *p++ = 'u';
42391     res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky"));
42392     if (res < 0)
42393         goto exception;
42394     if (res)
42395         *p++ = 'y';
42396     return JS_NewStringLen(ctx, str, p - str);
42397 
42398 exception:
42399     return JS_EXCEPTION;
42400 }
42401 
js_regexp_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)42402 static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,
42403                                   int argc, JSValueConst *argv)
42404 {
42405     JSValue pattern, flags;
42406     StringBuffer b_s, *b = &b_s;
42407 
42408     if (!JS_IsObject(this_val))
42409         return JS_ThrowTypeErrorNotAnObject(ctx);
42410 
42411     string_buffer_init(ctx, b, 0);
42412     string_buffer_putc8(b, '/');
42413     pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source);
42414     if (string_buffer_concat_value_free(b, pattern))
42415         goto fail;
42416     string_buffer_putc8(b, '/');
42417     flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags);
42418     if (string_buffer_concat_value_free(b, flags))
42419         goto fail;
42420     return string_buffer_end(b);
42421 
42422 fail:
42423     string_buffer_free(b);
42424     return JS_EXCEPTION;
42425 }
42426 
lre_check_stack_overflow(void * opaque,size_t alloca_size)42427 BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)
42428 {
42429     JSContext *ctx = opaque;
42430     return js_check_stack_overflow(ctx->rt, alloca_size);
42431 }
42432 
lre_realloc(void * opaque,void * ptr,size_t size)42433 void *lre_realloc(void *opaque, void *ptr, size_t size)
42434 {
42435     JSContext *ctx = opaque;
42436     /* No JS exception is raised here */
42437     return js_realloc_rt(ctx->rt, ptr, size);
42438 }
42439 
js_regexp_exec(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)42440 static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
42441                               int argc, JSValueConst *argv)
42442 {
42443     JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
42444     JSString *str;
42445     JSValue str_val, obj, val, groups = JS_UNDEFINED;
42446     uint8_t *re_bytecode;
42447     int ret;
42448     uint8_t **capture, *str_buf;
42449     int capture_count, shift, i, re_flags;
42450     int64_t last_index;
42451     const char *group_name_ptr;
42452 
42453     if (!re)
42454         return JS_EXCEPTION;
42455     str_val = JS_ToString(ctx, argv[0]);
42456     if (JS_IsException(str_val))
42457         return str_val;
42458     val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
42459     if (JS_IsException(val) ||
42460         JS_ToLengthFree(ctx, &last_index, val)) {
42461         JS_FreeValue(ctx, str_val);
42462         return JS_EXCEPTION;
42463     }
42464     re_bytecode = re->bytecode->u.str8;
42465     re_flags = lre_get_flags(re_bytecode);
42466     if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
42467         last_index = 0;
42468     }
42469     str = JS_VALUE_GET_STRING(str_val);
42470     capture_count = lre_get_capture_count(re_bytecode);
42471     capture = NULL;
42472     if (capture_count > 0) {
42473         capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
42474         if (!capture) {
42475             JS_FreeValue(ctx, str_val);
42476             return JS_EXCEPTION;
42477         }
42478     }
42479     shift = str->is_wide_char;
42480     str_buf = str->u.str8;
42481     if (last_index > str->len) {
42482         ret = 2;
42483     } else {
42484         ret = lre_exec(capture, re_bytecode,
42485                        str_buf, last_index, str->len,
42486                        shift, ctx);
42487     }
42488     obj = JS_NULL;
42489     if (ret != 1) {
42490         if (ret >= 0) {
42491             if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
42492                 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
42493                                    JS_NewInt32(ctx, 0)) < 0)
42494                     goto fail;
42495             }
42496         } else {
42497             JS_ThrowInternalError(ctx, "out of memory in regexp execution");
42498             goto fail;
42499         }
42500         JS_FreeValue(ctx, str_val);
42501     } else {
42502         int prop_flags;
42503         if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {
42504             if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
42505                                JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0)
42506                 goto fail;
42507         }
42508         obj = JS_NewArray(ctx);
42509         if (JS_IsException(obj))
42510             goto fail;
42511         prop_flags = JS_PROP_C_W_E | JS_PROP_THROW;
42512         group_name_ptr = lre_get_groupnames(re_bytecode);
42513         if (group_name_ptr) {
42514             groups = JS_NewObjectProto(ctx, JS_NULL);
42515             if (JS_IsException(groups))
42516                 goto fail;
42517         }
42518 
42519         for(i = 0; i < capture_count; i++) {
42520             int start, end;
42521             JSValue val;
42522             if (capture[2 * i] == NULL ||
42523                 capture[2 * i + 1] == NULL) {
42524                 val = JS_UNDEFINED;
42525             } else {
42526                 start = (capture[2 * i] - str_buf) >> shift;
42527                 end = (capture[2 * i + 1] - str_buf) >> shift;
42528                 val = js_sub_string(ctx, str, start, end);
42529                 if (JS_IsException(val))
42530                     goto fail;
42531             }
42532             if (group_name_ptr && i > 0) {
42533                 if (*group_name_ptr) {
42534                     if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr,
42535                                                   JS_DupValue(ctx, val),
42536                                                   prop_flags) < 0) {
42537                         JS_FreeValue(ctx, val);
42538                         goto fail;
42539                     }
42540                 }
42541                 group_name_ptr += strlen(group_name_ptr) + 1;
42542             }
42543             if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0)
42544                 goto fail;
42545         }
42546         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups,
42547                                    groups, prop_flags) < 0)
42548             goto fail;
42549         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index,
42550                                    JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0)
42551             goto fail;
42552         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0)
42553             goto fail1;
42554     }
42555     js_free(ctx, capture);
42556     return obj;
42557 fail:
42558     JS_FreeValue(ctx, groups);
42559     JS_FreeValue(ctx, str_val);
42560 fail1:
42561     JS_FreeValue(ctx, obj);
42562     js_free(ctx, capture);
42563     return JS_EXCEPTION;
42564 }
42565 
42566 /* delete portions of a string that match a given regex */
JS_RegExpDelete(JSContext * ctx,JSValueConst this_val,JSValueConst arg)42567 static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg)
42568 {
42569     JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
42570     JSString *str;
42571     JSValue str_val, val;
42572     uint8_t *re_bytecode;
42573     int ret;
42574     uint8_t **capture, *str_buf;
42575     int capture_count, shift, re_flags;
42576     int next_src_pos, start, end;
42577     int64_t last_index;
42578     StringBuffer b_s, *b = &b_s;
42579 
42580     if (!re)
42581         return JS_EXCEPTION;
42582 
42583     string_buffer_init(ctx, b, 0);
42584 
42585     capture = NULL;
42586     str_val = JS_ToString(ctx, arg);
42587     if (JS_IsException(str_val))
42588         goto fail;
42589     str = JS_VALUE_GET_STRING(str_val);
42590     re_bytecode = re->bytecode->u.str8;
42591     re_flags = lre_get_flags(re_bytecode);
42592     if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
42593         last_index = 0;
42594     } else {
42595         val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
42596         if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val))
42597             goto fail;
42598     }
42599     capture_count = lre_get_capture_count(re_bytecode);
42600     if (capture_count > 0) {
42601         capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
42602         if (!capture)
42603             goto fail;
42604     }
42605     shift = str->is_wide_char;
42606     str_buf = str->u.str8;
42607     next_src_pos = 0;
42608     for (;;) {
42609         if (last_index > str->len)
42610             break;
42611 
42612         ret = lre_exec(capture, re_bytecode,
42613                        str_buf, last_index, str->len, shift, ctx);
42614         if (ret != 1) {
42615             if (ret >= 0) {
42616                 if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
42617                     if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
42618                                        JS_NewInt32(ctx, 0)) < 0)
42619                         goto fail;
42620                 }
42621             } else {
42622                 JS_ThrowInternalError(ctx, "out of memory in regexp execution");
42623                 goto fail;
42624             }
42625             break;
42626         }
42627         start = (capture[0] - str_buf) >> shift;
42628         end = (capture[1] - str_buf) >> shift;
42629         last_index = end;
42630         if (next_src_pos < start) {
42631             if (string_buffer_concat(b, str, next_src_pos, start))
42632                 goto fail;
42633         }
42634         next_src_pos = end;
42635         if (!(re_flags & LRE_FLAG_GLOBAL)) {
42636             if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
42637                                JS_NewInt32(ctx, end)) < 0)
42638                 goto fail;
42639             break;
42640         }
42641         if (end == start) {
42642             if (!(re_flags & LRE_FLAG_UTF16) || (unsigned)end >= str->len || !str->is_wide_char) {
42643                 end++;
42644             } else {
42645                 string_getc(str, &end);
42646             }
42647         }
42648         last_index = end;
42649     }
42650     if (string_buffer_concat(b, str, next_src_pos, str->len))
42651         goto fail;
42652     JS_FreeValue(ctx, str_val);
42653     js_free(ctx, capture);
42654     return string_buffer_end(b);
42655 fail:
42656     JS_FreeValue(ctx, str_val);
42657     js_free(ctx, capture);
42658     string_buffer_free(b);
42659     return JS_EXCEPTION;
42660 }
42661 
JS_RegExpExec(JSContext * ctx,JSValueConst r,JSValueConst s)42662 static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s)
42663 {
42664     JSValue method, ret;
42665 
42666     method = JS_GetProperty(ctx, r, JS_ATOM_exec);
42667     if (JS_IsException(method))
42668         return method;
42669     if (JS_IsFunction(ctx, method)) {
42670         ret = JS_CallFree(ctx, method, r, 1, &s);
42671         if (JS_IsException(ret))
42672             return ret;
42673         if (!JS_IsObject(ret) && !JS_IsNull(ret)) {
42674             JS_FreeValue(ctx, ret);
42675             return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null");
42676         }
42677         return ret;
42678     }
42679     JS_FreeValue(ctx, method);
42680     return js_regexp_exec(ctx, r, 1, &s);
42681 }
42682 
42683 #if 0
42684 static JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val,
42685                                       int argc, JSValueConst *argv)
42686 {
42687     return JS_RegExpExec(ctx, argv[0], argv[1]);
42688 }
42689 static JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val,
42690                                         int argc, JSValueConst *argv)
42691 {
42692     return JS_RegExpDelete(ctx, argv[0], argv[1]);
42693 }
42694 #endif
42695 
js_regexp_test(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)42696 static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val,
42697                               int argc, JSValueConst *argv)
42698 {
42699     JSValue val;
42700     BOOL ret;
42701 
42702     val = JS_RegExpExec(ctx, this_val, argv[0]);
42703     if (JS_IsException(val))
42704         return JS_EXCEPTION;
42705     ret = !JS_IsNull(val);
42706     JS_FreeValue(ctx, val);
42707     return JS_NewBool(ctx, ret);
42708 }
42709 
js_regexp_Symbol_match(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)42710 static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val,
42711                                       int argc, JSValueConst *argv)
42712 {
42713     // [Symbol.match](str)
42714     JSValueConst rx = this_val;
42715     JSValue A, S, result, matchStr;
42716     int global, n, fullUnicode, isEmpty;
42717     JSString *p;
42718 
42719     if (!JS_IsObject(rx))
42720         return JS_ThrowTypeErrorNotAnObject(ctx);
42721 
42722     A = JS_UNDEFINED;
42723     result = JS_UNDEFINED;
42724     matchStr = JS_UNDEFINED;
42725     S = JS_ToString(ctx, argv[0]);
42726     if (JS_IsException(S))
42727         goto exception;
42728 
42729     global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));
42730     if (global < 0)
42731         goto exception;
42732 
42733     if (!global) {
42734         A = JS_RegExpExec(ctx, rx, S);
42735     } else {
42736         fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
42737         if (fullUnicode < 0)
42738             goto exception;
42739 
42740         if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
42741             goto exception;
42742         A = JS_NewArray(ctx);
42743         if (JS_IsException(A))
42744             goto exception;
42745         n = 0;
42746         for(;;) {
42747             JS_FreeValue(ctx, result);
42748             result = JS_RegExpExec(ctx, rx, S);
42749             if (JS_IsException(result))
42750                 goto exception;
42751             if (JS_IsNull(result))
42752                 break;
42753             matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
42754             if (JS_IsException(matchStr))
42755                 goto exception;
42756             isEmpty = JS_IsEmptyString(matchStr);
42757             if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0)
42758                 goto exception;
42759             if (isEmpty) {
42760                 int64_t thisIndex, nextIndex;
42761                 if (JS_ToLengthFree(ctx, &thisIndex,
42762                                     JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
42763                     goto exception;
42764                 p = JS_VALUE_GET_STRING(S);
42765                 nextIndex = string_advance_index(p, thisIndex, fullUnicode);
42766                 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
42767                     goto exception;
42768             }
42769         }
42770         if (n == 0) {
42771             JS_FreeValue(ctx, A);
42772             A = JS_NULL;
42773         }
42774     }
42775     JS_FreeValue(ctx, result);
42776     JS_FreeValue(ctx, S);
42777     return A;
42778 
42779 exception:
42780     JS_FreeValue(ctx, A);
42781     JS_FreeValue(ctx, result);
42782     JS_FreeValue(ctx, S);
42783     return JS_EXCEPTION;
42784 }
42785 
42786 typedef struct JSRegExpStringIteratorData {
42787     JSValue iterating_regexp;
42788     JSValue iterated_string;
42789     BOOL global;
42790     BOOL unicode;
42791     BOOL done;
42792 } JSRegExpStringIteratorData;
42793 
js_regexp_string_iterator_finalizer(JSRuntime * rt,JSValue val)42794 static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val)
42795 {
42796     JSObject *p = JS_VALUE_GET_OBJ(val);
42797     JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
42798     if (it) {
42799         JS_FreeValueRT(rt, it->iterating_regexp);
42800         JS_FreeValueRT(rt, it->iterated_string);
42801         js_free_rt(rt, it);
42802     }
42803 }
42804 
js_regexp_string_iterator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)42805 static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
42806                                            JS_MarkFunc *mark_func)
42807 {
42808     JSObject *p = JS_VALUE_GET_OBJ(val);
42809     JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
42810     if (it) {
42811         JS_MarkValue(rt, it->iterating_regexp, mark_func);
42812         JS_MarkValue(rt, it->iterated_string, mark_func);
42813     }
42814 }
42815 
js_regexp_string_iterator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,BOOL * pdone,int magic)42816 static JSValue js_regexp_string_iterator_next(JSContext *ctx,
42817                                               JSValueConst this_val,
42818                                               int argc, JSValueConst *argv,
42819                                               BOOL *pdone, int magic)
42820 {
42821     JSRegExpStringIteratorData *it;
42822     JSValueConst R, S;
42823     JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED;
42824     JSString *sp;
42825 
42826     it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR);
42827     if (!it)
42828         goto exception;
42829     if (it->done) {
42830         *pdone = TRUE;
42831         return JS_UNDEFINED;
42832     }
42833     R = it->iterating_regexp;
42834     S = it->iterated_string;
42835     match = JS_RegExpExec(ctx, R, S);
42836     if (JS_IsException(match))
42837         goto exception;
42838     if (JS_IsNull(match)) {
42839         it->done = TRUE;
42840         *pdone = TRUE;
42841         return JS_UNDEFINED;
42842     } else if (it->global) {
42843         matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0));
42844         if (JS_IsException(matchStr))
42845             goto exception;
42846         if (JS_IsEmptyString(matchStr)) {
42847             int64_t thisIndex, nextIndex;
42848             if (JS_ToLengthFree(ctx, &thisIndex,
42849                                 JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0)
42850                 goto exception;
42851             sp = JS_VALUE_GET_STRING(S);
42852             nextIndex = string_advance_index(sp, thisIndex, it->unicode);
42853             if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex,
42854                                JS_NewInt64(ctx, nextIndex)) < 0)
42855                 goto exception;
42856         }
42857         JS_FreeValue(ctx, matchStr);
42858     } else {
42859         it->done = TRUE;
42860     }
42861     *pdone = FALSE;
42862     return match;
42863  exception:
42864     JS_FreeValue(ctx, match);
42865     JS_FreeValue(ctx, matchStr);
42866     *pdone = FALSE;
42867     return JS_EXCEPTION;
42868 }
42869 
js_regexp_Symbol_matchAll(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)42870 static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val,
42871                                          int argc, JSValueConst *argv)
42872 {
42873     // [Symbol.matchAll](str)
42874     JSValueConst R = this_val;
42875     JSValue S, C, flags, matcher, iter;
42876     JSValueConst args[2];
42877     JSString *strp;
42878     int64_t lastIndex;
42879     JSRegExpStringIteratorData *it;
42880 
42881     if (!JS_IsObject(R))
42882         return JS_ThrowTypeErrorNotAnObject(ctx);
42883 
42884     C = JS_UNDEFINED;
42885     flags = JS_UNDEFINED;
42886     matcher = JS_UNDEFINED;
42887     iter = JS_UNDEFINED;
42888 
42889     S = JS_ToString(ctx, argv[0]);
42890     if (JS_IsException(S))
42891         goto exception;
42892     C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor);
42893     if (JS_IsException(C))
42894         goto exception;
42895     flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags));
42896     if (JS_IsException(flags))
42897         goto exception;
42898     args[0] = R;
42899     args[1] = flags;
42900     matcher = JS_CallConstructor(ctx, C, 2, args);
42901     if (JS_IsException(matcher))
42902         goto exception;
42903     if (JS_ToLengthFree(ctx, &lastIndex,
42904                         JS_GetProperty(ctx, R, JS_ATOM_lastIndex)))
42905         goto exception;
42906     if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex,
42907                        JS_NewInt64(ctx, lastIndex)) < 0)
42908         goto exception;
42909 
42910     iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR);
42911     if (JS_IsException(iter))
42912         goto exception;
42913     it = js_malloc(ctx, sizeof(*it));
42914     if (!it)
42915         goto exception;
42916     it->iterating_regexp = matcher;
42917     it->iterated_string = S;
42918     strp = JS_VALUE_GET_STRING(flags);
42919     it->global = string_indexof_char(strp, 'g', 0) >= 0;
42920     it->unicode = string_indexof_char(strp, 'u', 0) >= 0;
42921     it->done = FALSE;
42922     JS_SetOpaque(iter, it);
42923 
42924     JS_FreeValue(ctx, C);
42925     JS_FreeValue(ctx, flags);
42926     return iter;
42927  exception:
42928     JS_FreeValue(ctx, S);
42929     JS_FreeValue(ctx, C);
42930     JS_FreeValue(ctx, flags);
42931     JS_FreeValue(ctx, matcher);
42932     JS_FreeValue(ctx, iter);
42933     return JS_EXCEPTION;
42934 }
42935 
42936 typedef struct ValueBuffer {
42937     JSContext *ctx;
42938     JSValue *arr;
42939     JSValue def[4];
42940     int len;
42941     int size;
42942     int error_status;
42943 } ValueBuffer;
42944 
value_buffer_init(JSContext * ctx,ValueBuffer * b)42945 static int value_buffer_init(JSContext *ctx, ValueBuffer *b)
42946 {
42947     b->ctx = ctx;
42948     b->len = 0;
42949     b->size = 4;
42950     b->error_status = 0;
42951     b->arr = b->def;
42952     return 0;
42953 }
42954 
value_buffer_free(ValueBuffer * b)42955 static void value_buffer_free(ValueBuffer *b)
42956 {
42957     while (b->len > 0)
42958         JS_FreeValue(b->ctx, b->arr[--b->len]);
42959     if (b->arr != b->def)
42960         js_free(b->ctx, b->arr);
42961     b->arr = b->def;
42962     b->size = 4;
42963 }
42964 
value_buffer_append(ValueBuffer * b,JSValue val)42965 static int value_buffer_append(ValueBuffer *b, JSValue val)
42966 {
42967     if (b->error_status)
42968         return -1;
42969 
42970     if (b->len >= b->size) {
42971         int new_size = (b->len + (b->len >> 1) + 31) & ~16;
42972         size_t slack;
42973         JSValue *new_arr;
42974 
42975         if (b->arr == b->def) {
42976             new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack);
42977             if (new_arr)
42978                 memcpy(new_arr, b->def, sizeof b->def);
42979         } else {
42980             new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack);
42981         }
42982         if (!new_arr) {
42983             value_buffer_free(b);
42984             JS_FreeValue(b->ctx, val);
42985             b->error_status = -1;
42986             return -1;
42987         }
42988         new_size += slack / sizeof(*new_arr);
42989         b->arr = new_arr;
42990         b->size = new_size;
42991     }
42992     b->arr[b->len++] = val;
42993     return 0;
42994 }
42995 
js_is_standard_regexp(JSContext * ctx,JSValueConst rx)42996 static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx)
42997 {
42998     JSValue val;
42999     int res;
43000 
43001     val = JS_GetProperty(ctx, rx, JS_ATOM_constructor);
43002     if (JS_IsException(val))
43003         return -1;
43004     // rx.constructor === RegExp
43005     res = js_same_value(ctx, val, ctx->regexp_ctor);
43006     JS_FreeValue(ctx, val);
43007     if (res) {
43008         val = JS_GetProperty(ctx, rx, JS_ATOM_exec);
43009         if (JS_IsException(val))
43010             return -1;
43011         // rx.exec === RE_exec
43012         res = JS_IsCFunction(ctx, val, js_regexp_exec, 0);
43013         JS_FreeValue(ctx, val);
43014     }
43015     return res;
43016 }
43017 
js_regexp_Symbol_replace(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)43018 static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val,
43019                                         int argc, JSValueConst *argv)
43020 {
43021     // [Symbol.replace](str, rep)
43022     JSValueConst rx = this_val, rep = argv[1];
43023     JSValueConst args[6];
43024     JSValue str, rep_val, matched, tab, rep_str, namedCaptures, res;
43025     JSString *sp, *rp;
43026     StringBuffer b_s, *b = &b_s;
43027     ValueBuffer v_b, *results = &v_b;
43028     int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode;
43029     uint32_t nCaptures;
43030     int64_t position;
43031 
43032     if (!JS_IsObject(rx))
43033         return JS_ThrowTypeErrorNotAnObject(ctx);
43034 
43035     string_buffer_init(ctx, b, 0);
43036     value_buffer_init(ctx, results);
43037 
43038     rep_val = JS_UNDEFINED;
43039     matched = JS_UNDEFINED;
43040     tab = JS_UNDEFINED;
43041     rep_str = JS_UNDEFINED;
43042     namedCaptures = JS_UNDEFINED;
43043 
43044     str = JS_ToString(ctx, argv[0]);
43045     if (JS_IsException(str))
43046         goto exception;
43047 
43048     sp = JS_VALUE_GET_STRING(str);
43049     rp = NULL;
43050     functionalReplace = JS_IsFunction(ctx, rep);
43051     if (!functionalReplace) {
43052         rep_val = JS_ToString(ctx, rep);
43053         if (JS_IsException(rep_val))
43054             goto exception;
43055         rp = JS_VALUE_GET_STRING(rep_val);
43056     }
43057     fullUnicode = 0;
43058     is_global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));
43059     if (is_global < 0)
43060         goto exception;
43061     if (is_global) {
43062         fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
43063         if (fullUnicode < 0)
43064             goto exception;
43065         if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
43066             goto exception;
43067     }
43068 
43069     if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) {
43070         /* use faster version for simple cases */
43071         res = JS_RegExpDelete(ctx, rx, str);
43072         goto done;
43073     }
43074     for(;;) {
43075         JSValue result;
43076         result = JS_RegExpExec(ctx, rx, str);
43077         if (JS_IsException(result))
43078             goto exception;
43079         if (JS_IsNull(result))
43080             break;
43081         if (value_buffer_append(results, result) < 0)
43082             goto exception;
43083         if (!is_global)
43084             break;
43085         JS_FreeValue(ctx, matched);
43086         matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
43087         if (JS_IsException(matched))
43088             goto exception;
43089         if (JS_IsEmptyString(matched)) {
43090             /* always advance of at least one char */
43091             int64_t thisIndex, nextIndex;
43092             if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
43093                 goto exception;
43094             nextIndex = string_advance_index(sp, thisIndex, fullUnicode);
43095             if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
43096                 goto exception;
43097         }
43098     }
43099     nextSourcePosition = 0;
43100     for(j = 0; j < results->len; j++) {
43101         JSValueConst result;
43102         result = results->arr[j];
43103         if (js_get_length32(ctx, &nCaptures, result) < 0)
43104             goto exception;
43105         JS_FreeValue(ctx, matched);
43106         matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
43107         if (JS_IsException(matched))
43108             goto exception;
43109         if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index)))
43110             goto exception;
43111         if (position > sp->len)
43112             position = sp->len;
43113         else if (position < 0)
43114             position = 0;
43115         /* ignore substition if going backward (can happen
43116            with custom regexp object) */
43117         JS_FreeValue(ctx, tab);
43118         tab = JS_NewArray(ctx);
43119         if (JS_IsException(tab))
43120             goto exception;
43121         if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched),
43122                                         JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43123             goto exception;
43124         for(n = 1; n < nCaptures; n++) {
43125             JSValue capN;
43126             capN = JS_GetPropertyInt64(ctx, result, n);
43127             if (JS_IsException(capN))
43128                 goto exception;
43129             if (!JS_IsUndefined(capN)) {
43130                 capN = JS_ToStringFree(ctx, capN);
43131                 if (JS_IsException(capN))
43132                     goto exception;
43133             }
43134             if (JS_DefinePropertyValueInt64(ctx, tab, n, capN,
43135                                             JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43136                 goto exception;
43137         }
43138         JS_FreeValue(ctx, namedCaptures);
43139         namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups);
43140         if (JS_IsException(namedCaptures))
43141             goto exception;
43142         if (functionalReplace) {
43143             if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43144                 goto exception;
43145             if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43146                 goto exception;
43147             if (!JS_IsUndefined(namedCaptures)) {
43148                 if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43149                     goto exception;
43150             }
43151             args[0] = JS_UNDEFINED;
43152             args[1] = tab;
43153             JS_FreeValue(ctx, rep_str);
43154             rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0));
43155         } else {
43156             JSValue namedCaptures1;
43157             if (!JS_IsUndefined(namedCaptures)) {
43158                 namedCaptures1 = JS_ToObject(ctx, namedCaptures);
43159                 if (JS_IsException(namedCaptures1))
43160                     goto exception;
43161             } else {
43162                 namedCaptures1 = JS_UNDEFINED;
43163             }
43164             args[0] = matched;
43165             args[1] = str;
43166             args[2] = JS_NewInt32(ctx, position);
43167             args[3] = tab;
43168             args[4] = namedCaptures1;
43169             args[5] = rep_val;
43170             JS_FreeValue(ctx, rep_str);
43171             rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
43172             JS_FreeValue(ctx, namedCaptures1);
43173         }
43174         if (JS_IsException(rep_str))
43175             goto exception;
43176         if (position >= nextSourcePosition) {
43177             string_buffer_concat(b, sp, nextSourcePosition, position);
43178             string_buffer_concat_value(b, rep_str);
43179             nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len;
43180         }
43181     }
43182     string_buffer_concat(b, sp, nextSourcePosition, sp->len);
43183     res = string_buffer_end(b);
43184     goto done1;
43185 
43186 exception:
43187     res = JS_EXCEPTION;
43188 done:
43189     string_buffer_free(b);
43190 done1:
43191     value_buffer_free(results);
43192     JS_FreeValue(ctx, rep_val);
43193     JS_FreeValue(ctx, matched);
43194     JS_FreeValue(ctx, tab);
43195     JS_FreeValue(ctx, rep_str);
43196     JS_FreeValue(ctx, namedCaptures);
43197     JS_FreeValue(ctx, str);
43198     return res;
43199 }
43200 
js_regexp_Symbol_search(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)43201 static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val,
43202                                        int argc, JSValueConst *argv)
43203 {
43204     JSValueConst rx = this_val;
43205     JSValue str, previousLastIndex, currentLastIndex, result, index;
43206 
43207     if (!JS_IsObject(rx))
43208         return JS_ThrowTypeErrorNotAnObject(ctx);
43209 
43210     result = JS_UNDEFINED;
43211     currentLastIndex = JS_UNDEFINED;
43212     previousLastIndex = JS_UNDEFINED;
43213     str = JS_ToString(ctx, argv[0]);
43214     if (JS_IsException(str))
43215         goto exception;
43216 
43217     previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
43218     if (JS_IsException(previousLastIndex))
43219         goto exception;
43220 
43221     if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) {
43222         if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) {
43223             goto exception;
43224         }
43225     }
43226     result = JS_RegExpExec(ctx, rx, str);
43227     if (JS_IsException(result))
43228         goto exception;
43229     currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
43230     if (JS_IsException(currentLastIndex))
43231         goto exception;
43232     if (js_same_value(ctx, currentLastIndex, previousLastIndex)) {
43233         JS_FreeValue(ctx, previousLastIndex);
43234     } else {
43235         if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) {
43236             previousLastIndex = JS_UNDEFINED;
43237             goto exception;
43238         }
43239     }
43240     JS_FreeValue(ctx, str);
43241     JS_FreeValue(ctx, currentLastIndex);
43242 
43243     if (JS_IsNull(result)) {
43244         return JS_NewInt32(ctx, -1);
43245     } else {
43246         index = JS_GetProperty(ctx, result, JS_ATOM_index);
43247         JS_FreeValue(ctx, result);
43248         return index;
43249     }
43250 
43251 exception:
43252     JS_FreeValue(ctx, result);
43253     JS_FreeValue(ctx, str);
43254     JS_FreeValue(ctx, currentLastIndex);
43255     JS_FreeValue(ctx, previousLastIndex);
43256     return JS_EXCEPTION;
43257 }
43258 
js_regexp_Symbol_split(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)43259 static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val,
43260                                        int argc, JSValueConst *argv)
43261 {
43262     // [Symbol.split](str, limit)
43263     JSValueConst rx = this_val;
43264     JSValueConst args[2];
43265     JSValue str, ctor, splitter, A, flags, z, sub;
43266     JSString *strp;
43267     uint32_t lim, size, p, q;
43268     int unicodeMatching;
43269     int64_t lengthA, e, numberOfCaptures, i;
43270 
43271     if (!JS_IsObject(rx))
43272         return JS_ThrowTypeErrorNotAnObject(ctx);
43273 
43274     ctor = JS_UNDEFINED;
43275     splitter = JS_UNDEFINED;
43276     A = JS_UNDEFINED;
43277     flags = JS_UNDEFINED;
43278     z = JS_UNDEFINED;
43279     str = JS_ToString(ctx, argv[0]);
43280     if (JS_IsException(str))
43281         goto exception;
43282     ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor);
43283     if (JS_IsException(ctor))
43284         goto exception;
43285     flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags));
43286     if (JS_IsException(flags))
43287         goto exception;
43288     strp = JS_VALUE_GET_STRING(flags);
43289     unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0;
43290     if (string_indexof_char(strp, 'y', 0) < 0) {
43291         flags = JS_ConcatString3(ctx, "", flags, "y");
43292         if (JS_IsException(flags))
43293             goto exception;
43294     }
43295     args[0] = rx;
43296     args[1] = flags;
43297     splitter = JS_CallConstructor(ctx, ctor, 2, args);
43298     if (JS_IsException(splitter))
43299         goto exception;
43300     A = JS_NewArray(ctx);
43301     if (JS_IsException(A))
43302         goto exception;
43303     lengthA = 0;
43304     if (JS_IsUndefined(argv[1])) {
43305         lim = 0xffffffff;
43306     } else {
43307         if (JS_ToUint32(ctx, &lim, argv[1]) < 0)
43308             goto exception;
43309         if (lim == 0)
43310             goto done;
43311     }
43312     strp = JS_VALUE_GET_STRING(str);
43313     p = q = 0;
43314     size = strp->len;
43315     if (size == 0) {
43316         z = JS_RegExpExec(ctx, splitter, str);
43317         if (JS_IsException(z))
43318             goto exception;
43319         if (JS_IsNull(z))
43320             goto add_tail;
43321         goto done;
43322     }
43323     while (q < size) {
43324         if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0)
43325             goto exception;
43326         JS_FreeValue(ctx, z);
43327         z = JS_RegExpExec(ctx, splitter, str);
43328         if (JS_IsException(z))
43329             goto exception;
43330         if (JS_IsNull(z)) {
43331             q = string_advance_index(strp, q, unicodeMatching);
43332         } else {
43333             if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex)))
43334                 goto exception;
43335             if (e > size)
43336                 e = size;
43337             if (e == p) {
43338                 q = string_advance_index(strp, q, unicodeMatching);
43339             } else {
43340                 sub = js_sub_string(ctx, strp, p, q);
43341                 if (JS_IsException(sub))
43342                     goto exception;
43343                 if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub,
43344                                                 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43345                     goto exception;
43346                 if (lengthA == lim)
43347                     goto done;
43348                 p = e;
43349                 if (js_get_length64(ctx, &numberOfCaptures, z))
43350                     goto exception;
43351                 for(i = 1; i < numberOfCaptures; i++) {
43352                     sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i));
43353                     if (JS_IsException(sub))
43354                         goto exception;
43355                     if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43356                         goto exception;
43357                     if (lengthA == lim)
43358                         goto done;
43359                 }
43360                 q = p;
43361             }
43362         }
43363     }
43364 add_tail:
43365     if (p > size)
43366         p = size;
43367     sub = js_sub_string(ctx, strp, p, size);
43368     if (JS_IsException(sub))
43369         goto exception;
43370     if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
43371         goto exception;
43372     goto done;
43373 exception:
43374     JS_FreeValue(ctx, A);
43375     A = JS_EXCEPTION;
43376 done:
43377     JS_FreeValue(ctx, str);
43378     JS_FreeValue(ctx, ctor);
43379     JS_FreeValue(ctx, splitter);
43380     JS_FreeValue(ctx, flags);
43381     JS_FreeValue(ctx, z);
43382     return A;
43383 }
43384 
43385 static const JSCFunctionListEntry js_regexp_funcs[] = {
43386     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
43387     //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ),
43388     //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ),
43389 };
43390 
43391 static const JSCFunctionListEntry js_regexp_proto_funcs[] = {
43392     JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ),
43393     JS_CGETSET_DEF("source", js_regexp_get_source, NULL ),
43394     JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ),
43395     JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ),
43396     JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ),
43397     JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ),
43398     JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ),
43399     JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ),
43400     JS_CFUNC_DEF("exec", 1, js_regexp_exec ),
43401     JS_CFUNC_DEF("compile", 2, js_regexp_compile ),
43402     JS_CFUNC_DEF("test", 1, js_regexp_test ),
43403     JS_CFUNC_DEF("toString", 0, js_regexp_toString ),
43404     JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ),
43405     JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ),
43406     JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ),
43407     JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ),
43408     JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ),
43409     //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ),
43410     //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ),
43411 };
43412 
43413 static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = {
43414     JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ),
43415     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ),
43416 };
43417 
JS_AddIntrinsicRegExpCompiler(JSContext * ctx)43418 void JS_AddIntrinsicRegExpCompiler(JSContext *ctx)
43419 {
43420     ctx->compile_regexp = js_compile_regexp;
43421 }
43422 
JS_AddIntrinsicRegExp(JSContext * ctx)43423 void JS_AddIntrinsicRegExp(JSContext *ctx)
43424 {
43425     JSValueConst obj;
43426 
43427     JS_AddIntrinsicRegExpCompiler(ctx);
43428 
43429     ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx);
43430     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs,
43431                                countof(js_regexp_proto_funcs));
43432     obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2,
43433                                    ctx->class_proto[JS_CLASS_REGEXP]);
43434     ctx->regexp_ctor = JS_DupValue(ctx, obj);
43435     JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs));
43436 
43437     ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] =
43438         JS_NewObjectProto(ctx, ctx->iterator_proto);
43439     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR],
43440                                js_regexp_string_iterator_proto_funcs,
43441                                countof(js_regexp_string_iterator_proto_funcs));
43442 }
43443 
43444 /* JSON */
43445 
json_parse_expect(JSParseState * s,int tok)43446 static int json_parse_expect(JSParseState *s, int tok)
43447 {
43448     if (s->token.val != tok) {
43449         /* XXX: dump token correctly in all cases */
43450         return js_parse_error(s, "expecting '%c'", tok);
43451     }
43452     return json_next_token(s);
43453 }
43454 
json_parse_value(JSParseState * s)43455 static JSValue json_parse_value(JSParseState *s)
43456 {
43457     JSContext *ctx = s->ctx;
43458     JSValue val = JS_NULL;
43459     int ret;
43460 
43461     switch(s->token.val) {
43462     case '{':
43463         {
43464             JSValue prop_val;
43465             JSAtom prop_name;
43466 
43467             if (json_next_token(s))
43468                 goto fail;
43469             val = JS_NewObject(ctx);
43470             if (JS_IsException(val))
43471                 goto fail;
43472             if (s->token.val != '}') {
43473                 for(;;) {
43474                     if (s->token.val == TOK_STRING) {
43475                         prop_name = JS_ValueToAtom(ctx, s->token.u.str.str);
43476                         if (prop_name == JS_ATOM_NULL)
43477                             goto fail;
43478                     } else if (s->ext_json && s->token.val == TOK_IDENT) {
43479                         prop_name = JS_DupAtom(ctx, s->token.u.ident.atom);
43480                     } else {
43481                         js_parse_error(s, "expecting property name");
43482                         goto fail;
43483                     }
43484                     if (json_next_token(s))
43485                         goto fail1;
43486                     if (json_parse_expect(s, ':'))
43487                         goto fail1;
43488                     prop_val = json_parse_value(s);
43489                     if (JS_IsException(prop_val)) {
43490                     fail1:
43491                         JS_FreeAtom(ctx, prop_name);
43492                         goto fail;
43493                     }
43494                     ret = JS_DefinePropertyValue(ctx, val, prop_name,
43495                                                  prop_val, JS_PROP_C_W_E);
43496                     JS_FreeAtom(ctx, prop_name);
43497                     if (ret < 0)
43498                         goto fail;
43499 
43500                     if (s->token.val != ',')
43501                         break;
43502                     if (json_next_token(s))
43503                         goto fail;
43504                     if (s->ext_json && s->token.val == '}')
43505                         break;
43506                 }
43507             }
43508             if (json_parse_expect(s, '}'))
43509                 goto fail;
43510         }
43511         break;
43512     case '[':
43513         {
43514             JSValue el;
43515             uint32_t idx;
43516 
43517             if (json_next_token(s))
43518                 goto fail;
43519             val = JS_NewArray(ctx);
43520             if (JS_IsException(val))
43521                 goto fail;
43522             if (s->token.val != ']') {
43523                 idx = 0;
43524                 for(;;) {
43525                     el = json_parse_value(s);
43526                     if (JS_IsException(el))
43527                         goto fail;
43528                     ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E);
43529                     if (ret < 0)
43530                         goto fail;
43531                     if (s->token.val != ',')
43532                         break;
43533                     if (json_next_token(s))
43534                         goto fail;
43535                     idx++;
43536                     if (s->ext_json && s->token.val == ']')
43537                         break;
43538                 }
43539             }
43540             if (json_parse_expect(s, ']'))
43541                 goto fail;
43542         }
43543         break;
43544     case TOK_STRING:
43545         val = JS_DupValue(ctx, s->token.u.str.str);
43546         if (json_next_token(s))
43547             goto fail;
43548         break;
43549     case TOK_NUMBER:
43550         val = s->token.u.num.val;
43551         if (json_next_token(s))
43552             goto fail;
43553         break;
43554     case TOK_IDENT:
43555         if (s->token.u.ident.atom == JS_ATOM_false ||
43556             s->token.u.ident.atom == JS_ATOM_true) {
43557             val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true));
43558         } else if (s->token.u.ident.atom == JS_ATOM_null) {
43559             val = JS_NULL;
43560         } else {
43561             goto def_token;
43562         }
43563         if (json_next_token(s))
43564             goto fail;
43565         break;
43566     default:
43567     def_token:
43568         if (s->token.val == TOK_EOF) {
43569             js_parse_error(s, "unexpected end of input");
43570         } else {
43571             js_parse_error(s, "unexpected token: '%.*s'",
43572                            (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
43573         }
43574         goto fail;
43575     }
43576     return val;
43577  fail:
43578     JS_FreeValue(ctx, val);
43579     return JS_EXCEPTION;
43580 }
43581 
JS_ParseJSON2(JSContext * ctx,const char * buf,size_t buf_len,const char * filename,int flags)43582 JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
43583                       const char *filename, int flags)
43584 {
43585     JSParseState s1, *s = &s1;
43586     JSValue val = JS_UNDEFINED;
43587 
43588     js_parse_init(ctx, s, buf, buf_len, filename);
43589     s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);
43590     if (json_next_token(s))
43591         goto fail;
43592     val = json_parse_value(s);
43593     if (JS_IsException(val))
43594         goto fail;
43595     if (s->token.val != TOK_EOF) {
43596         if (js_parse_error(s, "unexpected data at the end"))
43597             goto fail;
43598     }
43599     return val;
43600  fail:
43601     JS_FreeValue(ctx, val);
43602     free_token(s, &s->token);
43603     return JS_EXCEPTION;
43604 }
43605 
JS_ParseJSON(JSContext * ctx,const char * buf,size_t buf_len,const char * filename)43606 JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
43607                      const char *filename)
43608 {
43609     return JS_ParseJSON2(ctx, buf, buf_len, filename, 0);
43610 }
43611 
internalize_json_property(JSContext * ctx,JSValueConst holder,JSAtom name,JSValueConst reviver)43612 static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
43613                                          JSAtom name, JSValueConst reviver)
43614 {
43615     JSValue val, new_el, name_val, res;
43616     JSValueConst args[2];
43617     int ret, is_array;
43618     uint32_t i, len = 0;
43619     JSAtom prop;
43620     JSPropertyEnum *atoms = NULL;
43621 
43622     if (js_check_stack_overflow(ctx->rt, 0)) {
43623         return JS_ThrowStackOverflow(ctx);
43624     }
43625 
43626     val = JS_GetProperty(ctx, holder, name);
43627     if (JS_IsException(val))
43628         return val;
43629     if (JS_IsObject(val)) {
43630         is_array = JS_IsArray(ctx, val);
43631         if (is_array < 0)
43632             goto fail;
43633         if (is_array) {
43634             if (js_get_length32(ctx, &len, val))
43635                 goto fail;
43636         } else {
43637             ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);
43638             if (ret < 0)
43639                 goto fail;
43640         }
43641         for(i = 0; i < len; i++) {
43642             if (is_array) {
43643                 prop = JS_NewAtomUInt32(ctx, i);
43644                 if (prop == JS_ATOM_NULL)
43645                     goto fail;
43646             } else {
43647                 prop = JS_DupAtom(ctx, atoms[i].atom);
43648             }
43649             new_el = internalize_json_property(ctx, val, prop, reviver);
43650             if (JS_IsException(new_el)) {
43651                 JS_FreeAtom(ctx, prop);
43652                 goto fail;
43653             }
43654             if (JS_IsUndefined(new_el)) {
43655                 ret = JS_DeleteProperty(ctx, val, prop, 0);
43656             } else {
43657                 ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E);
43658             }
43659             JS_FreeAtom(ctx, prop);
43660             if (ret < 0)
43661                 goto fail;
43662         }
43663     }
43664     js_free_prop_enum(ctx, atoms, len);
43665     atoms = NULL;
43666     name_val = JS_AtomToValue(ctx, name);
43667     if (JS_IsException(name_val))
43668         goto fail;
43669     args[0] = name_val;
43670     args[1] = val;
43671     res = JS_Call(ctx, reviver, holder, 2, args);
43672     JS_FreeValue(ctx, name_val);
43673     JS_FreeValue(ctx, val);
43674     return res;
43675  fail:
43676     js_free_prop_enum(ctx, atoms, len);
43677     JS_FreeValue(ctx, val);
43678     return JS_EXCEPTION;
43679 }
43680 
js_json_parse(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)43681 static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val,
43682                              int argc, JSValueConst *argv)
43683 {
43684     JSValue obj, root;
43685     JSValueConst reviver;
43686     const char *str;
43687     size_t len;
43688 
43689     str = JS_ToCStringLen(ctx, &len, argv[0]);
43690     if (!str)
43691         return JS_EXCEPTION;
43692     obj = JS_ParseJSON(ctx, str, len, "<input>");
43693     JS_FreeCString(ctx, str);
43694     if (JS_IsException(obj))
43695         return obj;
43696     if (argc > 1 && JS_IsFunction(ctx, argv[1])) {
43697         reviver = argv[1];
43698         root = JS_NewObject(ctx);
43699         if (JS_IsException(root)) {
43700             JS_FreeValue(ctx, obj);
43701             return JS_EXCEPTION;
43702         }
43703         if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj,
43704                                    JS_PROP_C_W_E) < 0) {
43705             JS_FreeValue(ctx, root);
43706             return JS_EXCEPTION;
43707         }
43708         obj = internalize_json_property(ctx, root, JS_ATOM_empty_string,
43709                                         reviver);
43710         JS_FreeValue(ctx, root);
43711     }
43712     return obj;
43713 }
43714 
43715 typedef struct JSONStringifyContext {
43716     JSValueConst replacer_func;
43717     JSValue stack;
43718     JSValue property_list;
43719     JSValue gap;
43720     JSValue empty;
43721     StringBuffer *b;
43722 } JSONStringifyContext;
43723 
JS_ToQuotedStringFree(JSContext * ctx,JSValue val)43724 static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) {
43725     JSValue r = JS_ToQuotedString(ctx, val);
43726     JS_FreeValue(ctx, val);
43727     return r;
43728 }
43729 
js_json_check(JSContext * ctx,JSONStringifyContext * jsc,JSValueConst holder,JSValue val,JSValueConst key)43730 static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
43731                              JSValueConst holder, JSValue val, JSValueConst key)
43732 {
43733     JSValue v;
43734     JSValueConst args[2];
43735 
43736     if (JS_IsObject(val)
43737 #ifdef CONFIG_BIGNUM
43738     ||  JS_IsBigInt(ctx, val)   /* XXX: probably useless */
43739 #endif
43740         ) {
43741             JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);
43742             if (JS_IsException(f))
43743                 goto exception;
43744             if (JS_IsFunction(ctx, f)) {
43745                 v = JS_CallFree(ctx, f, val, 1, &key);
43746                 JS_FreeValue(ctx, val);
43747                 val = v;
43748                 if (JS_IsException(val))
43749                     goto exception;
43750             } else {
43751                 JS_FreeValue(ctx, f);
43752             }
43753         }
43754 
43755     if (!JS_IsUndefined(jsc->replacer_func)) {
43756         args[0] = key;
43757         args[1] = val;
43758         v = JS_Call(ctx, jsc->replacer_func, holder, 2, args);
43759         JS_FreeValue(ctx, val);
43760         val = v;
43761         if (JS_IsException(val))
43762             goto exception;
43763     }
43764 
43765     switch (JS_VALUE_GET_NORM_TAG(val)) {
43766     case JS_TAG_OBJECT:
43767         if (JS_IsFunction(ctx, val))
43768             break;
43769     case JS_TAG_STRING:
43770     case JS_TAG_INT:
43771     case JS_TAG_FLOAT64:
43772 #ifdef CONFIG_BIGNUM
43773     case JS_TAG_BIG_FLOAT:
43774 #endif
43775     case JS_TAG_BOOL:
43776     case JS_TAG_NULL:
43777 #ifdef CONFIG_BIGNUM
43778     case JS_TAG_BIG_INT:
43779 #endif
43780     case JS_TAG_EXCEPTION:
43781         return val;
43782     default:
43783         break;
43784     }
43785     JS_FreeValue(ctx, val);
43786     return JS_UNDEFINED;
43787 
43788 exception:
43789     JS_FreeValue(ctx, val);
43790     return JS_EXCEPTION;
43791 }
43792 
js_json_to_str(JSContext * ctx,JSONStringifyContext * jsc,JSValueConst holder,JSValue val,JSValueConst indent)43793 static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
43794                           JSValueConst holder, JSValue val,
43795                           JSValueConst indent)
43796 {
43797     JSValue indent1, sep, sep1, tab, v, prop;
43798     JSObject *p;
43799     int64_t i, len;
43800     int cl, ret;
43801     BOOL has_content;
43802 
43803     indent1 = JS_UNDEFINED;
43804     sep = JS_UNDEFINED;
43805     sep1 = JS_UNDEFINED;
43806     tab = JS_UNDEFINED;
43807     prop = JS_UNDEFINED;
43808 
43809     switch (JS_VALUE_GET_NORM_TAG(val)) {
43810     case JS_TAG_OBJECT:
43811         p = JS_VALUE_GET_OBJ(val);
43812         cl = p->class_id;
43813         if (cl == JS_CLASS_STRING) {
43814             val = JS_ToStringFree(ctx, val);
43815             if (JS_IsException(val))
43816                 goto exception;
43817             val = JS_ToQuotedStringFree(ctx, val);
43818             if (JS_IsException(val))
43819                 goto exception;
43820             return string_buffer_concat_value_free(jsc->b, val);
43821         } else if (cl == JS_CLASS_NUMBER) {
43822             val = JS_ToNumberFree(ctx, val);
43823             if (JS_IsException(val))
43824                 goto exception;
43825             return string_buffer_concat_value_free(jsc->b, val);
43826         } else if (cl == JS_CLASS_BOOLEAN) {
43827             ret = string_buffer_concat_value(jsc->b, p->u.object_data);
43828             JS_FreeValue(ctx, val);
43829             return ret;
43830         }
43831 #ifdef CONFIG_BIGNUM
43832         else if (cl == JS_CLASS_BIG_FLOAT) {
43833             return string_buffer_concat_value_free(jsc->b, val);
43834         } else if (cl == JS_CLASS_BIG_INT) {
43835             JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");
43836             goto exception;
43837         }
43838 #endif
43839         v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);
43840         if (JS_IsException(v))
43841             goto exception;
43842         if (JS_ToBoolFree(ctx, v)) {
43843             JS_ThrowTypeError(ctx, "circular reference");
43844             goto exception;
43845         }
43846         indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap));
43847         if (JS_IsException(indent1))
43848             goto exception;
43849         if (!JS_IsEmptyString(jsc->gap)) {
43850             sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), "");
43851             if (JS_IsException(sep))
43852                 goto exception;
43853             sep1 = JS_NewString(ctx, " ");
43854             if (JS_IsException(sep1))
43855                 goto exception;
43856         } else {
43857             sep = JS_DupValue(ctx, jsc->empty);
43858             sep1 = JS_DupValue(ctx, jsc->empty);
43859         }
43860         v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0);
43861         if (check_exception_free(ctx, v))
43862             goto exception;
43863         ret = JS_IsArray(ctx, val);
43864         if (ret < 0)
43865             goto exception;
43866         if (ret) {
43867             if (js_get_length64(ctx, &len, val))
43868                 goto exception;
43869             string_buffer_putc8(jsc->b, '[');
43870             for(i = 0; i < len; i++) {
43871                 if (i > 0)
43872                     string_buffer_putc8(jsc->b, ',');
43873                 string_buffer_concat_value(jsc->b, sep);
43874                 v = JS_GetPropertyInt64(ctx, val, i);
43875                 if (JS_IsException(v))
43876                     goto exception;
43877                 /* XXX: could do this string conversion only when needed */
43878                 prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i));
43879                 if (JS_IsException(prop))
43880                     goto exception;
43881                 v = js_json_check(ctx, jsc, val, v, prop);
43882                 JS_FreeValue(ctx, prop);
43883                 prop = JS_UNDEFINED;
43884                 if (JS_IsException(v))
43885                     goto exception;
43886                 if (JS_IsUndefined(v))
43887                     v = JS_NULL;
43888                 if (js_json_to_str(ctx, jsc, val, v, indent1))
43889                     goto exception;
43890             }
43891             if (len > 0 && !JS_IsEmptyString(jsc->gap)) {
43892                 string_buffer_putc8(jsc->b, '\n');
43893                 string_buffer_concat_value(jsc->b, indent);
43894             }
43895             string_buffer_putc8(jsc->b, ']');
43896         } else {
43897             if (!JS_IsUndefined(jsc->property_list))
43898                 tab = JS_DupValue(ctx, jsc->property_list);
43899             else
43900                 tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY);
43901             if (JS_IsException(tab))
43902                 goto exception;
43903             if (js_get_length64(ctx, &len, tab))
43904                 goto exception;
43905             string_buffer_putc8(jsc->b, '{');
43906             has_content = FALSE;
43907             for(i = 0; i < len; i++) {
43908                 JS_FreeValue(ctx, prop);
43909                 prop = JS_GetPropertyInt64(ctx, tab, i);
43910                 if (JS_IsException(prop))
43911                     goto exception;
43912                 v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop));
43913                 if (JS_IsException(v))
43914                     goto exception;
43915                 v = js_json_check(ctx, jsc, val, v, prop);
43916                 if (JS_IsException(v))
43917                     goto exception;
43918                 if (!JS_IsUndefined(v)) {
43919                     if (has_content)
43920                         string_buffer_putc8(jsc->b, ',');
43921                     prop = JS_ToQuotedStringFree(ctx, prop);
43922                     if (JS_IsException(prop)) {
43923                         JS_FreeValue(ctx, v);
43924                         goto exception;
43925                     }
43926                     string_buffer_concat_value(jsc->b, sep);
43927                     string_buffer_concat_value(jsc->b, prop);
43928                     string_buffer_putc8(jsc->b, ':');
43929                     string_buffer_concat_value(jsc->b, sep1);
43930                     if (js_json_to_str(ctx, jsc, val, v, indent1))
43931                         goto exception;
43932                     has_content = TRUE;
43933                 }
43934             }
43935             if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) {
43936                 string_buffer_putc8(jsc->b, '\n');
43937                 string_buffer_concat_value(jsc->b, indent);
43938             }
43939             string_buffer_putc8(jsc->b, '}');
43940         }
43941         if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0)))
43942             goto exception;
43943         JS_FreeValue(ctx, val);
43944         JS_FreeValue(ctx, tab);
43945         JS_FreeValue(ctx, sep);
43946         JS_FreeValue(ctx, sep1);
43947         JS_FreeValue(ctx, indent1);
43948         JS_FreeValue(ctx, prop);
43949         return 0;
43950     case JS_TAG_STRING:
43951         val = JS_ToQuotedStringFree(ctx, val);
43952         if (JS_IsException(val))
43953             goto exception;
43954         goto concat_value;
43955     case JS_TAG_FLOAT64:
43956         if (!isfinite(JS_VALUE_GET_FLOAT64(val))) {
43957             val = JS_NULL;
43958         }
43959         goto concat_value;
43960     case JS_TAG_INT:
43961 #ifdef CONFIG_BIGNUM
43962     case JS_TAG_BIG_FLOAT:
43963 #endif
43964     case JS_TAG_BOOL:
43965     case JS_TAG_NULL:
43966     concat_value:
43967         return string_buffer_concat_value_free(jsc->b, val);
43968 #ifdef CONFIG_BIGNUM
43969     case JS_TAG_BIG_INT:
43970         JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");
43971         goto exception;
43972 #endif
43973     default:
43974         JS_FreeValue(ctx, val);
43975         return 0;
43976     }
43977 
43978 exception:
43979     JS_FreeValue(ctx, val);
43980     JS_FreeValue(ctx, tab);
43981     JS_FreeValue(ctx, sep);
43982     JS_FreeValue(ctx, sep1);
43983     JS_FreeValue(ctx, indent1);
43984     JS_FreeValue(ctx, prop);
43985     return -1;
43986 }
43987 
JS_JSONStringify(JSContext * ctx,JSValueConst obj,JSValueConst replacer,JSValueConst space0)43988 JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
43989                          JSValueConst replacer, JSValueConst space0)
43990 {
43991     StringBuffer b_s;
43992     JSONStringifyContext jsc_s, *jsc = &jsc_s;
43993     JSValue val, v, space, ret, wrapper;
43994     int res;
43995     int64_t i, j, n;
43996 
43997     jsc->replacer_func = JS_UNDEFINED;
43998     jsc->stack = JS_UNDEFINED;
43999     jsc->property_list = JS_UNDEFINED;
44000     jsc->gap = JS_UNDEFINED;
44001     jsc->b = &b_s;
44002     jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string);
44003     ret = JS_UNDEFINED;
44004     wrapper = JS_UNDEFINED;
44005 
44006     string_buffer_init(ctx, jsc->b, 0);
44007     jsc->stack = JS_NewArray(ctx);
44008     if (JS_IsException(jsc->stack))
44009         goto exception;
44010     if (JS_IsFunction(ctx, replacer)) {
44011         jsc->replacer_func = replacer;
44012     } else {
44013         res = JS_IsArray(ctx, replacer);
44014         if (res < 0)
44015             goto exception;
44016         if (res) {
44017             /* XXX: enumeration is not fully correct */
44018             jsc->property_list = JS_NewArray(ctx);
44019             if (JS_IsException(jsc->property_list))
44020                 goto exception;
44021             if (js_get_length64(ctx, &n, replacer))
44022                 goto exception;
44023             for (i = j = 0; i < n; i++) {
44024                 JSValue present;
44025                 v = JS_GetPropertyInt64(ctx, replacer, i);
44026                 if (JS_IsException(v))
44027                     goto exception;
44028                 if (JS_IsObject(v)) {
44029                     JSObject *p = JS_VALUE_GET_OBJ(v);
44030                     if (p->class_id == JS_CLASS_STRING ||
44031                         p->class_id == JS_CLASS_NUMBER) {
44032                         v = JS_ToStringFree(ctx, v);
44033                         if (JS_IsException(v))
44034                             goto exception;
44035                     } else {
44036                         JS_FreeValue(ctx, v);
44037                         continue;
44038                     }
44039                 } else if (JS_IsNumber(v)) {
44040                     v = JS_ToStringFree(ctx, v);
44041                     if (JS_IsException(v))
44042                         goto exception;
44043                 } else if (!JS_IsString(v)) {
44044                     JS_FreeValue(ctx, v);
44045                     continue;
44046                 }
44047                 present = js_array_includes(ctx, jsc->property_list,
44048                                             1, (JSValueConst *)&v);
44049                 if (JS_IsException(present)) {
44050                     JS_FreeValue(ctx, v);
44051                     goto exception;
44052                 }
44053                 if (!JS_ToBoolFree(ctx, present)) {
44054                     JS_SetPropertyInt64(ctx, jsc->property_list, j++, v);
44055                 } else {
44056                     JS_FreeValue(ctx, v);
44057                 }
44058             }
44059         }
44060     }
44061     space = JS_DupValue(ctx, space0);
44062     if (JS_IsObject(space)) {
44063         JSObject *p = JS_VALUE_GET_OBJ(space);
44064         if (p->class_id == JS_CLASS_NUMBER) {
44065             space = JS_ToNumberFree(ctx, space);
44066         } else if (p->class_id == JS_CLASS_STRING) {
44067             space = JS_ToStringFree(ctx, space);
44068         }
44069         if (JS_IsException(space)) {
44070             JS_FreeValue(ctx, space);
44071             goto exception;
44072         }
44073     }
44074     if (JS_IsNumber(space)) {
44075         int n;
44076         if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0))
44077             goto exception;
44078         jsc->gap = JS_NewStringLen(ctx, "          ", n);
44079     } else if (JS_IsString(space)) {
44080         JSString *p = JS_VALUE_GET_STRING(space);
44081         jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10));
44082     } else {
44083         jsc->gap = JS_DupValue(ctx, jsc->empty);
44084     }
44085     JS_FreeValue(ctx, space);
44086     if (JS_IsException(jsc->gap))
44087         goto exception;
44088     wrapper = JS_NewObject(ctx);
44089     if (JS_IsException(wrapper))
44090         goto exception;
44091     if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string,
44092                                JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0)
44093         goto exception;
44094     val = JS_DupValue(ctx, obj);
44095 
44096     val = js_json_check(ctx, jsc, wrapper, val, jsc->empty);
44097     if (JS_IsException(val))
44098         goto exception;
44099     if (JS_IsUndefined(val)) {
44100         ret = JS_UNDEFINED;
44101         goto done1;
44102     }
44103     if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty))
44104         goto exception;
44105 
44106     ret = string_buffer_end(jsc->b);
44107     goto done;
44108 
44109 exception:
44110     ret = JS_EXCEPTION;
44111 done1:
44112     string_buffer_free(jsc->b);
44113 done:
44114     JS_FreeValue(ctx, wrapper);
44115     JS_FreeValue(ctx, jsc->empty);
44116     JS_FreeValue(ctx, jsc->gap);
44117     JS_FreeValue(ctx, jsc->property_list);
44118     JS_FreeValue(ctx, jsc->stack);
44119     return ret;
44120 }
44121 
js_json_stringify(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44122 static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val,
44123                                  int argc, JSValueConst *argv)
44124 {
44125     // stringify(val, replacer, space)
44126     return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]);
44127 }
44128 
44129 static const JSCFunctionListEntry js_json_funcs[] = {
44130     JS_CFUNC_DEF("parse", 2, js_json_parse ),
44131     JS_CFUNC_DEF("stringify", 3, js_json_stringify ),
44132     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ),
44133 };
44134 
44135 static const JSCFunctionListEntry js_json_obj[] = {
44136     JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
44137 };
44138 
JS_AddIntrinsicJSON(JSContext * ctx)44139 void JS_AddIntrinsicJSON(JSContext *ctx)
44140 {
44141     /* add JSON as autoinit object */
44142     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj));
44143 }
44144 
44145 /* Reflect */
44146 
js_reflect_apply(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44147 static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val,
44148                                 int argc, JSValueConst *argv)
44149 {
44150     return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2);
44151 }
44152 
js_reflect_construct(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44153 static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val,
44154                                     int argc, JSValueConst *argv)
44155 {
44156     JSValueConst func, array_arg, new_target;
44157     JSValue *tab, ret;
44158     uint32_t len;
44159 
44160     func = argv[0];
44161     array_arg = argv[1];
44162     if (argc > 2) {
44163         new_target = argv[2];
44164         if (!JS_IsConstructor(ctx, new_target))
44165             return JS_ThrowTypeError(ctx, "not a constructor");
44166     } else {
44167         new_target = func;
44168     }
44169     tab = build_arg_list(ctx, &len, array_arg);
44170     if (!tab)
44171         return JS_EXCEPTION;
44172     ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab);
44173     free_arg_list(ctx, tab, len);
44174     return ret;
44175 }
44176 
js_reflect_deleteProperty(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44177 static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val,
44178                                          int argc, JSValueConst *argv)
44179 {
44180     JSValueConst obj;
44181     JSAtom atom;
44182     int ret;
44183 
44184     obj = argv[0];
44185     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
44186         return JS_ThrowTypeErrorNotAnObject(ctx);
44187     atom = JS_ValueToAtom(ctx, argv[1]);
44188     if (unlikely(atom == JS_ATOM_NULL))
44189         return JS_EXCEPTION;
44190     ret = JS_DeleteProperty(ctx, obj, atom, 0);
44191     JS_FreeAtom(ctx, atom);
44192     if (ret < 0)
44193         return JS_EXCEPTION;
44194     else
44195         return JS_NewBool(ctx, ret);
44196 }
44197 
js_reflect_get(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44198 static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val,
44199                               int argc, JSValueConst *argv)
44200 {
44201     JSValueConst obj, prop, receiver;
44202     JSAtom atom;
44203     JSValue ret;
44204 
44205     obj = argv[0];
44206     prop = argv[1];
44207     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
44208         return JS_ThrowTypeErrorNotAnObject(ctx);
44209     if (argc > 2)
44210         receiver = argv[2];
44211     else
44212         receiver = obj;
44213     atom = JS_ValueToAtom(ctx, prop);
44214     if (unlikely(atom == JS_ATOM_NULL))
44215         return JS_EXCEPTION;
44216     ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE);
44217     JS_FreeAtom(ctx, atom);
44218     return ret;
44219 }
44220 
js_reflect_has(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44221 static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val,
44222                               int argc, JSValueConst *argv)
44223 {
44224     JSValueConst obj, prop;
44225     JSAtom atom;
44226     int ret;
44227 
44228     obj = argv[0];
44229     prop = argv[1];
44230     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
44231         return JS_ThrowTypeErrorNotAnObject(ctx);
44232     atom = JS_ValueToAtom(ctx, prop);
44233     if (unlikely(atom == JS_ATOM_NULL))
44234         return JS_EXCEPTION;
44235     ret = JS_HasProperty(ctx, obj, atom);
44236     JS_FreeAtom(ctx, atom);
44237     if (ret < 0)
44238         return JS_EXCEPTION;
44239     else
44240         return JS_NewBool(ctx, ret);
44241 }
44242 
js_reflect_set(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44243 static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val,
44244                               int argc, JSValueConst *argv)
44245 {
44246     JSValueConst obj, prop, val, receiver;
44247     int ret;
44248     JSAtom atom;
44249 
44250     obj = argv[0];
44251     prop = argv[1];
44252     val = argv[2];
44253     if (argc > 3)
44254         receiver = argv[3];
44255     else
44256         receiver = obj;
44257     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
44258         return JS_ThrowTypeErrorNotAnObject(ctx);
44259     atom = JS_ValueToAtom(ctx, prop);
44260     if (unlikely(atom == JS_ATOM_NULL))
44261         return JS_EXCEPTION;
44262     ret = JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(obj), atom,
44263                                 JS_DupValue(ctx, val), receiver, 0);
44264     JS_FreeAtom(ctx, atom);
44265     if (ret < 0)
44266         return JS_EXCEPTION;
44267     else
44268         return JS_NewBool(ctx, ret);
44269 }
44270 
js_reflect_setPrototypeOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44271 static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
44272                                          int argc, JSValueConst *argv)
44273 {
44274     int ret;
44275     ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE);
44276     if (ret < 0)
44277         return JS_EXCEPTION;
44278     else
44279         return JS_NewBool(ctx, ret);
44280 }
44281 
js_reflect_ownKeys(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)44282 static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val,
44283                                   int argc, JSValueConst *argv)
44284 {
44285     if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
44286         return JS_ThrowTypeErrorNotAnObject(ctx);
44287     return JS_GetOwnPropertyNames2(ctx, argv[0],
44288                                    JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK,
44289                                    JS_ITERATOR_KIND_KEY);
44290 }
44291 
44292 static const JSCFunctionListEntry js_reflect_funcs[] = {
44293     JS_CFUNC_DEF("apply", 3, js_reflect_apply ),
44294     JS_CFUNC_DEF("construct", 2, js_reflect_construct ),
44295     JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ),
44296     JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ),
44297     JS_CFUNC_DEF("get", 2, js_reflect_get ),
44298     JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ),
44299     JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ),
44300     JS_CFUNC_DEF("has", 2, js_reflect_has ),
44301     JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ),
44302     JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ),
44303     JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ),
44304     JS_CFUNC_DEF("set", 3, js_reflect_set ),
44305     JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ),
44306     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ),
44307 };
44308 
44309 static const JSCFunctionListEntry js_reflect_obj[] = {
44310     JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
44311 };
44312 
44313 /* Proxy */
44314 
js_proxy_finalizer(JSRuntime * rt,JSValue val)44315 static void js_proxy_finalizer(JSRuntime *rt, JSValue val)
44316 {
44317     JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
44318     if (s) {
44319         JS_FreeValueRT(rt, s->target);
44320         JS_FreeValueRT(rt, s->handler);
44321         js_free_rt(rt, s);
44322     }
44323 }
44324 
js_proxy_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)44325 static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
44326                           JS_MarkFunc *mark_func)
44327 {
44328     JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
44329     if (s) {
44330         JS_MarkValue(rt, s->target, mark_func);
44331         JS_MarkValue(rt, s->handler, mark_func);
44332     }
44333 }
44334 
JS_ThrowTypeErrorRevokedProxy(JSContext * ctx)44335 static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx)
44336 {
44337     return JS_ThrowTypeError(ctx, "revoked proxy");
44338 }
44339 
get_proxy_method(JSContext * ctx,JSValue * pmethod,JSValueConst obj,JSAtom name)44340 static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod,
44341                                      JSValueConst obj, JSAtom name)
44342 {
44343     JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);
44344     JSValue method;
44345 
44346     /* safer to test recursion in all proxy methods */
44347     if (js_check_stack_overflow(ctx->rt, 0)) {
44348         JS_ThrowStackOverflow(ctx);
44349         return NULL;
44350     }
44351 
44352     /* 's' should never be NULL */
44353     if (s->is_revoked) {
44354         JS_ThrowTypeErrorRevokedProxy(ctx);
44355         return NULL;
44356     }
44357     method = JS_GetProperty(ctx, s->handler, name);
44358     if (JS_IsException(method))
44359         return NULL;
44360     if (JS_IsNull(method))
44361         method = JS_UNDEFINED;
44362     *pmethod = method;
44363     return s;
44364 }
44365 
js_proxy_getPrototypeOf(JSContext * ctx,JSValueConst obj)44366 static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)
44367 {
44368     JSProxyData *s;
44369     JSValue method, ret, proto1;
44370     int res;
44371 
44372     s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf);
44373     if (!s)
44374         return JS_EXCEPTION;
44375     if (JS_IsUndefined(method))
44376         return JS_GetPrototype(ctx, s->target);
44377     ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
44378     if (JS_IsException(ret))
44379         return ret;
44380     if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL &&
44381         JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
44382         goto fail;
44383     }
44384     res = JS_IsExtensible(ctx, s->target);
44385     if (res < 0) {
44386         JS_FreeValue(ctx, ret);
44387         return JS_EXCEPTION;
44388     }
44389     if (!res) {
44390         /* check invariant */
44391         proto1 = JS_GetPrototype(ctx, s->target);
44392         if (JS_IsException(proto1)) {
44393             JS_FreeValue(ctx, ret);
44394             return JS_EXCEPTION;
44395         }
44396         if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) {
44397             JS_FreeValue(ctx, proto1);
44398         fail:
44399             JS_FreeValue(ctx, ret);
44400             return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
44401         }
44402         JS_FreeValue(ctx, proto1);
44403     }
44404     return ret;
44405 }
44406 
js_proxy_setPrototypeOf(JSContext * ctx,JSValueConst obj,JSValueConst proto_val,BOOL throw_flag)44407 static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
44408                                    JSValueConst proto_val, BOOL throw_flag)
44409 {
44410     JSProxyData *s;
44411     JSValue method, ret, proto1;
44412     JSValueConst args[2];
44413     BOOL res;
44414     int res2;
44415 
44416     s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf);
44417     if (!s)
44418         return -1;
44419     if (JS_IsUndefined(method))
44420         return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag);
44421     args[0] = s->target;
44422     args[1] = proto_val;
44423     ret = JS_CallFree(ctx, method, s->handler, 2, args);
44424     if (JS_IsException(ret))
44425         return -1;
44426     res = JS_ToBoolFree(ctx, ret);
44427     if (!res) {
44428         if (throw_flag) {
44429             JS_ThrowTypeError(ctx, "proxy: bad prototype");
44430             return -1;
44431         } else {
44432             return FALSE;
44433         }
44434     }
44435     res2 = JS_IsExtensible(ctx, s->target);
44436     if (res2 < 0)
44437         return -1;
44438     if (!res2) {
44439         proto1 = JS_GetPrototype(ctx, s->target);
44440         if (JS_IsException(proto1))
44441             return -1;
44442         if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) {
44443             JS_FreeValue(ctx, proto1);
44444             JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
44445             return -1;
44446         }
44447         JS_FreeValue(ctx, proto1);
44448     }
44449     return TRUE;
44450 }
44451 
js_proxy_isExtensible(JSContext * ctx,JSValueConst obj)44452 static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj)
44453 {
44454     JSProxyData *s;
44455     JSValue method, ret;
44456     BOOL res;
44457     int res2;
44458 
44459     s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible);
44460     if (!s)
44461         return -1;
44462     if (JS_IsUndefined(method))
44463         return JS_IsExtensible(ctx, s->target);
44464     ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
44465     if (JS_IsException(ret))
44466         return -1;
44467     res = JS_ToBoolFree(ctx, ret);
44468     res2 = JS_IsExtensible(ctx, s->target);
44469     if (res2 < 0)
44470         return res2;
44471     if (res != res2) {
44472         JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible");
44473         return -1;
44474     }
44475     return res;
44476 }
44477 
js_proxy_preventExtensions(JSContext * ctx,JSValueConst obj)44478 static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj)
44479 {
44480     JSProxyData *s;
44481     JSValue method, ret;
44482     BOOL res;
44483     int res2;
44484 
44485     s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions);
44486     if (!s)
44487         return -1;
44488     if (JS_IsUndefined(method))
44489         return JS_PreventExtensions(ctx, s->target);
44490     ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
44491     if (JS_IsException(ret))
44492         return -1;
44493     res = JS_ToBoolFree(ctx, ret);
44494     if (res) {
44495         res2 = JS_IsExtensible(ctx, s->target);
44496         if (res2 < 0)
44497             return res2;
44498         if (res2) {
44499             JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions");
44500             return -1;
44501         }
44502     }
44503     return res;
44504 }
44505 
js_proxy_has(JSContext * ctx,JSValueConst obj,JSAtom atom)44506 static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
44507 {
44508     JSProxyData *s;
44509     JSValue method, ret1, atom_val;
44510     int ret, res;
44511     JSObject *p;
44512     JSValueConst args[2];
44513     BOOL res2;
44514 
44515     s = get_proxy_method(ctx, &method, obj, JS_ATOM_has);
44516     if (!s)
44517         return -1;
44518     if (JS_IsUndefined(method))
44519         return JS_HasProperty(ctx, s->target, atom);
44520     atom_val = JS_AtomToValue(ctx, atom);
44521     if (JS_IsException(atom_val)) {
44522         JS_FreeValue(ctx, method);
44523         return -1;
44524     }
44525     args[0] = s->target;
44526     args[1] = atom_val;
44527     ret1 = JS_CallFree(ctx, method, s->handler, 2, args);
44528     JS_FreeValue(ctx, atom_val);
44529     if (JS_IsException(ret1))
44530         return -1;
44531     ret = JS_ToBoolFree(ctx, ret1);
44532     if (!ret) {
44533         JSPropertyDescriptor desc;
44534         p = JS_VALUE_GET_OBJ(s->target);
44535         res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
44536         if (res < 0)
44537             return -1;
44538         if (res) {
44539             res2 = !(desc.flags & JS_PROP_CONFIGURABLE);
44540             js_free_desc(ctx, &desc);
44541             if (res2 || !p->extensible) {
44542                 JS_ThrowTypeError(ctx, "proxy: inconsistent has");
44543                 return -1;
44544             }
44545         }
44546     }
44547     return ret;
44548 }
44549 
js_proxy_get(JSContext * ctx,JSValueConst obj,JSAtom atom,JSValueConst receiver)44550 static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom,
44551                             JSValueConst receiver)
44552 {
44553     JSProxyData *s;
44554     JSValue method, ret, atom_val;
44555     int res;
44556     JSValueConst args[3];
44557     JSPropertyDescriptor desc;
44558 
44559     s = get_proxy_method(ctx, &method, obj, JS_ATOM_get);
44560     if (!s)
44561         return JS_EXCEPTION;
44562     /* Note: recursion is possible thru the prototype of s->target */
44563     if (JS_IsUndefined(method))
44564         return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE);
44565     atom_val = JS_AtomToValue(ctx, atom);
44566     if (JS_IsException(atom_val)) {
44567         JS_FreeValue(ctx, method);
44568         return JS_EXCEPTION;
44569     }
44570     args[0] = s->target;
44571     args[1] = atom_val;
44572     args[2] = receiver;
44573     ret = JS_CallFree(ctx, method, s->handler, 3, args);
44574     JS_FreeValue(ctx, atom_val);
44575     if (JS_IsException(ret))
44576         return JS_EXCEPTION;
44577     res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
44578     if (res < 0)
44579         return JS_EXCEPTION;
44580     if (res) {
44581         if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
44582             if (!js_same_value(ctx, desc.value, ret)) {
44583                 goto fail;
44584             }
44585         } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) {
44586             if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) {
44587             fail:
44588                 js_free_desc(ctx, &desc);
44589                 JS_FreeValue(ctx, ret);
44590                 return JS_ThrowTypeError(ctx, "proxy: inconsistent get");
44591             }
44592         }
44593         js_free_desc(ctx, &desc);
44594     }
44595     return ret;
44596 }
44597 
js_proxy_set(JSContext * ctx,JSValueConst obj,JSAtom atom,JSValueConst value,JSValueConst receiver,int flags)44598 static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom,
44599                         JSValueConst value, JSValueConst receiver, int flags)
44600 {
44601     JSProxyData *s;
44602     JSValue method, ret1, atom_val;
44603     int ret, res;
44604     JSValueConst args[4];
44605 
44606     s = get_proxy_method(ctx, &method, obj, JS_ATOM_set);
44607     if (!s)
44608         return -1;
44609     if (JS_IsUndefined(method)) {
44610         return JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(s->target), atom,
44611                                      JS_DupValue(ctx, value), receiver,
44612                                      flags);
44613     }
44614     atom_val = JS_AtomToValue(ctx, atom);
44615     if (JS_IsException(atom_val)) {
44616         JS_FreeValue(ctx, method);
44617         return -1;
44618     }
44619     args[0] = s->target;
44620     args[1] = atom_val;
44621     args[2] = value;
44622     args[3] = receiver;
44623     ret1 = JS_CallFree(ctx, method, s->handler, 4, args);
44624     JS_FreeValue(ctx, atom_val);
44625     if (JS_IsException(ret1))
44626         return -1;
44627     ret = JS_ToBoolFree(ctx, ret1);
44628     if (ret) {
44629         JSPropertyDescriptor desc;
44630         res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
44631         if (res < 0)
44632             return -1;
44633         if (res) {
44634             if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
44635                 if (!js_same_value(ctx, desc.value, value)) {
44636                     goto fail;
44637                 }
44638             } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) {
44639                 fail:
44640                     js_free_desc(ctx, &desc);
44641                     JS_ThrowTypeError(ctx, "proxy: inconsistent set");
44642                     return -1;
44643             }
44644             js_free_desc(ctx, &desc);
44645         }
44646     } else {
44647         if ((flags & JS_PROP_THROW) ||
44648             ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
44649             JS_ThrowTypeError(ctx, "proxy: cannot set property");
44650             return -1;
44651         }
44652     }
44653     return ret;
44654 }
44655 
js_create_desc(JSContext * ctx,JSValueConst val,JSValueConst getter,JSValueConst setter,int flags)44656 static JSValue js_create_desc(JSContext *ctx, JSValueConst val,
44657                               JSValueConst getter, JSValueConst setter,
44658                               int flags)
44659 {
44660     JSValue ret;
44661     ret = JS_NewObject(ctx);
44662     if (JS_IsException(ret))
44663         return ret;
44664     if (flags & JS_PROP_HAS_GET) {
44665         JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter),
44666                                JS_PROP_C_W_E);
44667     }
44668     if (flags & JS_PROP_HAS_SET) {
44669         JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter),
44670                                JS_PROP_C_W_E);
44671     }
44672     if (flags & JS_PROP_HAS_VALUE) {
44673         JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val),
44674                                JS_PROP_C_W_E);
44675     }
44676     if (flags & JS_PROP_HAS_WRITABLE) {
44677         JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
44678                                JS_NewBool(ctx, (flags & JS_PROP_WRITABLE) != 0),
44679                                JS_PROP_C_W_E);
44680     }
44681     if (flags & JS_PROP_HAS_ENUMERABLE) {
44682         JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
44683                                JS_NewBool(ctx, (flags & JS_PROP_ENUMERABLE) != 0),
44684                                JS_PROP_C_W_E);
44685     }
44686     if (flags & JS_PROP_HAS_CONFIGURABLE) {
44687         JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
44688                                JS_NewBool(ctx, (flags & JS_PROP_CONFIGURABLE) != 0),
44689                                JS_PROP_C_W_E);
44690     }
44691     return ret;
44692 }
44693 
js_proxy_get_own_property(JSContext * ctx,JSPropertyDescriptor * pdesc,JSValueConst obj,JSAtom prop)44694 static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc,
44695                                      JSValueConst obj, JSAtom prop)
44696 {
44697     JSProxyData *s;
44698     JSValue method, trap_result_obj, prop_val;
44699     int res, target_desc_ret, ret;
44700     JSObject *p;
44701     JSValueConst args[2];
44702     JSPropertyDescriptor result_desc, target_desc;
44703 
44704     s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor);
44705     if (!s)
44706         return -1;
44707     p = JS_VALUE_GET_OBJ(s->target);
44708     if (JS_IsUndefined(method)) {
44709         return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop);
44710     }
44711     prop_val = JS_AtomToValue(ctx, prop);
44712     if (JS_IsException(prop_val)) {
44713         JS_FreeValue(ctx, method);
44714         return -1;
44715     }
44716     args[0] = s->target;
44717     args[1] = prop_val;
44718     trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args);
44719     JS_FreeValue(ctx, prop_val);
44720     if (JS_IsException(trap_result_obj))
44721         return -1;
44722     if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) {
44723         JS_FreeValue(ctx, trap_result_obj);
44724         goto fail;
44725     }
44726     target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop);
44727     if (target_desc_ret < 0) {
44728         JS_FreeValue(ctx, trap_result_obj);
44729         return -1;
44730     }
44731     if (target_desc_ret)
44732         js_free_desc(ctx, &target_desc);
44733     if (JS_IsUndefined(trap_result_obj)) {
44734         if (target_desc_ret) {
44735             if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible)
44736                 goto fail;
44737         }
44738         ret = FALSE;
44739     } else {
44740         int flags1, extensible_target;
44741         extensible_target = JS_IsExtensible(ctx, s->target);
44742         if (extensible_target < 0) {
44743             JS_FreeValue(ctx, trap_result_obj);
44744             return -1;
44745         }
44746         res = js_obj_to_desc(ctx, &result_desc, trap_result_obj);
44747         JS_FreeValue(ctx, trap_result_obj);
44748         if (res < 0)
44749             return -1;
44750 
44751         if (target_desc_ret) {
44752             /* convert result_desc.flags to defineProperty flags */
44753             flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE;
44754             if (result_desc.flags & JS_PROP_GETSET)
44755                 flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET;
44756             else
44757                 flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE;
44758             /* XXX: not complete check: need to compare value &
44759                getter/setter as in defineproperty */
44760             if (!check_define_prop_flags(target_desc.flags, flags1))
44761                 goto fail1;
44762         } else {
44763             if (!extensible_target)
44764                 goto fail1;
44765         }
44766         if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) {
44767             if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE))
44768                 goto fail1;
44769             if ((result_desc.flags &
44770                  (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 &&
44771                 target_desc_ret &&
44772                 (target_desc.flags & JS_PROP_WRITABLE) != 0) {
44773                 /* proxy-missing-checks */
44774             fail1:
44775                 js_free_desc(ctx, &result_desc);
44776             fail:
44777                 JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor");
44778                 return -1;
44779             }
44780         }
44781         ret = TRUE;
44782         if (pdesc) {
44783             *pdesc = result_desc;
44784         } else {
44785             js_free_desc(ctx, &result_desc);
44786         }
44787     }
44788     return ret;
44789 }
44790 
js_proxy_define_own_property(JSContext * ctx,JSValueConst obj,JSAtom prop,JSValueConst val,JSValueConst getter,JSValueConst setter,int flags)44791 static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj,
44792                                         JSAtom prop, JSValueConst val,
44793                                         JSValueConst getter, JSValueConst setter,
44794                                         int flags)
44795 {
44796     JSProxyData *s;
44797     JSValue method, ret1, prop_val, desc_val;
44798     int res, ret;
44799     JSObject *p;
44800     JSValueConst args[3];
44801     JSPropertyDescriptor desc;
44802     BOOL setting_not_configurable;
44803 
44804     s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty);
44805     if (!s)
44806         return -1;
44807     if (JS_IsUndefined(method)) {
44808         return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags);
44809     }
44810     prop_val = JS_AtomToValue(ctx, prop);
44811     if (JS_IsException(prop_val)) {
44812         JS_FreeValue(ctx, method);
44813         return -1;
44814     }
44815     desc_val = js_create_desc(ctx, val, getter, setter, flags);
44816     if (JS_IsException(desc_val)) {
44817         JS_FreeValue(ctx, prop_val);
44818         JS_FreeValue(ctx, method);
44819         return -1;
44820     }
44821     args[0] = s->target;
44822     args[1] = prop_val;
44823     args[2] = desc_val;
44824     ret1 = JS_CallFree(ctx, method, s->handler, 3, args);
44825     JS_FreeValue(ctx, prop_val);
44826     JS_FreeValue(ctx, desc_val);
44827     if (JS_IsException(ret1))
44828         return -1;
44829     ret = JS_ToBoolFree(ctx, ret1);
44830     if (!ret) {
44831         if (flags & JS_PROP_THROW) {
44832             JS_ThrowTypeError(ctx, "proxy: defineProperty exception");
44833             return -1;
44834         } else {
44835             return 0;
44836         }
44837     }
44838     p = JS_VALUE_GET_OBJ(s->target);
44839     res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
44840     if (res < 0)
44841         return -1;
44842     setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE |
44843                                           JS_PROP_CONFIGURABLE)) ==
44844                                 JS_PROP_HAS_CONFIGURABLE);
44845     if (!res) {
44846         if (!p->extensible || setting_not_configurable)
44847             goto fail;
44848     } else {
44849         if (!check_define_prop_flags(desc.flags, flags) ||
44850             ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) {
44851             goto fail1;
44852         }
44853         if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
44854             if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) ==
44855                 JS_PROP_GETSET) {
44856                 if ((flags & JS_PROP_HAS_GET) &&
44857                     !js_same_value(ctx, getter, desc.getter)) {
44858                     goto fail1;
44859                 }
44860                 if ((flags & JS_PROP_HAS_SET) &&
44861                     !js_same_value(ctx, setter, desc.setter)) {
44862                     goto fail1;
44863                 }
44864             }
44865         } else if (flags & JS_PROP_HAS_VALUE) {
44866             if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) ==
44867                 JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) {
44868                 /* missing-proxy-check feature */
44869                 goto fail1;
44870             } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&
44871                 !js_same_value(ctx, val, desc.value)) {
44872                 goto fail1;
44873             }
44874         }
44875         if (flags & JS_PROP_HAS_WRITABLE) {
44876             if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE |
44877                                JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) {
44878                 /* proxy-missing-checks */
44879             fail1:
44880                 js_free_desc(ctx, &desc);
44881             fail:
44882                 JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty");
44883                 return -1;
44884             }
44885         }
44886         js_free_desc(ctx, &desc);
44887     }
44888     return 1;
44889 }
44890 
js_proxy_delete_property(JSContext * ctx,JSValueConst obj,JSAtom atom)44891 static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj,
44892                                     JSAtom atom)
44893 {
44894     JSProxyData *s;
44895     JSValue method, ret, atom_val;
44896     int res, res2, is_extensible;
44897     JSValueConst args[2];
44898 
44899     s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty);
44900     if (!s)
44901         return -1;
44902     if (JS_IsUndefined(method)) {
44903         return JS_DeleteProperty(ctx, s->target, atom, 0);
44904     }
44905     atom_val = JS_AtomToValue(ctx, atom);;
44906     if (JS_IsException(atom_val)) {
44907         JS_FreeValue(ctx, method);
44908         return -1;
44909     }
44910     args[0] = s->target;
44911     args[1] = atom_val;
44912     ret = JS_CallFree(ctx, method, s->handler, 2, args);
44913     JS_FreeValue(ctx, atom_val);
44914     if (JS_IsException(ret))
44915         return -1;
44916     res = JS_ToBoolFree(ctx, ret);
44917     if (res) {
44918         JSPropertyDescriptor desc;
44919         res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
44920         if (res2 < 0)
44921             return -1;
44922         if (res2) {
44923             if (!(desc.flags & JS_PROP_CONFIGURABLE))
44924                 goto fail;
44925             is_extensible = JS_IsExtensible(ctx, s->target);
44926             if (is_extensible < 0)
44927                 goto fail1;
44928             if (!is_extensible) {
44929                 /* proxy-missing-checks */
44930             fail:
44931                 JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty");
44932             fail1:
44933                 js_free_desc(ctx, &desc);
44934                 return -1;
44935             }
44936             js_free_desc(ctx, &desc);
44937         }
44938     }
44939     return res;
44940 }
44941 
44942 /* return the index of the property or -1 if not found */
find_prop_key(const JSPropertyEnum * tab,int n,JSAtom atom)44943 static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom)
44944 {
44945     int i;
44946     for(i = 0; i < n; i++) {
44947         if (tab[i].atom == atom)
44948             return i;
44949     }
44950     return -1;
44951 }
44952 
js_proxy_get_own_property_names(JSContext * ctx,JSPropertyEnum ** ptab,uint32_t * plen,JSValueConst obj)44953 static int js_proxy_get_own_property_names(JSContext *ctx,
44954                                            JSPropertyEnum **ptab,
44955                                            uint32_t *plen,
44956                                            JSValueConst obj)
44957 {
44958     JSProxyData *s;
44959     JSValue method, prop_array, val;
44960     uint32_t len, i, len2;
44961     JSPropertyEnum *tab, *tab2;
44962     JSAtom atom;
44963     JSPropertyDescriptor desc;
44964     int res, is_extensible, idx;
44965 
44966     s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys);
44967     if (!s)
44968         return -1;
44969     if (JS_IsUndefined(method)) {
44970         return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
44971                                       JS_VALUE_GET_OBJ(s->target),
44972                                       JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK);
44973     }
44974     prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
44975     if (JS_IsException(prop_array))
44976         return -1;
44977     tab = NULL;
44978     len = 0;
44979     tab2 = NULL;
44980     len2 = 0;
44981     if (js_get_length32(ctx, &len, prop_array))
44982         goto fail;
44983     if (len > 0) {
44984         tab = js_mallocz(ctx, sizeof(tab[0]) * len);
44985         if (!tab)
44986             goto fail;
44987     }
44988     for(i = 0; i < len; i++) {
44989         val = JS_GetPropertyUint32(ctx, prop_array, i);
44990         if (JS_IsException(val))
44991             goto fail;
44992         if (!JS_IsString(val) && !JS_IsSymbol(val)) {
44993             JS_FreeValue(ctx, val);
44994             JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols");
44995             goto fail;
44996         }
44997         atom = JS_ValueToAtom(ctx, val);
44998         JS_FreeValue(ctx, val);
44999         if (atom == JS_ATOM_NULL)
45000             goto fail;
45001         tab[i].atom = atom;
45002         tab[i].is_enumerable = FALSE; /* XXX: redundant? */
45003     }
45004 
45005     /* check duplicate properties (XXX: inefficient, could store the
45006      * properties an a temporary object to use the hash) */
45007     for(i = 1; i < len; i++) {
45008         if (find_prop_key(tab, i, tab[i].atom) >= 0) {
45009             JS_ThrowTypeError(ctx, "proxy: duplicate property");
45010             goto fail;
45011         }
45012     }
45013 
45014     is_extensible = JS_IsExtensible(ctx, s->target);
45015     if (is_extensible < 0)
45016         goto fail;
45017 
45018     /* check if there are non configurable properties */
45019     if (s->is_revoked) {
45020         JS_ThrowTypeErrorRevokedProxy(ctx);
45021         goto fail;
45022     }
45023     if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target),
45024                                JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
45025         goto fail;
45026     for(i = 0; i < len2; i++) {
45027         if (s->is_revoked) {
45028             JS_ThrowTypeErrorRevokedProxy(ctx);
45029             goto fail;
45030         }
45031         res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target),
45032                                 tab2[i].atom);
45033         if (res < 0)
45034             goto fail;
45035         if (res) {  /* safety, property should be found */
45036             js_free_desc(ctx, &desc);
45037             if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) {
45038                 idx = find_prop_key(tab, len, tab2[i].atom);
45039                 if (idx < 0) {
45040                     JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys");
45041                     goto fail;
45042                 }
45043                 /* mark the property as found */
45044                 if (!is_extensible)
45045                     tab[idx].is_enumerable = TRUE;
45046             }
45047         }
45048     }
45049     if (!is_extensible) {
45050         /* check that all property in 'tab' were checked */
45051         for(i = 0; i < len; i++) {
45052             if (!tab[i].is_enumerable) {
45053                 JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy");
45054                 goto fail;
45055             }
45056         }
45057     }
45058 
45059     js_free_prop_enum(ctx, tab2, len2);
45060     JS_FreeValue(ctx, prop_array);
45061     *ptab = tab;
45062     *plen = len;
45063     return 0;
45064  fail:
45065     js_free_prop_enum(ctx, tab2, len2);
45066     js_free_prop_enum(ctx, tab, len);
45067     JS_FreeValue(ctx, prop_array);
45068     return -1;
45069 }
45070 
js_proxy_call_constructor(JSContext * ctx,JSValueConst func_obj,JSValueConst new_target,int argc,JSValueConst * argv)45071 static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj,
45072                                          JSValueConst new_target,
45073                                          int argc, JSValueConst *argv)
45074 {
45075     JSProxyData *s;
45076     JSValue method, arg_array, ret;
45077     JSValueConst args[3];
45078 
45079     s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct);
45080     if (!s)
45081         return JS_EXCEPTION;
45082     if (!JS_IsConstructor(ctx, s->target))
45083         return JS_ThrowTypeError(ctx, "not a constructor");
45084     if (JS_IsUndefined(method))
45085         return JS_CallConstructor2(ctx, s->target, new_target, argc, argv);
45086     arg_array = js_create_array(ctx, argc, argv);
45087     if (JS_IsException(arg_array)) {
45088         ret = JS_EXCEPTION;
45089         goto fail;
45090     }
45091     args[0] = s->target;
45092     args[1] = arg_array;
45093     args[2] = new_target;
45094     ret = JS_Call(ctx, method, s->handler, 3, args);
45095     if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
45096         JS_FreeValue(ctx, ret);
45097         ret = JS_ThrowTypeErrorNotAnObject(ctx);
45098     }
45099  fail:
45100     JS_FreeValue(ctx, method);
45101     JS_FreeValue(ctx, arg_array);
45102     return ret;
45103 }
45104 
js_proxy_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_obj,int argc,JSValueConst * argv,int flags)45105 static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj,
45106                              JSValueConst this_obj,
45107                              int argc, JSValueConst *argv, int flags)
45108 {
45109     JSProxyData *s;
45110     JSValue method, arg_array, ret;
45111     JSValueConst args[3];
45112 
45113     if (flags & JS_CALL_FLAG_CONSTRUCTOR)
45114         return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv);
45115 
45116     s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply);
45117     if (!s)
45118         return JS_EXCEPTION;
45119     if (!s->is_func) {
45120         JS_FreeValue(ctx, method);
45121         return JS_ThrowTypeError(ctx, "not a function");
45122     }
45123     if (JS_IsUndefined(method))
45124         return JS_Call(ctx, s->target, this_obj, argc, argv);
45125     arg_array = js_create_array(ctx, argc, argv);
45126     if (JS_IsException(arg_array)) {
45127         ret = JS_EXCEPTION;
45128         goto fail;
45129     }
45130     args[0] = s->target;
45131     args[1] = this_obj;
45132     args[2] = arg_array;
45133     ret = JS_Call(ctx, method, s->handler, 3, args);
45134  fail:
45135     JS_FreeValue(ctx, method);
45136     JS_FreeValue(ctx, arg_array);
45137     return ret;
45138 }
45139 
js_proxy_isArray(JSContext * ctx,JSValueConst obj)45140 static int js_proxy_isArray(JSContext *ctx, JSValueConst obj)
45141 {
45142     JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);
45143     if (!s)
45144         return FALSE;
45145     if (s->is_revoked) {
45146         JS_ThrowTypeErrorRevokedProxy(ctx);
45147         return -1;
45148     }
45149     return JS_IsArray(ctx, s->target);
45150 }
45151 
45152 static const JSClassExoticMethods js_proxy_exotic_methods = {
45153     .get_own_property = js_proxy_get_own_property,
45154     .define_own_property = js_proxy_define_own_property,
45155     .delete_property = js_proxy_delete_property,
45156     .get_own_property_names = js_proxy_get_own_property_names,
45157     .has_property = js_proxy_has,
45158     .get_property = js_proxy_get,
45159     .set_property = js_proxy_set,
45160 };
45161 
js_proxy_constructor(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)45162 static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val,
45163                                     int argc, JSValueConst *argv)
45164 {
45165     JSValueConst target, handler;
45166     JSValue obj;
45167     JSProxyData *s;
45168 
45169     target = argv[0];
45170     handler = argv[1];
45171     if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT ||
45172         JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT)
45173         return JS_ThrowTypeErrorNotAnObject(ctx);
45174 
45175     obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY);
45176     if (JS_IsException(obj))
45177         return obj;
45178     s = js_malloc(ctx, sizeof(JSProxyData));
45179     if (!s) {
45180         JS_FreeValue(ctx, obj);
45181         return JS_EXCEPTION;
45182     }
45183     s->target = JS_DupValue(ctx, target);
45184     s->handler = JS_DupValue(ctx, handler);
45185     s->is_func = JS_IsFunction(ctx, target);
45186     s->is_revoked = FALSE;
45187     JS_SetOpaque(obj, s);
45188     JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target));
45189     return obj;
45190 }
45191 
js_proxy_revoke(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)45192 static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val,
45193                                int argc, JSValueConst *argv, int magic,
45194                                JSValue *func_data)
45195 {
45196     JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY);
45197     if (s) {
45198         /* We do not free the handler and target in case they are
45199            referenced as constants in the C call stack */
45200         s->is_revoked = TRUE;
45201         JS_FreeValue(ctx, func_data[0]);
45202         func_data[0] = JS_NULL;
45203     }
45204     return JS_UNDEFINED;
45205 }
45206 
js_proxy_revoke_constructor(JSContext * ctx,JSValueConst proxy_obj)45207 static JSValue js_proxy_revoke_constructor(JSContext *ctx,
45208                                            JSValueConst proxy_obj)
45209 {
45210     return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj);
45211 }
45212 
js_proxy_revocable(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)45213 static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val,
45214                                  int argc, JSValueConst *argv)
45215 {
45216     JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj;
45217 
45218     proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv);
45219     if (JS_IsException(proxy_obj))
45220         goto fail;
45221     revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj);
45222     if (JS_IsException(revoke_obj))
45223         goto fail;
45224     obj = JS_NewObject(ctx);
45225     if (JS_IsException(obj))
45226         goto fail;
45227     // XXX: exceptions?
45228     JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E);
45229     JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E);
45230     return obj;
45231  fail:
45232     JS_FreeValue(ctx, proxy_obj);
45233     JS_FreeValue(ctx, revoke_obj);
45234     return JS_EXCEPTION;
45235 }
45236 
45237 static const JSCFunctionListEntry js_proxy_funcs[] = {
45238     JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ),
45239 };
45240 
45241 static const JSClassShortDef js_proxy_class_def[] = {
45242     { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */
45243 };
45244 
JS_AddIntrinsicProxy(JSContext * ctx)45245 void JS_AddIntrinsicProxy(JSContext *ctx)
45246 {
45247     JSRuntime *rt = ctx->rt;
45248     JSValue obj1;
45249 
45250     if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) {
45251         init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY,
45252                          countof(js_proxy_class_def));
45253         rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods;
45254         rt->class_array[JS_CLASS_PROXY].call = js_proxy_call;
45255     }
45256 
45257     obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2,
45258                             JS_CFUNC_constructor, 0);
45259     JS_SetConstructorBit(ctx, obj1, TRUE);
45260     JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs,
45261                                countof(js_proxy_funcs));
45262     JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy",
45263                               obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
45264 }
45265 
45266 /* Symbol */
45267 
js_symbol_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)45268 static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target,
45269                                      int argc, JSValueConst *argv)
45270 {
45271     JSValue str;
45272     JSString *p;
45273 
45274     if (!JS_IsUndefined(new_target))
45275         return JS_ThrowTypeError(ctx, "not a constructor");
45276     if (argc == 0 || JS_IsUndefined(argv[0])) {
45277         p = NULL;
45278     } else {
45279         str = JS_ToString(ctx, argv[0]);
45280         if (JS_IsException(str))
45281             return JS_EXCEPTION;
45282         p = JS_VALUE_GET_STRING(str);
45283     }
45284     return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL);
45285 }
45286 
js_thisSymbolValue(JSContext * ctx,JSValueConst this_val)45287 static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val)
45288 {
45289     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL)
45290         return JS_DupValue(ctx, this_val);
45291 
45292     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
45293         JSObject *p = JS_VALUE_GET_OBJ(this_val);
45294         if (p->class_id == JS_CLASS_SYMBOL) {
45295             if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL)
45296                 return JS_DupValue(ctx, p->u.object_data);
45297         }
45298     }
45299     return JS_ThrowTypeError(ctx, "not a symbol");
45300 }
45301 
js_symbol_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)45302 static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val,
45303                                   int argc, JSValueConst *argv)
45304 {
45305     JSValue val, ret;
45306     val = js_thisSymbolValue(ctx, this_val);
45307     if (JS_IsException(val))
45308         return val;
45309     /* XXX: use JS_ToStringInternal() with a flags */
45310     ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val);
45311     JS_FreeValue(ctx, val);
45312     return ret;
45313 }
45314 
js_symbol_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)45315 static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val,
45316                                  int argc, JSValueConst *argv)
45317 {
45318     return js_thisSymbolValue(ctx, this_val);
45319 }
45320 
js_symbol_get_description(JSContext * ctx,JSValueConst this_val)45321 static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val)
45322 {
45323     JSValue val, ret;
45324     JSAtomStruct *p;
45325 
45326     val = js_thisSymbolValue(ctx, this_val);
45327     if (JS_IsException(val))
45328         return val;
45329     p = JS_VALUE_GET_PTR(val);
45330     if (p->len == 0 && p->is_wide_char != 0) {
45331         ret = JS_UNDEFINED;
45332     } else {
45333         ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p));
45334     }
45335     JS_FreeValue(ctx, val);
45336     return ret;
45337 }
45338 
45339 static const JSCFunctionListEntry js_symbol_proto_funcs[] = {
45340     JS_CFUNC_DEF("toString", 0, js_symbol_toString ),
45341     JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ),
45342     // XXX: should have writable: false
45343     JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ),
45344     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ),
45345     JS_CGETSET_DEF("description", js_symbol_get_description, NULL ),
45346 };
45347 
js_symbol_for(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)45348 static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val,
45349                              int argc, JSValueConst *argv)
45350 {
45351     JSValue str;
45352 
45353     str = JS_ToString(ctx, argv[0]);
45354     if (JS_IsException(str))
45355         return JS_EXCEPTION;
45356     return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL);
45357 }
45358 
js_symbol_keyFor(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)45359 static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val,
45360                                 int argc, JSValueConst *argv)
45361 {
45362     JSAtomStruct *p;
45363 
45364     if (!JS_IsSymbol(argv[0]))
45365         return JS_ThrowTypeError(ctx, "not a symbol");
45366     p = JS_VALUE_GET_PTR(argv[0]);
45367     if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL)
45368         return JS_UNDEFINED;
45369     return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
45370 }
45371 
45372 static const JSCFunctionListEntry js_symbol_funcs[] = {
45373     JS_CFUNC_DEF("for", 1, js_symbol_for ),
45374     JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ),
45375 };
45376 
45377 /* Set/Map/WeakSet/WeakMap */
45378 
45379 typedef struct JSMapRecord {
45380     int ref_count; /* used during enumeration to avoid freeing the record */
45381     BOOL empty; /* TRUE if the record is deleted */
45382     struct JSMapState *map;
45383     struct JSMapRecord *next_weak_ref;
45384     struct list_head link;
45385     struct list_head hash_link;
45386     JSValue key;
45387     JSValue value;
45388 } JSMapRecord;
45389 
45390 typedef struct JSMapState {
45391     BOOL is_weak; /* TRUE if WeakSet/WeakMap */
45392     struct list_head records; /* list of JSMapRecord.link */
45393     uint32_t record_count;
45394     struct list_head *hash_table;
45395     uint32_t hash_size; /* must be a power of two */
45396     uint32_t record_count_threshold; /* count at which a hash table
45397                                         resize is needed */
45398 } JSMapState;
45399 
45400 #define MAGIC_SET (1 << 0)
45401 #define MAGIC_WEAK (1 << 1)
45402 
js_map_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv,int magic)45403 static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
45404                                   int argc, JSValueConst *argv, int magic)
45405 {
45406     JSMapState *s;
45407     JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;
45408     JSValueConst arr;
45409     BOOL is_set, is_weak;
45410 
45411     is_set = magic & MAGIC_SET;
45412     is_weak = ((magic & MAGIC_WEAK) != 0);
45413     obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic);
45414     if (JS_IsException(obj))
45415         return JS_EXCEPTION;
45416     s = js_mallocz(ctx, sizeof(*s));
45417     if (!s)
45418         goto fail;
45419     init_list_head(&s->records);
45420     s->is_weak = is_weak;
45421     JS_SetOpaque(obj, s);
45422     s->hash_size = 1;
45423     s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size);
45424     if (!s->hash_table)
45425         goto fail;
45426     init_list_head(&s->hash_table[0]);
45427     s->record_count_threshold = 4;
45428 
45429     arr = JS_UNDEFINED;
45430     if (argc > 0)
45431         arr = argv[0];
45432     if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) {
45433         JSValue item, ret;
45434         BOOL done;
45435 
45436         adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set);
45437         if (JS_IsException(adder))
45438             goto fail;
45439         if (!JS_IsFunction(ctx, adder)) {
45440             JS_ThrowTypeError(ctx, "set/add is not a function");
45441             goto fail;
45442         }
45443 
45444         iter = JS_GetIterator(ctx, arr, FALSE);
45445         if (JS_IsException(iter))
45446             goto fail;
45447         next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
45448         if (JS_IsException(next_method))
45449             goto fail;
45450 
45451         for(;;) {
45452             item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
45453             if (JS_IsException(item))
45454                 goto fail;
45455             if (done) {
45456                 JS_FreeValue(ctx, item);
45457                 break;
45458             }
45459             if (is_set) {
45460                 ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item);
45461                 if (JS_IsException(ret)) {
45462                     JS_FreeValue(ctx, item);
45463                     goto fail;
45464                 }
45465             } else {
45466                 JSValue key, value;
45467                 JSValueConst args[2];
45468                 key = JS_UNDEFINED;
45469                 value = JS_UNDEFINED;
45470                 if (!JS_IsObject(item)) {
45471                     JS_ThrowTypeErrorNotAnObject(ctx);
45472                     goto fail1;
45473                 }
45474                 key = JS_GetPropertyUint32(ctx, item, 0);
45475                 if (JS_IsException(key))
45476                     goto fail1;
45477                 value = JS_GetPropertyUint32(ctx, item, 1);
45478                 if (JS_IsException(value))
45479                     goto fail1;
45480                 args[0] = key;
45481                 args[1] = value;
45482                 ret = JS_Call(ctx, adder, obj, 2, args);
45483                 if (JS_IsException(ret)) {
45484                 fail1:
45485                     JS_FreeValue(ctx, item);
45486                     JS_FreeValue(ctx, key);
45487                     JS_FreeValue(ctx, value);
45488                     goto fail;
45489                 }
45490                 JS_FreeValue(ctx, key);
45491                 JS_FreeValue(ctx, value);
45492             }
45493             JS_FreeValue(ctx, ret);
45494             JS_FreeValue(ctx, item);
45495         }
45496         JS_FreeValue(ctx, next_method);
45497         JS_FreeValue(ctx, iter);
45498         JS_FreeValue(ctx, adder);
45499     }
45500     return obj;
45501  fail:
45502     if (JS_IsObject(iter)) {
45503         /* close the iterator object, preserving pending exception */
45504         JS_IteratorClose(ctx, iter, TRUE);
45505     }
45506     JS_FreeValue(ctx, next_method);
45507     JS_FreeValue(ctx, iter);
45508     JS_FreeValue(ctx, adder);
45509     JS_FreeValue(ctx, obj);
45510     return JS_EXCEPTION;
45511 }
45512 
45513 /* XXX: could normalize strings to speed up comparison */
map_normalize_key(JSContext * ctx,JSValueConst key)45514 static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)
45515 {
45516     uint32_t tag = JS_VALUE_GET_TAG(key);
45517     /* convert -0.0 to +0.0 */
45518     if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) {
45519         key = JS_NewInt32(ctx, 0);
45520     }
45521     return key;
45522 }
45523 
45524 /* XXX: better hash ? */
map_hash_key(JSContext * ctx,JSValueConst key)45525 static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)
45526 {
45527     uint32_t tag = JS_VALUE_GET_NORM_TAG(key);
45528     uint32_t h;
45529     double d;
45530     JSFloat64Union u;
45531 
45532     switch(tag) {
45533     case JS_TAG_BOOL:
45534         h = JS_VALUE_GET_INT(key);
45535         break;
45536     case JS_TAG_STRING:
45537         h = hash_string(JS_VALUE_GET_STRING(key), 0);
45538         break;
45539     case JS_TAG_OBJECT:
45540     case JS_TAG_SYMBOL:
45541         h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163;
45542         break;
45543     case JS_TAG_INT:
45544         d = JS_VALUE_GET_INT(key) * 3163;
45545         goto hash_float64;
45546     case JS_TAG_FLOAT64:
45547         d = JS_VALUE_GET_FLOAT64(key);
45548         /* normalize the NaN */
45549         if (isnan(d))
45550             d = JS_FLOAT64_NAN;
45551     hash_float64:
45552         u.d = d;
45553         h = (u.u32[0] ^ u.u32[1]) * 3163;
45554         break;
45555     default:
45556         h = 0; /* XXX: bignum support */
45557         break;
45558     }
45559     h ^= tag;
45560     return h;
45561 }
45562 
map_find_record(JSContext * ctx,JSMapState * s,JSValueConst key)45563 static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
45564                                     JSValueConst key)
45565 {
45566     struct list_head *el;
45567     JSMapRecord *mr;
45568     uint32_t h;
45569     h = map_hash_key(ctx, key) & (s->hash_size - 1);
45570     list_for_each(el, &s->hash_table[h]) {
45571         mr = list_entry(el, JSMapRecord, hash_link);
45572         if (js_same_value_zero(ctx, mr->key, key))
45573             return mr;
45574     }
45575     return NULL;
45576 }
45577 
map_hash_resize(JSContext * ctx,JSMapState * s)45578 static void map_hash_resize(JSContext *ctx, JSMapState *s)
45579 {
45580     uint32_t new_hash_size, i, h;
45581     size_t slack;
45582     struct list_head *new_hash_table, *el;
45583     JSMapRecord *mr;
45584 
45585     /* XXX: no reporting of memory allocation failure */
45586     if (s->hash_size == 1)
45587         new_hash_size = 4;
45588     else
45589         new_hash_size = s->hash_size * 2;
45590     new_hash_table = js_realloc2(ctx, s->hash_table,
45591                                  sizeof(new_hash_table[0]) * new_hash_size, &slack);
45592     if (!new_hash_table)
45593         return;
45594     new_hash_size += slack / sizeof(*new_hash_table);
45595 
45596     for(i = 0; i < new_hash_size; i++)
45597         init_list_head(&new_hash_table[i]);
45598 
45599     list_for_each(el, &s->records) {
45600         mr = list_entry(el, JSMapRecord, link);
45601         if (!mr->empty) {
45602             h = map_hash_key(ctx, mr->key) & (new_hash_size - 1);
45603             list_add_tail(&mr->hash_link, &new_hash_table[h]);
45604         }
45605     }
45606     s->hash_table = new_hash_table;
45607     s->hash_size = new_hash_size;
45608     s->record_count_threshold = new_hash_size * 2;
45609 }
45610 
map_add_record(JSContext * ctx,JSMapState * s,JSValueConst key)45611 static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
45612                                    JSValueConst key)
45613 {
45614     uint32_t h;
45615     JSMapRecord *mr;
45616 
45617     mr = js_malloc(ctx, sizeof(*mr));
45618     if (!mr)
45619         return NULL;
45620     mr->ref_count = 1;
45621     mr->map = s;
45622     mr->empty = FALSE;
45623     if (s->is_weak) {
45624         JSObject *p = JS_VALUE_GET_OBJ(key);
45625         /* Add the weak reference */
45626         mr->next_weak_ref = p->first_weak_ref;
45627         p->first_weak_ref = mr;
45628     } else {
45629         JS_DupValue(ctx, key);
45630     }
45631     mr->key = (JSValue)key;
45632     h = map_hash_key(ctx, key) & (s->hash_size - 1);
45633     list_add_tail(&mr->hash_link, &s->hash_table[h]);
45634     list_add_tail(&mr->link, &s->records);
45635     s->record_count++;
45636     if (s->record_count >= s->record_count_threshold) {
45637         map_hash_resize(ctx, s);
45638     }
45639     return mr;
45640 }
45641 
45642 /* Remove the weak reference from the object weak
45643    reference list. we don't use a doubly linked list to
45644    save space, assuming a given object has few weak
45645        references to it */
delete_weak_ref(JSRuntime * rt,JSMapRecord * mr)45646 static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)
45647 {
45648     JSMapRecord **pmr, *mr1;
45649     JSObject *p;
45650 
45651     p = JS_VALUE_GET_OBJ(mr->key);
45652     pmr = &p->first_weak_ref;
45653     for(;;) {
45654         mr1 = *pmr;
45655         assert(mr1 != NULL);
45656         if (mr1 == mr)
45657             break;
45658         pmr = &mr1->next_weak_ref;
45659     }
45660     *pmr = mr1->next_weak_ref;
45661 }
45662 
map_delete_record(JSRuntime * rt,JSMapState * s,JSMapRecord * mr)45663 static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
45664 {
45665     if (mr->empty)
45666         return;
45667     list_del(&mr->hash_link);
45668     if (s->is_weak) {
45669         delete_weak_ref(rt, mr);
45670     } else {
45671         JS_FreeValueRT(rt, mr->key);
45672     }
45673     JS_FreeValueRT(rt, mr->value);
45674     if (--mr->ref_count == 0) {
45675         list_del(&mr->link);
45676         js_free_rt(rt, mr);
45677     } else {
45678         /* keep a zombie record for iterators */
45679         mr->empty = TRUE;
45680         mr->key = JS_UNDEFINED;
45681         mr->value = JS_UNDEFINED;
45682     }
45683     s->record_count--;
45684 }
45685 
map_decref_record(JSRuntime * rt,JSMapRecord * mr)45686 static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
45687 {
45688     if (--mr->ref_count == 0) {
45689         /* the record can be safely removed */
45690         assert(mr->empty);
45691         list_del(&mr->link);
45692         js_free_rt(rt, mr);
45693     }
45694 }
45695 
reset_weak_ref(JSRuntime * rt,JSObject * p)45696 static void reset_weak_ref(JSRuntime *rt, JSObject *p)
45697 {
45698     JSMapRecord *mr, *mr_next;
45699     JSMapState *s;
45700 
45701     /* first pass to remove the records from the WeakMap/WeakSet
45702        lists */
45703     for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) {
45704         s = mr->map;
45705         assert(s->is_weak);
45706         assert(!mr->empty); /* no iterator on WeakMap/WeakSet */
45707         list_del(&mr->hash_link);
45708         list_del(&mr->link);
45709     }
45710 
45711     /* second pass to free the values to avoid modifying the weak
45712        reference list while traversing it. */
45713     for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) {
45714         mr_next = mr->next_weak_ref;
45715         JS_FreeValueRT(rt, mr->value);
45716         js_free_rt(rt, mr);
45717     }
45718 
45719     p->first_weak_ref = NULL; /* fail safe */
45720 }
45721 
js_map_set(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)45722 static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,
45723                           int argc, JSValueConst *argv, int magic)
45724 {
45725     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45726     JSMapRecord *mr;
45727     JSValueConst key, value;
45728 
45729     if (!s)
45730         return JS_EXCEPTION;
45731     key = map_normalize_key(ctx, argv[0]);
45732     if (s->is_weak && !JS_IsObject(key))
45733         return JS_ThrowTypeErrorNotAnObject(ctx);
45734     if (magic & MAGIC_SET)
45735         value = JS_UNDEFINED;
45736     else
45737         value = argv[1];
45738     mr = map_find_record(ctx, s, key);
45739     if (mr) {
45740         JS_FreeValue(ctx, mr->value);
45741     } else {
45742         mr = map_add_record(ctx, s, key);
45743         if (!mr)
45744             return JS_EXCEPTION;
45745     }
45746     mr->value = JS_DupValue(ctx, value);
45747     return JS_DupValue(ctx, this_val);
45748 }
45749 
js_map_get(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)45750 static JSValue js_map_get(JSContext *ctx, JSValueConst this_val,
45751                           int argc, JSValueConst *argv, int magic)
45752 {
45753     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45754     JSMapRecord *mr;
45755     JSValueConst key;
45756 
45757     if (!s)
45758         return JS_EXCEPTION;
45759     key = map_normalize_key(ctx, argv[0]);
45760     mr = map_find_record(ctx, s, key);
45761     if (!mr)
45762         return JS_UNDEFINED;
45763     else
45764         return JS_DupValue(ctx, mr->value);
45765 }
45766 
js_map_has(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)45767 static JSValue js_map_has(JSContext *ctx, JSValueConst this_val,
45768                           int argc, JSValueConst *argv, int magic)
45769 {
45770     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45771     JSMapRecord *mr;
45772     JSValueConst key;
45773 
45774     if (!s)
45775         return JS_EXCEPTION;
45776     key = map_normalize_key(ctx, argv[0]);
45777     mr = map_find_record(ctx, s, key);
45778     return JS_NewBool(ctx, (mr != NULL));
45779 }
45780 
js_map_delete(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)45781 static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,
45782                              int argc, JSValueConst *argv, int magic)
45783 {
45784     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45785     JSMapRecord *mr;
45786     JSValueConst key;
45787 
45788     if (!s)
45789         return JS_EXCEPTION;
45790     key = map_normalize_key(ctx, argv[0]);
45791     mr = map_find_record(ctx, s, key);
45792     if (!mr)
45793         return JS_FALSE;
45794     map_delete_record(ctx->rt, s, mr);
45795     return JS_TRUE;
45796 }
45797 
js_map_clear(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)45798 static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,
45799                             int argc, JSValueConst *argv, int magic)
45800 {
45801     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45802     struct list_head *el, *el1;
45803     JSMapRecord *mr;
45804 
45805     if (!s)
45806         return JS_EXCEPTION;
45807     list_for_each_safe(el, el1, &s->records) {
45808         mr = list_entry(el, JSMapRecord, link);
45809         map_delete_record(ctx->rt, s, mr);
45810     }
45811     return JS_UNDEFINED;
45812 }
45813 
js_map_get_size(JSContext * ctx,JSValueConst this_val,int magic)45814 static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic)
45815 {
45816     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45817     if (!s)
45818         return JS_EXCEPTION;
45819     return JS_NewUint32(ctx, s->record_count);
45820 }
45821 
js_map_forEach(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)45822 static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,
45823                               int argc, JSValueConst *argv, int magic)
45824 {
45825     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45826     JSValueConst func, this_arg;
45827     JSValue ret, args[3];
45828     struct list_head *el;
45829     JSMapRecord *mr;
45830 
45831     if (!s)
45832         return JS_EXCEPTION;
45833     func = argv[0];
45834     if (argc > 1)
45835         this_arg = argv[1];
45836     else
45837         this_arg = JS_UNDEFINED;
45838     if (check_function(ctx, func))
45839         return JS_EXCEPTION;
45840     /* Note: the list can be modified while traversing it, but the
45841        current element is locked */
45842     el = s->records.next;
45843     while (el != &s->records) {
45844         mr = list_entry(el, JSMapRecord, link);
45845         if (!mr->empty) {
45846             mr->ref_count++;
45847             /* must duplicate in case the record is deleted */
45848             args[1] = JS_DupValue(ctx, mr->key);
45849             if (magic)
45850                 args[0] = args[1];
45851             else
45852                 args[0] = JS_DupValue(ctx, mr->value);
45853             args[2] = (JSValue)this_val;
45854             ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);
45855             JS_FreeValue(ctx, args[0]);
45856             if (!magic)
45857                 JS_FreeValue(ctx, args[1]);
45858             el = el->next;
45859             map_decref_record(ctx->rt, mr);
45860             if (JS_IsException(ret))
45861                 return ret;
45862             JS_FreeValue(ctx, ret);
45863         } else {
45864             el = el->next;
45865         }
45866     }
45867     return JS_UNDEFINED;
45868 }
45869 
js_map_finalizer(JSRuntime * rt,JSValue val)45870 static void js_map_finalizer(JSRuntime *rt, JSValue val)
45871 {
45872     JSObject *p;
45873     JSMapState *s;
45874     struct list_head *el, *el1;
45875     JSMapRecord *mr;
45876 
45877     p = JS_VALUE_GET_OBJ(val);
45878     s = p->u.map_state;
45879     if (s) {
45880         /* if the object is deleted we are sure that no iterator is
45881            using it */
45882         list_for_each_safe(el, el1, &s->records) {
45883             mr = list_entry(el, JSMapRecord, link);
45884             if (!mr->empty) {
45885                 if (s->is_weak)
45886                     delete_weak_ref(rt, mr);
45887                 else
45888                     JS_FreeValueRT(rt, mr->key);
45889                 JS_FreeValueRT(rt, mr->value);
45890             }
45891             js_free_rt(rt, mr);
45892         }
45893         js_free_rt(rt, s->hash_table);
45894         js_free_rt(rt, s);
45895     }
45896 }
45897 
js_map_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)45898 static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
45899 {
45900     JSObject *p = JS_VALUE_GET_OBJ(val);
45901     JSMapState *s;
45902     struct list_head *el;
45903     JSMapRecord *mr;
45904 
45905     s = p->u.map_state;
45906     if (s) {
45907         list_for_each(el, &s->records) {
45908             mr = list_entry(el, JSMapRecord, link);
45909             if (!s->is_weak)
45910                 JS_MarkValue(rt, mr->key, mark_func);
45911             JS_MarkValue(rt, mr->value, mark_func);
45912         }
45913     }
45914 }
45915 
45916 /* Map Iterator */
45917 
45918 typedef struct JSMapIteratorData {
45919     JSValue obj;
45920     JSIteratorKindEnum kind;
45921     JSMapRecord *cur_record;
45922 } JSMapIteratorData;
45923 
js_map_iterator_finalizer(JSRuntime * rt,JSValue val)45924 static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val)
45925 {
45926     JSObject *p;
45927     JSMapIteratorData *it;
45928 
45929     p = JS_VALUE_GET_OBJ(val);
45930     it = p->u.map_iterator_data;
45931     if (it) {
45932         /* During the GC sweep phase the Map finalizer may be
45933            called before the Map iterator finalizer */
45934         if (JS_IsLiveObject(rt, it->obj) && it->cur_record) {
45935             map_decref_record(rt, it->cur_record);
45936         }
45937         JS_FreeValueRT(rt, it->obj);
45938         js_free_rt(rt, it);
45939     }
45940 }
45941 
js_map_iterator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)45942 static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
45943                                  JS_MarkFunc *mark_func)
45944 {
45945     JSObject *p = JS_VALUE_GET_OBJ(val);
45946     JSMapIteratorData *it;
45947     it = p->u.map_iterator_data;
45948     if (it) {
45949         /* the record is already marked by the object */
45950         JS_MarkValue(rt, it->obj, mark_func);
45951     }
45952 }
45953 
js_create_map_iterator(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)45954 static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val,
45955                                       int argc, JSValueConst *argv, int magic)
45956 {
45957     JSIteratorKindEnum kind;
45958     JSMapState *s;
45959     JSMapIteratorData *it;
45960     JSValue enum_obj;
45961 
45962     kind = magic >> 2;
45963     magic &= 3;
45964     s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
45965     if (!s)
45966         return JS_EXCEPTION;
45967     enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic);
45968     if (JS_IsException(enum_obj))
45969         goto fail;
45970     it = js_malloc(ctx, sizeof(*it));
45971     if (!it) {
45972         JS_FreeValue(ctx, enum_obj);
45973         goto fail;
45974     }
45975     it->obj = JS_DupValue(ctx, this_val);
45976     it->kind = kind;
45977     it->cur_record = NULL;
45978     JS_SetOpaque(enum_obj, it);
45979     return enum_obj;
45980  fail:
45981     return JS_EXCEPTION;
45982 }
45983 
js_map_iterator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,BOOL * pdone,int magic)45984 static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,
45985                                     int argc, JSValueConst *argv,
45986                                     BOOL *pdone, int magic)
45987 {
45988     JSMapIteratorData *it;
45989     JSMapState *s;
45990     JSMapRecord *mr;
45991     struct list_head *el;
45992 
45993     it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic);
45994     if (!it) {
45995         *pdone = FALSE;
45996         return JS_EXCEPTION;
45997     }
45998     if (JS_IsUndefined(it->obj))
45999         goto done;
46000     s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic);
46001     assert(s != NULL);
46002     if (!it->cur_record) {
46003         el = s->records.next;
46004     } else {
46005         mr = it->cur_record;
46006         el = mr->link.next;
46007         map_decref_record(ctx->rt, mr); /* the record can be freed here */
46008     }
46009     for(;;) {
46010         if (el == &s->records) {
46011             /* no more record  */
46012             it->cur_record = NULL;
46013             JS_FreeValue(ctx, it->obj);
46014             it->obj = JS_UNDEFINED;
46015         done:
46016             /* end of enumeration */
46017             *pdone = TRUE;
46018             return JS_UNDEFINED;
46019         }
46020         mr = list_entry(el, JSMapRecord, link);
46021         if (!mr->empty)
46022             break;
46023         /* get the next record */
46024         el = mr->link.next;
46025     }
46026 
46027     /* lock the record so that it won't be freed */
46028     mr->ref_count++;
46029     it->cur_record = mr;
46030     *pdone = FALSE;
46031 
46032     if (it->kind == JS_ITERATOR_KIND_KEY) {
46033         return JS_DupValue(ctx, mr->key);
46034     } else {
46035         JSValueConst args[2];
46036         args[0] = mr->key;
46037         if (magic)
46038             args[1] = mr->key;
46039         else
46040             args[1] = mr->value;
46041         if (it->kind == JS_ITERATOR_KIND_VALUE) {
46042             return JS_DupValue(ctx, args[1]);
46043         } else {
46044             return js_create_array(ctx, 2, args);
46045         }
46046     }
46047 }
46048 
46049 static const JSCFunctionListEntry js_map_funcs[] = {
46050     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
46051 };
46052 
46053 static const JSCFunctionListEntry js_map_proto_funcs[] = {
46054     JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ),
46055     JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ),
46056     JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ),
46057     JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ),
46058     JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ),
46059     JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0),
46060     JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ),
46061     JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ),
46062     JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ),
46063     JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ),
46064     JS_ALIAS_DEF("[Symbol.iterator]", "entries" ),
46065     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ),
46066 };
46067 
46068 static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = {
46069     JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ),
46070     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ),
46071 };
46072 
46073 static const JSCFunctionListEntry js_set_proto_funcs[] = {
46074     JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ),
46075     JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ),
46076     JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ),
46077     JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ),
46078     JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ),
46079     JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ),
46080     JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ),
46081     JS_ALIAS_DEF("keys", "values" ),
46082     JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
46083     JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ),
46084     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ),
46085 };
46086 
46087 static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {
46088     JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ),
46089     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ),
46090 };
46091 
46092 static const JSCFunctionListEntry js_weak_map_proto_funcs[] = {
46093     JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ),
46094     JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ),
46095     JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ),
46096     JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ),
46097     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ),
46098 };
46099 
46100 static const JSCFunctionListEntry js_weak_set_proto_funcs[] = {
46101     JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ),
46102     JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ),
46103     JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ),
46104     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ),
46105 };
46106 
46107 static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {
46108     js_map_proto_funcs,
46109     js_set_proto_funcs,
46110     js_weak_map_proto_funcs,
46111     js_weak_set_proto_funcs,
46112     js_map_iterator_proto_funcs,
46113     js_set_iterator_proto_funcs,
46114 };
46115 
46116 static const uint8_t js_map_proto_funcs_count[6] = {
46117     countof(js_map_proto_funcs),
46118     countof(js_set_proto_funcs),
46119     countof(js_weak_map_proto_funcs),
46120     countof(js_weak_set_proto_funcs),
46121     countof(js_map_iterator_proto_funcs),
46122     countof(js_set_iterator_proto_funcs),
46123 };
46124 
JS_AddIntrinsicMapSet(JSContext * ctx)46125 void JS_AddIntrinsicMapSet(JSContext *ctx)
46126 {
46127     int i;
46128     JSValue obj1;
46129     char buf[ATOM_GET_STR_BUF_SIZE];
46130 
46131     for(i = 0; i < 4; i++) {
46132         const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf),
46133                                          JS_ATOM_Map + i);
46134         ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx);
46135         JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i],
46136                                    js_map_proto_funcs_ptr[i],
46137                                    js_map_proto_funcs_count[i]);
46138         obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0,
46139                                     JS_CFUNC_constructor_magic, i);
46140         if (i < 2) {
46141             JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs,
46142                                        countof(js_map_funcs));
46143         }
46144         JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]);
46145     }
46146 
46147     for(i = 0; i < 2; i++) {
46148         ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] =
46149             JS_NewObjectProto(ctx, ctx->iterator_proto);
46150         JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i],
46151                                    js_map_proto_funcs_ptr[i + 4],
46152                                    js_map_proto_funcs_count[i + 4]);
46153     }
46154 }
46155 
46156 /* Generator */
46157 static const JSCFunctionListEntry js_generator_function_proto_funcs[] = {
46158     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE),
46159 };
46160 
46161 static const JSCFunctionListEntry js_generator_proto_funcs[] = {
46162     JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ),
46163     JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ),
46164     JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ),
46165     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE),
46166 };
46167 
46168 /* Promise */
46169 
46170 typedef enum JSPromiseStateEnum {
46171     JS_PROMISE_PENDING,
46172     JS_PROMISE_FULFILLED,
46173     JS_PROMISE_REJECTED,
46174 } JSPromiseStateEnum;
46175 
46176 typedef struct JSPromiseData {
46177     JSPromiseStateEnum promise_state;
46178     /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */
46179     struct list_head promise_reactions[2];
46180     BOOL is_handled; /* Note: only useful to debug */
46181     JSValue promise_result;
46182 } JSPromiseData;
46183 
46184 typedef struct JSPromiseFunctionDataResolved {
46185     int ref_count;
46186     BOOL already_resolved;
46187 } JSPromiseFunctionDataResolved;
46188 
46189 typedef struct JSPromiseFunctionData {
46190     JSValue promise;
46191     JSPromiseFunctionDataResolved *presolved;
46192 } JSPromiseFunctionData;
46193 
46194 typedef struct JSPromiseReactionData {
46195     struct list_head link; /* not used in promise_reaction_job */
46196     JSValue resolving_funcs[2];
46197     JSValue handler;
46198 } JSPromiseReactionData;
46199 
46200 static int js_create_resolving_functions(JSContext *ctx, JSValue *args,
46201                                          JSValueConst promise);
46202 
promise_reaction_data_free(JSRuntime * rt,JSPromiseReactionData * rd)46203 static void promise_reaction_data_free(JSRuntime *rt,
46204                                        JSPromiseReactionData *rd)
46205 {
46206     JS_FreeValueRT(rt, rd->resolving_funcs[0]);
46207     JS_FreeValueRT(rt, rd->resolving_funcs[1]);
46208     JS_FreeValueRT(rt, rd->handler);
46209     js_free_rt(rt, rd);
46210 }
46211 
promise_reaction_job(JSContext * ctx,int argc,JSValueConst * argv)46212 static JSValue promise_reaction_job(JSContext *ctx, int argc,
46213                                     JSValueConst *argv)
46214 {
46215     JSValueConst handler, arg, func;
46216     JSValue res, res2;
46217     BOOL is_reject;
46218 
46219     assert(argc == 5);
46220     handler = argv[2];
46221     is_reject = JS_ToBool(ctx, argv[3]);
46222     arg = argv[4];
46223 #ifdef DUMP_PROMISE
46224     printf("promise_reaction_job: is_reject=%d\n", is_reject);
46225 #endif
46226 
46227     if (JS_IsUndefined(handler)) {
46228         if (is_reject) {
46229             res = JS_Throw(ctx, JS_DupValue(ctx, arg));
46230         } else {
46231             res = JS_DupValue(ctx, arg);
46232         }
46233     } else {
46234         res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg);
46235     }
46236     is_reject = JS_IsException(res);
46237     if (is_reject)
46238         res = JS_GetException(ctx);
46239     func = argv[is_reject];
46240     /* as an extension, we support undefined as value to avoid
46241        creating a dummy promise in the 'await' implementation of async
46242        functions */
46243     if (!JS_IsUndefined(func)) {
46244         res2 = JS_Call(ctx, func, JS_UNDEFINED,
46245                        1, (JSValueConst *)&res);
46246     } else {
46247         res2 = JS_UNDEFINED;
46248     }
46249     JS_FreeValue(ctx, res);
46250 
46251     return res2;
46252 }
46253 
JS_SetHostPromiseRejectionTracker(JSRuntime * rt,JSHostPromiseRejectionTracker * cb,void * opaque)46254 void JS_SetHostPromiseRejectionTracker(JSRuntime *rt,
46255                                        JSHostPromiseRejectionTracker *cb,
46256                                        void *opaque)
46257 {
46258     rt->host_promise_rejection_tracker = cb;
46259     rt->host_promise_rejection_tracker_opaque = opaque;
46260 }
46261 
fulfill_or_reject_promise(JSContext * ctx,JSValueConst promise,JSValueConst value,BOOL is_reject)46262 static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise,
46263                                       JSValueConst value, BOOL is_reject)
46264 {
46265     JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
46266     struct list_head *el, *el1;
46267     JSPromiseReactionData *rd;
46268     JSValueConst args[5];
46269 
46270     if (!s || s->promise_state != JS_PROMISE_PENDING)
46271         return; /* should never happen */
46272     set_value(ctx, &s->promise_result, JS_DupValue(ctx, value));
46273     s->promise_state = JS_PROMISE_FULFILLED + is_reject;
46274 #ifdef DUMP_PROMISE
46275     printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject);
46276 #endif
46277     if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
46278         JSRuntime *rt = ctx->rt;
46279         if (rt->host_promise_rejection_tracker) {
46280             rt->host_promise_rejection_tracker(ctx, promise, value, FALSE,
46281                                                rt->host_promise_rejection_tracker_opaque);
46282         }
46283     }
46284 
46285     list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) {
46286         rd = list_entry(el, JSPromiseReactionData, link);
46287         args[0] = rd->resolving_funcs[0];
46288         args[1] = rd->resolving_funcs[1];
46289         args[2] = rd->handler;
46290         args[3] = JS_NewBool(ctx, is_reject);
46291         args[4] = value;
46292         JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
46293         list_del(&rd->link);
46294         promise_reaction_data_free(ctx->rt, rd);
46295     }
46296 
46297     list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) {
46298         rd = list_entry(el, JSPromiseReactionData, link);
46299         list_del(&rd->link);
46300         promise_reaction_data_free(ctx->rt, rd);
46301     }
46302 }
46303 
reject_promise(JSContext * ctx,JSValueConst promise,JSValueConst value)46304 static void reject_promise(JSContext *ctx, JSValueConst promise,
46305                            JSValueConst value)
46306 {
46307     fulfill_or_reject_promise(ctx, promise, value, TRUE);
46308 }
46309 
js_promise_resolve_thenable_job(JSContext * ctx,int argc,JSValueConst * argv)46310 static JSValue js_promise_resolve_thenable_job(JSContext *ctx,
46311                                                int argc, JSValueConst *argv)
46312 {
46313     JSValueConst promise, thenable, then;
46314     JSValue args[2], res;
46315 
46316 #ifdef DUMP_PROMISE
46317     printf("js_promise_resolve_thenable_job\n");
46318 #endif
46319     assert(argc == 3);
46320     promise = argv[0];
46321     thenable = argv[1];
46322     then = argv[2];
46323     if (js_create_resolving_functions(ctx, args, promise) < 0)
46324         return JS_EXCEPTION;
46325     res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args);
46326     if (JS_IsException(res)) {
46327         JSValue error = JS_GetException(ctx);
46328         res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
46329         JS_FreeValue(ctx, error);
46330     }
46331     JS_FreeValue(ctx, args[0]);
46332     JS_FreeValue(ctx, args[1]);
46333     return res;
46334 }
46335 
js_promise_resolve_function_free_resolved(JSRuntime * rt,JSPromiseFunctionDataResolved * sr)46336 static void js_promise_resolve_function_free_resolved(JSRuntime *rt,
46337                                                       JSPromiseFunctionDataResolved *sr)
46338 {
46339     if (--sr->ref_count == 0) {
46340         js_free_rt(rt, sr);
46341     }
46342 }
46343 
js_create_resolving_functions(JSContext * ctx,JSValue * resolving_funcs,JSValueConst promise)46344 static int js_create_resolving_functions(JSContext *ctx,
46345                                          JSValue *resolving_funcs,
46346                                          JSValueConst promise)
46347 
46348 {
46349     JSValue obj;
46350     JSPromiseFunctionData *s;
46351     JSPromiseFunctionDataResolved *sr;
46352     int i, ret;
46353 
46354     sr = js_malloc(ctx, sizeof(*sr));
46355     if (!sr)
46356         return -1;
46357     sr->ref_count = 1;
46358     sr->already_resolved = FALSE; /* must be shared between the two functions */
46359     ret = 0;
46360     for(i = 0; i < 2; i++) {
46361         obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
46362                                      JS_CLASS_PROMISE_RESOLVE_FUNCTION + i);
46363         if (JS_IsException(obj))
46364             goto fail;
46365         s = js_malloc(ctx, sizeof(*s));
46366         if (!s) {
46367             JS_FreeValue(ctx, obj);
46368         fail:
46369 
46370             if (i != 0)
46371                 JS_FreeValue(ctx, resolving_funcs[0]);
46372             ret = -1;
46373             break;
46374         }
46375         sr->ref_count++;
46376         s->presolved = sr;
46377         s->promise = JS_DupValue(ctx, promise);
46378         JS_SetOpaque(obj, s);
46379         js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1);
46380         resolving_funcs[i] = obj;
46381     }
46382     js_promise_resolve_function_free_resolved(ctx->rt, sr);
46383     return ret;
46384 }
46385 
js_promise_resolve_function_finalizer(JSRuntime * rt,JSValue val)46386 static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val)
46387 {
46388     JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
46389     if (s) {
46390         js_promise_resolve_function_free_resolved(rt, s->presolved);
46391         JS_FreeValueRT(rt, s->promise);
46392         js_free_rt(rt, s);
46393     }
46394 }
46395 
js_promise_resolve_function_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)46396 static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
46397                                              JS_MarkFunc *mark_func)
46398 {
46399     JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
46400     if (s) {
46401         JS_MarkValue(rt, s->promise, mark_func);
46402     }
46403 }
46404 
js_promise_resolve_function_call(JSContext * ctx,JSValueConst func_obj,JSValueConst this_val,int argc,JSValueConst * argv,int flags)46405 static JSValue js_promise_resolve_function_call(JSContext *ctx,
46406                                                 JSValueConst func_obj,
46407                                                 JSValueConst this_val,
46408                                                 int argc, JSValueConst *argv,
46409                                                 int flags)
46410 {
46411     JSObject *p = JS_VALUE_GET_OBJ(func_obj);
46412     JSPromiseFunctionData *s;
46413     JSValueConst resolution, args[3];
46414     JSValue then;
46415     BOOL is_reject;
46416 
46417     s = p->u.promise_function_data;
46418     if (!s || s->presolved->already_resolved)
46419         return JS_UNDEFINED;
46420     s->presolved->already_resolved = TRUE;
46421     is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION;
46422     if (argc > 0)
46423         resolution = argv[0];
46424     else
46425         resolution = JS_UNDEFINED;
46426 #ifdef DUMP_PROMISE
46427     printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject);
46428     JS_DumpValue(ctx, resolution);
46429     printf("\n");
46430 #endif
46431     if (is_reject || !JS_IsObject(resolution)) {
46432         goto done;
46433     } else if (js_same_value(ctx, resolution, s->promise)) {
46434         JS_ThrowTypeError(ctx, "promise self resolution");
46435         goto fail_reject;
46436     }
46437     then = JS_GetProperty(ctx, resolution, JS_ATOM_then);
46438     if (JS_IsException(then)) {
46439         JSValue error;
46440     fail_reject:
46441         error = JS_GetException(ctx);
46442         reject_promise(ctx, s->promise, error);
46443         JS_FreeValue(ctx, error);
46444     } else if (!JS_IsFunction(ctx, then)) {
46445         JS_FreeValue(ctx, then);
46446     done:
46447         fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject);
46448     } else {
46449         args[0] = s->promise;
46450         args[1] = resolution;
46451         args[2] = then;
46452         JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args);
46453         JS_FreeValue(ctx, then);
46454     }
46455     return JS_UNDEFINED;
46456 }
46457 
js_promise_finalizer(JSRuntime * rt,JSValue val)46458 static void js_promise_finalizer(JSRuntime *rt, JSValue val)
46459 {
46460     JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
46461     struct list_head *el, *el1;
46462     int i;
46463 
46464     if (!s)
46465         return;
46466     for(i = 0; i < 2; i++) {
46467         list_for_each_safe(el, el1, &s->promise_reactions[i]) {
46468             JSPromiseReactionData *rd =
46469                 list_entry(el, JSPromiseReactionData, link);
46470             promise_reaction_data_free(rt, rd);
46471         }
46472     }
46473     JS_FreeValueRT(rt, s->promise_result);
46474     js_free_rt(rt, s);
46475 }
46476 
js_promise_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)46477 static void js_promise_mark(JSRuntime *rt, JSValueConst val,
46478                             JS_MarkFunc *mark_func)
46479 {
46480     JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
46481     struct list_head *el;
46482     int i;
46483 
46484     if (!s)
46485         return;
46486     for(i = 0; i < 2; i++) {
46487         list_for_each(el, &s->promise_reactions[i]) {
46488             JSPromiseReactionData *rd =
46489                 list_entry(el, JSPromiseReactionData, link);
46490             JS_MarkValue(rt, rd->resolving_funcs[0], mark_func);
46491             JS_MarkValue(rt, rd->resolving_funcs[1], mark_func);
46492             JS_MarkValue(rt, rd->handler, mark_func);
46493         }
46494     }
46495     JS_MarkValue(rt, s->promise_result, mark_func);
46496 }
46497 
js_promise_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)46498 static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target,
46499                                       int argc, JSValueConst *argv)
46500 {
46501     JSValueConst executor;
46502     JSValue obj;
46503     JSPromiseData *s;
46504     JSValue args[2], ret;
46505     int i;
46506 
46507     executor = argv[0];
46508     if (check_function(ctx, executor))
46509         return JS_EXCEPTION;
46510     obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE);
46511     if (JS_IsException(obj))
46512         return JS_EXCEPTION;
46513     s = js_mallocz(ctx, sizeof(*s));
46514     if (!s)
46515         goto fail;
46516     s->promise_state = JS_PROMISE_PENDING;
46517     s->is_handled = FALSE;
46518     for(i = 0; i < 2; i++)
46519         init_list_head(&s->promise_reactions[i]);
46520     s->promise_result = JS_UNDEFINED;
46521     JS_SetOpaque(obj, s);
46522     if (js_create_resolving_functions(ctx, args, obj))
46523         goto fail;
46524     ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args);
46525     if (JS_IsException(ret)) {
46526         JSValue ret2, error;
46527         error = JS_GetException(ctx);
46528         ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
46529         JS_FreeValue(ctx, error);
46530         if (JS_IsException(ret2))
46531             goto fail1;
46532         JS_FreeValue(ctx, ret2);
46533     }
46534     JS_FreeValue(ctx, ret);
46535     JS_FreeValue(ctx, args[0]);
46536     JS_FreeValue(ctx, args[1]);
46537     return obj;
46538  fail1:
46539     JS_FreeValue(ctx, args[0]);
46540     JS_FreeValue(ctx, args[1]);
46541  fail:
46542     JS_FreeValue(ctx, obj);
46543     return JS_EXCEPTION;
46544 }
46545 
js_promise_executor(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)46546 static JSValue js_promise_executor(JSContext *ctx,
46547                                    JSValueConst this_val,
46548                                    int argc, JSValueConst *argv,
46549                                    int magic, JSValue *func_data)
46550 {
46551     int i;
46552 
46553     for(i = 0; i < 2; i++) {
46554         if (!JS_IsUndefined(func_data[i]))
46555             return JS_ThrowTypeError(ctx, "resolving function already set");
46556         func_data[i] = JS_DupValue(ctx, argv[i]);
46557     }
46558     return JS_UNDEFINED;
46559 }
46560 
js_promise_executor_new(JSContext * ctx)46561 static JSValue js_promise_executor_new(JSContext *ctx)
46562 {
46563     JSValueConst func_data[2];
46564 
46565     func_data[0] = JS_UNDEFINED;
46566     func_data[1] = JS_UNDEFINED;
46567     return JS_NewCFunctionData(ctx, js_promise_executor, 2,
46568                                0, 2, func_data);
46569 }
46570 
js_new_promise_capability(JSContext * ctx,JSValue * resolving_funcs,JSValueConst ctor)46571 static JSValue js_new_promise_capability(JSContext *ctx,
46572                                          JSValue *resolving_funcs,
46573                                          JSValueConst ctor)
46574 {
46575     JSValue executor, result_promise;
46576     JSCFunctionDataRecord *s;
46577     int i;
46578 
46579     executor = js_promise_executor_new(ctx);
46580     if (JS_IsException(executor))
46581         return executor;
46582 
46583     if (JS_IsUndefined(ctor)) {
46584         result_promise = js_promise_constructor(ctx, ctor, 1,
46585                                                 (JSValueConst *)&executor);
46586     } else {
46587         result_promise = JS_CallConstructor(ctx, ctor, 1,
46588                                             (JSValueConst *)&executor);
46589     }
46590     if (JS_IsException(result_promise))
46591         goto fail;
46592     s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA);
46593     for(i = 0; i < 2; i++) {
46594         if (check_function(ctx, s->data[i]))
46595             goto fail;
46596     }
46597     for(i = 0; i < 2; i++)
46598         resolving_funcs[i] = JS_DupValue(ctx, s->data[i]);
46599     JS_FreeValue(ctx, executor);
46600     return result_promise;
46601  fail:
46602     JS_FreeValue(ctx, executor);
46603     JS_FreeValue(ctx, result_promise);
46604     return JS_EXCEPTION;
46605 }
46606 
JS_NewPromiseCapability(JSContext * ctx,JSValue * resolving_funcs)46607 JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs)
46608 {
46609     return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED);
46610 }
46611 
js_promise_resolve(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)46612 static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
46613                                   int argc, JSValueConst *argv, int magic)
46614 {
46615     JSValue result_promise, resolving_funcs[2], ret;
46616     BOOL is_reject = magic;
46617 
46618     if (!JS_IsObject(this_val))
46619         return JS_ThrowTypeErrorNotAnObject(ctx);
46620     if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) {
46621         JSValue ctor;
46622         BOOL is_same;
46623         ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor);
46624         if (JS_IsException(ctor))
46625             return ctor;
46626         is_same = js_same_value(ctx, ctor, this_val);
46627         JS_FreeValue(ctx, ctor);
46628         if (is_same)
46629             return JS_DupValue(ctx, argv[0]);
46630     }
46631     result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
46632     if (JS_IsException(result_promise))
46633         return result_promise;
46634     ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv);
46635     JS_FreeValue(ctx, resolving_funcs[0]);
46636     JS_FreeValue(ctx, resolving_funcs[1]);
46637     if (JS_IsException(ret)) {
46638         JS_FreeValue(ctx, result_promise);
46639         return ret;
46640     }
46641     JS_FreeValue(ctx, ret);
46642     return result_promise;
46643 }
46644 
46645 #if 0
46646 static JSValue js_promise___newPromiseCapability(JSContext *ctx,
46647                                                  JSValueConst this_val,
46648                                                  int argc, JSValueConst *argv)
46649 {
46650     JSValue result_promise, resolving_funcs[2], obj;
46651     JSValueConst ctor;
46652     ctor = argv[0];
46653     if (!JS_IsObject(ctor))
46654         return JS_ThrowTypeErrorNotAnObject(ctx);
46655     result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
46656     if (JS_IsException(result_promise))
46657         return result_promise;
46658     obj = JS_NewObject(ctx);
46659     if (JS_IsException(obj)) {
46660         JS_FreeValue(ctx, resolving_funcs[0]);
46661         JS_FreeValue(ctx, resolving_funcs[1]);
46662         JS_FreeValue(ctx, result_promise);
46663         return JS_EXCEPTION;
46664     }
46665     JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E);
46666     JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E);
46667     JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E);
46668     return obj;
46669 }
46670 #endif
46671 
remainingElementsCount_add(JSContext * ctx,JSValueConst resolve_element_env,int addend)46672 static __exception int remainingElementsCount_add(JSContext *ctx,
46673                                                   JSValueConst resolve_element_env,
46674                                                   int addend)
46675 {
46676     JSValue val;
46677     int remainingElementsCount;
46678 
46679     val = JS_GetPropertyUint32(ctx, resolve_element_env, 0);
46680     if (JS_IsException(val))
46681         return -1;
46682     if (JS_ToInt32Free(ctx, &remainingElementsCount, val))
46683         return -1;
46684     remainingElementsCount += addend;
46685     if (JS_SetPropertyUint32(ctx, resolve_element_env, 0,
46686                              JS_NewInt32(ctx, remainingElementsCount)) < 0)
46687         return -1;
46688     return (remainingElementsCount == 0);
46689 }
46690 
46691 #define PROMISE_MAGIC_all        0
46692 #define PROMISE_MAGIC_allSettled 1
46693 #define PROMISE_MAGIC_any        2
46694 
js_promise_all_resolve_element(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)46695 static JSValue js_promise_all_resolve_element(JSContext *ctx,
46696                                               JSValueConst this_val,
46697                                               int argc, JSValueConst *argv,
46698                                               int magic,
46699                                               JSValue *func_data)
46700 {
46701     int resolve_type = magic & 3;
46702     int is_reject = magic & 4;
46703     BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]);
46704     JSValueConst values = func_data[2];
46705     JSValueConst resolve = func_data[3];
46706     JSValueConst resolve_element_env = func_data[4];
46707     JSValue ret, obj;
46708     int is_zero, index;
46709 
46710     if (JS_ToInt32(ctx, &index, func_data[1]))
46711         return JS_EXCEPTION;
46712     if (alreadyCalled)
46713         return JS_UNDEFINED;
46714     func_data[0] = JS_NewBool(ctx, TRUE);
46715 
46716     if (resolve_type == PROMISE_MAGIC_allSettled) {
46717         JSValue str;
46718 
46719         obj = JS_NewObject(ctx);
46720         if (JS_IsException(obj))
46721             return JS_EXCEPTION;
46722         str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled");
46723         if (JS_IsException(str))
46724             goto fail1;
46725         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status,
46726                                    str,
46727                                    JS_PROP_C_W_E) < 0)
46728             goto fail1;
46729         if (JS_DefinePropertyValue(ctx, obj,
46730                                    is_reject ? JS_ATOM_reason : JS_ATOM_value,
46731                                    JS_DupValue(ctx, argv[0]),
46732                                    JS_PROP_C_W_E) < 0) {
46733         fail1:
46734             JS_FreeValue(ctx, obj);
46735             return JS_EXCEPTION;
46736         }
46737     } else {
46738         obj = JS_DupValue(ctx, argv[0]);
46739     }
46740     if (JS_DefinePropertyValueUint32(ctx, values, index,
46741                                      obj, JS_PROP_C_W_E) < 0)
46742         return JS_EXCEPTION;
46743 
46744     is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
46745     if (is_zero < 0)
46746         return JS_EXCEPTION;
46747     if (is_zero) {
46748         if (resolve_type == PROMISE_MAGIC_any) {
46749             JSValue error;
46750             error = js_aggregate_error_constructor(ctx, values);
46751             if (JS_IsException(error))
46752                 return JS_EXCEPTION;
46753             ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error);
46754             JS_FreeValue(ctx, error);
46755         } else {
46756             ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values);
46757         }
46758         if (JS_IsException(ret))
46759             return ret;
46760         JS_FreeValue(ctx, ret);
46761     }
46762     return JS_UNDEFINED;
46763 }
46764 
46765 /* magic = 0: Promise.all 1: Promise.allSettled */
js_promise_all(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)46766 static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
46767                               int argc, JSValueConst *argv, int magic)
46768 {
46769     JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
46770     JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED;
46771     JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element;
46772     JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED;
46773     JSValueConst then_args[2], resolve_element_data[5];
46774     BOOL done;
46775     int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any);
46776 
46777     if (!JS_IsObject(this_val))
46778         return JS_ThrowTypeErrorNotAnObject(ctx);
46779     result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
46780     if (JS_IsException(result_promise))
46781         return result_promise;
46782     promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
46783     if (JS_IsException(promise_resolve) ||
46784         check_function(ctx, promise_resolve))
46785         goto fail_reject;
46786     iter = JS_GetIterator(ctx, argv[0], FALSE);
46787     if (JS_IsException(iter)) {
46788         JSValue error;
46789     fail_reject:
46790         error = JS_GetException(ctx);
46791         ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
46792                        (JSValueConst *)&error);
46793         JS_FreeValue(ctx, error);
46794         if (JS_IsException(ret))
46795             goto fail;
46796         JS_FreeValue(ctx, ret);
46797     } else {
46798         next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
46799         if (JS_IsException(next_method))
46800             goto fail_reject;
46801         values = JS_NewArray(ctx);
46802         if (JS_IsException(values))
46803             goto fail_reject;
46804         resolve_element_env = JS_NewArray(ctx);
46805         if (JS_IsException(resolve_element_env))
46806             goto fail_reject;
46807         /* remainingElementsCount field */
46808         if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0,
46809                                          JS_NewInt32(ctx, 1),
46810                                          JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
46811             goto fail_reject;
46812 
46813         index = 0;
46814         for(;;) {
46815             /* XXX: conformance: should close the iterator if error on 'done'
46816                access, but not on 'value' access */
46817             item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
46818             if (JS_IsException(item))
46819                 goto fail_reject;
46820             if (done)
46821                 break;
46822             next_promise = JS_Call(ctx, promise_resolve,
46823                                    this_val, 1, (JSValueConst *)&item);
46824             JS_FreeValue(ctx, item);
46825             if (JS_IsException(next_promise)) {
46826             fail_reject1:
46827                 JS_IteratorClose(ctx, iter, TRUE);
46828                 goto fail_reject;
46829             }
46830             resolve_element_data[0] = JS_NewBool(ctx, FALSE);
46831             resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index);
46832             resolve_element_data[2] = values;
46833             resolve_element_data[3] = resolving_funcs[is_promise_any];
46834             resolve_element_data[4] = resolve_element_env;
46835             resolve_element =
46836                 JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
46837                                     magic, 5, resolve_element_data);
46838             if (JS_IsException(resolve_element)) {
46839                 JS_FreeValue(ctx, next_promise);
46840                 goto fail_reject1;
46841             }
46842 
46843             if (magic == PROMISE_MAGIC_allSettled) {
46844                 reject_element =
46845                     JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
46846                                         magic | 4, 5, resolve_element_data);
46847                 if (JS_IsException(reject_element)) {
46848                     JS_FreeValue(ctx, next_promise);
46849                     goto fail_reject1;
46850                 }
46851             } else if (magic == PROMISE_MAGIC_any) {
46852                 if (JS_DefinePropertyValueUint32(ctx, values, index,
46853                                                  JS_UNDEFINED, JS_PROP_C_W_E) < 0)
46854                     goto fail_reject1;
46855                 reject_element = resolve_element;
46856                 resolve_element = JS_DupValue(ctx, resolving_funcs[0]);
46857             } else {
46858                 reject_element = JS_DupValue(ctx, resolving_funcs[1]);
46859             }
46860 
46861             if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) {
46862                 JS_FreeValue(ctx, next_promise);
46863                 JS_FreeValue(ctx, resolve_element);
46864                 JS_FreeValue(ctx, reject_element);
46865                 goto fail_reject1;
46866             }
46867 
46868             then_args[0] = resolve_element;
46869             then_args[1] = reject_element;
46870             ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args);
46871             JS_FreeValue(ctx, resolve_element);
46872             JS_FreeValue(ctx, reject_element);
46873             if (check_exception_free(ctx, ret))
46874                 goto fail_reject1;
46875             index++;
46876         }
46877 
46878         is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
46879         if (is_zero < 0)
46880             goto fail_reject;
46881         if (is_zero) {
46882             if (magic == PROMISE_MAGIC_any) {
46883                 JSValue error;
46884                 error = js_aggregate_error_constructor(ctx, values);
46885                 if (JS_IsException(error))
46886                     goto fail_reject;
46887                 JS_FreeValue(ctx, values);
46888                 values = error;
46889             }
46890             ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED,
46891                           1, (JSValueConst *)&values);
46892             if (check_exception_free(ctx, ret))
46893                 goto fail_reject;
46894         }
46895     }
46896  done:
46897     JS_FreeValue(ctx, promise_resolve);
46898     JS_FreeValue(ctx, resolve_element_env);
46899     JS_FreeValue(ctx, values);
46900     JS_FreeValue(ctx, next_method);
46901     JS_FreeValue(ctx, iter);
46902     JS_FreeValue(ctx, resolving_funcs[0]);
46903     JS_FreeValue(ctx, resolving_funcs[1]);
46904     return result_promise;
46905  fail:
46906     JS_FreeValue(ctx, result_promise);
46907     result_promise = JS_EXCEPTION;
46908     goto done;
46909 }
46910 
js_promise_race(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)46911 static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
46912                                int argc, JSValueConst *argv)
46913 {
46914     JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
46915     JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED;
46916     JSValue promise_resolve = JS_UNDEFINED;
46917     BOOL done;
46918 
46919     if (!JS_IsObject(this_val))
46920         return JS_ThrowTypeErrorNotAnObject(ctx);
46921     result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
46922     if (JS_IsException(result_promise))
46923         return result_promise;
46924     promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
46925     if (JS_IsException(promise_resolve) ||
46926         check_function(ctx, promise_resolve))
46927         goto fail_reject;
46928     iter = JS_GetIterator(ctx, argv[0], FALSE);
46929     if (JS_IsException(iter)) {
46930         JSValue error;
46931     fail_reject:
46932         error = JS_GetException(ctx);
46933         ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
46934                        (JSValueConst *)&error);
46935         JS_FreeValue(ctx, error);
46936         if (JS_IsException(ret))
46937             goto fail;
46938         JS_FreeValue(ctx, ret);
46939     } else {
46940         next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
46941         if (JS_IsException(next_method))
46942             goto fail_reject;
46943 
46944         for(;;) {
46945             /* XXX: conformance: should close the iterator if error on 'done'
46946                access, but not on 'value' access */
46947             item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
46948             if (JS_IsException(item))
46949                 goto fail_reject;
46950             if (done)
46951                 break;
46952             next_promise = JS_Call(ctx, promise_resolve,
46953                                    this_val, 1, (JSValueConst *)&item);
46954             JS_FreeValue(ctx, item);
46955             if (JS_IsException(next_promise)) {
46956             fail_reject1:
46957                 JS_IteratorClose(ctx, iter, TRUE);
46958                 goto fail_reject;
46959             }
46960             ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2,
46961                                 (JSValueConst *)resolving_funcs);
46962             if (check_exception_free(ctx, ret))
46963                 goto fail_reject1;
46964         }
46965     }
46966  done:
46967     JS_FreeValue(ctx, promise_resolve);
46968     JS_FreeValue(ctx, next_method);
46969     JS_FreeValue(ctx, iter);
46970     JS_FreeValue(ctx, resolving_funcs[0]);
46971     JS_FreeValue(ctx, resolving_funcs[1]);
46972     return result_promise;
46973  fail:
46974     //JS_FreeValue(ctx, next_method); // why not???
46975     JS_FreeValue(ctx, result_promise);
46976     result_promise = JS_EXCEPTION;
46977     goto done;
46978 }
46979 
perform_promise_then(JSContext * ctx,JSValueConst promise,JSValueConst * resolve_reject,JSValueConst * cap_resolving_funcs)46980 static __exception int perform_promise_then(JSContext *ctx,
46981                                             JSValueConst promise,
46982                                             JSValueConst *resolve_reject,
46983                                             JSValueConst *cap_resolving_funcs)
46984 {
46985     JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
46986     JSPromiseReactionData *rd_array[2], *rd;
46987     int i, j;
46988 
46989     rd_array[0] = NULL;
46990     rd_array[1] = NULL;
46991     for(i = 0; i < 2; i++) {
46992         JSValueConst handler;
46993         rd = js_mallocz(ctx, sizeof(*rd));
46994         if (!rd) {
46995             if (i == 1)
46996                 promise_reaction_data_free(ctx->rt, rd_array[0]);
46997             return -1;
46998         }
46999         for(j = 0; j < 2; j++)
47000             rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]);
47001         handler = resolve_reject[i];
47002         if (!JS_IsFunction(ctx, handler))
47003             handler = JS_UNDEFINED;
47004         rd->handler = JS_DupValue(ctx, handler);
47005         rd_array[i] = rd;
47006     }
47007 
47008     if (s->promise_state == JS_PROMISE_PENDING) {
47009         for(i = 0; i < 2; i++)
47010             list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]);
47011     } else {
47012         JSValueConst args[5];
47013         if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
47014             JSRuntime *rt = ctx->rt;
47015             if (rt->host_promise_rejection_tracker) {
47016                 rt->host_promise_rejection_tracker(ctx, promise, s->promise_result,
47017                                                    TRUE, rt->host_promise_rejection_tracker_opaque);
47018             }
47019         }
47020         i = s->promise_state - JS_PROMISE_FULFILLED;
47021         rd = rd_array[i];
47022         args[0] = rd->resolving_funcs[0];
47023         args[1] = rd->resolving_funcs[1];
47024         args[2] = rd->handler;
47025         args[3] = JS_NewBool(ctx, i);
47026         args[4] = s->promise_result;
47027         JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
47028         for(i = 0; i < 2; i++)
47029             promise_reaction_data_free(ctx->rt, rd_array[i]);
47030     }
47031     s->is_handled = TRUE;
47032     return 0;
47033 }
47034 
js_promise_then(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)47035 static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,
47036                                int argc, JSValueConst *argv)
47037 {
47038     JSValue ctor, result_promise, resolving_funcs[2];
47039     JSPromiseData *s;
47040     int i, ret;
47041 
47042     s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE);
47043     if (!s)
47044         return JS_EXCEPTION;
47045 
47046     ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
47047     if (JS_IsException(ctor))
47048         return ctor;
47049     result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
47050     JS_FreeValue(ctx, ctor);
47051     if (JS_IsException(result_promise))
47052         return result_promise;
47053     ret = perform_promise_then(ctx, this_val, argv,
47054                                (JSValueConst *)resolving_funcs);
47055     for(i = 0; i < 2; i++)
47056         JS_FreeValue(ctx, resolving_funcs[i]);
47057     if (ret) {
47058         JS_FreeValue(ctx, result_promise);
47059         return JS_EXCEPTION;
47060     }
47061     return result_promise;
47062 }
47063 
js_promise_catch(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)47064 static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val,
47065                                 int argc, JSValueConst *argv)
47066 {
47067     JSValueConst args[2];
47068     args[0] = JS_UNDEFINED;
47069     args[1] = argv[0];
47070     return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args);
47071 }
47072 
js_promise_finally_value_thunk(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)47073 static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val,
47074                                               int argc, JSValueConst *argv,
47075                                               int magic, JSValue *func_data)
47076 {
47077     return JS_DupValue(ctx, func_data[0]);
47078 }
47079 
js_promise_finally_thrower(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)47080 static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val,
47081                                           int argc, JSValueConst *argv,
47082                                           int magic, JSValue *func_data)
47083 {
47084     return JS_Throw(ctx, JS_DupValue(ctx, func_data[0]));
47085 }
47086 
js_promise_then_finally_func(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)47087 static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val,
47088                                             int argc, JSValueConst *argv,
47089                                             int magic, JSValue *func_data)
47090 {
47091     JSValueConst ctor = func_data[0];
47092     JSValueConst onFinally = func_data[1];
47093     JSValue res, promise, ret, then_func;
47094 
47095     res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL);
47096     if (JS_IsException(res))
47097         return res;
47098     promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0);
47099     JS_FreeValue(ctx, res);
47100     if (JS_IsException(promise))
47101         return promise;
47102     if (magic == 0) {
47103         then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0,
47104                                         0, 1, argv);
47105     } else {
47106         then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0,
47107                                         0, 1, argv);
47108     }
47109     if (JS_IsException(then_func)) {
47110         JS_FreeValue(ctx, promise);
47111         return then_func;
47112     }
47113     ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func);
47114     JS_FreeValue(ctx, then_func);
47115     return ret;
47116 }
47117 
js_promise_finally(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)47118 static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val,
47119                                   int argc, JSValueConst *argv)
47120 {
47121     JSValueConst onFinally = argv[0];
47122     JSValue ctor, ret;
47123     JSValue then_funcs[2];
47124     JSValueConst func_data[2];
47125     int i;
47126 
47127     ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
47128     if (JS_IsException(ctor))
47129         return ctor;
47130     if (!JS_IsFunction(ctx, onFinally)) {
47131         then_funcs[0] = JS_DupValue(ctx, onFinally);
47132         then_funcs[1] = JS_DupValue(ctx, onFinally);
47133     } else {
47134         func_data[0] = ctor;
47135         func_data[1] = onFinally;
47136         for(i = 0; i < 2; i++) {
47137             then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data);
47138             if (JS_IsException(then_funcs[i])) {
47139                 if (i == 1)
47140                     JS_FreeValue(ctx, then_funcs[0]);
47141                 JS_FreeValue(ctx, ctor);
47142                 return JS_EXCEPTION;
47143             }
47144         }
47145     }
47146     JS_FreeValue(ctx, ctor);
47147     ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs);
47148     JS_FreeValue(ctx, then_funcs[0]);
47149     JS_FreeValue(ctx, then_funcs[1]);
47150     return ret;
47151 }
47152 
47153 static const JSCFunctionListEntry js_promise_funcs[] = {
47154     JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ),
47155     JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ),
47156     JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ),
47157     JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ),
47158     JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ),
47159     JS_CFUNC_DEF("race", 1, js_promise_race ),
47160     //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ),
47161     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL),
47162 };
47163 
47164 static const JSCFunctionListEntry js_promise_proto_funcs[] = {
47165     JS_CFUNC_DEF("then", 2, js_promise_then ),
47166     JS_CFUNC_DEF("catch", 1, js_promise_catch ),
47167     JS_CFUNC_DEF("finally", 1, js_promise_finally ),
47168     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ),
47169 };
47170 
47171 /* AsyncFunction */
47172 static const JSCFunctionListEntry js_async_function_proto_funcs[] = {
47173     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ),
47174 };
47175 
js_async_from_sync_iterator_unwrap(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic,JSValue * func_data)47176 static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx,
47177                                                   JSValueConst this_val,
47178                                                   int argc, JSValueConst *argv,
47179                                                   int magic, JSValue *func_data)
47180 {
47181     return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]),
47182                                      JS_ToBool(ctx, func_data[0]));
47183 }
47184 
js_async_from_sync_iterator_unwrap_func_create(JSContext * ctx,BOOL done)47185 static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,
47186                                                               BOOL done)
47187 {
47188     JSValueConst func_data[1];
47189 
47190     func_data[0] = (JSValueConst)JS_NewBool(ctx, done);
47191     return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap,
47192                                1, 0, 1, func_data);
47193 }
47194 
47195 /* AsyncIteratorPrototype */
47196 
47197 static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = {
47198     JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ),
47199 };
47200 
47201 /* AsyncFromSyncIteratorPrototype */
47202 
47203 typedef struct JSAsyncFromSyncIteratorData {
47204     JSValue sync_iter;
47205     JSValue next_method;
47206 } JSAsyncFromSyncIteratorData;
47207 
js_async_from_sync_iterator_finalizer(JSRuntime * rt,JSValue val)47208 static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val)
47209 {
47210     JSAsyncFromSyncIteratorData *s =
47211         JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
47212     if (s) {
47213         JS_FreeValueRT(rt, s->sync_iter);
47214         JS_FreeValueRT(rt, s->next_method);
47215         js_free_rt(rt, s);
47216     }
47217 }
47218 
js_async_from_sync_iterator_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)47219 static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val,
47220                                              JS_MarkFunc *mark_func)
47221 {
47222     JSAsyncFromSyncIteratorData *s =
47223         JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
47224     if (s) {
47225         JS_MarkValue(rt, s->sync_iter, mark_func);
47226         JS_MarkValue(rt, s->next_method, mark_func);
47227     }
47228 }
47229 
JS_CreateAsyncFromSyncIterator(JSContext * ctx,JSValueConst sync_iter)47230 static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
47231                                               JSValueConst sync_iter)
47232 {
47233     JSValue async_iter, next_method;
47234     JSAsyncFromSyncIteratorData *s;
47235 
47236     next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next);
47237     if (JS_IsException(next_method))
47238         return JS_EXCEPTION;
47239     async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
47240     if (JS_IsException(async_iter)) {
47241         JS_FreeValue(ctx, next_method);
47242         return async_iter;
47243     }
47244     s = js_mallocz(ctx, sizeof(*s));
47245     if (!s) {
47246         JS_FreeValue(ctx, async_iter);
47247         JS_FreeValue(ctx, next_method);
47248         return JS_EXCEPTION;
47249     }
47250     s->sync_iter = JS_DupValue(ctx, sync_iter);
47251     s->next_method = next_method;
47252     JS_SetOpaque(async_iter, s);
47253     return async_iter;
47254 }
47255 
js_async_from_sync_iterator_next(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)47256 static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val,
47257                                                 int argc, JSValueConst *argv,
47258                                                 int magic)
47259 {
47260     JSValue promise, resolving_funcs[2], value, err, method;
47261     JSAsyncFromSyncIteratorData *s;
47262     int done;
47263     int is_reject;
47264 
47265     promise = JS_NewPromiseCapability(ctx, resolving_funcs);
47266     if (JS_IsException(promise))
47267         return JS_EXCEPTION;
47268     s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
47269     if (!s) {
47270         JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator");
47271         goto reject;
47272     }
47273 
47274     if (magic == GEN_MAGIC_NEXT) {
47275         method = JS_DupValue(ctx, s->next_method);
47276     } else {
47277         method = JS_GetProperty(ctx, s->sync_iter,
47278                                 magic == GEN_MAGIC_RETURN ? JS_ATOM_return :
47279                                 JS_ATOM_throw);
47280         if (JS_IsException(method))
47281             goto reject;
47282         if (JS_IsUndefined(method) || JS_IsNull(method)) {
47283             if (magic == GEN_MAGIC_RETURN) {
47284                 err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE);
47285                 is_reject = 0;
47286             } else {
47287                 err = JS_DupValue(ctx, argv[0]);
47288                 is_reject = 1;
47289             }
47290             goto done_resolve;
47291         }
47292     }
47293     value = JS_IteratorNext2(ctx, s->sync_iter, method,
47294                              argc >= 1 ? 1 : 0, argv, &done);
47295     JS_FreeValue(ctx, method);
47296     if (JS_IsException(value))
47297         goto reject;
47298     if (done == 2) {
47299         JSValue obj = value;
47300         value = JS_IteratorGetCompleteValue(ctx, obj, &done);
47301         JS_FreeValue(ctx, obj);
47302         if (JS_IsException(value))
47303             goto reject;
47304     }
47305 
47306     if (JS_IsException(value)) {
47307         JSValue res2;
47308     reject:
47309         err = JS_GetException(ctx);
47310         is_reject = 1;
47311     done_resolve:
47312         res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED,
47313                        1, (JSValueConst *)&err);
47314         JS_FreeValue(ctx, err);
47315         JS_FreeValue(ctx, res2);
47316         JS_FreeValue(ctx, resolving_funcs[0]);
47317         JS_FreeValue(ctx, resolving_funcs[1]);
47318         return promise;
47319     }
47320     {
47321         JSValue value_wrapper_promise, resolve_reject[2];
47322         int res;
47323 
47324         value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,
47325                                                    1, (JSValueConst *)&value, 0);
47326         if (JS_IsException(value_wrapper_promise)) {
47327             JS_FreeValue(ctx, value);
47328             goto reject;
47329         }
47330 
47331         resolve_reject[0] =
47332             js_async_from_sync_iterator_unwrap_func_create(ctx, done);
47333         if (JS_IsException(resolve_reject[0])) {
47334             JS_FreeValue(ctx, value_wrapper_promise);
47335             goto fail;
47336         }
47337         JS_FreeValue(ctx, value);
47338         resolve_reject[1] = JS_UNDEFINED;
47339 
47340         res = perform_promise_then(ctx, value_wrapper_promise,
47341                                    (JSValueConst *)resolve_reject,
47342                                    (JSValueConst *)resolving_funcs);
47343         JS_FreeValue(ctx, resolve_reject[0]);
47344         JS_FreeValue(ctx, value_wrapper_promise);
47345         JS_FreeValue(ctx, resolving_funcs[0]);
47346         JS_FreeValue(ctx, resolving_funcs[1]);
47347         if (res) {
47348             JS_FreeValue(ctx, promise);
47349             return JS_EXCEPTION;
47350         }
47351     }
47352     return promise;
47353  fail:
47354     JS_FreeValue(ctx, value);
47355     JS_FreeValue(ctx, resolving_funcs[0]);
47356     JS_FreeValue(ctx, resolving_funcs[1]);
47357     JS_FreeValue(ctx, promise);
47358     return JS_EXCEPTION;
47359 }
47360 
47361 static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = {
47362     JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ),
47363     JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ),
47364     JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ),
47365 };
47366 
47367 /* AsyncGeneratorFunction */
47368 
47369 static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = {
47370     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ),
47371 };
47372 
47373 /* AsyncGenerator prototype */
47374 
47375 static const JSCFunctionListEntry js_async_generator_proto_funcs[] = {
47376     JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ),
47377     JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ),
47378     JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ),
47379     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ),
47380 };
47381 
47382 static JSClassShortDef const js_async_class_def[] = {
47383     { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark },                      /* JS_CLASS_PROMISE */
47384     { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */
47385     { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */
47386     { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark },  /* JS_CLASS_ASYNC_FUNCTION */
47387     { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */
47388     { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */
47389     { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
47390     { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark },  /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */
47391     { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark },  /* JS_CLASS_ASYNC_GENERATOR */
47392 };
47393 
JS_AddIntrinsicPromise(JSContext * ctx)47394 void JS_AddIntrinsicPromise(JSContext *ctx)
47395 {
47396     JSRuntime *rt = ctx->rt;
47397     JSValue obj1;
47398 
47399     if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) {
47400         init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE,
47401                          countof(js_async_class_def));
47402         rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call;
47403         rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call;
47404         rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call;
47405         rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call;
47406         rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call;
47407         rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call;
47408     }
47409 
47410     /* Promise */
47411     ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx);
47412     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE],
47413                                js_promise_proto_funcs,
47414                                countof(js_promise_proto_funcs));
47415     obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1,
47416                             JS_CFUNC_constructor, 0);
47417     ctx->promise_ctor = JS_DupValue(ctx, obj1);
47418     JS_SetPropertyFunctionList(ctx, obj1,
47419                                js_promise_funcs,
47420                                countof(js_promise_funcs));
47421     JS_NewGlobalCConstructor2(ctx, obj1, "Promise",
47422                               ctx->class_proto[JS_CLASS_PROMISE]);
47423 
47424     /* AsyncFunction */
47425     ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
47426     obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
47427                             "AsyncFunction", 1,
47428                             JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC,
47429                             ctx->function_ctor);
47430     JS_SetPropertyFunctionList(ctx,
47431                                ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
47432                                js_async_function_proto_funcs,
47433                                countof(js_async_function_proto_funcs));
47434     JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
47435                        0, JS_PROP_CONFIGURABLE);
47436     JS_FreeValue(ctx, obj1);
47437 
47438     /* AsyncIteratorPrototype */
47439     ctx->async_iterator_proto = JS_NewObject(ctx);
47440     JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto,
47441                                js_async_iterator_proto_funcs,
47442                                countof(js_async_iterator_proto_funcs));
47443 
47444     /* AsyncFromSyncIteratorPrototype */
47445     ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] =
47446         JS_NewObjectProto(ctx, ctx->async_iterator_proto);
47447     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR],
47448                                js_async_from_sync_iterator_proto_funcs,
47449                                countof(js_async_from_sync_iterator_proto_funcs));
47450 
47451     /* AsyncGeneratorPrototype */
47452     ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] =
47453         JS_NewObjectProto(ctx, ctx->async_iterator_proto);
47454     JS_SetPropertyFunctionList(ctx,
47455                                ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
47456                                js_async_generator_proto_funcs,
47457                                countof(js_async_generator_proto_funcs));
47458 
47459     /* AsyncGeneratorFunction */
47460     ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] =
47461         JS_NewObjectProto(ctx, ctx->function_proto);
47462     obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
47463                             "AsyncGeneratorFunction", 1,
47464                             JS_CFUNC_constructor_or_func_magic,
47465                             JS_FUNC_ASYNC_GENERATOR,
47466                             ctx->function_ctor);
47467     JS_SetPropertyFunctionList(ctx,
47468                                ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
47469                                js_async_generator_function_proto_funcs,
47470                                countof(js_async_generator_function_proto_funcs));
47471     JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
47472                        ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
47473                        JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
47474     JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
47475                        0, JS_PROP_CONFIGURABLE);
47476     JS_FreeValue(ctx, obj1);
47477 }
47478 
47479 /* URI handling */
47480 
string_get_hex(JSString * p,int k,int n)47481 static int string_get_hex(JSString *p, int k, int n) {
47482     int c = 0, h;
47483     while (n-- > 0) {
47484         if ((h = from_hex(string_get(p, k++))) < 0)
47485             return -1;
47486         c = (c << 4) | h;
47487     }
47488     return c;
47489 }
47490 
isURIReserved(int c)47491 static int isURIReserved(int c) {
47492     return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;
47493 }
47494 
js_throw_URIError(JSContext * ctx,const char * fmt,...)47495 static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...)
47496 {
47497     va_list ap;
47498 
47499     va_start(ap, fmt);
47500     JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap);
47501     va_end(ap);
47502     return -1;
47503 }
47504 
hex_decode(JSContext * ctx,JSString * p,int k)47505 static int hex_decode(JSContext *ctx, JSString *p, int k) {
47506     int c;
47507 
47508     if (k >= p->len || string_get(p, k) != '%')
47509         return js_throw_URIError(ctx, "expecting %%");
47510     if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0)
47511         return js_throw_URIError(ctx, "expecting hex digit");
47512 
47513     return c;
47514 }
47515 
js_global_decodeURI(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int isComponent)47516 static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val,
47517                                    int argc, JSValueConst *argv, int isComponent)
47518 {
47519     JSValue str;
47520     StringBuffer b_s, *b = &b_s;
47521     JSString *p;
47522     int k, c, c1, n, c_min;
47523 
47524     str = JS_ToString(ctx, argv[0]);
47525     if (JS_IsException(str))
47526         return str;
47527 
47528     string_buffer_init(ctx, b, 0);
47529 
47530     p = JS_VALUE_GET_STRING(str);
47531     for (k = 0; k < p->len;) {
47532         c = string_get(p, k);
47533         if (c == '%') {
47534             c = hex_decode(ctx, p, k);
47535             if (c < 0)
47536                 goto fail;
47537             k += 3;
47538             if (c < 0x80) {
47539                 if (!isComponent && isURIReserved(c)) {
47540                     c = '%';
47541                     k -= 2;
47542                 }
47543             } else {
47544                 /* Decode URI-encoded UTF-8 sequence */
47545                 if (c >= 0xc0 && c <= 0xdf) {
47546                     n = 1;
47547                     c_min = 0x80;
47548                     c &= 0x1f;
47549                 } else if (c >= 0xe0 && c <= 0xef) {
47550                     n = 2;
47551                     c_min = 0x800;
47552                     c &= 0xf;
47553                 } else if (c >= 0xf0 && c <= 0xf7) {
47554                     n = 3;
47555                     c_min = 0x10000;
47556                     c &= 0x7;
47557                 } else {
47558                     n = 0;
47559                     c_min = 1;
47560                     c = 0;
47561                 }
47562                 while (n-- > 0) {
47563                     c1 = hex_decode(ctx, p, k);
47564                     if (c1 < 0)
47565                         goto fail;
47566                     k += 3;
47567                     if ((c1 & 0xc0) != 0x80) {
47568                         c = 0;
47569                         break;
47570                     }
47571                     c = (c << 6) | (c1 & 0x3f);
47572                 }
47573                 if (c < c_min || c > 0x10FFFF ||
47574                     (c >= 0xd800 && c < 0xe000)) {
47575                     js_throw_URIError(ctx, "malformed UTF-8");
47576                     goto fail;
47577                 }
47578             }
47579         } else {
47580             k++;
47581         }
47582         string_buffer_putc(b, c);
47583     }
47584     JS_FreeValue(ctx, str);
47585     return string_buffer_end(b);
47586 
47587 fail:
47588     JS_FreeValue(ctx, str);
47589     string_buffer_free(b);
47590     return JS_EXCEPTION;
47591 }
47592 
isUnescaped(int c)47593 static int isUnescaped(int c) {
47594     static char const unescaped_chars[] =
47595         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
47596         "abcdefghijklmnopqrstuvwxyz"
47597         "0123456789"
47598         "@*_+-./";
47599     return c < 0x100 &&
47600         memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1);
47601 }
47602 
isURIUnescaped(int c,int isComponent)47603 static int isURIUnescaped(int c, int isComponent) {
47604     return c < 0x100 &&
47605         ((c >= 0x61 && c <= 0x7a) ||
47606          (c >= 0x41 && c <= 0x5a) ||
47607          (c >= 0x30 && c <= 0x39) ||
47608          memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL ||
47609          (!isComponent && isURIReserved(c)));
47610 }
47611 
encodeURI_hex(StringBuffer * b,int c)47612 static int encodeURI_hex(StringBuffer *b, int c) {
47613     uint8_t buf[6];
47614     int n = 0;
47615     const char *hex = "0123456789ABCDEF";
47616 
47617     buf[n++] = '%';
47618     if (c >= 256) {
47619         buf[n++] = 'u';
47620         buf[n++] = hex[(c >> 12) & 15];
47621         buf[n++] = hex[(c >>  8) & 15];
47622     }
47623     buf[n++] = hex[(c >> 4) & 15];
47624     buf[n++] = hex[(c >> 0) & 15];
47625     return string_buffer_write8(b, buf, n);
47626 }
47627 
js_global_encodeURI(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int isComponent)47628 static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val,
47629                                    int argc, JSValueConst *argv,
47630                                    int isComponent)
47631 {
47632     JSValue str;
47633     StringBuffer b_s, *b = &b_s;
47634     JSString *p;
47635     int k, c, c1;
47636 
47637     str = JS_ToString(ctx, argv[0]);
47638     if (JS_IsException(str))
47639         return str;
47640 
47641     p = JS_VALUE_GET_STRING(str);
47642     string_buffer_init(ctx, b, p->len);
47643     for (k = 0; k < p->len;) {
47644         c = string_get(p, k);
47645         k++;
47646         if (isURIUnescaped(c, isComponent)) {
47647             string_buffer_putc16(b, c);
47648         } else {
47649             if (c >= 0xdc00 && c <= 0xdfff) {
47650                 js_throw_URIError(ctx, "invalid character");
47651                 goto fail;
47652             } else if (c >= 0xd800 && c <= 0xdbff) {
47653                 if (k >= p->len) {
47654                     js_throw_URIError(ctx, "expecting surrogate pair");
47655                     goto fail;
47656                 }
47657                 c1 = string_get(p, k);
47658                 k++;
47659                 if (c1 < 0xdc00 || c1 > 0xdfff) {
47660                     js_throw_URIError(ctx, "expecting surrogate pair");
47661                     goto fail;
47662                 }
47663                 c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
47664             }
47665             if (c < 0x80) {
47666                 encodeURI_hex(b, c);
47667             } else {
47668                 /* XXX: use C UTF-8 conversion ? */
47669                 if (c < 0x800) {
47670                     encodeURI_hex(b, (c >> 6) | 0xc0);
47671                 } else {
47672                     if (c < 0x10000) {
47673                         encodeURI_hex(b, (c >> 12) | 0xe0);
47674                     } else {
47675                         encodeURI_hex(b, (c >> 18) | 0xf0);
47676                         encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80);
47677                     }
47678                     encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80);
47679                 }
47680                 encodeURI_hex(b, (c & 0x3f) | 0x80);
47681             }
47682         }
47683     }
47684     JS_FreeValue(ctx, str);
47685     return string_buffer_end(b);
47686 
47687 fail:
47688     JS_FreeValue(ctx, str);
47689     string_buffer_free(b);
47690     return JS_EXCEPTION;
47691 }
47692 
js_global_escape(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)47693 static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val,
47694                                 int argc, JSValueConst *argv)
47695 {
47696     JSValue str;
47697     StringBuffer b_s, *b = &b_s;
47698     JSString *p;
47699     int i, len, c;
47700 
47701     str = JS_ToString(ctx, argv[0]);
47702     if (JS_IsException(str))
47703         return str;
47704 
47705     p = JS_VALUE_GET_STRING(str);
47706     string_buffer_init(ctx, b, p->len);
47707     for (i = 0, len = p->len; i < len; i++) {
47708         c = string_get(p, i);
47709         if (isUnescaped(c)) {
47710             string_buffer_putc16(b, c);
47711         } else {
47712             encodeURI_hex(b, c);
47713         }
47714     }
47715     JS_FreeValue(ctx, str);
47716     return string_buffer_end(b);
47717 }
47718 
js_global_unescape(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)47719 static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,
47720                                   int argc, JSValueConst *argv)
47721 {
47722     JSValue str;
47723     StringBuffer b_s, *b = &b_s;
47724     JSString *p;
47725     int i, len, c, n;
47726 
47727     str = JS_ToString(ctx, argv[0]);
47728     if (JS_IsException(str))
47729         return str;
47730 
47731     string_buffer_init(ctx, b, 0);
47732     p = JS_VALUE_GET_STRING(str);
47733     for (i = 0, len = p->len; i < len; i++) {
47734         c = string_get(p, i);
47735         if (c == '%') {
47736             if (i + 6 <= len
47737             &&  string_get(p, i + 1) == 'u'
47738             &&  (n = string_get_hex(p, i + 2, 4)) >= 0) {
47739                 c = n;
47740                 i += 6 - 1;
47741             } else
47742             if (i + 3 <= len
47743             &&  (n = string_get_hex(p, i + 1, 2)) >= 0) {
47744                 c = n;
47745                 i += 3 - 1;
47746             }
47747         }
47748         string_buffer_putc16(b, c);
47749     }
47750     JS_FreeValue(ctx, str);
47751     return string_buffer_end(b);
47752 }
47753 
47754 /* global object */
47755 
47756 static const JSCFunctionListEntry js_global_funcs[] = {
47757     JS_CFUNC_DEF("parseInt", 2, js_parseInt ),
47758     JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),
47759     JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
47760     JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
47761 
47762     JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),
47763     JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),
47764     JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),
47765     JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),
47766     JS_CFUNC_DEF("escape", 1, js_global_escape ),
47767     JS_CFUNC_DEF("unescape", 1, js_global_unescape ),
47768     JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
47769     JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
47770     JS_PROP_UNDEFINED_DEF("undefined", 0 ),
47771 
47772     /* for the 'Date' implementation */
47773     JS_CFUNC_DEF("__date_clock", 0, js___date_clock ),
47774     //JS_CFUNC_DEF("__date_now", 0, js___date_now ),
47775     //JS_CFUNC_DEF("__date_getTimezoneOffset", 1, js___date_getTimezoneOffset ),
47776     //JS_CFUNC_DEF("__date_create", 3, js___date_create ),
47777 };
47778 
47779 /* Date */
47780 
math_mod(int64_t a,int64_t b)47781 static int64_t math_mod(int64_t a, int64_t b) {
47782     /* return positive modulo */
47783     int64_t m = a % b;
47784     return m + (m < 0) * b;
47785 }
47786 
floor_div(int64_t a,int64_t b)47787 static int64_t floor_div(int64_t a, int64_t b) {
47788     /* integer division rounding toward -Infinity */
47789     int64_t m = a % b;
47790     return (a - (m + (m < 0) * b)) / b;
47791 }
47792 
47793 static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
47794                              int argc, JSValueConst *argv);
47795 
JS_ThisTimeValue(JSContext * ctx,double * valp,JSValueConst this_val)47796 static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
47797 {
47798     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
47799         JSObject *p = JS_VALUE_GET_OBJ(this_val);
47800         if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))
47801             return JS_ToFloat64(ctx, valp, p->u.object_data);
47802     }
47803     JS_ThrowTypeError(ctx, "not a Date object");
47804     return -1;
47805 }
47806 
JS_SetThisTimeValue(JSContext * ctx,JSValueConst this_val,double v)47807 static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)
47808 {
47809     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
47810         JSObject *p = JS_VALUE_GET_OBJ(this_val);
47811         if (p->class_id == JS_CLASS_DATE) {
47812             JS_FreeValue(ctx, p->u.object_data);
47813             p->u.object_data = JS_NewFloat64(ctx, v);
47814             return JS_DupValue(ctx, p->u.object_data);
47815         }
47816     }
47817     return JS_ThrowTypeError(ctx, "not a Date object");
47818 }
47819 
days_from_year(int64_t y)47820 static int64_t days_from_year(int64_t y) {
47821     return 365 * (y - 1970) + floor_div(y - 1969, 4) -
47822         floor_div(y - 1901, 100) + floor_div(y - 1601, 400);
47823 }
47824 
days_in_year(int64_t y)47825 static int64_t days_in_year(int64_t y) {
47826     return 365 + !(y % 4) - !(y % 100) + !(y % 400);
47827 }
47828 
47829 /* return the year, update days */
year_from_days(int64_t * days)47830 static int64_t year_from_days(int64_t *days) {
47831     int64_t y, d1, nd, d = *days;
47832     y = floor_div(d * 10000, 3652425) + 1970;
47833     /* the initial approximation is very good, so only a few
47834        iterations are necessary */
47835     for(;;) {
47836         d1 = d - days_from_year(y);
47837         if (d1 < 0) {
47838             y--;
47839             d1 += days_in_year(y);
47840         } else {
47841             nd = days_in_year(y);
47842             if (d1 < nd)
47843                 break;
47844             d1 -= nd;
47845             y++;
47846         }
47847     }
47848     *days = d1;
47849     return y;
47850 }
47851 
47852 static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
47853 static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
47854 static char const day_names[] = "SunMonTueWedThuFriSat";
47855 
get_date_fields(JSContext * ctx,JSValueConst obj,double fields[9],int is_local,int force)47856 static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
47857                                        double fields[9], int is_local, int force)
47858 {
47859     double dval;
47860     int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
47861 
47862     if (JS_ThisTimeValue(ctx, &dval, obj))
47863         return -1;
47864 
47865     if (isnan(dval)) {
47866         if (!force)
47867             return FALSE; /* NaN */
47868         d = 0;        /* initialize all fields to 0 */
47869     } else {
47870         d = dval;
47871         if (is_local) {
47872             tz = -getTimezoneOffset(d);
47873             d += tz * 60000;
47874         }
47875     }
47876 
47877     /* result is >= 0, we can use % */
47878     h = math_mod(d, 86400000);
47879     days = (d - h) / 86400000;
47880     ms = h % 1000;
47881     h = (h - ms) / 1000;
47882     s = h % 60;
47883     h = (h - s) / 60;
47884     m = h % 60;
47885     h = (h - m) / 60;
47886     wd = math_mod(days + 4, 7); /* week day */
47887     y = year_from_days(&days);
47888 
47889     for(i = 0; i < 11; i++) {
47890         md = month_days[i];
47891         if (i == 1)
47892             md += days_in_year(y) - 365;
47893         if (days < md)
47894             break;
47895         days -= md;
47896     }
47897     fields[0] = y;
47898     fields[1] = i;
47899     fields[2] = days + 1;
47900     fields[3] = h;
47901     fields[4] = m;
47902     fields[5] = s;
47903     fields[6] = ms;
47904     fields[7] = wd;
47905     fields[8] = tz;
47906     return TRUE;
47907 }
47908 
time_clip(double t)47909 static double time_clip(double t) {
47910     if (t >= -8.64e15 && t <= 8.64e15)
47911         return trunc(t) + 0.0;  /* convert -0 to +0 */
47912     else
47913         return NAN;
47914 }
47915 
47916 /* The spec mandates the use of 'double' and it fixes the order
47917    of the operations */
set_date_fields(double fields[],int is_local)47918 static double set_date_fields(double fields[], int is_local) {
47919     int64_t y;
47920     double days, d, h, m1;
47921     int i, m, md;
47922 
47923     m1 = fields[1];
47924     m = fmod(m1, 12);
47925     if (m < 0)
47926         m += 12;
47927     y = (int64_t)(fields[0] + floor(m1 / 12));
47928     days = days_from_year(y);
47929 
47930     for(i = 0; i < m; i++) {
47931         md = month_days[i];
47932         if (i == 1)
47933             md += days_in_year(y) - 365;
47934         days += md;
47935     }
47936     days += fields[2] - 1;
47937     h = fields[3] * 3600000 + fields[4] * 60000 +
47938         fields[5] * 1000 + fields[6];
47939     d = days * 86400000 + h;
47940     if (is_local)
47941         d += getTimezoneOffset(d) * 60000;
47942     return time_clip(d);
47943 }
47944 
get_date_field(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)47945 static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,
47946                               int argc, JSValueConst *argv, int magic)
47947 {
47948     // get_date_field(obj, n, is_local)
47949     double fields[9];
47950     int res, n, is_local;
47951 
47952     is_local = magic & 0x0F;
47953     n = (magic >> 4) & 0x0F;
47954     res = get_date_fields(ctx, this_val, fields, is_local, 0);
47955     if (res < 0)
47956         return JS_EXCEPTION;
47957     if (!res)
47958         return JS_NAN;
47959 
47960     if (magic & 0x100) {    // getYear
47961         fields[0] -= 1900;
47962     }
47963     return JS_NewFloat64(ctx, fields[n]);
47964 }
47965 
set_date_field(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)47966 static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,
47967                               int argc, JSValueConst *argv, int magic)
47968 {
47969     // _field(obj, first_field, end_field, args, is_local)
47970     double fields[9];
47971     int res, first_field, end_field, is_local, i, n;
47972     double d, a;
47973 
47974     d = NAN;
47975     first_field = (magic >> 8) & 0x0F;
47976     end_field = (magic >> 4) & 0x0F;
47977     is_local = magic & 0x0F;
47978 
47979     res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);
47980     if (res < 0)
47981         return JS_EXCEPTION;
47982     if (res && argc > 0) {
47983         n = end_field - first_field;
47984         if (argc < n)
47985             n = argc;
47986         for(i = 0; i < n; i++) {
47987             if (JS_ToFloat64(ctx, &a, argv[i]))
47988                 return JS_EXCEPTION;
47989             if (!isfinite(a))
47990                 goto done;
47991             fields[first_field + i] = trunc(a);
47992         }
47993         d = set_date_fields(fields, is_local);
47994     }
47995 done:
47996     return JS_SetThisTimeValue(ctx, this_val, d);
47997 }
47998 
47999 /* fmt:
48000    0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"
48001    1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"
48002    2: toISOString: "2018-01-02T23:02:56.927Z"
48003    3: toLocaleString: "1/2/2018, 11:40:40 PM"
48004    part: 1=date, 2=time 3=all
48005    XXX: should use a variant of strftime().
48006  */
get_date_string(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)48007 static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
48008                                int argc, JSValueConst *argv, int magic)
48009 {
48010     // _string(obj, fmt, part)
48011     char buf[64];
48012     double fields[9];
48013     int res, fmt, part, pos;
48014     int y, mon, d, h, m, s, ms, wd, tz;
48015 
48016     fmt = (magic >> 4) & 0x0F;
48017     part = magic & 0x0F;
48018 
48019     res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);
48020     if (res < 0)
48021         return JS_EXCEPTION;
48022     if (!res) {
48023         if (fmt == 2)
48024             return JS_ThrowRangeError(ctx, "Date value is NaN");
48025         else
48026             return JS_NewString(ctx, "Invalid Date");
48027     }
48028 
48029     y = fields[0];
48030     mon = fields[1];
48031     d = fields[2];
48032     h = fields[3];
48033     m = fields[4];
48034     s = fields[5];
48035     ms = fields[6];
48036     wd = fields[7];
48037     tz = fields[8];
48038 
48039     pos = 0;
48040 
48041     if (part & 1) { /* date part */
48042         switch(fmt) {
48043         case 0:
48044             pos += snprintf(buf + pos, sizeof(buf) - pos,
48045                             "%.3s, %02d %.3s %0*d ",
48046                             day_names + wd * 3, d,
48047                             month_names + mon * 3, 4 + (y < 0), y);
48048             break;
48049         case 1:
48050             pos += snprintf(buf + pos, sizeof(buf) - pos,
48051                             "%.3s %.3s %02d %0*d",
48052                             day_names + wd * 3,
48053                             month_names + mon * 3, d, 4 + (y < 0), y);
48054             if (part == 3) {
48055                 buf[pos++] = ' ';
48056             }
48057             break;
48058         case 2:
48059             if (y >= 0 && y <= 9999) {
48060                 pos += snprintf(buf + pos, sizeof(buf) - pos,
48061                                 "%04d", y);
48062             } else {
48063                 pos += snprintf(buf + pos, sizeof(buf) - pos,
48064                                 "%+07d", y);
48065             }
48066             pos += snprintf(buf + pos, sizeof(buf) - pos,
48067                             "-%02d-%02dT", mon + 1, d);
48068             break;
48069         case 3:
48070             pos += snprintf(buf + pos, sizeof(buf) - pos,
48071                             "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);
48072             if (part == 3) {
48073                 buf[pos++] = ',';
48074                 buf[pos++] = ' ';
48075             }
48076             break;
48077         }
48078     }
48079     if (part & 2) { /* time part */
48080         switch(fmt) {
48081         case 0:
48082             pos += snprintf(buf + pos, sizeof(buf) - pos,
48083                             "%02d:%02d:%02d GMT", h, m, s);
48084             break;
48085         case 1:
48086             pos += snprintf(buf + pos, sizeof(buf) - pos,
48087                             "%02d:%02d:%02d GMT", h, m, s);
48088             if (tz < 0) {
48089                 buf[pos++] = '-';
48090                 tz = -tz;
48091             } else {
48092                 buf[pos++] = '+';
48093             }
48094             /* tz is >= 0, can use % */
48095             pos += snprintf(buf + pos, sizeof(buf) - pos,
48096                             "%02d%02d", tz / 60, tz % 60);
48097             /* XXX: tack the time zone code? */
48098             break;
48099         case 2:
48100             pos += snprintf(buf + pos, sizeof(buf) - pos,
48101                             "%02d:%02d:%02d.%03dZ", h, m, s, ms);
48102             break;
48103         case 3:
48104             pos += snprintf(buf + pos, sizeof(buf) - pos,
48105                             "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s,
48106                             (h < 12) ? 'A' : 'P');
48107             break;
48108         }
48109     }
48110     return JS_NewStringLen(ctx, buf, pos);
48111 }
48112 
48113 /* OS dependent: return the UTC time in ms since 1970. */
date_now(void)48114 static int64_t date_now(void) {
48115     struct timeval tv;
48116     gettimeofday(&tv, NULL);
48117     return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
48118 }
48119 
js_date_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)48120 static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
48121                                    int argc, JSValueConst *argv)
48122 {
48123     // Date(y, mon, d, h, m, s, ms)
48124     JSValue rv;
48125     int i, n;
48126     double a, val;
48127 
48128     if (JS_IsUndefined(new_target)) {
48129         /* invoked as function */
48130         argc = 0;
48131     }
48132     n = argc;
48133     if (n == 0) {
48134         val = date_now();
48135     } else if (n == 1) {
48136         JSValue v, dv;
48137         if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
48138             JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
48139             if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {
48140                 if (JS_ToFloat64(ctx, &val, p->u.object_data))
48141                     return JS_EXCEPTION;
48142                 val = time_clip(val);
48143                 goto has_val;
48144             }
48145         }
48146         v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);
48147         if (JS_IsString(v)) {
48148             dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);
48149             JS_FreeValue(ctx, v);
48150             if (JS_IsException(dv))
48151                 return JS_EXCEPTION;
48152             if (JS_ToFloat64Free(ctx, &val, dv))
48153                 return JS_EXCEPTION;
48154         } else {
48155             if (JS_ToFloat64Free(ctx, &val, v))
48156                 return JS_EXCEPTION;
48157         }
48158         val = time_clip(val);
48159     } else {
48160         double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
48161         if (n > 7)
48162             n = 7;
48163         for(i = 0; i < n; i++) {
48164             if (JS_ToFloat64(ctx, &a, argv[i]))
48165                 return JS_EXCEPTION;
48166             if (!isfinite(a))
48167                 break;
48168             fields[i] = trunc(a);
48169             if (i == 0 && fields[0] >= 0 && fields[0] < 100)
48170                 fields[0] += 1900;
48171         }
48172         val = (i == n) ? set_date_fields(fields, 1) : NAN;
48173     }
48174 has_val:
48175 #if 0
48176     JSValueConst args[3];
48177     args[0] = new_target;
48178     args[1] = ctx->class_proto[JS_CLASS_DATE];
48179     args[2] = JS_NewFloat64(ctx, val);
48180     rv = js___date_create(ctx, JS_UNDEFINED, 3, args);
48181 #else
48182     rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);
48183     if (!JS_IsException(rv))
48184         JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val));
48185 #endif
48186     if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {
48187         /* invoked as a function, return (new Date()).toString(); */
48188         JSValue s;
48189         s = get_date_string(ctx, rv, 0, NULL, 0x13);
48190         JS_FreeValue(ctx, rv);
48191         rv = s;
48192     }
48193     return rv;
48194 }
48195 
js_Date_UTC(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48196 static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
48197                            int argc, JSValueConst *argv)
48198 {
48199     // UTC(y, mon, d, h, m, s, ms)
48200     double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
48201     int i, n;
48202     double a;
48203 
48204     n = argc;
48205     if (n == 0)
48206         return JS_NAN;
48207     if (n > 7)
48208         n = 7;
48209     for(i = 0; i < n; i++) {
48210         if (JS_ToFloat64(ctx, &a, argv[i]))
48211             return JS_EXCEPTION;
48212         if (!isfinite(a))
48213             return JS_NAN;
48214         fields[i] = trunc(a);
48215         if (i == 0 && fields[0] >= 0 && fields[0] < 100)
48216             fields[0] += 1900;
48217     }
48218     return JS_NewFloat64(ctx, set_date_fields(fields, 0));
48219 }
48220 
string_skip_spaces(JSString * sp,int * pp)48221 static void string_skip_spaces(JSString *sp, int *pp) {
48222     while (*pp < sp->len && string_get(sp, *pp) == ' ')
48223         *pp += 1;
48224 }
48225 
string_skip_non_spaces(JSString * sp,int * pp)48226 static void string_skip_non_spaces(JSString *sp, int *pp) {
48227     while (*pp < sp->len && string_get(sp, *pp) != ' ')
48228         *pp += 1;
48229 }
48230 
48231 /* parse a numeric field */
string_get_field(JSString * sp,int * pp,int64_t * pval)48232 static int string_get_field(JSString *sp, int *pp, int64_t *pval) {
48233     int64_t v = 0;
48234     int c, p = *pp;
48235 
48236     /* skip non digits, should only skip spaces? */
48237     while (p < sp->len) {
48238         c = string_get(sp, p);
48239         if (c >= '0' && c <= '9')
48240             break;
48241         p++;
48242     }
48243     if (p >= sp->len)
48244         return -1;
48245     while (p < sp->len) {
48246         c = string_get(sp, p);
48247         if (!(c >= '0' && c <= '9'))
48248             break;
48249         v = v * 10 + c - '0';
48250         p++;
48251     }
48252     *pval = v;
48253     *pp = p;
48254     return 0;
48255 }
48256 
48257 /* parse a fixed width numeric field */
string_get_digits(JSString * sp,int * pp,int n,int64_t * pval)48258 static int string_get_digits(JSString *sp, int *pp, int n, int64_t *pval) {
48259     int64_t v = 0;
48260     int i, c, p = *pp;
48261 
48262     for(i = 0; i < n; i++) {
48263         if (p >= sp->len)
48264             return -1;
48265         c = string_get(sp, p);
48266         if (!(c >= '0' && c <= '9'))
48267             return -1;
48268         v = v * 10 + c - '0';
48269         p++;
48270     }
48271     *pval = v;
48272     *pp = p;
48273     return 0;
48274 }
48275 
48276 /* parse a signed numeric field */
string_get_signed_field(JSString * sp,int * pp,int64_t * pval)48277 static int string_get_signed_field(JSString *sp, int *pp, int64_t *pval) {
48278     int sgn, res;
48279 
48280     if (*pp >= sp->len)
48281         return -1;
48282 
48283     sgn = string_get(sp, *pp);
48284     if (sgn == '-' || sgn == '+')
48285         *pp += 1;
48286 
48287     res = string_get_field(sp, pp, pval);
48288     if (res == 0 && sgn == '-')
48289         *pval = -*pval;
48290     return res;
48291 }
48292 
find_abbrev(JSString * sp,int p,const char * list,int count)48293 static int find_abbrev(JSString *sp, int p, const char *list, int count) {
48294     int n, i;
48295 
48296     if (p + 3 <= sp->len) {
48297         for (n = 0; n < count; n++) {
48298             for (i = 0; i < 3; i++) {
48299                 if (string_get(sp, p + i) != month_names[n * 3 + i])
48300                     goto next;
48301             }
48302             return n;
48303         next:;
48304         }
48305     }
48306     return -1;
48307 }
48308 
string_get_month(JSString * sp,int * pp,int64_t * pval)48309 static int string_get_month(JSString *sp, int *pp, int64_t *pval) {
48310     int n;
48311 
48312     string_skip_spaces(sp, pp);
48313     n = find_abbrev(sp, *pp, month_names, 12);
48314     if (n < 0)
48315         return -1;
48316 
48317     *pval = n;
48318     *pp += 3;
48319     return 0;
48320 }
48321 
js_Date_parse(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48322 static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
48323                              int argc, JSValueConst *argv)
48324 {
48325     // parse(s)
48326     JSValue s, rv;
48327     int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 };
48328     double fields1[7];
48329     int64_t tz, hh, mm;
48330     double d;
48331     int p, i, c, sgn;
48332     JSString *sp;
48333     BOOL is_local;
48334 
48335     rv = JS_NAN;
48336 
48337     s = JS_ToString(ctx, argv[0]);
48338     if (JS_IsException(s))
48339         return JS_EXCEPTION;
48340 
48341     sp = JS_VALUE_GET_STRING(s);
48342     p = 0;
48343     if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) {
48344         /* ISO format */
48345         /* year field can be negative */
48346         /* XXX: could be stricter */
48347         if (string_get_signed_field(sp, &p, &fields[0]))
48348             goto done;
48349 
48350         is_local = TRUE;
48351         for (i = 1; i < 6; i++) {
48352             if (string_get_field(sp, &p, &fields[i]))
48353                 break;
48354         }
48355         if (i <= 3) {
48356             /* no time: UTC by default */
48357             is_local = FALSE;
48358         } else if (i == 6 && p < sp->len && string_get(sp, p) == '.') {
48359             /* parse milliseconds as a fractional part, round to nearest */
48360             /* XXX: the spec does not indicate which rounding should be used */
48361             int mul = 1000, ms = 0;
48362             while (++p < sp->len) {
48363                 int c = string_get(sp, p);
48364                 if (!(c >= '0' && c <= '9'))
48365                     break;
48366                 if (mul == 1 && c >= '5')
48367                     ms += 1;
48368                 ms += (c - '0') * (mul /= 10);
48369             }
48370             fields[6] = ms;
48371         }
48372         fields[1] -= 1;
48373 
48374         /* parse the time zone offset if present: [+-]HH:mm */
48375         tz = 0;
48376         if (p < sp->len) {
48377             sgn = string_get(sp, p);
48378             if (sgn == '+' || sgn == '-') {
48379                 if (string_get_field(sp, &p, &hh))
48380                     goto done;
48381                 if (string_get_field(sp, &p, &mm))
48382                     goto done;
48383                 tz = hh * 60 + mm;
48384                 if (sgn == '-')
48385                     tz = -tz;
48386                 is_local = FALSE;
48387             } else if (sgn == 'Z') {
48388                 is_local = FALSE;
48389             }
48390         }
48391     } else {
48392         /* toString or toUTCString format */
48393         /* skip the day of the week */
48394         string_skip_non_spaces(sp, &p);
48395         string_skip_spaces(sp, &p);
48396         if (p >= sp->len)
48397             goto done;
48398         c = string_get(sp, p);
48399         if (c >= '0' && c <= '9') {
48400             /* day of month first */
48401             if (string_get_field(sp, &p, &fields[2]))
48402                 goto done;
48403             if (string_get_month(sp, &p, &fields[1]))
48404                 goto done;
48405         } else {
48406             /* month first */
48407             if (string_get_month(sp, &p, &fields[1]))
48408                 goto done;
48409             if (string_get_field(sp, &p, &fields[2]))
48410                 goto done;
48411         }
48412         string_skip_spaces(sp, &p);
48413         if (string_get_signed_field(sp, &p, &fields[0]))
48414             goto done;
48415 
48416         /* hour, min, seconds */
48417         for(i = 0; i < 3; i++) {
48418             if (string_get_field(sp, &p, &fields[3 + i]))
48419                 goto done;
48420         }
48421         // XXX: parse optional milliseconds?
48422 
48423         /* parse the time zone offset if present: [+-]HHmm */
48424         is_local = FALSE;
48425         tz = 0;
48426         for (tz = 0; p < sp->len; p++) {
48427             sgn = string_get(sp, p);
48428             if (sgn == '+' || sgn == '-') {
48429                 p++;
48430                 if (string_get_digits(sp, &p, 2, &hh))
48431                     goto done;
48432                 if (string_get_digits(sp, &p, 2, &mm))
48433                     goto done;
48434                 tz = hh * 60 + mm;
48435                 if (sgn == '-')
48436                     tz = -tz;
48437                 break;
48438             }
48439         }
48440     }
48441     for(i = 0; i < 7; i++)
48442         fields1[i] = fields[i];
48443     d = set_date_fields(fields1, is_local) - tz * 60000;
48444     rv = JS_NewFloat64(ctx, d);
48445 
48446 done:
48447     JS_FreeValue(ctx, s);
48448     return rv;
48449 }
48450 
js_Date_now(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48451 static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,
48452                            int argc, JSValueConst *argv)
48453 {
48454     // now()
48455     return JS_NewInt64(ctx, date_now());
48456 }
48457 
js_date_Symbol_toPrimitive(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48458 static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,
48459                                           int argc, JSValueConst *argv)
48460 {
48461     // Symbol_toPrimitive(hint)
48462     JSValueConst obj = this_val;
48463     JSAtom hint = JS_ATOM_NULL;
48464     int hint_num;
48465 
48466     if (!JS_IsObject(obj))
48467         return JS_ThrowTypeErrorNotAnObject(ctx);
48468 
48469     if (JS_IsString(argv[0])) {
48470         hint = JS_ValueToAtom(ctx, argv[0]);
48471         if (hint == JS_ATOM_NULL)
48472             return JS_EXCEPTION;
48473         JS_FreeAtom(ctx, hint);
48474     }
48475     switch (hint) {
48476     case JS_ATOM_number:
48477 #ifdef CONFIG_BIGNUM
48478     case JS_ATOM_integer:
48479 #endif
48480         hint_num = HINT_NUMBER;
48481         break;
48482     case JS_ATOM_string:
48483     case JS_ATOM_default:
48484         hint_num = HINT_STRING;
48485         break;
48486     default:
48487         return JS_ThrowTypeError(ctx, "invalid hint");
48488     }
48489     return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);
48490 }
48491 
js_date_getTimezoneOffset(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48492 static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
48493                                          int argc, JSValueConst *argv)
48494 {
48495     // getTimezoneOffset()
48496     double v;
48497 
48498     if (JS_ThisTimeValue(ctx, &v, this_val))
48499         return JS_EXCEPTION;
48500     if (isnan(v))
48501         return JS_NAN;
48502     else
48503         return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));
48504 }
48505 
js_date_getTime(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48506 static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,
48507                                int argc, JSValueConst *argv)
48508 {
48509     // getTime()
48510     double v;
48511 
48512     if (JS_ThisTimeValue(ctx, &v, this_val))
48513         return JS_EXCEPTION;
48514     return JS_NewFloat64(ctx, v);
48515 }
48516 
js_date_setTime(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48517 static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,
48518                                int argc, JSValueConst *argv)
48519 {
48520     // setTime(v)
48521     double v;
48522 
48523     if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))
48524         return JS_EXCEPTION;
48525     return JS_SetThisTimeValue(ctx, this_val, time_clip(v));
48526 }
48527 
js_date_setYear(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48528 static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,
48529                                int argc, JSValueConst *argv)
48530 {
48531     // setYear(y)
48532     double y;
48533     JSValueConst args[1];
48534 
48535     if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))
48536         return JS_EXCEPTION;
48537     y = +y;
48538     if (isfinite(y)) {
48539         y = trunc(y);
48540         if (y >= 0 && y < 100)
48541             y += 1900;
48542     }
48543     args[0] = JS_NewFloat64(ctx, y);
48544     return set_date_field(ctx, this_val, 1, args, 0x011);
48545 }
48546 
js_date_toJSON(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48547 static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,
48548                               int argc, JSValueConst *argv)
48549 {
48550     // toJSON(key)
48551     JSValue obj, tv, method, rv;
48552     double d;
48553 
48554     rv = JS_EXCEPTION;
48555     tv = JS_UNDEFINED;
48556 
48557     obj = JS_ToObject(ctx, this_val);
48558     tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);
48559     if (JS_IsException(tv))
48560         goto exception;
48561     if (JS_IsNumber(tv)) {
48562         if (JS_ToFloat64(ctx, &d, tv) < 0)
48563             goto exception;
48564         if (!isfinite(d)) {
48565             rv = JS_NULL;
48566             goto done;
48567         }
48568     }
48569     method = JS_GetPropertyStr(ctx, obj, "toISOString");
48570     if (JS_IsException(method))
48571         goto exception;
48572     if (!JS_IsFunction(ctx, method)) {
48573         JS_ThrowTypeError(ctx, "object needs toISOString method");
48574         JS_FreeValue(ctx, method);
48575         goto exception;
48576     }
48577     rv = JS_CallFree(ctx, method, obj, 0, NULL);
48578 exception:
48579 done:
48580     JS_FreeValue(ctx, obj);
48581     JS_FreeValue(ctx, tv);
48582     return rv;
48583 }
48584 
48585 static const JSCFunctionListEntry js_date_funcs[] = {
48586     JS_CFUNC_DEF("now", 0, js_Date_now ),
48587     JS_CFUNC_DEF("parse", 1, js_Date_parse ),
48588     JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),
48589 };
48590 
48591 static const JSCFunctionListEntry js_date_proto_funcs[] = {
48592     JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),
48593     JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),
48594     JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),
48595     JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),
48596     JS_ALIAS_DEF("toGMTString", "toUTCString" ),
48597     JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),
48598     JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),
48599     JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),
48600     JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),
48601     JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),
48602     JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),
48603     JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),
48604     JS_CFUNC_DEF("getTime", 0, js_date_getTime ),
48605     JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),
48606     JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),
48607     JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),
48608     JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),
48609     JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),
48610     JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),
48611     JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),
48612     JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),
48613     JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),
48614     JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),
48615     JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),
48616     JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),
48617     JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),
48618     JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),
48619     JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),
48620     JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),
48621     JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),
48622     JS_CFUNC_DEF("setTime", 1, js_date_setTime ),
48623     JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),
48624     JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),
48625     JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),
48626     JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),
48627     JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),
48628     JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),
48629     JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),
48630     JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),
48631     JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),
48632     JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),
48633     JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),
48634     JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),
48635     JS_CFUNC_DEF("setYear", 1, js_date_setYear ),
48636     JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),
48637     JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),
48638     JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),
48639 };
48640 
JS_AddIntrinsicDate(JSContext * ctx)48641 void JS_AddIntrinsicDate(JSContext *ctx)
48642 {
48643     JSValueConst obj;
48644 
48645     /* Date */
48646     ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx);
48647     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs,
48648                                countof(js_date_proto_funcs));
48649     obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7,
48650                                    ctx->class_proto[JS_CLASS_DATE]);
48651     JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs));
48652 }
48653 
48654 /* eval */
48655 
JS_AddIntrinsicEval(JSContext * ctx)48656 void JS_AddIntrinsicEval(JSContext *ctx)
48657 {
48658     ctx->eval_internal = __JS_EvalInternal;
48659 }
48660 
48661 #ifdef CONFIG_BIGNUM
48662 
48663 /* Operators */
48664 
js_operator_set_finalizer(JSRuntime * rt,JSValue val)48665 static void js_operator_set_finalizer(JSRuntime *rt, JSValue val)
48666 {
48667     JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
48668     int i, j;
48669     JSBinaryOperatorDefEntry *ent;
48670 
48671     if (opset) {
48672         for(i = 0; i < JS_OVOP_COUNT; i++) {
48673             if (opset->self_ops[i])
48674                 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]));
48675         }
48676         for(j = 0; j < opset->left.count; j++) {
48677             ent = &opset->left.tab[j];
48678             for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
48679                 if (ent->ops[i])
48680                     JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
48681             }
48682         }
48683         js_free_rt(rt, opset->left.tab);
48684         for(j = 0; j < opset->right.count; j++) {
48685             ent = &opset->right.tab[j];
48686             for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
48687                 if (ent->ops[i])
48688                     JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
48689             }
48690         }
48691         js_free_rt(rt, opset->right.tab);
48692         js_free_rt(rt, opset);
48693     }
48694 }
48695 
js_operator_set_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)48696 static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
48697                                  JS_MarkFunc *mark_func)
48698 {
48699     JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
48700     int i, j;
48701     JSBinaryOperatorDefEntry *ent;
48702 
48703     if (opset) {
48704         for(i = 0; i < JS_OVOP_COUNT; i++) {
48705             if (opset->self_ops[i])
48706                 JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]),
48707                              mark_func);
48708         }
48709         for(j = 0; j < opset->left.count; j++) {
48710             ent = &opset->left.tab[j];
48711             for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
48712                 if (ent->ops[i])
48713                     JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
48714                                  mark_func);
48715             }
48716         }
48717         for(j = 0; j < opset->right.count; j++) {
48718             ent = &opset->right.tab[j];
48719             for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
48720                 if (ent->ops[i])
48721                     JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
48722                                  mark_func);
48723             }
48724         }
48725     }
48726 }
48727 
48728 
48729 /* create an OperatorSet object */
js_operators_create_internal(JSContext * ctx,int argc,JSValueConst * argv,BOOL is_primitive)48730 static JSValue js_operators_create_internal(JSContext *ctx,
48731                                             int argc, JSValueConst *argv,
48732                                             BOOL is_primitive)
48733 {
48734     JSValue opset_obj, prop, obj;
48735     JSOperatorSetData *opset, *opset1;
48736     JSBinaryOperatorDef *def;
48737     JSValueConst arg;
48738     int i, j;
48739     JSBinaryOperatorDefEntry *new_tab;
48740     JSBinaryOperatorDefEntry *ent;
48741     uint32_t op_count;
48742 
48743     if (ctx->rt->operator_count == UINT32_MAX) {
48744         return JS_ThrowTypeError(ctx, "too many operators");
48745     }
48746     opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET);
48747     if (JS_IsException(opset_obj))
48748         goto fail;
48749     opset = js_mallocz(ctx, sizeof(*opset));
48750     if (!opset)
48751         goto fail;
48752     JS_SetOpaque(opset_obj, opset);
48753     if (argc >= 1) {
48754         arg = argv[0];
48755         /* self operators */
48756         for(i = 0; i < JS_OVOP_COUNT; i++) {
48757             prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]);
48758             if (JS_IsException(prop))
48759                 goto fail;
48760             if (!JS_IsUndefined(prop)) {
48761                 if (check_function(ctx, prop)) {
48762                     JS_FreeValue(ctx, prop);
48763                     goto fail;
48764                 }
48765                 opset->self_ops[i] = JS_VALUE_GET_OBJ(prop);
48766             }
48767         }
48768     }
48769     /* left & right operators */
48770     for(j = 1; j < argc; j++) {
48771         arg = argv[j];
48772         prop = JS_GetPropertyStr(ctx, arg, "left");
48773         if (JS_IsException(prop))
48774             goto fail;
48775         def = &opset->right;
48776         if (JS_IsUndefined(prop)) {
48777             prop = JS_GetPropertyStr(ctx, arg, "right");
48778             if (JS_IsException(prop))
48779                 goto fail;
48780             if (JS_IsUndefined(prop)) {
48781                 JS_ThrowTypeError(ctx, "left or right property must be present");
48782                 goto fail;
48783             }
48784             def = &opset->left;
48785         }
48786         /* get the operator set */
48787         obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype);
48788         JS_FreeValue(ctx, prop);
48789         if (JS_IsException(obj))
48790             goto fail;
48791         prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
48792         JS_FreeValue(ctx, obj);
48793         if (JS_IsException(prop))
48794             goto fail;
48795         opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET);
48796         if (!opset1) {
48797             JS_FreeValue(ctx, prop);
48798             goto fail;
48799         }
48800         op_count = opset1->operator_counter;
48801         JS_FreeValue(ctx, prop);
48802 
48803         /* we assume there are few entries */
48804         new_tab = js_realloc(ctx, def->tab,
48805                              (def->count + 1) * sizeof(def->tab[0]));
48806         if (!new_tab)
48807             goto fail;
48808         def->tab = new_tab;
48809         def->count++;
48810         ent = def->tab + def->count - 1;
48811         memset(ent, 0, sizeof(def->tab[0]));
48812         ent->operator_index = op_count;
48813 
48814         for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
48815             prop = JS_GetPropertyStr(ctx, arg,
48816                                      js_overloadable_operator_names[i]);
48817             if (JS_IsException(prop))
48818                 goto fail;
48819             if (!JS_IsUndefined(prop)) {
48820                 if (check_function(ctx, prop)) {
48821                     JS_FreeValue(ctx, prop);
48822                     goto fail;
48823                 }
48824                 ent->ops[i] = JS_VALUE_GET_OBJ(prop);
48825             }
48826         }
48827     }
48828     opset->is_primitive = is_primitive;
48829     opset->operator_counter = ctx->rt->operator_count++;
48830     return opset_obj;
48831  fail:
48832     JS_FreeValue(ctx, opset_obj);
48833     return JS_EXCEPTION;
48834 }
48835 
js_operators_create(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48836 static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val,
48837                                 int argc, JSValueConst *argv)
48838 {
48839     return js_operators_create_internal(ctx, argc, argv, FALSE);
48840 }
48841 
js_operators_updateBigIntOperators(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48842 static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val,
48843                                                   int argc, JSValueConst *argv)
48844 {
48845     JSValue opset_obj, prop;
48846     JSOperatorSetData *opset;
48847     const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW };
48848     JSOverloadableOperatorEnum op;
48849     int i;
48850 
48851     opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
48852                                JS_ATOM_Symbol_operatorSet);
48853     if (JS_IsException(opset_obj))
48854         goto fail;
48855     opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET);
48856     if (!opset)
48857         goto fail;
48858     for(i = 0; i < countof(ops); i++) {
48859         op = ops[i];
48860         prop = JS_GetPropertyStr(ctx, argv[0],
48861                                  js_overloadable_operator_names[op]);
48862         if (JS_IsException(prop))
48863             goto fail;
48864         if (!JS_IsUndefined(prop)) {
48865             if (!JS_IsNull(prop) && check_function(ctx, prop)) {
48866                 JS_FreeValue(ctx, prop);
48867                 goto fail;
48868             }
48869             if (opset->self_ops[op])
48870                 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op]));
48871             if (JS_IsNull(prop)) {
48872                 opset->self_ops[op] = NULL;
48873             } else {
48874                 opset->self_ops[op] = JS_VALUE_GET_PTR(prop);
48875             }
48876         }
48877     }
48878     JS_FreeValue(ctx, opset_obj);
48879     return JS_UNDEFINED;
48880  fail:
48881     JS_FreeValue(ctx, opset_obj);
48882     return JS_EXCEPTION;
48883 }
48884 
js_operators_set_default(JSContext * ctx,JSValueConst obj)48885 static int js_operators_set_default(JSContext *ctx, JSValueConst obj)
48886 {
48887     JSValue opset_obj;
48888 
48889     if (!JS_IsObject(obj)) /* in case the prototype is not defined */
48890         return 0;
48891     opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE);
48892     if (JS_IsException(opset_obj))
48893         return -1;
48894     /* cannot be modified by the user */
48895     JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet,
48896                            opset_obj, 0);
48897     return 0;
48898 }
48899 
js_dummy_operators_ctor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)48900 static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target,
48901                                        int argc, JSValueConst *argv)
48902 {
48903     return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
48904 }
48905 
js_global_operators(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)48906 static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val,
48907                                    int argc, JSValueConst *argv)
48908 {
48909     JSValue func_obj, proto, opset_obj;
48910 
48911     func_obj = JS_UNDEFINED;
48912     proto = JS_NewObject(ctx);
48913     if (JS_IsException(proto))
48914         return JS_EXCEPTION;
48915     opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE);
48916     if (JS_IsException(opset_obj))
48917         goto fail;
48918     JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet,
48919                            opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
48920     func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators",
48921                                 0, JS_CFUNC_constructor, 0);
48922     if (JS_IsException(func_obj))
48923         goto fail;
48924     JS_SetConstructor2(ctx, func_obj, proto,
48925                        0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
48926     JS_FreeValue(ctx, proto);
48927     return func_obj;
48928  fail:
48929     JS_FreeValue(ctx, proto);
48930     JS_FreeValue(ctx, func_obj);
48931     return JS_EXCEPTION;
48932 }
48933 
48934 static const JSCFunctionListEntry js_operators_funcs[] = {
48935     JS_CFUNC_DEF("create", 1, js_operators_create ),
48936     JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ),
48937 };
48938 
48939 /* must be called after all overloadable base types are initialized */
JS_AddIntrinsicOperators(JSContext * ctx)48940 void JS_AddIntrinsicOperators(JSContext *ctx)
48941 {
48942     JSValue obj;
48943 
48944     ctx->allow_operator_overloading = TRUE;
48945     obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1);
48946     JS_SetPropertyFunctionList(ctx, obj,
48947                                js_operators_funcs,
48948                                countof(js_operators_funcs));
48949     JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators,
48950                            obj,
48951                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
48952     /* add default operatorSets */
48953     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]);
48954     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]);
48955     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]);
48956     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]);
48957     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]);
48958     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
48959 }
48960 
48961 /* BigInt */
48962 
JS_ToBigIntCtorFree(JSContext * ctx,JSValue val)48963 static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
48964 {
48965     uint32_t tag;
48966 
48967  redo:
48968     tag = JS_VALUE_GET_NORM_TAG(val);
48969     switch(tag) {
48970     case JS_TAG_INT:
48971     case JS_TAG_BOOL:
48972         val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
48973         break;
48974     case JS_TAG_BIG_INT:
48975         break;
48976     case JS_TAG_FLOAT64:
48977     case JS_TAG_BIG_FLOAT:
48978         {
48979             bf_t *a, a_s;
48980 
48981             a = JS_ToBigFloat(ctx, &a_s, val);
48982             if (!bf_is_finite(a)) {
48983                 JS_FreeValue(ctx, val);
48984                 val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint");
48985             } else {
48986                 JSValue val1 = JS_NewBigInt(ctx);
48987                 bf_t *r;
48988                 int ret;
48989                 if (JS_IsException(val1)) {
48990                     JS_FreeValue(ctx, val);
48991                     return JS_EXCEPTION;
48992                 }
48993                 r = JS_GetBigInt(val1);
48994                 ret = bf_set(r, a);
48995                 ret |= bf_rint(r, BF_RNDZ);
48996                 JS_FreeValue(ctx, val);
48997                 if (ret & BF_ST_MEM_ERROR) {
48998                     JS_FreeValue(ctx, val1);
48999                     val = JS_ThrowOutOfMemory(ctx);
49000                 } else if (ret & BF_ST_INEXACT) {
49001                     JS_FreeValue(ctx, val1);
49002                     val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer");
49003                 } else {
49004                     val = JS_CompactBigInt(ctx, val1);
49005                 }
49006             }
49007             if (a == &a_s)
49008                 bf_delete(a);
49009         }
49010         break;
49011     case JS_TAG_BIG_DECIMAL:
49012         val = JS_ToStringFree(ctx, val);
49013          if (JS_IsException(val))
49014             break;
49015         goto redo;
49016     case JS_TAG_STRING:
49017         val = JS_StringToBigIntErr(ctx, val);
49018         break;
49019     case JS_TAG_OBJECT:
49020         val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
49021         if (JS_IsException(val))
49022             break;
49023         goto redo;
49024     case JS_TAG_NULL:
49025     case JS_TAG_UNDEFINED:
49026     default:
49027         JS_FreeValue(ctx, val);
49028         return JS_ThrowTypeError(ctx, "cannot convert to bigint");
49029     }
49030     return val;
49031 }
49032 
js_bigint_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)49033 static JSValue js_bigint_constructor(JSContext *ctx,
49034                                      JSValueConst new_target,
49035                                      int argc, JSValueConst *argv)
49036 {
49037     if (!JS_IsUndefined(new_target))
49038         return JS_ThrowTypeError(ctx, "not a constructor");
49039     return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
49040 }
49041 
js_thisBigIntValue(JSContext * ctx,JSValueConst this_val)49042 static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
49043 {
49044     if (JS_IsBigInt(ctx, this_val))
49045         return JS_DupValue(ctx, this_val);
49046 
49047     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
49048         JSObject *p = JS_VALUE_GET_OBJ(this_val);
49049         if (p->class_id == JS_CLASS_BIG_INT) {
49050             if (JS_IsBigInt(ctx, p->u.object_data))
49051                 return JS_DupValue(ctx, p->u.object_data);
49052         }
49053     }
49054     return JS_ThrowTypeError(ctx, "not a bigint");
49055 }
49056 
js_bigint_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49057 static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
49058                                   int argc, JSValueConst *argv)
49059 {
49060     JSValue val;
49061     int base;
49062     JSValue ret;
49063 
49064     val = js_thisBigIntValue(ctx, this_val);
49065     if (JS_IsException(val))
49066         return val;
49067     if (argc == 0 || JS_IsUndefined(argv[0])) {
49068         base = 10;
49069     } else {
49070         base = js_get_radix(ctx, argv[0]);
49071         if (base < 0)
49072             goto fail;
49073     }
49074     ret = js_bigint_to_string1(ctx, val, base);
49075     JS_FreeValue(ctx, val);
49076     return ret;
49077  fail:
49078     JS_FreeValue(ctx, val);
49079     return JS_EXCEPTION;
49080 }
49081 
js_bigint_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49082 static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,
49083                                  int argc, JSValueConst *argv)
49084 {
49085     return js_thisBigIntValue(ctx, this_val);
49086 }
49087 
js_bigint_div(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)49088 static JSValue js_bigint_div(JSContext *ctx,
49089                               JSValueConst this_val,
49090                               int argc, JSValueConst *argv, int magic)
49091 {
49092     bf_t a_s, b_s, *a, *b, *r, *q;
49093     int status;
49094     JSValue q_val, r_val;
49095 
49096     q_val = JS_NewBigInt(ctx);
49097     if (JS_IsException(q_val))
49098         return JS_EXCEPTION;
49099     r_val = JS_NewBigInt(ctx);
49100     if (JS_IsException(r_val))
49101         goto fail;
49102     b = NULL;
49103     a = JS_ToBigInt(ctx, &a_s, argv[0]);
49104     if (!a)
49105         goto fail;
49106     b = JS_ToBigInt(ctx, &b_s, argv[1]);
49107     if (!b) {
49108         JS_FreeBigInt(ctx, a, &a_s);
49109         goto fail;
49110     }
49111     q = JS_GetBigInt(q_val);
49112     r = JS_GetBigInt(r_val);
49113     status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf);
49114     JS_FreeBigInt(ctx, a, &a_s);
49115     JS_FreeBigInt(ctx, b, &b_s);
49116     if (unlikely(status)) {
49117         throw_bf_exception(ctx, status);
49118         goto fail;
49119     }
49120     q_val = JS_CompactBigInt(ctx, q_val);
49121     if (magic & 0x10) {
49122         JSValue ret;
49123         ret = JS_NewArray(ctx);
49124         if (JS_IsException(ret))
49125             goto fail;
49126         JS_SetPropertyUint32(ctx, ret, 0, q_val);
49127         JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val));
49128         return ret;
49129     } else {
49130         JS_FreeValue(ctx, r_val);
49131         return q_val;
49132     }
49133  fail:
49134     JS_FreeValue(ctx, q_val);
49135     JS_FreeValue(ctx, r_val);
49136     return JS_EXCEPTION;
49137 }
49138 
js_bigint_sqrt(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)49139 static JSValue js_bigint_sqrt(JSContext *ctx,
49140                                JSValueConst this_val,
49141                                int argc, JSValueConst *argv, int magic)
49142 {
49143     bf_t a_s, *a, *r, *rem;
49144     int status;
49145     JSValue r_val, rem_val;
49146 
49147     r_val = JS_NewBigInt(ctx);
49148     if (JS_IsException(r_val))
49149         return JS_EXCEPTION;
49150     rem_val = JS_NewBigInt(ctx);
49151     if (JS_IsException(rem_val))
49152         return JS_EXCEPTION;
49153     r = JS_GetBigInt(r_val);
49154     rem = JS_GetBigInt(rem_val);
49155 
49156     a = JS_ToBigInt(ctx, &a_s, argv[0]);
49157     if (!a)
49158         goto fail;
49159     status = bf_sqrtrem(r, rem, a);
49160     JS_FreeBigInt(ctx, a, &a_s);
49161     if (unlikely(status & ~BF_ST_INEXACT)) {
49162         throw_bf_exception(ctx, status);
49163         goto fail;
49164     }
49165     r_val = JS_CompactBigInt(ctx, r_val);
49166     if (magic) {
49167         JSValue ret;
49168         ret = JS_NewArray(ctx);
49169         if (JS_IsException(ret))
49170             goto fail;
49171         JS_SetPropertyUint32(ctx, ret, 0, r_val);
49172         JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val));
49173         return ret;
49174     } else {
49175         JS_FreeValue(ctx, rem_val);
49176         return r_val;
49177     }
49178  fail:
49179     JS_FreeValue(ctx, r_val);
49180     JS_FreeValue(ctx, rem_val);
49181     return JS_EXCEPTION;
49182 }
49183 
js_bigint_op1(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)49184 static JSValue js_bigint_op1(JSContext *ctx,
49185                               JSValueConst this_val,
49186                               int argc, JSValueConst *argv,
49187                               int magic)
49188 {
49189     bf_t a_s, *a;
49190     int64_t res;
49191 
49192     a = JS_ToBigInt(ctx, &a_s, argv[0]);
49193     if (!a)
49194         return JS_EXCEPTION;
49195     switch(magic) {
49196     case 0: /* floorLog2 */
49197         if (a->sign || a->expn <= 0) {
49198             res = -1;
49199         } else {
49200             res = a->expn - 1;
49201         }
49202         break;
49203     case 1: /* ctz */
49204         if (bf_is_zero(a)) {
49205             res = -1;
49206         } else {
49207             res = bf_get_exp_min(a);
49208         }
49209         break;
49210     default:
49211         abort();
49212     }
49213     JS_FreeBigInt(ctx, a, &a_s);
49214     return JS_NewBigInt64(ctx, res);
49215 }
49216 
js_bigint_asUintN(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int asIntN)49217 static JSValue js_bigint_asUintN(JSContext *ctx,
49218                                   JSValueConst this_val,
49219                                   int argc, JSValueConst *argv, int asIntN)
49220 {
49221     uint64_t bits;
49222     bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s;
49223     JSValue res;
49224 
49225     if (JS_ToIndex(ctx, &bits, argv[0]))
49226         return JS_EXCEPTION;
49227     res = JS_NewBigInt(ctx);
49228     if (JS_IsException(res))
49229         return JS_EXCEPTION;
49230     r = JS_GetBigInt(res);
49231     a = JS_ToBigInt(ctx, &a_s, argv[1]);
49232     if (!a) {
49233         JS_FreeValue(ctx, res);
49234         return JS_EXCEPTION;
49235     }
49236     /* XXX: optimize */
49237     r = JS_GetBigInt(res);
49238     bf_init(ctx->bf_ctx, mask);
49239     bf_set_ui(mask, 1);
49240     bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
49241     bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ);
49242     bf_logic_and(r, a, mask);
49243     if (asIntN && bits != 0) {
49244         bf_set_ui(mask, 1);
49245         bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ);
49246         if (bf_cmpu(r, mask) >= 0) {
49247             bf_set_ui(mask, 1);
49248             bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
49249             bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ);
49250         }
49251     }
49252     bf_delete(mask);
49253     JS_FreeBigInt(ctx, a, &a_s);
49254     return JS_CompactBigInt(ctx, res);
49255 }
49256 
49257 static const JSCFunctionListEntry js_bigint_funcs[] = {
49258     JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),
49259     JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),
49260     /* QuickJS extensions */
49261     JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ),
49262     JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ),
49263     JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ),
49264     JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ),
49265     JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ),
49266     JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ),
49267     JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ),
49268     JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ),
49269     JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ),
49270     JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ),
49271     JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ),
49272     JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ),
49273 };
49274 
49275 static const JSCFunctionListEntry js_bigint_proto_funcs[] = {
49276     JS_CFUNC_DEF("toString", 0, js_bigint_toString ),
49277     JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),
49278     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),
49279 };
49280 
JS_AddIntrinsicBigInt(JSContext * ctx)49281 void JS_AddIntrinsicBigInt(JSContext *ctx)
49282 {
49283     JSRuntime *rt = ctx->rt;
49284     JSValueConst obj1;
49285 
49286     rt->bigint_ops.to_string = js_bigint_to_string;
49287     rt->bigint_ops.from_string = js_string_to_bigint;
49288     rt->bigint_ops.unary_arith = js_unary_arith_bigint;
49289     rt->bigint_ops.binary_arith = js_binary_arith_bigint;
49290     rt->bigint_ops.compare = js_compare_bigfloat;
49291 
49292     ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);
49293     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
49294                                js_bigint_proto_funcs,
49295                                countof(js_bigint_proto_funcs));
49296     obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1,
49297                                     ctx->class_proto[JS_CLASS_BIG_INT]);
49298     JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,
49299                                countof(js_bigint_funcs));
49300 }
49301 
49302 /* BigFloat */
49303 
js_thisBigFloatValue(JSContext * ctx,JSValueConst this_val)49304 static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val)
49305 {
49306     if (JS_IsBigFloat(this_val))
49307         return JS_DupValue(ctx, this_val);
49308 
49309     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
49310         JSObject *p = JS_VALUE_GET_OBJ(this_val);
49311         if (p->class_id == JS_CLASS_BIG_FLOAT) {
49312             if (JS_IsBigFloat(p->u.object_data))
49313                 return JS_DupValue(ctx, p->u.object_data);
49314         }
49315     }
49316     return JS_ThrowTypeError(ctx, "not a bigfloat");
49317 }
49318 
js_bigfloat_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49319 static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val,
49320                                     int argc, JSValueConst *argv)
49321 {
49322     JSValue val;
49323     int base;
49324     JSValue ret;
49325 
49326     val = js_thisBigFloatValue(ctx, this_val);
49327     if (JS_IsException(val))
49328         return val;
49329     if (argc == 0 || JS_IsUndefined(argv[0])) {
49330         base = 10;
49331     } else {
49332         base = js_get_radix(ctx, argv[0]);
49333         if (base < 0)
49334             goto fail;
49335     }
49336     ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
49337     JS_FreeValue(ctx, val);
49338     return ret;
49339  fail:
49340     JS_FreeValue(ctx, val);
49341     return JS_EXCEPTION;
49342 }
49343 
js_bigfloat_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49344 static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val,
49345                                    int argc, JSValueConst *argv)
49346 {
49347     return js_thisBigFloatValue(ctx, this_val);
49348 }
49349 
bigfloat_get_rnd_mode(JSContext * ctx,JSValueConst val)49350 static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val)
49351 {
49352     int rnd_mode;
49353     if (JS_ToInt32Sat(ctx, &rnd_mode, val))
49354         return -1;
49355     if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) {
49356         JS_ThrowRangeError(ctx, "invalid rounding mode");
49357         return -1;
49358     }
49359     return rnd_mode;
49360 }
49361 
js_bigfloat_toFixed(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49362 static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val,
49363                                  int argc, JSValueConst *argv)
49364 {
49365     JSValue val, ret;
49366     int64_t f;
49367     int rnd_mode, radix;
49368 
49369     val = js_thisBigFloatValue(ctx, this_val);
49370     if (JS_IsException(val))
49371         return val;
49372     if (JS_ToInt64Sat(ctx, &f, argv[0]))
49373         goto fail;
49374     if (f < 0 || f > BF_PREC_MAX) {
49375         JS_ThrowRangeError(ctx, "invalid number of digits");
49376         goto fail;
49377     }
49378     rnd_mode = BF_RNDNA;
49379     radix = 10;
49380     /* XXX: swap parameter order for rounding mode and radix */
49381     if (argc > 1) {
49382         rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
49383         if (rnd_mode < 0)
49384             goto fail;
49385     }
49386     if (argc > 2) {
49387         radix = js_get_radix(ctx, argv[2]);
49388         if (radix < 0)
49389             goto fail;
49390     }
49391     ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
49392     JS_FreeValue(ctx, val);
49393     return ret;
49394  fail:
49395     JS_FreeValue(ctx, val);
49396     return JS_EXCEPTION;
49397 }
49398 
js_bigfloat_is_finite(JSContext * ctx,JSValueConst val)49399 static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val)
49400 {
49401     BOOL res;
49402     uint32_t tag;
49403 
49404     tag = JS_VALUE_GET_NORM_TAG(val);
49405     switch(tag) {
49406     case JS_TAG_BIG_FLOAT:
49407         {
49408             JSBigFloat *p = JS_VALUE_GET_PTR(val);
49409             res = bf_is_finite(&p->num);
49410         }
49411         break;
49412     default:
49413         res = FALSE;
49414         break;
49415     }
49416     return res;
49417 }
49418 
js_bigfloat_toExponential(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49419 static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val,
49420                                        int argc, JSValueConst *argv)
49421 {
49422     JSValue val, ret;
49423     int64_t f;
49424     int rnd_mode, radix;
49425 
49426     val = js_thisBigFloatValue(ctx, this_val);
49427     if (JS_IsException(val))
49428         return val;
49429     if (JS_ToInt64Sat(ctx, &f, argv[0]))
49430         goto fail;
49431     if (!js_bigfloat_is_finite(ctx, val)) {
49432         ret = JS_ToString(ctx, val);
49433     } else if (JS_IsUndefined(argv[0])) {
49434         ret = js_ftoa(ctx, val, 10, 0,
49435                       BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
49436     } else {
49437         if (f < 0 || f > BF_PREC_MAX) {
49438             JS_ThrowRangeError(ctx, "invalid number of digits");
49439             goto fail;
49440         }
49441         rnd_mode = BF_RNDNA;
49442         radix = 10;
49443         if (argc > 1) {
49444             rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
49445             if (rnd_mode < 0)
49446                 goto fail;
49447         }
49448         if (argc > 2) {
49449             radix = js_get_radix(ctx, argv[2]);
49450             if (radix < 0)
49451                 goto fail;
49452         }
49453         ret = js_ftoa(ctx, val, radix, f + 1,
49454                       rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
49455     }
49456     JS_FreeValue(ctx, val);
49457     return ret;
49458  fail:
49459     JS_FreeValue(ctx, val);
49460     return JS_EXCEPTION;
49461 }
49462 
js_bigfloat_toPrecision(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49463 static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val,
49464                                      int argc, JSValueConst *argv)
49465 {
49466     JSValue val, ret;
49467     int64_t p;
49468     int rnd_mode, radix;
49469 
49470     val = js_thisBigFloatValue(ctx, this_val);
49471     if (JS_IsException(val))
49472         return val;
49473     if (JS_IsUndefined(argv[0]))
49474         goto to_string;
49475     if (JS_ToInt64Sat(ctx, &p, argv[0]))
49476         goto fail;
49477     if (!js_bigfloat_is_finite(ctx, val)) {
49478     to_string:
49479         ret = JS_ToString(ctx, this_val);
49480     } else {
49481         if (p < 1 || p > BF_PREC_MAX) {
49482             JS_ThrowRangeError(ctx, "invalid number of digits");
49483             goto fail;
49484         }
49485         rnd_mode = BF_RNDNA;
49486         radix = 10;
49487         if (argc > 1) {
49488             rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
49489             if (rnd_mode < 0)
49490                 goto fail;
49491         }
49492         if (argc > 2) {
49493             radix = js_get_radix(ctx, argv[2]);
49494             if (radix < 0)
49495                 goto fail;
49496         }
49497         ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED);
49498     }
49499     JS_FreeValue(ctx, val);
49500     return ret;
49501  fail:
49502     JS_FreeValue(ctx, val);
49503     return JS_EXCEPTION;
49504 }
49505 
49506 static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = {
49507     JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ),
49508     JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ),
49509     JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ),
49510     JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ),
49511     JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ),
49512 };
49513 
js_bigfloat_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)49514 static JSValue js_bigfloat_constructor(JSContext *ctx,
49515                                        JSValueConst new_target,
49516                                        int argc, JSValueConst *argv)
49517 {
49518     JSValue val;
49519     if (!JS_IsUndefined(new_target))
49520         return JS_ThrowTypeError(ctx, "not a constructor");
49521     if (argc == 0) {
49522         bf_t *r;
49523         val = JS_NewBigFloat(ctx);
49524         if (JS_IsException(val))
49525             return val;
49526         r = JS_GetBigFloat(val);
49527         bf_set_zero(r, 0);
49528     } else {
49529         val = JS_DupValue(ctx, argv[0]);
49530     redo:
49531         switch(JS_VALUE_GET_NORM_TAG(val)) {
49532         case JS_TAG_BIG_FLOAT:
49533             break;
49534         case JS_TAG_FLOAT64:
49535             {
49536                 bf_t *r;
49537                 double d = JS_VALUE_GET_FLOAT64(val);
49538                 val = JS_NewBigFloat(ctx);
49539                 if (JS_IsException(val))
49540                     break;
49541                 r = JS_GetBigFloat(val);
49542                 if (bf_set_float64(r, d))
49543                     goto fail;
49544             }
49545             break;
49546         case JS_TAG_INT:
49547             {
49548                 bf_t *r;
49549                 int32_t v = JS_VALUE_GET_INT(val);
49550                 val = JS_NewBigFloat(ctx);
49551                 if (JS_IsException(val))
49552                     break;
49553                 r = JS_GetBigFloat(val);
49554                 if (bf_set_si(r, v))
49555                     goto fail;
49556             }
49557             break;
49558         case JS_TAG_BIG_INT:
49559             /* We keep the full precision of the integer */
49560             {
49561                 JSBigFloat *p = JS_VALUE_GET_PTR(val);
49562                 val = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
49563             }
49564             break;
49565         case JS_TAG_BIG_DECIMAL:
49566             val = JS_ToStringFree(ctx, val);
49567             if (JS_IsException(val))
49568                 break;
49569             goto redo;
49570         case JS_TAG_STRING:
49571             {
49572                 const char *str, *p;
49573                 size_t len;
49574                 int err;
49575 
49576                 str = JS_ToCStringLen(ctx, &len, val);
49577                 JS_FreeValue(ctx, val);
49578                 if (!str)
49579                     return JS_EXCEPTION;
49580                 p = str;
49581                 p += skip_spaces(p);
49582                 if ((p - str) == len) {
49583                     bf_t *r;
49584                     val = JS_NewBigFloat(ctx);
49585                     if (JS_IsException(val))
49586                         break;
49587                     r = JS_GetBigFloat(val);
49588                     bf_set_zero(r, 0);
49589                     err = 0;
49590                 } else {
49591                     val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT |
49592                                   ATOD_TYPE_BIG_FLOAT |
49593                                   ATOD_ACCEPT_PREFIX_AFTER_SIGN);
49594                     if (JS_IsException(val)) {
49595                         JS_FreeCString(ctx, str);
49596                         return JS_EXCEPTION;
49597                     }
49598                     p += skip_spaces(p);
49599                     err = ((p - str) != len);
49600                 }
49601                 JS_FreeCString(ctx, str);
49602                 if (err) {
49603                     JS_FreeValue(ctx, val);
49604                     return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal");
49605                 }
49606             }
49607             break;
49608         case JS_TAG_OBJECT:
49609             val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
49610             if (JS_IsException(val))
49611                 break;
49612             goto redo;
49613         case JS_TAG_NULL:
49614         case JS_TAG_UNDEFINED:
49615         default:
49616             JS_FreeValue(ctx, val);
49617             return JS_ThrowTypeError(ctx, "cannot convert to bigfloat");
49618         }
49619     }
49620     return val;
49621  fail:
49622     JS_FreeValue(ctx, val);
49623     return JS_EXCEPTION;
49624 }
49625 
js_bigfloat_get_const(JSContext * ctx,JSValueConst this_val,int magic)49626 static JSValue js_bigfloat_get_const(JSContext *ctx,
49627                                      JSValueConst this_val, int magic)
49628 {
49629     bf_t *r;
49630     JSValue val;
49631     val = JS_NewBigFloat(ctx);
49632     if (JS_IsException(val))
49633         return val;
49634     r = JS_GetBigFloat(val);
49635     switch(magic) {
49636     case 0: /* PI */
49637         bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags);
49638         break;
49639     case 1: /* LN2 */
49640         bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags);
49641         break;
49642     case 2: /* MIN_VALUE */
49643     case 3: /* MAX_VALUE */
49644         {
49645             slimb_t e_range, e;
49646             e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1);
49647             bf_set_ui(r, 1);
49648             if (magic == 2) {
49649                 e = -e_range + 2;
49650                 if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL)
49651                     e -= ctx->fp_env.prec - 1;
49652                 bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags);
49653             } else {
49654                 bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec,
49655                             ctx->fp_env.flags);
49656                 bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags);
49657                 bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec,
49658                             ctx->fp_env.flags);
49659             }
49660         }
49661         break;
49662     case 4: /* EPSILON */
49663         bf_set_ui(r, 1);
49664         bf_mul_2exp(r, 1 - ctx->fp_env.prec,
49665                     ctx->fp_env.prec, ctx->fp_env.flags);
49666         break;
49667     default:
49668         abort();
49669     }
49670     return val;
49671 }
49672 
js_bigfloat_parseFloat(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49673 static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val,
49674                                       int argc, JSValueConst *argv)
49675 {
49676     bf_t *a;
49677     const char *str;
49678     JSValue ret;
49679     int radix;
49680     JSFloatEnv *fe;
49681 
49682     str = JS_ToCString(ctx, argv[0]);
49683     if (!str)
49684         return JS_EXCEPTION;
49685     if (JS_ToInt32(ctx, &radix, argv[1])) {
49686     fail:
49687         JS_FreeCString(ctx, str);
49688         return JS_EXCEPTION;
49689     }
49690     if (radix != 0 && (radix < 2 || radix > 36)) {
49691         JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
49692         goto fail;
49693     }
49694     fe = &ctx->fp_env;
49695     if (argc > 2) {
49696         fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
49697         if (!fe)
49698             goto fail;
49699     }
49700     ret = JS_NewBigFloat(ctx);
49701     if (JS_IsException(ret))
49702         goto done;
49703     a = JS_GetBigFloat(ret);
49704     /* XXX: use js_atof() */
49705     bf_atof(a, str, NULL, radix, fe->prec, fe->flags);
49706  done:
49707     JS_FreeCString(ctx, str);
49708     return ret;
49709 }
49710 
js_bigfloat_isFinite(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49711 static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val,
49712                                     int argc, JSValueConst *argv)
49713 {
49714     JSValueConst val = argv[0];
49715     JSBigFloat *p;
49716 
49717     if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
49718         return JS_FALSE;
49719     p = JS_VALUE_GET_PTR(val);
49720     return JS_NewBool(ctx, bf_is_finite(&p->num));
49721 }
49722 
js_bigfloat_isNaN(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)49723 static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val,
49724                                  int argc, JSValueConst *argv)
49725 {
49726     JSValueConst val = argv[0];
49727     JSBigFloat *p;
49728 
49729     if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
49730         return JS_FALSE;
49731     p = JS_VALUE_GET_PTR(val);
49732     return JS_NewBool(ctx, bf_is_nan(&p->num));
49733 }
49734 
49735 enum {
49736     MATH_OP_ABS,
49737     MATH_OP_FLOOR,
49738     MATH_OP_CEIL,
49739     MATH_OP_ROUND,
49740     MATH_OP_TRUNC,
49741     MATH_OP_SQRT,
49742     MATH_OP_FPROUND,
49743     MATH_OP_ACOS,
49744     MATH_OP_ASIN,
49745     MATH_OP_ATAN,
49746     MATH_OP_ATAN2,
49747     MATH_OP_COS,
49748     MATH_OP_EXP,
49749     MATH_OP_LOG,
49750     MATH_OP_POW,
49751     MATH_OP_SIN,
49752     MATH_OP_TAN,
49753     MATH_OP_FMOD,
49754     MATH_OP_REM,
49755     MATH_OP_SIGN,
49756 
49757     MATH_OP_ADD,
49758     MATH_OP_SUB,
49759     MATH_OP_MUL,
49760     MATH_OP_DIV,
49761 };
49762 
js_bigfloat_fop(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)49763 static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val,
49764                            int argc, JSValueConst *argv, int magic)
49765 {
49766     bf_t a_s, *a, *r;
49767     JSFloatEnv *fe;
49768     int rnd_mode;
49769     JSValue op1, res;
49770 
49771     op1 = JS_ToNumeric(ctx, argv[0]);
49772     if (JS_IsException(op1))
49773         return op1;
49774     a = JS_ToBigFloat(ctx, &a_s, op1);
49775     fe = &ctx->fp_env;
49776     if (argc > 1) {
49777         fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV);
49778         if (!fe)
49779             goto fail;
49780     }
49781     res = JS_NewBigFloat(ctx);
49782     if (JS_IsException(res)) {
49783     fail:
49784         if (a == &a_s)
49785             bf_delete(a);
49786         JS_FreeValue(ctx, op1);
49787         return JS_EXCEPTION;
49788     }
49789     r = JS_GetBigFloat(res);
49790     switch (magic) {
49791     case MATH_OP_ABS:
49792         bf_set(r, a);
49793         r->sign = 0;
49794         break;
49795     case MATH_OP_FLOOR:
49796         rnd_mode = BF_RNDD;
49797         goto rint;
49798     case MATH_OP_CEIL:
49799         rnd_mode = BF_RNDU;
49800         goto rint;
49801     case MATH_OP_ROUND:
49802         rnd_mode = BF_RNDNA;
49803         goto rint;
49804     case MATH_OP_TRUNC:
49805         rnd_mode = BF_RNDZ;
49806     rint:
49807         bf_set(r, a);
49808         fe->status |= bf_rint(r, rnd_mode);
49809         break;
49810     case MATH_OP_SQRT:
49811         fe->status |= bf_sqrt(r, a, fe->prec, fe->flags);
49812         break;
49813     case MATH_OP_FPROUND:
49814         bf_set(r, a);
49815         fe->status |= bf_round(r, fe->prec, fe->flags);
49816         break;
49817     case MATH_OP_ACOS:
49818         fe->status |= bf_acos(r, a, fe->prec, fe->flags);
49819         break;
49820     case MATH_OP_ASIN:
49821         fe->status |= bf_asin(r, a, fe->prec, fe->flags);
49822         break;
49823     case MATH_OP_ATAN:
49824         fe->status |= bf_atan(r, a, fe->prec, fe->flags);
49825         break;
49826     case MATH_OP_COS:
49827         fe->status |= bf_cos(r, a, fe->prec, fe->flags);
49828         break;
49829     case MATH_OP_EXP:
49830         fe->status |= bf_exp(r, a, fe->prec, fe->flags);
49831         break;
49832     case MATH_OP_LOG:
49833         fe->status |= bf_log(r, a, fe->prec, fe->flags);
49834         break;
49835     case MATH_OP_SIN:
49836         fe->status |= bf_sin(r, a, fe->prec, fe->flags);
49837         break;
49838     case MATH_OP_TAN:
49839         fe->status |= bf_tan(r, a, fe->prec, fe->flags);
49840         break;
49841     case MATH_OP_SIGN:
49842         if (bf_is_nan(a) || bf_is_zero(a)) {
49843             bf_set(r, a);
49844         } else {
49845             bf_set_si(r, 1 - 2 * a->sign);
49846         }
49847         break;
49848     default:
49849         abort();
49850     }
49851     if (a == &a_s)
49852         bf_delete(a);
49853     JS_FreeValue(ctx, op1);
49854     return res;
49855 }
49856 
js_bigfloat_fop2(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)49857 static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val,
49858                             int argc, JSValueConst *argv, int magic)
49859 {
49860     bf_t a_s, *a, b_s, *b, r_s, *r = &r_s;
49861     JSFloatEnv *fe;
49862     JSValue op1, op2, res;
49863 
49864     op1 = JS_ToNumeric(ctx, argv[0]);
49865     if (JS_IsException(op1))
49866         return op1;
49867     op2 = JS_ToNumeric(ctx, argv[1]);
49868     if (JS_IsException(op2)) {
49869         JS_FreeValue(ctx, op1);
49870         return op2;
49871     }
49872     a = JS_ToBigFloat(ctx, &a_s, op1);
49873     b = JS_ToBigFloat(ctx, &b_s, op2);
49874     fe = &ctx->fp_env;
49875     if (argc > 2) {
49876         fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
49877         if (!fe)
49878             goto fail;
49879     }
49880     res = JS_NewBigFloat(ctx);
49881     if (JS_IsException(res)) {
49882     fail:
49883         if (a == &a_s)
49884             bf_delete(a);
49885         if (b == &b_s)
49886             bf_delete(b);
49887         JS_FreeValue(ctx, op1);
49888         JS_FreeValue(ctx, op2);
49889         return JS_EXCEPTION;
49890     }
49891     r = JS_GetBigFloat(res);
49892     switch (magic) {
49893     case MATH_OP_ATAN2:
49894         fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags);
49895         break;
49896     case MATH_OP_POW:
49897         fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS);
49898         break;
49899     case MATH_OP_FMOD:
49900         fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
49901         break;
49902     case MATH_OP_REM:
49903         fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN);
49904         break;
49905     case MATH_OP_ADD:
49906         fe->status |= bf_add(r, a, b, fe->prec, fe->flags);
49907         break;
49908     case MATH_OP_SUB:
49909         fe->status |= bf_sub(r, a, b, fe->prec, fe->flags);
49910         break;
49911     case MATH_OP_MUL:
49912         fe->status |= bf_mul(r, a, b, fe->prec, fe->flags);
49913         break;
49914     case MATH_OP_DIV:
49915         fe->status |= bf_div(r, a, b, fe->prec, fe->flags);
49916         break;
49917     default:
49918         abort();
49919     }
49920     if (a == &a_s)
49921         bf_delete(a);
49922     if (b == &b_s)
49923         bf_delete(b);
49924     JS_FreeValue(ctx, op1);
49925     JS_FreeValue(ctx, op2);
49926     return res;
49927 }
49928 
49929 static const JSCFunctionListEntry js_bigfloat_funcs[] = {
49930     JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ),
49931     JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ),
49932     JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ),
49933     JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ),
49934     JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ),
49935     JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ),
49936     JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ),
49937     JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ),
49938     JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ),
49939     JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ),
49940     JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ),
49941     JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ),
49942     JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ),
49943     JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ),
49944     JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ),
49945     JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ),
49946     JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ),
49947     JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ),
49948     JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ),
49949     JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ),
49950     JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ),
49951     JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ),
49952     JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ),
49953     JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ),
49954     JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ),
49955     JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ),
49956     JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ),
49957     JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ),
49958     JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ),
49959     JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ),
49960     JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ),
49961     JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ),
49962 };
49963 
49964 /* FloatEnv */
49965 
js_float_env_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)49966 static JSValue js_float_env_constructor(JSContext *ctx,
49967                                         JSValueConst new_target,
49968                                         int argc, JSValueConst *argv)
49969 {
49970     JSValue obj;
49971     JSFloatEnv *fe;
49972     int64_t prec;
49973     int flags, rndmode;
49974 
49975     prec = ctx->fp_env.prec;
49976     flags = ctx->fp_env.flags;
49977     if (!JS_IsUndefined(argv[0])) {
49978         if (JS_ToInt64Sat(ctx, &prec, argv[0]))
49979             return JS_EXCEPTION;
49980         if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
49981             return JS_ThrowRangeError(ctx, "invalid precision");
49982         flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */
49983         if (argc > 1 && !JS_IsUndefined(argv[1])) {
49984             if (JS_ToInt32Sat(ctx, &rndmode, argv[1]))
49985                 return JS_EXCEPTION;
49986             if (rndmode < BF_RNDN || rndmode > BF_RNDF)
49987                 return JS_ThrowRangeError(ctx, "invalid rounding mode");
49988             flags = rndmode;
49989         }
49990     }
49991 
49992     obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV);
49993     if (JS_IsException(obj))
49994         return JS_EXCEPTION;
49995     fe = js_malloc(ctx, sizeof(*fe));
49996     if (!fe)
49997         return JS_EXCEPTION;
49998     fe->prec = prec;
49999     fe->flags = flags;
50000     fe->status = 0;
50001     JS_SetOpaque(obj, fe);
50002     return obj;
50003 }
50004 
js_float_env_finalizer(JSRuntime * rt,JSValue val)50005 static void js_float_env_finalizer(JSRuntime *rt, JSValue val)
50006 {
50007     JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV);
50008     js_free_rt(rt, fe);
50009 }
50010 
js_float_env_get_prec(JSContext * ctx,JSValueConst this_val)50011 static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val)
50012 {
50013     return JS_NewInt64(ctx, ctx->fp_env.prec);
50014 }
50015 
js_float_env_get_expBits(JSContext * ctx,JSValueConst this_val)50016 static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val)
50017 {
50018     return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags));
50019 }
50020 
js_float_env_setPrec(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50021 static JSValue js_float_env_setPrec(JSContext *ctx,
50022                                     JSValueConst this_val,
50023                                     int argc, JSValueConst *argv)
50024 {
50025     JSValueConst func;
50026     int exp_bits, flags, saved_flags;
50027     JSValue ret;
50028     limb_t saved_prec;
50029     int64_t prec;
50030 
50031     func = argv[0];
50032     if (JS_ToInt64Sat(ctx, &prec, argv[1]))
50033         return JS_EXCEPTION;
50034     if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
50035         return JS_ThrowRangeError(ctx, "invalid precision");
50036     exp_bits = BF_EXP_BITS_MAX;
50037 
50038     if (argc > 2 && !JS_IsUndefined(argv[2])) {
50039         if (JS_ToInt32Sat(ctx, &exp_bits, argv[2]))
50040             return JS_EXCEPTION;
50041         if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX)
50042             return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
50043     }
50044 
50045     flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits);
50046 
50047     saved_prec = ctx->fp_env.prec;
50048     saved_flags = ctx->fp_env.flags;
50049 
50050     ctx->fp_env.prec = prec;
50051     ctx->fp_env.flags = flags;
50052 
50053     ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL);
50054     /* always restore the floating point precision */
50055     ctx->fp_env.prec = saved_prec;
50056     ctx->fp_env.flags = saved_flags;
50057     return ret;
50058 }
50059 
50060 #define FE_PREC      (-1)
50061 #define FE_EXP       (-2)
50062 #define FE_RNDMODE   (-3)
50063 #define FE_SUBNORMAL (-4)
50064 
js_float_env_proto_get_status(JSContext * ctx,JSValueConst this_val,int magic)50065 static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic)
50066 {
50067     JSFloatEnv *fe;
50068     fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
50069     if (!fe)
50070         return JS_EXCEPTION;
50071     switch(magic) {
50072     case FE_PREC:
50073         return JS_NewInt64(ctx, fe->prec);
50074     case FE_EXP:
50075         return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags));
50076     case FE_RNDMODE:
50077         return JS_NewInt32(ctx, fe->flags & BF_RND_MASK);
50078     case FE_SUBNORMAL:
50079         return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0);
50080     default:
50081         return JS_NewBool(ctx, (fe->status & magic) != 0);
50082     }
50083 }
50084 
js_float_env_proto_set_status(JSContext * ctx,JSValueConst this_val,JSValueConst val,int magic)50085 static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic)
50086 {
50087     JSFloatEnv *fe;
50088     int b;
50089     int64_t prec;
50090 
50091     fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
50092     if (!fe)
50093         return JS_EXCEPTION;
50094     switch(magic) {
50095     case FE_PREC:
50096         if (JS_ToInt64Sat(ctx, &prec, val))
50097             return JS_EXCEPTION;
50098         if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
50099             return JS_ThrowRangeError(ctx, "invalid precision");
50100         fe->prec = prec;
50101         break;
50102     case FE_EXP:
50103         if (JS_ToInt32Sat(ctx, &b, val))
50104             return JS_EXCEPTION;
50105         if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX)
50106             return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
50107         fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) |
50108             bf_set_exp_bits(b);
50109         break;
50110     case FE_RNDMODE:
50111         b = bigfloat_get_rnd_mode(ctx, val);
50112         if (b < 0)
50113             return JS_EXCEPTION;
50114         fe->flags = (fe->flags & ~BF_RND_MASK) | b;
50115         break;
50116     case FE_SUBNORMAL:
50117         b = JS_ToBool(ctx, val);
50118         fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0);
50119         break;
50120     default:
50121         b = JS_ToBool(ctx, val);
50122         fe->status = (fe->status & ~magic) & ((-b) & magic);
50123         break;
50124     }
50125     return JS_UNDEFINED;
50126 }
50127 
js_float_env_clearStatus(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50128 static JSValue js_float_env_clearStatus(JSContext *ctx,
50129                                         JSValueConst this_val,
50130                                         int argc, JSValueConst *argv)
50131 {
50132     JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
50133     if (!fe)
50134         return JS_EXCEPTION;
50135     fe->status = 0;
50136     return JS_UNDEFINED;
50137 }
50138 
50139 static const JSCFunctionListEntry js_float_env_funcs[] = {
50140     JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ),
50141     JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ),
50142     JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ),
50143     JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ),
50144     JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ),
50145     JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ),
50146     JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ),
50147     JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ),
50148     JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ),
50149     JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ),
50150     JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ),
50151     JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ),
50152     JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ),
50153     JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ),
50154 };
50155 
50156 static const JSCFunctionListEntry js_float_env_proto_funcs[] = {
50157     JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status,
50158                          js_float_env_proto_set_status, FE_PREC ),
50159     JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status,
50160                          js_float_env_proto_set_status, FE_EXP ),
50161     JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status,
50162                          js_float_env_proto_set_status, FE_RNDMODE ),
50163     JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status,
50164                          js_float_env_proto_set_status, FE_SUBNORMAL ),
50165     JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status,
50166                          js_float_env_proto_set_status, BF_ST_INVALID_OP ),
50167     JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status,
50168                          js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ),
50169     JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status,
50170                          js_float_env_proto_set_status, BF_ST_OVERFLOW ),
50171     JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status,
50172                          js_float_env_proto_set_status, BF_ST_UNDERFLOW ),
50173     JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status,
50174                          js_float_env_proto_set_status, BF_ST_INEXACT ),
50175     JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ),
50176 };
50177 
JS_AddIntrinsicBigFloat(JSContext * ctx)50178 void JS_AddIntrinsicBigFloat(JSContext *ctx)
50179 {
50180     JSRuntime *rt = ctx->rt;
50181     JSValueConst obj1;
50182 
50183     rt->bigfloat_ops.to_string = js_bigfloat_to_string;
50184     rt->bigfloat_ops.from_string = js_string_to_bigfloat;
50185     rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat;
50186     rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat;
50187     rt->bigfloat_ops.compare = js_compare_bigfloat;
50188     rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64;
50189     rt->bigfloat_ops.mul_pow10 = js_mul_pow10;
50190 
50191     ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx);
50192     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT],
50193                                js_bigfloat_proto_funcs,
50194                                countof(js_bigfloat_proto_funcs));
50195     obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1,
50196                                     ctx->class_proto[JS_CLASS_BIG_FLOAT]);
50197     JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs,
50198                                countof(js_bigfloat_funcs));
50199 
50200     ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx);
50201     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV],
50202                                js_float_env_proto_funcs,
50203                                countof(js_float_env_proto_funcs));
50204     obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv",
50205                                         js_float_env_constructor, 1,
50206                                         ctx->class_proto[JS_CLASS_FLOAT_ENV]);
50207     JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs,
50208                                countof(js_float_env_funcs));
50209 }
50210 
50211 /* BigDecimal */
50212 
JS_ToBigDecimalFree(JSContext * ctx,JSValue val,BOOL allow_null_or_undefined)50213 static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
50214                                    BOOL allow_null_or_undefined)
50215 {
50216  redo:
50217     switch(JS_VALUE_GET_NORM_TAG(val)) {
50218     case JS_TAG_BIG_DECIMAL:
50219         break;
50220     case JS_TAG_NULL:
50221         if (!allow_null_or_undefined)
50222             goto fail;
50223         /* fall thru */
50224     case JS_TAG_BOOL:
50225     case JS_TAG_INT:
50226         {
50227             bfdec_t *r;
50228             int32_t v = JS_VALUE_GET_INT(val);
50229 
50230             val = JS_NewBigDecimal(ctx);
50231             if (JS_IsException(val))
50232                 break;
50233             r = JS_GetBigDecimal(val);
50234             if (bfdec_set_si(r, v)) {
50235                 JS_FreeValue(ctx, val);
50236                 val = JS_EXCEPTION;
50237                 break;
50238             }
50239         }
50240         break;
50241     case JS_TAG_FLOAT64:
50242     case JS_TAG_BIG_INT:
50243     case JS_TAG_BIG_FLOAT:
50244         val = JS_ToStringFree(ctx, val);
50245         if (JS_IsException(val))
50246             break;
50247         goto redo;
50248     case JS_TAG_STRING:
50249         {
50250             const char *str, *p;
50251             size_t len;
50252             int err;
50253 
50254             str = JS_ToCStringLen(ctx, &len, val);
50255             JS_FreeValue(ctx, val);
50256             if (!str)
50257                 return JS_EXCEPTION;
50258             p = str;
50259             p += skip_spaces(p);
50260             if ((p - str) == len) {
50261                 bfdec_t *r;
50262                 val = JS_NewBigDecimal(ctx);
50263                 if (JS_IsException(val))
50264                     break;
50265                 r = JS_GetBigDecimal(val);
50266                 bfdec_set_zero(r, 0);
50267                 err = 0;
50268             } else {
50269                 val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL);
50270                 if (JS_IsException(val)) {
50271                     JS_FreeCString(ctx, str);
50272                     return JS_EXCEPTION;
50273                 }
50274                 p += skip_spaces(p);
50275                 err = ((p - str) != len);
50276             }
50277             JS_FreeCString(ctx, str);
50278             if (err) {
50279                 JS_FreeValue(ctx, val);
50280                 return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal");
50281             }
50282         }
50283         break;
50284     case JS_TAG_OBJECT:
50285         val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
50286         if (JS_IsException(val))
50287             break;
50288         goto redo;
50289     case JS_TAG_UNDEFINED:
50290         {
50291             bfdec_t *r;
50292             if (!allow_null_or_undefined)
50293                 goto fail;
50294             val = JS_NewBigDecimal(ctx);
50295             if (JS_IsException(val))
50296                 break;
50297             r = JS_GetBigDecimal(val);
50298             bfdec_set_nan(r);
50299         }
50300         break;
50301     default:
50302     fail:
50303         JS_FreeValue(ctx, val);
50304         return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal");
50305     }
50306     return val;
50307 }
50308 
js_bigdecimal_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)50309 static JSValue js_bigdecimal_constructor(JSContext *ctx,
50310                                          JSValueConst new_target,
50311                                          int argc, JSValueConst *argv)
50312 {
50313     JSValue val;
50314     if (!JS_IsUndefined(new_target))
50315         return JS_ThrowTypeError(ctx, "not a constructor");
50316     if (argc == 0) {
50317         bfdec_t *r;
50318         val = JS_NewBigDecimal(ctx);
50319         if (JS_IsException(val))
50320             return val;
50321         r = JS_GetBigDecimal(val);
50322         bfdec_set_zero(r, 0);
50323     } else {
50324         val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE);
50325     }
50326     return val;
50327 }
50328 
js_thisBigDecimalValue(JSContext * ctx,JSValueConst this_val)50329 static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val)
50330 {
50331     if (JS_IsBigDecimal(this_val))
50332         return JS_DupValue(ctx, this_val);
50333 
50334     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
50335         JSObject *p = JS_VALUE_GET_OBJ(this_val);
50336         if (p->class_id == JS_CLASS_BIG_DECIMAL) {
50337             if (JS_IsBigDecimal(p->u.object_data))
50338                 return JS_DupValue(ctx, p->u.object_data);
50339         }
50340     }
50341     return JS_ThrowTypeError(ctx, "not a bigdecimal");
50342 }
50343 
js_bigdecimal_toString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50344 static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val,
50345                                       int argc, JSValueConst *argv)
50346 {
50347     JSValue val;
50348 
50349     val = js_thisBigDecimalValue(ctx, this_val);
50350     if (JS_IsException(val))
50351         return val;
50352     return JS_ToStringFree(ctx, val);
50353 }
50354 
js_bigdecimal_valueOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50355 static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val,
50356                                    int argc, JSValueConst *argv)
50357 {
50358     return js_thisBigDecimalValue(ctx, this_val);
50359 }
50360 
js_bigdecimal_get_rnd_mode(JSContext * ctx,JSValueConst obj)50361 static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj)
50362 {
50363     const char *str;
50364     size_t size;
50365     int rnd_mode;
50366 
50367     str = JS_ToCStringLen(ctx, &size, obj);
50368     if (!str)
50369         return -1;
50370     if (strlen(str) != size)
50371         goto invalid_rounding_mode;
50372     if (!strcmp(str, "floor")) {
50373         rnd_mode = BF_RNDD;
50374     } else if (!strcmp(str, "ceiling")) {
50375         rnd_mode = BF_RNDU;
50376     } else if (!strcmp(str, "down")) {
50377         rnd_mode = BF_RNDZ;
50378     } else if (!strcmp(str, "up")) {
50379         rnd_mode = BF_RNDA;
50380     } else if (!strcmp(str, "half-even")) {
50381         rnd_mode = BF_RNDN;
50382     } else if (!strcmp(str, "half-up")) {
50383         rnd_mode = BF_RNDNA;
50384     } else {
50385     invalid_rounding_mode:
50386         JS_FreeCString(ctx, str);
50387         JS_ThrowTypeError(ctx, "invalid rounding mode");
50388         return -1;
50389     }
50390     JS_FreeCString(ctx, str);
50391     return rnd_mode;
50392 }
50393 
50394 typedef struct {
50395     int64_t prec;
50396     bf_flags_t flags;
50397 } BigDecimalEnv;
50398 
js_bigdecimal_get_env(JSContext * ctx,BigDecimalEnv * fe,JSValueConst obj)50399 static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,
50400                                  JSValueConst obj)
50401 {
50402     JSValue prop;
50403     int64_t val;
50404     BOOL has_prec;
50405     int rnd_mode;
50406 
50407     if (!JS_IsObject(obj)) {
50408         JS_ThrowTypeErrorNotAnObject(ctx);
50409         return -1;
50410     }
50411     prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode);
50412     if (JS_IsException(prop))
50413         return -1;
50414     rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop);
50415     JS_FreeValue(ctx, prop);
50416     if (rnd_mode < 0)
50417         return -1;
50418     fe->flags = rnd_mode;
50419 
50420     prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits);
50421     if (JS_IsException(prop))
50422         return -1;
50423     has_prec = FALSE;
50424     if (!JS_IsUndefined(prop)) {
50425         if (JS_ToInt64SatFree(ctx, &val, prop))
50426             return -1;
50427         if (val < 1 || val > BF_PREC_MAX)
50428             goto invalid_precision;
50429         fe->prec = val;
50430         has_prec = TRUE;
50431     }
50432 
50433     prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits);
50434     if (JS_IsException(prop))
50435         return -1;
50436     if (!JS_IsUndefined(prop)) {
50437         if (has_prec) {
50438             JS_FreeValue(ctx, prop);
50439             JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits");
50440             return -1;
50441         }
50442         if (JS_ToInt64SatFree(ctx, &val, prop))
50443             return -1;
50444         if (val < 0 || val > BF_PREC_MAX) {
50445         invalid_precision:
50446             JS_ThrowTypeError(ctx, "invalid precision");
50447             return -1;
50448         }
50449         fe->prec = val;
50450         fe->flags |= BF_FLAG_RADPNT_PREC;
50451         has_prec = TRUE;
50452     }
50453     if (!has_prec) {
50454         JS_ThrowTypeError(ctx, "precision must be present");
50455         return -1;
50456     }
50457     return 0;
50458 }
50459 
50460 
js_bigdecimal_fop(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)50461 static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,
50462                                  int argc, JSValueConst *argv, int magic)
50463 {
50464     bfdec_t *a, *b, r_s, *r = &r_s;
50465     JSValue op1, op2, res;
50466     BigDecimalEnv fe_s, *fe = &fe_s;
50467     int op_count, ret;
50468 
50469     if (magic == MATH_OP_SQRT ||
50470         magic == MATH_OP_ROUND)
50471         op_count = 1;
50472     else
50473         op_count = 2;
50474 
50475     op1 = JS_ToNumeric(ctx, argv[0]);
50476     if (JS_IsException(op1))
50477         return op1;
50478     a = JS_ToBigDecimal(ctx, op1);
50479     if (!a) {
50480         JS_FreeValue(ctx, op1);
50481         return JS_EXCEPTION;
50482     }
50483     if (op_count >= 2) {
50484         op2 = JS_ToNumeric(ctx, argv[1]);
50485         if (JS_IsException(op2)) {
50486             JS_FreeValue(ctx, op1);
50487             return op2;
50488         }
50489         b = JS_ToBigDecimal(ctx, op2);
50490         if (!b)
50491             goto fail;
50492     } else {
50493         op2 = JS_UNDEFINED;
50494         b = NULL;
50495     }
50496     fe->flags = BF_RNDZ;
50497     fe->prec = BF_PREC_INF;
50498     if (op_count < argc) {
50499         if (js_bigdecimal_get_env(ctx, fe, argv[op_count]))
50500             goto fail;
50501     }
50502 
50503     res = JS_NewBigDecimal(ctx);
50504     if (JS_IsException(res)) {
50505     fail:
50506         JS_FreeValue(ctx, op1);
50507         JS_FreeValue(ctx, op2);
50508         return JS_EXCEPTION;
50509     }
50510     r = JS_GetBigDecimal(res);
50511     switch (magic) {
50512     case MATH_OP_ADD:
50513         ret = bfdec_add(r, a, b, fe->prec, fe->flags);
50514         break;
50515     case MATH_OP_SUB:
50516         ret = bfdec_sub(r, a, b, fe->prec, fe->flags);
50517         break;
50518     case MATH_OP_MUL:
50519         ret = bfdec_mul(r, a, b, fe->prec, fe->flags);
50520         break;
50521     case MATH_OP_DIV:
50522         ret = bfdec_div(r, a, b, fe->prec, fe->flags);
50523         break;
50524     case MATH_OP_FMOD:
50525         ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
50526         break;
50527     case MATH_OP_SQRT:
50528         ret = bfdec_sqrt(r, a, fe->prec, fe->flags);
50529         break;
50530     case MATH_OP_ROUND:
50531         ret = bfdec_set(r, a);
50532         if (!(ret & BF_ST_MEM_ERROR))
50533             ret = bfdec_round(r, fe->prec, fe->flags);
50534         break;
50535     default:
50536         abort();
50537     }
50538     JS_FreeValue(ctx, op1);
50539     JS_FreeValue(ctx, op2);
50540     ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP |
50541         BF_ST_OVERFLOW;
50542     if (ret != 0) {
50543         JS_FreeValue(ctx, res);
50544         return throw_bf_exception(ctx, ret);
50545     } else {
50546         return res;
50547     }
50548 }
50549 
js_bigdecimal_toFixed(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50550 static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val,
50551                                  int argc, JSValueConst *argv)
50552 {
50553     JSValue val, ret;
50554     int64_t f;
50555     int rnd_mode;
50556 
50557     val = js_thisBigDecimalValue(ctx, this_val);
50558     if (JS_IsException(val))
50559         return val;
50560     if (JS_ToInt64Sat(ctx, &f, argv[0]))
50561         goto fail;
50562     if (f < 0 || f > BF_PREC_MAX) {
50563         JS_ThrowRangeError(ctx, "invalid number of digits");
50564         goto fail;
50565     }
50566     rnd_mode = BF_RNDNA;
50567     if (argc > 1) {
50568         rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
50569         if (rnd_mode < 0)
50570             goto fail;
50571     }
50572     ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
50573     JS_FreeValue(ctx, val);
50574     return ret;
50575  fail:
50576     JS_FreeValue(ctx, val);
50577     return JS_EXCEPTION;
50578 }
50579 
js_bigdecimal_toExponential(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50580 static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val,
50581                                        int argc, JSValueConst *argv)
50582 {
50583     JSValue val, ret;
50584     int64_t f;
50585     int rnd_mode;
50586 
50587     val = js_thisBigDecimalValue(ctx, this_val);
50588     if (JS_IsException(val))
50589         return val;
50590     if (JS_ToInt64Sat(ctx, &f, argv[0]))
50591         goto fail;
50592     if (JS_IsUndefined(argv[0])) {
50593         ret = js_bigdecimal_to_string1(ctx, val, 0,
50594                   BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
50595     } else {
50596         if (f < 0 || f > BF_PREC_MAX) {
50597             JS_ThrowRangeError(ctx, "invalid number of digits");
50598             goto fail;
50599         }
50600         rnd_mode = BF_RNDNA;
50601         if (argc > 1) {
50602             rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
50603             if (rnd_mode < 0)
50604                 goto fail;
50605         }
50606         ret = js_bigdecimal_to_string1(ctx, val, f + 1,
50607                       rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
50608     }
50609     JS_FreeValue(ctx, val);
50610     return ret;
50611  fail:
50612     JS_FreeValue(ctx, val);
50613     return JS_EXCEPTION;
50614 }
50615 
js_bigdecimal_toPrecision(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50616 static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val,
50617                                      int argc, JSValueConst *argv)
50618 {
50619     JSValue val, ret;
50620     int64_t p;
50621     int rnd_mode;
50622 
50623     val = js_thisBigDecimalValue(ctx, this_val);
50624     if (JS_IsException(val))
50625         return val;
50626     if (JS_IsUndefined(argv[0])) {
50627         return JS_ToStringFree(ctx, val);
50628     }
50629     if (JS_ToInt64Sat(ctx, &p, argv[0]))
50630         goto fail;
50631     if (p < 1 || p > BF_PREC_MAX) {
50632         JS_ThrowRangeError(ctx, "invalid number of digits");
50633         goto fail;
50634     }
50635     rnd_mode = BF_RNDNA;
50636     if (argc > 1) {
50637         rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
50638         if (rnd_mode < 0)
50639             goto fail;
50640     }
50641     ret = js_bigdecimal_to_string1(ctx, val, p,
50642                                    rnd_mode | BF_FTOA_FORMAT_FIXED);
50643     JS_FreeValue(ctx, val);
50644     return ret;
50645  fail:
50646     JS_FreeValue(ctx, val);
50647     return JS_EXCEPTION;
50648 }
50649 
50650 static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = {
50651     JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ),
50652     JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ),
50653     JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ),
50654     JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ),
50655     JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ),
50656 };
50657 
50658 static const JSCFunctionListEntry js_bigdecimal_funcs[] = {
50659     JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ),
50660     JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ),
50661     JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ),
50662     JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ),
50663     JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ),
50664     JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ),
50665     JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ),
50666 };
50667 
JS_AddIntrinsicBigDecimal(JSContext * ctx)50668 void JS_AddIntrinsicBigDecimal(JSContext *ctx)
50669 {
50670     JSRuntime *rt = ctx->rt;
50671     JSValueConst obj1;
50672 
50673     rt->bigdecimal_ops.to_string = js_bigdecimal_to_string;
50674     rt->bigdecimal_ops.from_string = js_string_to_bigdecimal;
50675     rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal;
50676     rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal;
50677     rt->bigdecimal_ops.compare = js_compare_bigdecimal;
50678 
50679     ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx);
50680     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL],
50681                                js_bigdecimal_proto_funcs,
50682                                countof(js_bigdecimal_proto_funcs));
50683     obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal",
50684                                     js_bigdecimal_constructor, 1,
50685                                     ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
50686     JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs,
50687                                countof(js_bigdecimal_funcs));
50688 }
50689 
JS_EnableBignumExt(JSContext * ctx,BOOL enable)50690 void JS_EnableBignumExt(JSContext *ctx, BOOL enable)
50691 {
50692     ctx->bignum_ext = enable;
50693 }
50694 
50695 #endif /* CONFIG_BIGNUM */
50696 
50697 static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = {
50698     "EvalError", "RangeError", "ReferenceError",
50699     "SyntaxError", "TypeError", "URIError",
50700     "InternalError", "AggregateError",
50701 };
50702 
50703 /* Minimum amount of objects to be able to compile code and display
50704    error messages. No JSAtom should be allocated by this function. */
JS_AddIntrinsicBasicObjects(JSContext * ctx)50705 static void JS_AddIntrinsicBasicObjects(JSContext *ctx)
50706 {
50707     JSValue proto;
50708     int i;
50709 
50710     ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL);
50711     ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0,
50712                                            JS_CFUNC_generic, 0,
50713                                            ctx->class_proto[JS_CLASS_OBJECT]);
50714     ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto);
50715     ctx->class_proto[JS_CLASS_ERROR] = JS_NewObject(ctx);
50716 #if 0
50717     /* these are auto-initialized from js_error_proto_funcs,
50718        but delaying might be a problem */
50719     JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_name,
50720                            JS_AtomToString(ctx, JS_ATOM_Error),
50721                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
50722     JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_message,
50723                            JS_AtomToString(ctx, JS_ATOM_empty_string),
50724                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
50725 #endif
50726     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ERROR],
50727                                js_error_proto_funcs,
50728                                countof(js_error_proto_funcs));
50729 
50730     for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
50731         proto = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ERROR]);
50732         JS_DefinePropertyValue(ctx, proto, JS_ATOM_name,
50733                                JS_NewAtomString(ctx, native_error_name[i]),
50734                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
50735         JS_DefinePropertyValue(ctx, proto, JS_ATOM_message,
50736                                JS_AtomToString(ctx, JS_ATOM_empty_string),
50737                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
50738         ctx->native_error_proto[i] = proto;
50739     }
50740 
50741     /* the array prototype is an array */
50742     ctx->class_proto[JS_CLASS_ARRAY] =
50743         JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
50744                                JS_CLASS_ARRAY);
50745 
50746     ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]),
50747                                      JS_PROP_INITIAL_HASH_SIZE, 1);
50748     add_shape_property(ctx, &ctx->array_shape, NULL,
50749                        JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH);
50750 
50751     /* XXX: could test it on first context creation to ensure that no
50752        new atoms are created in JS_AddIntrinsicBasicObjects(). It is
50753        necessary to avoid useless renumbering of atoms after
50754        JS_EvalBinary() if it is done just after
50755        JS_AddIntrinsicBasicObjects(). */
50756     //    assert(ctx->rt->atom_count == JS_ATOM_END);
50757 }
50758 
JS_AddIntrinsicBaseObjects(JSContext * ctx)50759 void JS_AddIntrinsicBaseObjects(JSContext *ctx)
50760 {
50761     int i;
50762     JSValueConst obj, number_obj;
50763     JSValue obj1;
50764 
50765     ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0);
50766 
50767     /* add caller and arguments properties to throw a TypeError */
50768     obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0);
50769     JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED,
50770                       obj1, ctx->throw_type_error,
50771                       JS_PROP_HAS_GET | JS_PROP_HAS_SET |
50772                       JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
50773     JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED,
50774                       obj1, ctx->throw_type_error,
50775                       JS_PROP_HAS_GET | JS_PROP_HAS_SET |
50776                       JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
50777     JS_FreeValue(ctx, obj1);
50778     JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1));
50779 
50780     ctx->global_obj = JS_NewObject(ctx);
50781     ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL);
50782 
50783     /* Object */
50784     obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1,
50785                                    ctx->class_proto[JS_CLASS_OBJECT]);
50786     JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs));
50787     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT],
50788                                js_object_proto_funcs, countof(js_object_proto_funcs));
50789 
50790     /* Function */
50791     JS_SetPropertyFunctionList(ctx, ctx->function_proto, js_function_proto_funcs, countof(js_function_proto_funcs));
50792     ctx->function_ctor = JS_NewCFunctionMagic(ctx, js_function_constructor,
50793                                               "Function", 1, JS_CFUNC_constructor_or_func_magic,
50794                                               JS_FUNC_NORMAL);
50795     JS_NewGlobalCConstructor2(ctx, JS_DupValue(ctx, ctx->function_ctor), "Function",
50796                               ctx->function_proto);
50797 
50798     /* Error */
50799     obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor,
50800                                 "Error", 1, JS_CFUNC_constructor_or_func_magic, -1);
50801     JS_NewGlobalCConstructor2(ctx, obj1,
50802                               "Error", ctx->class_proto[JS_CLASS_ERROR]);
50803 
50804     for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
50805         JSValue func_obj;
50806         int n_args;
50807         n_args = 1 + (i == JS_AGGREGATE_ERROR);
50808         func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_error_constructor,
50809                                     native_error_name[i], n_args,
50810                                     JS_CFUNC_constructor_or_func_magic, i, obj1);
50811         JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i],
50812                                   ctx->native_error_proto[i]);
50813     }
50814 
50815     /* Iterator prototype */
50816     ctx->iterator_proto = JS_NewObject(ctx);
50817     JS_SetPropertyFunctionList(ctx, ctx->iterator_proto,
50818                                js_iterator_proto_funcs,
50819                                countof(js_iterator_proto_funcs));
50820 
50821     /* Array */
50822     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY],
50823                                js_array_proto_funcs,
50824                                countof(js_array_proto_funcs));
50825 
50826     obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1,
50827                                    ctx->class_proto[JS_CLASS_ARRAY]);
50828     ctx->array_ctor = JS_DupValue(ctx, obj);
50829     JS_SetPropertyFunctionList(ctx, obj, js_array_funcs,
50830                                countof(js_array_funcs));
50831 
50832     /* XXX: create auto_initializer */
50833     {
50834         /* initialize Array.prototype[Symbol.unscopables] */
50835         char const unscopables[] = "copyWithin" "\0" "entries" "\0" "fill" "\0" "find" "\0"
50836             "findIndex" "\0" "flat" "\0" "flatMap" "\0" "includes" "\0" "keys" "\0" "values" "\0";
50837         const char *p = unscopables;
50838         obj1 = JS_NewObjectProto(ctx, JS_NULL);
50839         for(p = unscopables; *p; p += strlen(p) + 1) {
50840             JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E);
50841         }
50842         JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY],
50843                                JS_ATOM_Symbol_unscopables, obj1,
50844                                JS_PROP_CONFIGURABLE);
50845     }
50846 
50847     /* needed to initialize arguments[Symbol.iterator] */
50848     ctx->array_proto_values =
50849         JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values);
50850 
50851     ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
50852     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR],
50853                                js_array_iterator_proto_funcs,
50854                                countof(js_array_iterator_proto_funcs));
50855 
50856     /* parseFloat and parseInteger must be defined before Number
50857        because of the Number.parseFloat and Number.parseInteger
50858        aliases */
50859     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs,
50860                                countof(js_global_funcs));
50861 
50862     /* Number */
50863     ctx->class_proto[JS_CLASS_NUMBER] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
50864                                                                JS_CLASS_NUMBER);
50865     JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], JS_NewInt32(ctx, 0));
50866     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_NUMBER],
50867                                js_number_proto_funcs,
50868                                countof(js_number_proto_funcs));
50869     number_obj = JS_NewGlobalCConstructor(ctx, "Number", js_number_constructor, 1,
50870                                           ctx->class_proto[JS_CLASS_NUMBER]);
50871     JS_SetPropertyFunctionList(ctx, number_obj, js_number_funcs, countof(js_number_funcs));
50872 
50873     /* Boolean */
50874     ctx->class_proto[JS_CLASS_BOOLEAN] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
50875                                                                 JS_CLASS_BOOLEAN);
50876     JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], JS_NewBool(ctx, FALSE));
50877     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], js_boolean_proto_funcs,
50878                                countof(js_boolean_proto_funcs));
50879     JS_NewGlobalCConstructor(ctx, "Boolean", js_boolean_constructor, 1,
50880                              ctx->class_proto[JS_CLASS_BOOLEAN]);
50881 
50882     /* String */
50883     ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
50884                                                                JS_CLASS_STRING);
50885     JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string));
50886     obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1,
50887                                    ctx->class_proto[JS_CLASS_STRING]);
50888     JS_SetPropertyFunctionList(ctx, obj, js_string_funcs,
50889                                countof(js_string_funcs));
50890     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_funcs,
50891                                countof(js_string_proto_funcs));
50892 
50893     ctx->class_proto[JS_CLASS_STRING_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
50894     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING_ITERATOR],
50895                                js_string_iterator_proto_funcs,
50896                                countof(js_string_iterator_proto_funcs));
50897 
50898     /* Math: create as autoinit object */
50899     js_random_init(ctx);
50900     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj));
50901 
50902     /* ES6 Reflect: create as autoinit object */
50903     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj));
50904 
50905     /* ES6 Symbol */
50906     ctx->class_proto[JS_CLASS_SYMBOL] = JS_NewObject(ctx);
50907     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SYMBOL], js_symbol_proto_funcs,
50908                                countof(js_symbol_proto_funcs));
50909     obj = JS_NewGlobalCConstructor(ctx, "Symbol", js_symbol_constructor, 0,
50910                                    ctx->class_proto[JS_CLASS_SYMBOL]);
50911     JS_SetPropertyFunctionList(ctx, obj, js_symbol_funcs,
50912                                countof(js_symbol_funcs));
50913     for(i = JS_ATOM_Symbol_toPrimitive; i < JS_ATOM_END; i++) {
50914         char buf[ATOM_GET_STR_BUF_SIZE];
50915         const char *str, *p;
50916         str = JS_AtomGetStr(ctx, buf, sizeof(buf), i);
50917         /* skip "Symbol." */
50918         p = strchr(str, '.');
50919         if (p)
50920             str = p + 1;
50921         JS_DefinePropertyValueStr(ctx, obj, str, JS_AtomToValue(ctx, i), 0);
50922     }
50923 
50924     /* ES6 Generator */
50925     ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
50926     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR],
50927                                js_generator_proto_funcs,
50928                                countof(js_generator_proto_funcs));
50929 
50930     ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
50931     obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor,
50932                                 "GeneratorFunction", 1,
50933                                 JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR);
50934     JS_SetPropertyFunctionList(ctx,
50935                                ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
50936                                js_generator_function_proto_funcs,
50937                                countof(js_generator_function_proto_funcs));
50938     JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
50939                        ctx->class_proto[JS_CLASS_GENERATOR],
50940                        JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
50941     JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
50942                        0, JS_PROP_CONFIGURABLE);
50943     JS_FreeValue(ctx, obj1);
50944 
50945     /* global properties */
50946     ctx->eval_obj = JS_NewCFunction(ctx, js_global_eval, "eval", 1);
50947     JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_eval,
50948                            JS_DupValue(ctx, ctx->eval_obj),
50949                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
50950 
50951     JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis,
50952                            JS_DupValue(ctx, ctx->global_obj),
50953                            JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
50954 }
50955 
50956 /* Typed Arrays */
50957 
50958 static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {
50959     0, 0, 0, 1, 1, 2, 2,
50960 #ifdef CONFIG_BIGNUM
50961     3, 3, /* BigInt64Array, BigUint64Array */
50962 #endif
50963     2, 3
50964 };
50965 
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)50966 static JSValue js_array_buffer_constructor3(JSContext *ctx,
50967                                             JSValueConst new_target,
50968                                             uint64_t len, JSClassID class_id,
50969                                             uint8_t *buf,
50970                                             JSFreeArrayBufferDataFunc *free_func,
50971                                             void *opaque, BOOL alloc_flag)
50972 {
50973     JSRuntime *rt = ctx->rt;
50974     JSValue obj;
50975     JSArrayBuffer *abuf = NULL;
50976 
50977     obj = js_create_from_ctor(ctx, new_target, class_id);
50978     if (JS_IsException(obj))
50979         return obj;
50980     /* XXX: we are currently limited to 2 GB */
50981     if (len > INT32_MAX) {
50982         JS_ThrowRangeError(ctx, "invalid array buffer length");
50983         goto fail;
50984     }
50985     abuf = js_malloc(ctx, sizeof(*abuf));
50986     if (!abuf)
50987         goto fail;
50988     abuf->byte_length = len;
50989     if (alloc_flag) {
50990         if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
50991             rt->sab_funcs.sab_alloc) {
50992             abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque,
50993                                                  max_int(len, 1));
50994             if (!abuf->data)
50995                 goto fail;
50996             memset(abuf->data, 0, len);
50997         } else {
50998             /* the allocation must be done after the object creation */
50999             abuf->data = js_mallocz(ctx, max_int(len, 1));
51000             if (!abuf->data)
51001                 goto fail;
51002         }
51003     } else {
51004         if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
51005             rt->sab_funcs.sab_dup) {
51006             rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf);
51007         }
51008         abuf->data = buf;
51009     }
51010     init_list_head(&abuf->array_list);
51011     abuf->detached = FALSE;
51012     abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER);
51013     abuf->opaque = opaque;
51014     abuf->free_func = free_func;
51015     if (alloc_flag && buf)
51016         memcpy(abuf->data, buf, len);
51017     JS_SetOpaque(obj, abuf);
51018     return obj;
51019  fail:
51020     JS_FreeValue(ctx, obj);
51021     js_free(ctx, abuf);
51022     return JS_EXCEPTION;
51023 }
51024 
js_array_buffer_free(JSRuntime * rt,void * opaque,void * ptr)51025 static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr)
51026 {
51027     js_free_rt(rt, ptr);
51028 }
51029 
js_array_buffer_constructor2(JSContext * ctx,JSValueConst new_target,uint64_t len,JSClassID class_id)51030 static JSValue js_array_buffer_constructor2(JSContext *ctx,
51031                                             JSValueConst new_target,
51032                                             uint64_t len, JSClassID class_id)
51033 {
51034     return js_array_buffer_constructor3(ctx, new_target, len, class_id,
51035                                         NULL, js_array_buffer_free, NULL,
51036                                         TRUE);
51037 }
51038 
js_array_buffer_constructor1(JSContext * ctx,JSValueConst new_target,uint64_t len)51039 static JSValue js_array_buffer_constructor1(JSContext *ctx,
51040                                             JSValueConst new_target,
51041                                             uint64_t len)
51042 {
51043     return js_array_buffer_constructor2(ctx, new_target, len,
51044                                         JS_CLASS_ARRAY_BUFFER);
51045 }
51046 
JS_NewArrayBuffer(JSContext * ctx,uint8_t * buf,size_t len,JSFreeArrayBufferDataFunc * free_func,void * opaque,BOOL is_shared)51047 JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
51048                           JSFreeArrayBufferDataFunc *free_func, void *opaque,
51049                           BOOL is_shared)
51050 {
51051     return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,
51052                                         is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER,
51053                                         buf, free_func, opaque, FALSE);
51054 }
51055 
51056 /* create a new ArrayBuffer of length 'len' and copy 'buf' to it */
JS_NewArrayBufferCopy(JSContext * ctx,const uint8_t * buf,size_t len)51057 JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len)
51058 {
51059     return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,
51060                                         JS_CLASS_ARRAY_BUFFER,
51061                                         (uint8_t *)buf,
51062                                         js_array_buffer_free, NULL,
51063                                         TRUE);
51064 }
51065 
js_array_buffer_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)51066 static JSValue js_array_buffer_constructor(JSContext *ctx,
51067                                            JSValueConst new_target,
51068                                            int argc, JSValueConst *argv)
51069 {
51070     uint64_t len;
51071     if (JS_ToIndex(ctx, &len, argv[0]))
51072         return JS_EXCEPTION;
51073     return js_array_buffer_constructor1(ctx, new_target, len);
51074 }
51075 
js_shared_array_buffer_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)51076 static JSValue js_shared_array_buffer_constructor(JSContext *ctx,
51077                                                   JSValueConst new_target,
51078                                                   int argc, JSValueConst *argv)
51079 {
51080     uint64_t len;
51081     if (JS_ToIndex(ctx, &len, argv[0]))
51082         return JS_EXCEPTION;
51083     return js_array_buffer_constructor2(ctx, new_target, len,
51084                                         JS_CLASS_SHARED_ARRAY_BUFFER);
51085 }
51086 
51087 /* also used for SharedArrayBuffer */
js_array_buffer_finalizer(JSRuntime * rt,JSValue val)51088 static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val)
51089 {
51090     JSObject *p = JS_VALUE_GET_OBJ(val);
51091     JSArrayBuffer *abuf = p->u.array_buffer;
51092     if (abuf) {
51093         /* The ArrayBuffer finalizer may be called before the typed
51094            array finalizers using it, so abuf->array_list is not
51095            necessarily empty. */
51096         // assert(list_empty(&abuf->array_list));
51097         if (abuf->shared && rt->sab_funcs.sab_free) {
51098             rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data);
51099         } else {
51100             if (abuf->free_func)
51101                 abuf->free_func(rt, abuf->opaque, abuf->data);
51102         }
51103         js_free_rt(rt, abuf);
51104     }
51105 }
51106 
js_array_buffer_isView(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51107 static JSValue js_array_buffer_isView(JSContext *ctx,
51108                                       JSValueConst this_val,
51109                                       int argc, JSValueConst *argv)
51110 {
51111     JSObject *p;
51112     BOOL res;
51113     res = FALSE;
51114     if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
51115         p = JS_VALUE_GET_OBJ(argv[0]);
51116         if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
51117             p->class_id <= JS_CLASS_DATAVIEW) {
51118             res = TRUE;
51119         }
51120     }
51121     return JS_NewBool(ctx, res);
51122 }
51123 
51124 static const JSCFunctionListEntry js_array_buffer_funcs[] = {
51125     JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ),
51126     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
51127 };
51128 
JS_ThrowTypeErrorDetachedArrayBuffer(JSContext * ctx)51129 static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx)
51130 {
51131     return JS_ThrowTypeError(ctx, "ArrayBuffer is detached");
51132 }
51133 
js_array_buffer_get_byteLength(JSContext * ctx,JSValueConst this_val,int class_id)51134 static JSValue js_array_buffer_get_byteLength(JSContext *ctx,
51135                                               JSValueConst this_val,
51136                                               int class_id)
51137 {
51138     JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id);
51139     if (!abuf)
51140         return JS_EXCEPTION;
51141     /* return 0 if detached */
51142     return JS_NewUint32(ctx, abuf->byte_length);
51143 }
JS_DetachArrayBuffer(JSContext * ctx,JSValueConst obj)51144 void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj)
51145 {
51146     JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER);
51147     struct list_head *el;
51148 
51149     if (!abuf || abuf->detached)
51150         return;
51151     if (abuf->free_func)
51152         abuf->free_func(ctx->rt, abuf->opaque, abuf->data);
51153     abuf->data = NULL;
51154     abuf->byte_length = 0;
51155     abuf->detached = TRUE;
51156 
51157     list_for_each(el, &abuf->array_list) {
51158         JSTypedArray *ta;
51159         JSObject *p;
51160 
51161         ta = list_entry(el, JSTypedArray, link);
51162         p = ta->obj;
51163         /* Note: the typed array length and offset fields are not modified */
51164         if (p->class_id != JS_CLASS_DATAVIEW) {
51165             p->u.array.count = 0;
51166             p->u.array.u.ptr = NULL;
51167         }
51168     }
51169 }
51170 
51171 /* get an ArrayBuffer or SharedArrayBuffer */
js_get_array_buffer(JSContext * ctx,JSValueConst obj)51172 static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj)
51173 {
51174     JSObject *p;
51175     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
51176         goto fail;
51177     p = JS_VALUE_GET_OBJ(obj);
51178     if (p->class_id != JS_CLASS_ARRAY_BUFFER &&
51179         p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) {
51180     fail:
51181         JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER);
51182         return NULL;
51183     }
51184     return p->u.array_buffer;
51185 }
51186 
51187 /* return NULL if exception. WARNING: any JS call can detach the
51188    buffer and render the returned pointer invalid */
JS_GetArrayBuffer(JSContext * ctx,size_t * psize,JSValueConst obj)51189 uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj)
51190 {
51191     JSArrayBuffer *abuf = js_get_array_buffer(ctx, obj);
51192     if (!abuf)
51193         goto fail;
51194     if (abuf->detached) {
51195         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51196         goto fail;
51197     }
51198     *psize = abuf->byte_length;
51199     return abuf->data;
51200  fail:
51201     *psize = 0;
51202     return NULL;
51203 }
51204 
js_array_buffer_slice(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int class_id)51205 static JSValue js_array_buffer_slice(JSContext *ctx,
51206                                      JSValueConst this_val,
51207                                      int argc, JSValueConst *argv, int class_id)
51208 {
51209     JSArrayBuffer *abuf, *new_abuf;
51210     int64_t len, start, end, new_len;
51211     JSValue ctor, new_obj;
51212 
51213     abuf = JS_GetOpaque2(ctx, this_val, class_id);
51214     if (!abuf)
51215         return JS_EXCEPTION;
51216     if (abuf->detached)
51217         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51218     len = abuf->byte_length;
51219 
51220     if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
51221         return JS_EXCEPTION;
51222 
51223     end = len;
51224     if (!JS_IsUndefined(argv[1])) {
51225         if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len))
51226             return JS_EXCEPTION;
51227     }
51228     new_len = max_int64(end - start, 0);
51229     ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
51230     if (JS_IsException(ctor))
51231         return ctor;
51232     if (JS_IsUndefined(ctor)) {
51233         new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len,
51234                                                class_id);
51235     } else {
51236         JSValue args[1];
51237         args[0] = JS_NewInt64(ctx, new_len);
51238         new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)args);
51239         JS_FreeValue(ctx, ctor);
51240         JS_FreeValue(ctx, args[0]);
51241     }
51242     if (JS_IsException(new_obj))
51243         return new_obj;
51244     new_abuf = JS_GetOpaque2(ctx, new_obj, class_id);
51245     if (!new_abuf)
51246         goto fail;
51247     if (js_same_value(ctx, new_obj, this_val)) {
51248         JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer");
51249         goto fail;
51250     }
51251     if (new_abuf->detached) {
51252         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51253         goto fail;
51254     }
51255     if (new_abuf->byte_length < new_len) {
51256         JS_ThrowTypeError(ctx, "new ArrayBuffer is too small");
51257         goto fail;
51258     }
51259     /* must test again because of side effects */
51260     if (abuf->detached) {
51261         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51262         goto fail;
51263     }
51264     memcpy(new_abuf->data, abuf->data + start, new_len);
51265     return new_obj;
51266  fail:
51267     JS_FreeValue(ctx, new_obj);
51268     return JS_EXCEPTION;
51269 }
51270 
51271 static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = {
51272     JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ),
51273     JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ),
51274     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ),
51275 };
51276 
51277 /* SharedArrayBuffer */
51278 
51279 static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = {
51280     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
51281 };
51282 
51283 static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = {
51284     JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ),
51285     JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ),
51286     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ),
51287 };
51288 
get_typed_array(JSContext * ctx,JSValueConst this_val,int is_dataview)51289 static JSObject *get_typed_array(JSContext *ctx,
51290                                  JSValueConst this_val,
51291                                  int is_dataview)
51292 {
51293     JSObject *p;
51294     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
51295         goto fail;
51296     p = JS_VALUE_GET_OBJ(this_val);
51297     if (is_dataview) {
51298         if (p->class_id != JS_CLASS_DATAVIEW)
51299             goto fail;
51300     } else {
51301         if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
51302               p->class_id <= JS_CLASS_FLOAT64_ARRAY)) {
51303         fail:
51304             JS_ThrowTypeError(ctx, "not a %s", is_dataview ? "DataView" : "TypedArray");
51305             return NULL;
51306         }
51307     }
51308     return p;
51309 }
51310 
51311 /* WARNING: 'p' must be a typed array */
typed_array_is_detached(JSContext * ctx,JSObject * p)51312 static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p)
51313 {
51314     JSTypedArray *ta = p->u.typed_array;
51315     JSArrayBuffer *abuf = ta->buffer->u.array_buffer;
51316     /* XXX: could simplify test by ensuring that
51317        p->u.array.u.ptr is NULL iff it is detached */
51318     return abuf->detached;
51319 }
51320 
51321 /* WARNING: 'p' must be a typed array. Works even if the array buffer
51322    is detached */
typed_array_get_length(JSContext * ctx,JSObject * p)51323 static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p)
51324 {
51325     JSTypedArray *ta = p->u.typed_array;
51326     int size_log2 = typed_array_size_log2(p->class_id);
51327     return ta->length >> size_log2;
51328 }
51329 
validate_typed_array(JSContext * ctx,JSValueConst this_val)51330 static int validate_typed_array(JSContext *ctx, JSValueConst this_val)
51331 {
51332     JSObject *p;
51333     p = get_typed_array(ctx, this_val, 0);
51334     if (!p)
51335         return -1;
51336     if (typed_array_is_detached(ctx, p)) {
51337         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51338         return -1;
51339     }
51340     return 0;
51341 }
51342 
js_typed_array_get_length(JSContext * ctx,JSValueConst this_val)51343 static JSValue js_typed_array_get_length(JSContext *ctx,
51344                                          JSValueConst this_val)
51345 {
51346     JSObject *p;
51347     p = get_typed_array(ctx, this_val, 0);
51348     if (!p)
51349         return JS_EXCEPTION;
51350     return JS_NewInt32(ctx, p->u.array.count);
51351 }
51352 
js_typed_array_get_buffer(JSContext * ctx,JSValueConst this_val,int is_dataview)51353 static JSValue js_typed_array_get_buffer(JSContext *ctx,
51354                                          JSValueConst this_val, int is_dataview)
51355 {
51356     JSObject *p;
51357     JSTypedArray *ta;
51358     p = get_typed_array(ctx, this_val, is_dataview);
51359     if (!p)
51360         return JS_EXCEPTION;
51361     ta = p->u.typed_array;
51362     return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
51363 }
51364 
js_typed_array_get_byteLength(JSContext * ctx,JSValueConst this_val,int is_dataview)51365 static JSValue js_typed_array_get_byteLength(JSContext *ctx,
51366                                              JSValueConst this_val,
51367                                              int is_dataview)
51368 {
51369     JSObject *p;
51370     JSTypedArray *ta;
51371     p = get_typed_array(ctx, this_val, is_dataview);
51372     if (!p)
51373         return JS_EXCEPTION;
51374     if (typed_array_is_detached(ctx, p)) {
51375         if (is_dataview) {
51376             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51377         } else {
51378             return JS_NewInt32(ctx, 0);
51379         }
51380     }
51381     ta = p->u.typed_array;
51382     return JS_NewInt32(ctx, ta->length);
51383 }
51384 
js_typed_array_get_byteOffset(JSContext * ctx,JSValueConst this_val,int is_dataview)51385 static JSValue js_typed_array_get_byteOffset(JSContext *ctx,
51386                                              JSValueConst this_val,
51387                                              int is_dataview)
51388 {
51389     JSObject *p;
51390     JSTypedArray *ta;
51391     p = get_typed_array(ctx, this_val, is_dataview);
51392     if (!p)
51393         return JS_EXCEPTION;
51394     if (typed_array_is_detached(ctx, p)) {
51395         if (is_dataview) {
51396             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51397         } else {
51398             return JS_NewInt32(ctx, 0);
51399         }
51400     }
51401     ta = p->u.typed_array;
51402     return JS_NewInt32(ctx, ta->offset);
51403 }
51404 
51405 /* Return the buffer associated to the typed array or an exception if
51406    it is not a typed array or if the buffer is detached. pbyte_offset,
51407    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)51408 JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
51409                                size_t *pbyte_offset,
51410                                size_t *pbyte_length,
51411                                size_t *pbytes_per_element)
51412 {
51413     JSObject *p;
51414     JSTypedArray *ta;
51415     p = get_typed_array(ctx, obj, FALSE);
51416     if (!p)
51417         return JS_EXCEPTION;
51418     if (typed_array_is_detached(ctx, p))
51419         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51420     ta = p->u.typed_array;
51421     if (pbyte_offset)
51422         *pbyte_offset = ta->offset;
51423     if (pbyte_length)
51424         *pbyte_length = ta->length;
51425     if (pbytes_per_element) {
51426         *pbytes_per_element = 1 << typed_array_size_log2(p->class_id);
51427     }
51428     return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
51429 }
51430 
js_typed_array_get_toStringTag(JSContext * ctx,JSValueConst this_val)51431 static JSValue js_typed_array_get_toStringTag(JSContext *ctx,
51432                                               JSValueConst this_val)
51433 {
51434     JSObject *p;
51435     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
51436         return JS_UNDEFINED;
51437     p = JS_VALUE_GET_OBJ(this_val);
51438     if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
51439           p->class_id <= JS_CLASS_FLOAT64_ARRAY))
51440         return JS_UNDEFINED;
51441     return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name);
51442 }
51443 
js_typed_array_set_internal(JSContext * ctx,JSValueConst dst,JSValueConst src,JSValueConst off)51444 static JSValue js_typed_array_set_internal(JSContext *ctx,
51445                                            JSValueConst dst,
51446                                            JSValueConst src,
51447                                            JSValueConst off)
51448 {
51449     JSObject *p;
51450     JSObject *src_p;
51451     uint32_t i;
51452     int64_t src_len, offset;
51453     JSValue val, src_obj = JS_UNDEFINED;
51454 
51455     p = get_typed_array(ctx, dst, 0);
51456     if (!p)
51457         goto fail;
51458     if (JS_ToInt64Sat(ctx, &offset, off))
51459         goto fail;
51460     if (offset < 0)
51461         goto range_error;
51462     if (typed_array_is_detached(ctx, p)) {
51463     detached:
51464         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51465         goto fail;
51466     }
51467     src_obj = JS_ToObject(ctx, src);
51468     if (JS_IsException(src_obj))
51469         goto fail;
51470     src_p = JS_VALUE_GET_OBJ(src_obj);
51471     if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY &&
51472         src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
51473         JSTypedArray *dest_ta = p->u.typed_array;
51474         JSArrayBuffer *dest_abuf = dest_ta->buffer->u.array_buffer;
51475         JSTypedArray *src_ta = src_p->u.typed_array;
51476         JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer;
51477         int shift = typed_array_size_log2(p->class_id);
51478 
51479         if (src_abuf->detached)
51480             goto detached;
51481 
51482         src_len = src_p->u.array.count;
51483         if (offset > (int64_t)(p->u.array.count - src_len))
51484             goto range_error;
51485 
51486         /* copying between typed objects */
51487         if (src_p->class_id == p->class_id) {
51488             /* same type, use memmove */
51489             memmove(dest_abuf->data + dest_ta->offset + (offset << shift),
51490                     src_abuf->data + src_ta->offset, src_len << shift);
51491             goto done;
51492         }
51493         if (dest_abuf->data == src_abuf->data) {
51494             /* copying between the same buffer using different types of mappings
51495                would require a temporary buffer */
51496         }
51497         /* otherwise, default behavior is slow but correct */
51498     } else {
51499         if (js_get_length64(ctx, &src_len, src_obj))
51500             goto fail;
51501         if (offset > (int64_t)(p->u.array.count - src_len)) {
51502         range_error:
51503             JS_ThrowRangeError(ctx, "invalid array length");
51504             goto fail;
51505         }
51506     }
51507     for(i = 0; i < src_len; i++) {
51508         val = JS_GetPropertyUint32(ctx, src_obj, i);
51509         if (JS_IsException(val))
51510             goto fail;
51511         if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0)
51512             goto fail;
51513     }
51514 done:
51515     JS_FreeValue(ctx, src_obj);
51516     return JS_UNDEFINED;
51517 fail:
51518     JS_FreeValue(ctx, src_obj);
51519     return JS_EXCEPTION;
51520 }
51521 
js_typed_array_set(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51522 static JSValue js_typed_array_set(JSContext *ctx,
51523                                   JSValueConst this_val,
51524                                   int argc, JSValueConst *argv)
51525 {
51526     JSValueConst offset = JS_UNDEFINED;
51527     if (argc > 1) {
51528         offset = argv[1];
51529     }
51530     return js_typed_array_set_internal(ctx, this_val, argv[0], offset);
51531 }
51532 
js_create_typed_array_iterator(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)51533 static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValueConst this_val,
51534                                               int argc, JSValueConst *argv, int magic)
51535 {
51536     if (validate_typed_array(ctx, this_val))
51537         return JS_EXCEPTION;
51538     return js_create_array_iterator(ctx, this_val, argc, argv, magic);
51539 }
51540 
51541 /* return < 0 if exception */
js_typed_array_get_length_internal(JSContext * ctx,JSValueConst obj)51542 static int js_typed_array_get_length_internal(JSContext *ctx,
51543                                               JSValueConst obj)
51544 {
51545     JSObject *p;
51546     p = get_typed_array(ctx, obj, 0);
51547     if (!p)
51548         return -1;
51549     if (typed_array_is_detached(ctx, p)) {
51550         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51551         return -1;
51552     }
51553     return p->u.array.count;
51554 }
51555 
51556 #if 0
51557 /* validate a typed array and return its length */
51558 static JSValue js_typed_array___getLength(JSContext *ctx,
51559                                           JSValueConst this_val,
51560                                           int argc, JSValueConst *argv)
51561 {
51562     BOOL ignore_detached = JS_ToBool(ctx, argv[1]);
51563 
51564     if (ignore_detached) {
51565         return js_typed_array_get_length(ctx, argv[0]);
51566     } else {
51567         int len;
51568         len = js_typed_array_get_length_internal(ctx, argv[0]);
51569         if (len < 0)
51570             return JS_EXCEPTION;
51571         return JS_NewInt32(ctx, len);
51572     }
51573 }
51574 #endif
51575 
js_typed_array_create(JSContext * ctx,JSValueConst ctor,int argc,JSValueConst * argv)51576 static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor,
51577                                      int argc, JSValueConst *argv)
51578 {
51579     JSValue ret;
51580     int new_len;
51581     int64_t len;
51582 
51583     ret = JS_CallConstructor(ctx, ctor, argc, argv);
51584     if (JS_IsException(ret))
51585         return ret;
51586     /* validate the typed array */
51587     new_len = js_typed_array_get_length_internal(ctx, ret);
51588     if (new_len < 0)
51589         goto fail;
51590     if (argc == 1) {
51591         /* ensure that it is large enough */
51592         if (JS_ToLengthFree(ctx, &len, JS_DupValue(ctx, argv[0])))
51593             goto fail;
51594         if (new_len < len) {
51595             JS_ThrowTypeError(ctx, "TypedArray length is too small");
51596         fail:
51597             JS_FreeValue(ctx, ret);
51598             return JS_EXCEPTION;
51599         }
51600     }
51601     return ret;
51602 }
51603 
51604 #if 0
51605 static JSValue js_typed_array___create(JSContext *ctx,
51606                                        JSValueConst this_val,
51607                                        int argc, JSValueConst *argv)
51608 {
51609     return js_typed_array_create(ctx, argv[0], max_int(argc - 1, 0), argv + 1);
51610 }
51611 #endif
51612 
js_typed_array___speciesCreate(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51613 static JSValue js_typed_array___speciesCreate(JSContext *ctx,
51614                                               JSValueConst this_val,
51615                                               int argc, JSValueConst *argv)
51616 {
51617     JSValueConst obj;
51618     JSObject *p;
51619     JSValue ctor, ret;
51620     int argc1;
51621 
51622     obj = argv[0];
51623     p = get_typed_array(ctx, obj, 0);
51624     if (!p)
51625         return JS_EXCEPTION;
51626     ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED);
51627     if (JS_IsException(ctor))
51628         return ctor;
51629     argc1 = max_int(argc - 1, 0);
51630     if (JS_IsUndefined(ctor)) {
51631         ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1,
51632                                          p->class_id);
51633     } else {
51634         ret = js_typed_array_create(ctx, ctor, argc1, argv + 1);
51635         JS_FreeValue(ctx, ctor);
51636     }
51637     return ret;
51638 }
51639 
js_typed_array_from(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51640 static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val,
51641                                    int argc, JSValueConst *argv)
51642 {
51643     // from(items, mapfn = void 0, this_arg = void 0)
51644     JSValueConst items = argv[0], mapfn, this_arg;
51645     JSValueConst args[2];
51646     JSValue stack[2];
51647     JSValue iter, arr, r, v, v2;
51648     int64_t k, len;
51649     int done, mapping;
51650 
51651     mapping = FALSE;
51652     mapfn = JS_UNDEFINED;
51653     this_arg = JS_UNDEFINED;
51654     r = JS_UNDEFINED;
51655     arr = JS_UNDEFINED;
51656     stack[0] = JS_UNDEFINED;
51657     stack[1] = JS_UNDEFINED;
51658 
51659     if (argc > 1) {
51660         mapfn = argv[1];
51661         if (!JS_IsUndefined(mapfn)) {
51662             if (check_function(ctx, mapfn))
51663                 goto exception;
51664             mapping = 1;
51665             if (argc > 2)
51666                 this_arg = argv[2];
51667         }
51668     }
51669     iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
51670     if (JS_IsException(iter))
51671         goto exception;
51672     if (!JS_IsUndefined(iter)) {
51673         JS_FreeValue(ctx, iter);
51674         arr = JS_NewArray(ctx);
51675         if (JS_IsException(arr))
51676             goto exception;
51677         stack[0] = JS_DupValue(ctx, items);
51678         if (js_for_of_start(ctx, &stack[1], FALSE))
51679             goto exception;
51680         for (k = 0;; k++) {
51681             v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);
51682             if (JS_IsException(v))
51683                 goto exception_close;
51684             if (done)
51685                 break;
51686             if (JS_DefinePropertyValueInt64(ctx, arr, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
51687                 goto exception_close;
51688         }
51689     } else {
51690         arr = JS_ToObject(ctx, items);
51691         if (JS_IsException(arr))
51692             goto exception;
51693     }
51694     if (js_get_length64(ctx, &len, arr) < 0)
51695         goto exception;
51696     v = JS_NewInt64(ctx, len);
51697     args[0] = v;
51698     r = js_typed_array_create(ctx, this_val, 1, args);
51699     JS_FreeValue(ctx, v);
51700     if (JS_IsException(r))
51701         goto exception;
51702     for(k = 0; k < len; k++) {
51703         v = JS_GetPropertyInt64(ctx, arr, k);
51704         if (JS_IsException(v))
51705             goto exception;
51706         if (mapping) {
51707             args[0] = v;
51708             args[1] = JS_NewInt32(ctx, k);
51709             v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
51710             JS_FreeValue(ctx, v);
51711             v = v2;
51712             if (JS_IsException(v))
51713                 goto exception;
51714         }
51715         if (JS_SetPropertyInt64(ctx, r, k, v) < 0)
51716             goto exception;
51717     }
51718     goto done;
51719 
51720  exception_close:
51721     if (!JS_IsUndefined(stack[0]))
51722         JS_IteratorClose(ctx, stack[0], TRUE);
51723  exception:
51724     JS_FreeValue(ctx, r);
51725     r = JS_EXCEPTION;
51726  done:
51727     JS_FreeValue(ctx, arr);
51728     JS_FreeValue(ctx, stack[0]);
51729     JS_FreeValue(ctx, stack[1]);
51730     return r;
51731 }
51732 
js_typed_array_of(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51733 static JSValue js_typed_array_of(JSContext *ctx, JSValueConst this_val,
51734                                  int argc, JSValueConst *argv)
51735 {
51736     JSValue obj;
51737     JSValueConst args[1];
51738     int i;
51739 
51740     args[0] = JS_NewInt32(ctx, argc);
51741     obj = js_typed_array_create(ctx, this_val, 1, args);
51742     if (JS_IsException(obj))
51743         return obj;
51744 
51745     for(i = 0; i < argc; i++) {
51746         if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) {
51747             JS_FreeValue(ctx, obj);
51748             return JS_EXCEPTION;
51749         }
51750     }
51751     return obj;
51752 }
51753 
js_typed_array_copyWithin(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51754 static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val,
51755                                          int argc, JSValueConst *argv)
51756 {
51757     JSObject *p;
51758     int len, to, from, final, count, shift;
51759 
51760     len = js_typed_array_get_length_internal(ctx, this_val);
51761     if (len < 0)
51762         return JS_EXCEPTION;
51763 
51764     if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len))
51765         return JS_EXCEPTION;
51766 
51767     if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len))
51768         return JS_EXCEPTION;
51769 
51770     final = len;
51771     if (argc > 2 && !JS_IsUndefined(argv[2])) {
51772         if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
51773             return JS_EXCEPTION;
51774     }
51775 
51776     count = min_int(final - from, len - to);
51777     if (count > 0) {
51778         p = JS_VALUE_GET_OBJ(this_val);
51779         if (typed_array_is_detached(ctx, p))
51780             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51781         shift = typed_array_size_log2(p->class_id);
51782         memmove(p->u.array.u.uint8_ptr + (to << shift),
51783                 p->u.array.u.uint8_ptr + (from << shift),
51784                 count << shift);
51785     }
51786     return JS_DupValue(ctx, this_val);
51787 }
51788 
js_typed_array_fill(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)51789 static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val,
51790                                    int argc, JSValueConst *argv)
51791 {
51792     JSObject *p;
51793     int len, k, final, shift;
51794     uint64_t v64;
51795 
51796     len = js_typed_array_get_length_internal(ctx, this_val);
51797     if (len < 0)
51798         return JS_EXCEPTION;
51799     p = JS_VALUE_GET_OBJ(this_val);
51800 
51801     if (p->class_id == JS_CLASS_UINT8C_ARRAY) {
51802         int32_t v;
51803         if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0])))
51804             return JS_EXCEPTION;
51805         v64 = v;
51806     } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) {
51807         uint32_t v;
51808         if (JS_ToUint32(ctx, &v, argv[0]))
51809             return JS_EXCEPTION;
51810         v64 = v;
51811     } else
51812 #ifdef CONFIG_BIGNUM
51813     if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
51814         if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0]))
51815             return JS_EXCEPTION;
51816     } else
51817 #endif
51818     {
51819         double d;
51820         if (JS_ToFloat64(ctx, &d, argv[0]))
51821             return JS_EXCEPTION;
51822         if (p->class_id == JS_CLASS_FLOAT32_ARRAY) {
51823             union {
51824                 float f;
51825                 uint32_t u32;
51826             } u;
51827             u.f = d;
51828             v64 = u.u32;
51829         } else {
51830             JSFloat64Union u;
51831             u.d = d;
51832             v64 = u.u64;
51833         }
51834     }
51835 
51836     k = 0;
51837     if (argc > 1) {
51838         if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
51839             return JS_EXCEPTION;
51840     }
51841 
51842     final = len;
51843     if (argc > 2 && !JS_IsUndefined(argv[2])) {
51844         if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
51845             return JS_EXCEPTION;
51846     }
51847 
51848     if (typed_array_is_detached(ctx, p))
51849         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51850 
51851     shift = typed_array_size_log2(p->class_id);
51852     switch(shift) {
51853     case 0:
51854         if (k < final) {
51855             memset(p->u.array.u.uint8_ptr + k, v64, final - k);
51856         }
51857         break;
51858     case 1:
51859         for(; k < final; k++) {
51860             p->u.array.u.uint16_ptr[k] = v64;
51861         }
51862         break;
51863     case 2:
51864         for(; k < final; k++) {
51865             p->u.array.u.uint32_ptr[k] = v64;
51866         }
51867         break;
51868     case 3:
51869         for(; k < final; k++) {
51870             p->u.array.u.uint64_ptr[k] = v64;
51871         }
51872         break;
51873     default:
51874         abort();
51875     }
51876     return JS_DupValue(ctx, this_val);
51877 }
51878 
js_typed_array_find(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int findIndex)51879 static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val,
51880                                    int argc, JSValueConst *argv, int findIndex)
51881 {
51882     JSValueConst func, this_arg;
51883     JSValueConst args[3];
51884     JSValue val, index_val, res;
51885     int len, k;
51886 
51887     val = JS_UNDEFINED;
51888     len = js_typed_array_get_length_internal(ctx, this_val);
51889     if (len < 0)
51890         goto exception;
51891 
51892     func = argv[0];
51893     if (check_function(ctx, func))
51894         goto exception;
51895 
51896     this_arg = JS_UNDEFINED;
51897     if (argc > 1)
51898         this_arg = argv[1];
51899 
51900     for(k = 0; k < len; k++) {
51901         index_val = JS_NewInt32(ctx, k);
51902         val = JS_GetPropertyValue(ctx, this_val, index_val);
51903         if (JS_IsException(val))
51904             goto exception;
51905         args[0] = val;
51906         args[1] = index_val;
51907         args[2] = this_val;
51908         res = JS_Call(ctx, func, this_arg, 3, args);
51909         if (JS_IsException(res))
51910             goto exception;
51911         if (JS_ToBoolFree(ctx, res)) {
51912             if (findIndex) {
51913                 JS_FreeValue(ctx, val);
51914                 return index_val;
51915             } else {
51916                 return val;
51917             }
51918         }
51919         JS_FreeValue(ctx, val);
51920     }
51921     if (findIndex)
51922         return JS_NewInt32(ctx, -1);
51923     else
51924         return JS_UNDEFINED;
51925 
51926 exception:
51927     JS_FreeValue(ctx, val);
51928     return JS_EXCEPTION;
51929 }
51930 
51931 #define special_indexOf 0
51932 #define special_lastIndexOf 1
51933 #define special_includes -1
51934 
js_typed_array_indexOf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int special)51935 static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
51936                                       int argc, JSValueConst *argv, int special)
51937 {
51938     JSObject *p;
51939     int len, tag, is_int, is_bigint, k, stop, inc, res = -1;
51940     int64_t v64;
51941     double d;
51942     float f;
51943 
51944     len = js_typed_array_get_length_internal(ctx, this_val);
51945     if (len < 0)
51946         goto exception;
51947     if (len == 0)
51948         goto done;
51949 
51950     if (special == special_lastIndexOf) {
51951         k = len - 1;
51952         if (argc > 1) {
51953             if (JS_ToFloat64(ctx, &d, argv[1]))
51954                 goto exception;
51955             if (isnan(d)) {
51956                 k = 0;
51957             } else {
51958                 if (d >= 0) {
51959                     if (d < k) {
51960                         k = d;
51961                     }
51962                 } else {
51963                     d += len;
51964                     if (d < 0)
51965                         goto done;
51966                     k = d;
51967                 }
51968             }
51969         }
51970         stop = -1;
51971         inc = -1;
51972     } else {
51973         k = 0;
51974         if (argc > 1) {
51975             if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
51976                 goto exception;
51977         }
51978         stop = len;
51979         inc = 1;
51980     }
51981 
51982     if (validate_typed_array(ctx, this_val))
51983         goto exception;
51984     p = JS_VALUE_GET_OBJ(this_val);
51985 
51986     is_bigint = 0;
51987     is_int = 0; /* avoid warning */
51988     v64 = 0; /* avoid warning */
51989     tag = JS_VALUE_GET_NORM_TAG(argv[0]);
51990     if (tag == JS_TAG_INT) {
51991         is_int = 1;
51992         v64 = JS_VALUE_GET_INT(argv[0]);
51993         d = v64;
51994     } else
51995     if (tag == JS_TAG_FLOAT64) {
51996         d = JS_VALUE_GET_FLOAT64(argv[0]);
51997         v64 = d;
51998         is_int = (v64 == d);
51999     } else
52000 #ifdef CONFIG_BIGNUM
52001     if (tag == JS_TAG_BIG_INT) {
52002         JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]);
52003 
52004         if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) {
52005             if (bf_get_int64(&v64, &p1->num, 0) != 0)
52006                 goto done;
52007         } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) {
52008             if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0)
52009                 goto done;
52010         } else {
52011             goto done;
52012         }
52013         d = 0;
52014         is_bigint = 1;
52015     } else
52016 #endif
52017     {
52018         goto done;
52019     }
52020 
52021     p = JS_VALUE_GET_OBJ(this_val);
52022     switch (p->class_id) {
52023     case JS_CLASS_INT8_ARRAY:
52024         if (is_int && (int8_t)v64 == v64)
52025             goto scan8;
52026         break;
52027     case JS_CLASS_UINT8C_ARRAY:
52028     case JS_CLASS_UINT8_ARRAY:
52029         if (is_int && (uint8_t)v64 == v64) {
52030             const uint8_t *pv, *pp;
52031             uint16_t v;
52032         scan8:
52033             pv = p->u.array.u.uint8_ptr;
52034             v = v64;
52035             if (inc > 0) {
52036                 pp = memchr(pv + k, v, len - k);
52037                 if (pp)
52038                     res = pp - pv;
52039             } else {
52040                 for (; k != stop; k += inc) {
52041                     if (pv[k] == v) {
52042                         res = k;
52043                         break;
52044                     }
52045                 }
52046             }
52047         }
52048         break;
52049     case JS_CLASS_INT16_ARRAY:
52050         if (is_int && (int16_t)v64 == v64)
52051             goto scan16;
52052         break;
52053     case JS_CLASS_UINT16_ARRAY:
52054         if (is_int && (uint16_t)v64 == v64) {
52055             const uint16_t *pv;
52056             uint16_t v;
52057         scan16:
52058             pv = p->u.array.u.uint16_ptr;
52059             v = v64;
52060             for (; k != stop; k += inc) {
52061                 if (pv[k] == v) {
52062                     res = k;
52063                     break;
52064                 }
52065             }
52066         }
52067         break;
52068     case JS_CLASS_INT32_ARRAY:
52069         if (is_int && (int32_t)v64 == v64)
52070             goto scan32;
52071         break;
52072     case JS_CLASS_UINT32_ARRAY:
52073         if (is_int && (uint32_t)v64 == v64) {
52074             const uint32_t *pv;
52075             uint32_t v;
52076         scan32:
52077             pv = p->u.array.u.uint32_ptr;
52078             v = v64;
52079             for (; k != stop; k += inc) {
52080                 if (pv[k] == v) {
52081                     res = k;
52082                     break;
52083                 }
52084             }
52085         }
52086         break;
52087     case JS_CLASS_FLOAT32_ARRAY:
52088         if (is_bigint)
52089             break;
52090         if (isnan(d)) {
52091             const float *pv = p->u.array.u.float_ptr;
52092             /* special case: indexOf returns -1, includes finds NaN */
52093             if (special != special_includes)
52094                 goto done;
52095             for (; k != stop; k += inc) {
52096                 if (isnan(pv[k])) {
52097                     res = k;
52098                     break;
52099                 }
52100             }
52101         } else if ((f = (float)d) == d) {
52102             const float *pv = p->u.array.u.float_ptr;
52103             for (; k != stop; k += inc) {
52104                 if (pv[k] == f) {
52105                     res = k;
52106                     break;
52107                 }
52108             }
52109         }
52110         break;
52111     case JS_CLASS_FLOAT64_ARRAY:
52112         if (is_bigint)
52113             break;
52114         if (isnan(d)) {
52115             const double *pv = p->u.array.u.double_ptr;
52116             /* special case: indexOf returns -1, includes finds NaN */
52117             if (special != special_includes)
52118                 goto done;
52119             for (; k != stop; k += inc) {
52120                 if (isnan(pv[k])) {
52121                     res = k;
52122                     break;
52123                 }
52124             }
52125         } else {
52126             const double *pv = p->u.array.u.double_ptr;
52127             for (; k != stop; k += inc) {
52128                 if (pv[k] == d) {
52129                     res = k;
52130                     break;
52131                 }
52132             }
52133         }
52134         break;
52135 #ifdef CONFIG_BIGNUM
52136     case JS_CLASS_BIG_INT64_ARRAY:
52137         if (is_bigint || (is_math_mode(ctx) && is_int &&
52138                           v64 >= -MAX_SAFE_INTEGER &&
52139                           v64 <= MAX_SAFE_INTEGER)) {
52140             goto scan64;
52141         }
52142         break;
52143     case JS_CLASS_BIG_UINT64_ARRAY:
52144         if (is_bigint || (is_math_mode(ctx) && is_int &&
52145                           v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) {
52146             const uint64_t *pv;
52147             uint64_t v;
52148         scan64:
52149             pv = p->u.array.u.uint64_ptr;
52150             v = v64;
52151             for (; k != stop; k += inc) {
52152                 if (pv[k] == v) {
52153                     res = k;
52154                     break;
52155                 }
52156             }
52157         }
52158         break;
52159 #endif
52160     }
52161 
52162 done:
52163     if (special == special_includes)
52164         return JS_NewBool(ctx, res >= 0);
52165     else
52166         return JS_NewInt32(ctx, res);
52167 
52168 exception:
52169     return JS_EXCEPTION;
52170 }
52171 
js_typed_array_join(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int toLocaleString)52172 static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val,
52173                                    int argc, JSValueConst *argv, int toLocaleString)
52174 {
52175     JSValue sep = JS_UNDEFINED, el;
52176     StringBuffer b_s, *b = &b_s;
52177     JSString *p = NULL;
52178     int i, n;
52179     int c;
52180 
52181     n = js_typed_array_get_length_internal(ctx, this_val);
52182     if (n < 0)
52183         goto exception;
52184 
52185     c = ',';    /* default separator */
52186     if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
52187         sep = JS_ToString(ctx, argv[0]);
52188         if (JS_IsException(sep))
52189             goto exception;
52190         p = JS_VALUE_GET_STRING(sep);
52191         if (p->len == 1 && !p->is_wide_char)
52192             c = p->u.str8[0];
52193         else
52194             c = -1;
52195     }
52196     string_buffer_init(ctx, b, 0);
52197 
52198     /* XXX: optimize with direct access */
52199     for(i = 0; i < n; i++) {
52200         if (i > 0) {
52201             if (c >= 0) {
52202                 if (string_buffer_putc8(b, c))
52203                     goto fail;
52204             } else {
52205                 if (string_buffer_concat(b, p, 0, p->len))
52206                     goto fail;
52207             }
52208         }
52209         el = JS_GetPropertyUint32(ctx, this_val, i);
52210         if (JS_IsException(el))
52211             goto fail;
52212         if (toLocaleString) {
52213             el = JS_ToLocaleStringFree(ctx, el);
52214         }
52215         if (string_buffer_concat_value_free(b, el))
52216             goto fail;
52217     }
52218     JS_FreeValue(ctx, sep);
52219     return string_buffer_end(b);
52220 
52221 fail:
52222     string_buffer_free(b);
52223     JS_FreeValue(ctx, sep);
52224 exception:
52225     return JS_EXCEPTION;
52226 }
52227 
js_typed_array_reverse(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)52228 static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val,
52229                                       int argc, JSValueConst *argv)
52230 {
52231     JSObject *p;
52232     int len;
52233 
52234     len = js_typed_array_get_length_internal(ctx, this_val);
52235     if (len < 0)
52236         return JS_EXCEPTION;
52237     if (len > 0) {
52238         p = JS_VALUE_GET_OBJ(this_val);
52239         switch (typed_array_size_log2(p->class_id)) {
52240         case 0:
52241             {
52242                 uint8_t *p1 = p->u.array.u.uint8_ptr;
52243                 uint8_t *p2 = p1 + len - 1;
52244                 while (p1 < p2) {
52245                     uint8_t v = *p1;
52246                     *p1++ = *p2;
52247                     *p2-- = v;
52248                 }
52249             }
52250             break;
52251         case 1:
52252             {
52253                 uint16_t *p1 = p->u.array.u.uint16_ptr;
52254                 uint16_t *p2 = p1 + len - 1;
52255                 while (p1 < p2) {
52256                     uint16_t v = *p1;
52257                     *p1++ = *p2;
52258                     *p2-- = v;
52259                 }
52260             }
52261             break;
52262         case 2:
52263             {
52264                 uint32_t *p1 = p->u.array.u.uint32_ptr;
52265                 uint32_t *p2 = p1 + len - 1;
52266                 while (p1 < p2) {
52267                     uint32_t v = *p1;
52268                     *p1++ = *p2;
52269                     *p2-- = v;
52270                 }
52271             }
52272             break;
52273         case 3:
52274             {
52275                 uint64_t *p1 = p->u.array.u.uint64_ptr;
52276                 uint64_t *p2 = p1 + len - 1;
52277                 while (p1 < p2) {
52278                     uint64_t v = *p1;
52279                     *p1++ = *p2;
52280                     *p2-- = v;
52281                 }
52282             }
52283             break;
52284         default:
52285             abort();
52286         }
52287     }
52288     return JS_DupValue(ctx, this_val);
52289 }
52290 
js_typed_array_slice(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)52291 static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val,
52292                                     int argc, JSValueConst *argv)
52293 {
52294     JSValueConst args[2];
52295     JSValue arr, val;
52296     JSObject *p, *p1;
52297     int n, len, start, final, count, shift;
52298 
52299     arr = JS_UNDEFINED;
52300     len = js_typed_array_get_length_internal(ctx, this_val);
52301     if (len < 0)
52302         goto exception;
52303 
52304     if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
52305         goto exception;
52306     final = len;
52307     if (!JS_IsUndefined(argv[1])) {
52308         if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
52309             goto exception;
52310     }
52311     count = max_int(final - start, 0);
52312 
52313     p = get_typed_array(ctx, this_val, 0);
52314     if (p == NULL)
52315         goto exception;
52316     shift = typed_array_size_log2(p->class_id);
52317 
52318     args[0] = this_val;
52319     args[1] = JS_NewInt32(ctx, count);
52320     arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
52321     if (JS_IsException(arr))
52322         goto exception;
52323 
52324     if (count > 0) {
52325         if (validate_typed_array(ctx, this_val)
52326         ||  validate_typed_array(ctx, arr))
52327             goto exception;
52328 
52329         p1 = get_typed_array(ctx, arr, 0);
52330         if (p1 != NULL && p->class_id == p1->class_id &&
52331             typed_array_get_length(ctx, p1) >= count &&
52332             typed_array_get_length(ctx, p) >= start + count) {
52333             memcpy(p1->u.array.u.uint8_ptr,
52334                    p->u.array.u.uint8_ptr + (start << shift),
52335                    count << shift);
52336         } else {
52337             for (n = 0; n < count; n++) {
52338                 val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n));
52339                 if (JS_IsException(val))
52340                     goto exception;
52341                 if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val,
52342                                         JS_PROP_THROW) < 0)
52343                     goto exception;
52344             }
52345         }
52346     }
52347     return arr;
52348 
52349  exception:
52350     JS_FreeValue(ctx, arr);
52351     return JS_EXCEPTION;
52352 }
52353 
js_typed_array_subarray(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)52354 static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val,
52355                                        int argc, JSValueConst *argv)
52356 {
52357     JSValueConst args[4];
52358     JSValue arr, byteOffset, ta_buffer;
52359     JSObject *p;
52360     int len, start, final, count, shift, offset;
52361 
52362     p = get_typed_array(ctx, this_val, 0);
52363     if (!p)
52364         goto exception;
52365     len = p->u.array.count;
52366     if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
52367         goto exception;
52368 
52369     final = len;
52370     if (!JS_IsUndefined(argv[1])) {
52371         if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
52372             goto exception;
52373     }
52374     count = max_int(final - start, 0);
52375     byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0);
52376     if (JS_IsException(byteOffset))
52377         goto exception;
52378     shift = typed_array_size_log2(p->class_id);
52379     offset = JS_VALUE_GET_INT(byteOffset) + (start << shift);
52380     JS_FreeValue(ctx, byteOffset);
52381     ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0);
52382     if (JS_IsException(ta_buffer))
52383         goto exception;
52384     args[0] = this_val;
52385     args[1] = ta_buffer;
52386     args[2] = JS_NewInt32(ctx, offset);
52387     args[3] = JS_NewInt32(ctx, count);
52388     arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args);
52389     JS_FreeValue(ctx, ta_buffer);
52390     return arr;
52391 
52392  exception:
52393     return JS_EXCEPTION;
52394 }
52395 
52396 /* TypedArray.prototype.sort */
52397 
js_cmp_doubles(double x,double y)52398 static int js_cmp_doubles(double x, double y)
52399 {
52400     if (isnan(x))    return isnan(y) ? 0 : +1;
52401     if (isnan(y))    return -1;
52402     if (x < y)       return -1;
52403     if (x > y)       return 1;
52404     if (x != 0)      return 0;
52405     if (signbit(x))  return signbit(y) ? 0 : -1;
52406     else             return signbit(y) ? 1 : 0;
52407 }
52408 
js_TA_cmp_int8(const void * a,const void * b,void * opaque)52409 static int js_TA_cmp_int8(const void *a, const void *b, void *opaque) {
52410     return *(const int8_t *)a - *(const int8_t *)b;
52411 }
52412 
js_TA_cmp_uint8(const void * a,const void * b,void * opaque)52413 static int js_TA_cmp_uint8(const void *a, const void *b, void *opaque) {
52414     return *(const uint8_t *)a - *(const uint8_t *)b;
52415 }
52416 
js_TA_cmp_int16(const void * a,const void * b,void * opaque)52417 static int js_TA_cmp_int16(const void *a, const void *b, void *opaque) {
52418     return *(const int16_t *)a - *(const int16_t *)b;
52419 }
52420 
js_TA_cmp_uint16(const void * a,const void * b,void * opaque)52421 static int js_TA_cmp_uint16(const void *a, const void *b, void *opaque) {
52422     return *(const uint16_t *)a - *(const uint16_t *)b;
52423 }
52424 
js_TA_cmp_int32(const void * a,const void * b,void * opaque)52425 static int js_TA_cmp_int32(const void *a, const void *b, void *opaque) {
52426     int32_t x = *(const int32_t *)a;
52427     int32_t y = *(const int32_t *)b;
52428     return (y < x) - (y > x);
52429 }
52430 
js_TA_cmp_uint32(const void * a,const void * b,void * opaque)52431 static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) {
52432     uint32_t x = *(const uint32_t *)a;
52433     uint32_t y = *(const uint32_t *)b;
52434     return (y < x) - (y > x);
52435 }
52436 
52437 #ifdef CONFIG_BIGNUM
js_TA_cmp_int64(const void * a,const void * b,void * opaque)52438 static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) {
52439     int64_t x = *(const int64_t *)a;
52440     int64_t y = *(const int64_t *)b;
52441     return (y < x) - (y > x);
52442 }
52443 
js_TA_cmp_uint64(const void * a,const void * b,void * opaque)52444 static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) {
52445     uint64_t x = *(const uint64_t *)a;
52446     uint64_t y = *(const uint64_t *)b;
52447     return (y < x) - (y > x);
52448 }
52449 #endif
52450 
js_TA_cmp_float32(const void * a,const void * b,void * opaque)52451 static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) {
52452     return js_cmp_doubles(*(const float *)a, *(const float *)b);
52453 }
52454 
js_TA_cmp_float64(const void * a,const void * b,void * opaque)52455 static int js_TA_cmp_float64(const void *a, const void *b, void *opaque) {
52456     return js_cmp_doubles(*(const double *)a, *(const double *)b);
52457 }
52458 
js_TA_get_int8(JSContext * ctx,const void * a)52459 static JSValue js_TA_get_int8(JSContext *ctx, const void *a) {
52460     return JS_NewInt32(ctx, *(const int8_t *)a);
52461 }
52462 
js_TA_get_uint8(JSContext * ctx,const void * a)52463 static JSValue js_TA_get_uint8(JSContext *ctx, const void *a) {
52464     return JS_NewInt32(ctx, *(const uint8_t *)a);
52465 }
52466 
js_TA_get_int16(JSContext * ctx,const void * a)52467 static JSValue js_TA_get_int16(JSContext *ctx, const void *a) {
52468     return JS_NewInt32(ctx, *(const int16_t *)a);
52469 }
52470 
js_TA_get_uint16(JSContext * ctx,const void * a)52471 static JSValue js_TA_get_uint16(JSContext *ctx, const void *a) {
52472     return JS_NewInt32(ctx, *(const uint16_t *)a);
52473 }
52474 
js_TA_get_int32(JSContext * ctx,const void * a)52475 static JSValue js_TA_get_int32(JSContext *ctx, const void *a) {
52476     return JS_NewInt32(ctx, *(const int32_t *)a);
52477 }
52478 
js_TA_get_uint32(JSContext * ctx,const void * a)52479 static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) {
52480     return JS_NewUint32(ctx, *(const uint32_t *)a);
52481 }
52482 
52483 #ifdef CONFIG_BIGNUM
js_TA_get_int64(JSContext * ctx,const void * a)52484 static JSValue js_TA_get_int64(JSContext *ctx, const void *a) {
52485     return JS_NewBigInt64(ctx, *(int64_t *)a);
52486 }
52487 
js_TA_get_uint64(JSContext * ctx,const void * a)52488 static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) {
52489     return JS_NewBigUint64(ctx, *(uint64_t *)a);
52490 }
52491 #endif
52492 
js_TA_get_float32(JSContext * ctx,const void * a)52493 static JSValue js_TA_get_float32(JSContext *ctx, const void *a) {
52494     return __JS_NewFloat64(ctx, *(const float *)a);
52495 }
52496 
js_TA_get_float64(JSContext * ctx,const void * a)52497 static JSValue js_TA_get_float64(JSContext *ctx, const void *a) {
52498     return __JS_NewFloat64(ctx, *(const double *)a);
52499 }
52500 
52501 struct TA_sort_context {
52502     JSContext *ctx;
52503     int exception;
52504     JSValueConst arr;
52505     JSValueConst cmp;
52506     JSValue (*getfun)(JSContext *ctx, const void *a);
52507     uint8_t *array_ptr; /* cannot change unless the array is detached */
52508     int elt_size;
52509 };
52510 
js_TA_cmp_generic(const void * a,const void * b,void * opaque)52511 static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
52512     struct TA_sort_context *psc = opaque;
52513     JSContext *ctx = psc->ctx;
52514     uint32_t a_idx, b_idx;
52515     JSValueConst argv[2];
52516     JSValue res;
52517     int cmp;
52518 
52519     cmp = 0;
52520     if (!psc->exception) {
52521         a_idx = *(uint32_t *)a;
52522         b_idx = *(uint32_t *)b;
52523         argv[0] = psc->getfun(ctx, psc->array_ptr +
52524                               a_idx * (size_t)psc->elt_size);
52525         argv[1] = psc->getfun(ctx, psc->array_ptr +
52526                               b_idx * (size_t)(psc->elt_size));
52527         res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv);
52528         if (JS_IsException(res)) {
52529             psc->exception = 1;
52530             goto done;
52531         }
52532         if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
52533             int val = JS_VALUE_GET_INT(res);
52534             cmp = (val > 0) - (val < 0);
52535         } else {
52536             double val;
52537             if (JS_ToFloat64Free(ctx, &val, res) < 0) {
52538                 psc->exception = 1;
52539                 goto done;
52540             } else {
52541                 cmp = (val > 0) - (val < 0);
52542             }
52543         }
52544         if (cmp == 0) {
52545             /* make sort stable: compare array offsets */
52546             cmp = (a_idx > b_idx) - (a_idx < b_idx);
52547         }
52548         if (validate_typed_array(ctx, psc->arr) < 0) {
52549             psc->exception = 1;
52550         }
52551     done:
52552         JS_FreeValue(ctx, (JSValue)argv[0]);
52553         JS_FreeValue(ctx, (JSValue)argv[1]);
52554     }
52555     return cmp;
52556 }
52557 
js_typed_array_sort(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)52558 static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
52559                                    int argc, JSValueConst *argv)
52560 {
52561     JSObject *p;
52562     int len;
52563     size_t elt_size;
52564     struct TA_sort_context tsc;
52565     void *array_ptr;
52566     int (*cmpfun)(const void *a, const void *b, void *opaque);
52567 
52568     tsc.ctx = ctx;
52569     tsc.exception = 0;
52570     tsc.arr = this_val;
52571     tsc.cmp = argv[0];
52572 
52573     len = js_typed_array_get_length_internal(ctx, this_val);
52574     if (len < 0)
52575         return JS_EXCEPTION;
52576     if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp))
52577         return JS_EXCEPTION;
52578 
52579     if (len > 1) {
52580         p = JS_VALUE_GET_OBJ(this_val);
52581         switch (p->class_id) {
52582         case JS_CLASS_INT8_ARRAY:
52583             tsc.getfun = js_TA_get_int8;
52584             cmpfun = js_TA_cmp_int8;
52585             break;
52586         case JS_CLASS_UINT8C_ARRAY:
52587         case JS_CLASS_UINT8_ARRAY:
52588             tsc.getfun = js_TA_get_uint8;
52589             cmpfun = js_TA_cmp_uint8;
52590             break;
52591         case JS_CLASS_INT16_ARRAY:
52592             tsc.getfun = js_TA_get_int16;
52593             cmpfun = js_TA_cmp_int16;
52594             break;
52595         case JS_CLASS_UINT16_ARRAY:
52596             tsc.getfun = js_TA_get_uint16;
52597             cmpfun = js_TA_cmp_uint16;
52598             break;
52599         case JS_CLASS_INT32_ARRAY:
52600             tsc.getfun = js_TA_get_int32;
52601             cmpfun = js_TA_cmp_int32;
52602             break;
52603         case JS_CLASS_UINT32_ARRAY:
52604             tsc.getfun = js_TA_get_uint32;
52605             cmpfun = js_TA_cmp_uint32;
52606             break;
52607 #ifdef CONFIG_BIGNUM
52608         case JS_CLASS_BIG_INT64_ARRAY:
52609             tsc.getfun = js_TA_get_int64;
52610             cmpfun = js_TA_cmp_int64;
52611             break;
52612         case JS_CLASS_BIG_UINT64_ARRAY:
52613             tsc.getfun = js_TA_get_uint64;
52614             cmpfun = js_TA_cmp_uint64;
52615             break;
52616 #endif
52617         case JS_CLASS_FLOAT32_ARRAY:
52618             tsc.getfun = js_TA_get_float32;
52619             cmpfun = js_TA_cmp_float32;
52620             break;
52621         case JS_CLASS_FLOAT64_ARRAY:
52622             tsc.getfun = js_TA_get_float64;
52623             cmpfun = js_TA_cmp_float64;
52624             break;
52625         default:
52626             abort();
52627         }
52628         array_ptr = p->u.array.u.ptr;
52629         elt_size = 1 << typed_array_size_log2(p->class_id);
52630         if (!JS_IsUndefined(tsc.cmp)) {
52631             uint32_t *array_idx;
52632             void *array_tmp;
52633             size_t i, j;
52634 
52635             /* XXX: a stable sort would use less memory */
52636             array_idx = js_malloc(ctx, len * sizeof(array_idx[0]));
52637             if (!array_idx)
52638                 return JS_EXCEPTION;
52639             for(i = 0; i < len; i++)
52640                 array_idx[i] = i;
52641             tsc.array_ptr = array_ptr;
52642             tsc.elt_size = elt_size;
52643             rqsort(array_idx, len, sizeof(array_idx[0]),
52644                    js_TA_cmp_generic, &tsc);
52645             if (tsc.exception)
52646                 goto fail;
52647             array_tmp = js_malloc(ctx, len * elt_size);
52648             if (!array_tmp) {
52649             fail:
52650                 js_free(ctx, array_idx);
52651                 return JS_EXCEPTION;
52652             }
52653             memcpy(array_tmp, array_ptr, len * elt_size);
52654             switch(elt_size) {
52655             case 1:
52656                 for(i = 0; i < len; i++) {
52657                     j = array_idx[i];
52658                     ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j];
52659                 }
52660                 break;
52661             case 2:
52662                 for(i = 0; i < len; i++) {
52663                     j = array_idx[i];
52664                     ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j];
52665                 }
52666                 break;
52667             case 4:
52668                 for(i = 0; i < len; i++) {
52669                     j = array_idx[i];
52670                     ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j];
52671                 }
52672                 break;
52673             case 8:
52674                 for(i = 0; i < len; i++) {
52675                     j = array_idx[i];
52676                     ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j];
52677                 }
52678                 break;
52679             default:
52680                 abort();
52681             }
52682             js_free(ctx, array_tmp);
52683             js_free(ctx, array_idx);
52684         } else {
52685             rqsort(array_ptr, len, elt_size, cmpfun, &tsc);
52686             if (tsc.exception)
52687                 return JS_EXCEPTION;
52688         }
52689     }
52690     return JS_DupValue(ctx, this_val);
52691 }
52692 
52693 static const JSCFunctionListEntry js_typed_array_base_funcs[] = {
52694     JS_CFUNC_DEF("from", 1, js_typed_array_from ),
52695     JS_CFUNC_DEF("of", 0, js_typed_array_of ),
52696     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
52697     //JS_CFUNC_DEF("__getLength", 2, js_typed_array___getLength ),
52698     //JS_CFUNC_DEF("__create", 2, js_typed_array___create ),
52699     //JS_CFUNC_DEF("__speciesCreate", 2, js_typed_array___speciesCreate ),
52700 };
52701 
52702 static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = {
52703     JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ),
52704     JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0 ),
52705     JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0 ),
52706     JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0 ),
52707     JS_CFUNC_DEF("set", 1, js_typed_array_set ),
52708     JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ),
52709     JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
52710     JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY ),
52711     JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
52712     JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL ),
52713     JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin ),
52714     JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA ),
52715     JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA ),
52716     JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA ),
52717     JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA ),
52718     JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA ),
52719     JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ),
52720     JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ),
52721     JS_CFUNC_DEF("fill", 1, js_typed_array_fill ),
52722     JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, 0 ),
52723     JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, 1 ),
52724     JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ),
52725     JS_CFUNC_DEF("slice", 2, js_typed_array_slice ),
52726     JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ),
52727     JS_CFUNC_DEF("sort", 1, js_typed_array_sort ),
52728     JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ),
52729     JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ),
52730     JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ),
52731     JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf ),
52732     JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes ),
52733     //JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@
52734 };
52735 
js_typed_array_base_constructor(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)52736 static JSValue js_typed_array_base_constructor(JSContext *ctx,
52737                                                JSValueConst this_val,
52738                                                int argc, JSValueConst *argv)
52739 {
52740     return JS_ThrowTypeError(ctx, "cannot be called");
52741 }
52742 
52743 /* 'obj' must be an allocated typed array object */
typed_array_init(JSContext * ctx,JSValueConst obj,JSValue buffer,uint64_t offset,uint64_t len)52744 static int typed_array_init(JSContext *ctx, JSValueConst obj,
52745                             JSValue buffer, uint64_t offset, uint64_t len)
52746 {
52747     JSTypedArray *ta;
52748     JSObject *p, *pbuffer;
52749     JSArrayBuffer *abuf;
52750     int size_log2;
52751 
52752     p = JS_VALUE_GET_OBJ(obj);
52753     size_log2 = typed_array_size_log2(p->class_id);
52754     ta = js_malloc(ctx, sizeof(*ta));
52755     if (!ta) {
52756         JS_FreeValue(ctx, buffer);
52757         return -1;
52758     }
52759     pbuffer = JS_VALUE_GET_OBJ(buffer);
52760     abuf = pbuffer->u.array_buffer;
52761     ta->obj = p;
52762     ta->buffer = pbuffer;
52763     ta->offset = offset;
52764     ta->length = len << size_log2;
52765     list_add_tail(&ta->link, &abuf->array_list);
52766     p->u.typed_array = ta;
52767     p->u.array.count = len;
52768     p->u.array.u.ptr = abuf->data + offset;
52769     return 0;
52770 }
52771 
52772 
js_array_from_iterator(JSContext * ctx,uint32_t * plen,JSValueConst obj,JSValueConst method)52773 static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen,
52774                                       JSValueConst obj, JSValueConst method)
52775 {
52776     JSValue arr, iter, next_method = JS_UNDEFINED, val;
52777     BOOL done;
52778     uint32_t k;
52779 
52780     *plen = 0;
52781     arr = JS_NewArray(ctx);
52782     if (JS_IsException(arr))
52783         return arr;
52784     iter = JS_GetIterator2(ctx, obj, method);
52785     if (JS_IsException(iter))
52786         goto fail;
52787     next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
52788     if (JS_IsException(next_method))
52789         goto fail;
52790     k = 0;
52791     for(;;) {
52792         val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
52793         if (JS_IsException(val))
52794             goto fail;
52795         if (done) {
52796             JS_FreeValue(ctx, val);
52797             break;
52798         }
52799         if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0)
52800             goto fail;
52801         k++;
52802     }
52803     JS_FreeValue(ctx, next_method);
52804     JS_FreeValue(ctx, iter);
52805     *plen = k;
52806     return arr;
52807  fail:
52808     JS_FreeValue(ctx, next_method);
52809     JS_FreeValue(ctx, iter);
52810     JS_FreeValue(ctx, arr);
52811     return JS_EXCEPTION;
52812 }
52813 
js_typed_array_constructor_obj(JSContext * ctx,JSValueConst new_target,JSValueConst obj,int classid)52814 static JSValue js_typed_array_constructor_obj(JSContext *ctx,
52815                                               JSValueConst new_target,
52816                                               JSValueConst obj,
52817                                               int classid)
52818 {
52819     JSValue iter, ret, arr = JS_UNDEFINED, val, buffer;
52820     uint32_t i;
52821     int size_log2;
52822     int64_t len;
52823 
52824     size_log2 = typed_array_size_log2(classid);
52825     ret = js_create_from_ctor(ctx, new_target, classid);
52826     if (JS_IsException(ret))
52827         return JS_EXCEPTION;
52828 
52829     iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
52830     if (JS_IsException(iter))
52831         goto fail;
52832     if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) {
52833         uint32_t len1;
52834         arr = js_array_from_iterator(ctx, &len1, obj, iter);
52835         JS_FreeValue(ctx, iter);
52836         if (JS_IsException(arr))
52837             goto fail;
52838         len = len1;
52839     } else {
52840         if (js_get_length64(ctx, &len, obj))
52841             goto fail;
52842         arr = JS_DupValue(ctx, obj);
52843     }
52844 
52845     buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
52846                                           len << size_log2);
52847     if (JS_IsException(buffer))
52848         goto fail;
52849     if (typed_array_init(ctx, ret, buffer, 0, len))
52850         goto fail;
52851 
52852     for(i = 0; i < len; i++) {
52853         val = JS_GetPropertyUint32(ctx, arr, i);
52854         if (JS_IsException(val))
52855             goto fail;
52856         if (JS_SetPropertyUint32(ctx, ret, i, val) < 0)
52857             goto fail;
52858     }
52859     JS_FreeValue(ctx, arr);
52860     return ret;
52861  fail:
52862     JS_FreeValue(ctx, arr);
52863     JS_FreeValue(ctx, ret);
52864     return JS_EXCEPTION;
52865 }
52866 
js_typed_array_constructor_ta(JSContext * ctx,JSValueConst new_target,JSValueConst src_obj,int classid)52867 static JSValue js_typed_array_constructor_ta(JSContext *ctx,
52868                                              JSValueConst new_target,
52869                                              JSValueConst src_obj,
52870                                              int classid)
52871 {
52872     JSObject *p, *src_buffer;
52873     JSTypedArray *ta;
52874     JSValue ctor, obj, buffer;
52875     uint32_t len, i;
52876     int size_log2;
52877     JSArrayBuffer *src_abuf, *abuf;
52878 
52879     obj = js_create_from_ctor(ctx, new_target, classid);
52880     if (JS_IsException(obj))
52881         return obj;
52882     p = JS_VALUE_GET_OBJ(src_obj);
52883     if (typed_array_is_detached(ctx, p)) {
52884         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
52885         goto fail;
52886     }
52887     ta = p->u.typed_array;
52888     len = p->u.array.count;
52889     src_buffer = ta->buffer;
52890     src_abuf = src_buffer->u.array_buffer;
52891     if (!src_abuf->shared) {
52892         ctor = JS_SpeciesConstructor(ctx, JS_MKPTR(JS_TAG_OBJECT, src_buffer),
52893                                      JS_UNDEFINED);
52894         if (JS_IsException(ctor))
52895             goto fail;
52896     } else {
52897         /* force ArrayBuffer default constructor */
52898         ctor = JS_UNDEFINED;
52899     }
52900     size_log2 = typed_array_size_log2(classid);
52901     buffer = js_array_buffer_constructor1(ctx, ctor,
52902                                           (uint64_t)len << size_log2);
52903     JS_FreeValue(ctx, ctor);
52904     if (JS_IsException(buffer))
52905         goto fail;
52906     /* necessary because it could have been detached */
52907     if (typed_array_is_detached(ctx, p)) {
52908         JS_FreeValue(ctx, buffer);
52909         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
52910         goto fail;
52911     }
52912     abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER);
52913     if (typed_array_init(ctx, obj, buffer, 0, len))
52914         goto fail;
52915     if (p->class_id == classid) {
52916         /* same type: copy the content */
52917         memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length);
52918     } else {
52919         for(i = 0; i < len; i++) {
52920             JSValue val;
52921             val = JS_GetPropertyUint32(ctx, src_obj, i);
52922             if (JS_IsException(val))
52923                 goto fail;
52924             if (JS_SetPropertyUint32(ctx, obj, i, val) < 0)
52925                 goto fail;
52926         }
52927     }
52928     return obj;
52929  fail:
52930     JS_FreeValue(ctx, obj);
52931     return JS_EXCEPTION;
52932 }
52933 
js_typed_array_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv,int classid)52934 static JSValue js_typed_array_constructor(JSContext *ctx,
52935                                           JSValueConst new_target,
52936                                           int argc, JSValueConst *argv,
52937                                           int classid)
52938 {
52939     JSValue buffer, obj;
52940     JSArrayBuffer *abuf;
52941     int size_log2;
52942     uint64_t len, offset;
52943 
52944     size_log2 = typed_array_size_log2(classid);
52945     if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) {
52946         if (JS_ToIndex(ctx, &len, argv[0]))
52947             return JS_EXCEPTION;
52948         buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
52949                                               len << size_log2);
52950         if (JS_IsException(buffer))
52951             return JS_EXCEPTION;
52952         offset = 0;
52953     } else {
52954         JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
52955         if (p->class_id == JS_CLASS_ARRAY_BUFFER ||
52956             p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) {
52957             abuf = p->u.array_buffer;
52958             if (JS_ToIndex(ctx, &offset, argv[1]))
52959                 return JS_EXCEPTION;
52960             if (abuf->detached)
52961                 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
52962             if ((offset & ((1 << size_log2) - 1)) != 0 ||
52963                 offset > abuf->byte_length)
52964                 return JS_ThrowRangeError(ctx, "invalid offset");
52965             if (JS_IsUndefined(argv[2])) {
52966                 if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0)
52967                     goto invalid_length;
52968                 len = (abuf->byte_length - offset) >> size_log2;
52969             } else {
52970                 if (JS_ToIndex(ctx, &len, argv[2]))
52971                     return JS_EXCEPTION;
52972                 if (abuf->detached)
52973                     return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
52974                 if ((offset + (len << size_log2)) > abuf->byte_length) {
52975                 invalid_length:
52976                     return JS_ThrowRangeError(ctx, "invalid length");
52977                 }
52978             }
52979             buffer = JS_DupValue(ctx, argv[0]);
52980         } else {
52981             if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
52982                 p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
52983                 return js_typed_array_constructor_ta(ctx, new_target, argv[0], classid);
52984             } else {
52985                 return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid);
52986             }
52987         }
52988     }
52989 
52990     obj = js_create_from_ctor(ctx, new_target, classid);
52991     if (JS_IsException(obj)) {
52992         JS_FreeValue(ctx, buffer);
52993         return JS_EXCEPTION;
52994     }
52995     if (typed_array_init(ctx, obj, buffer, offset, len)) {
52996         JS_FreeValue(ctx, obj);
52997         return JS_EXCEPTION;
52998     }
52999     return obj;
53000 }
53001 
js_typed_array_finalizer(JSRuntime * rt,JSValue val)53002 static void js_typed_array_finalizer(JSRuntime *rt, JSValue val)
53003 {
53004     JSObject *p = JS_VALUE_GET_OBJ(val);
53005     JSTypedArray *ta = p->u.typed_array;
53006     if (ta) {
53007         /* during the GC the finalizers are called in an arbitrary
53008            order so the ArrayBuffer finalizer may have been called */
53009         if (JS_IsLiveObject(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) {
53010             list_del(&ta->link);
53011         }
53012         JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
53013         js_free_rt(rt, ta);
53014     }
53015 }
53016 
js_typed_array_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)53017 static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
53018                                 JS_MarkFunc *mark_func)
53019 {
53020     JSObject *p = JS_VALUE_GET_OBJ(val);
53021     JSTypedArray *ta = p->u.typed_array;
53022     if (ta) {
53023         JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func);
53024     }
53025 }
53026 
js_dataview_constructor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)53027 static JSValue js_dataview_constructor(JSContext *ctx,
53028                                        JSValueConst new_target,
53029                                        int argc, JSValueConst *argv)
53030 {
53031     JSArrayBuffer *abuf;
53032     uint64_t offset;
53033     uint32_t len;
53034     JSValueConst buffer;
53035     JSValue obj;
53036     JSTypedArray *ta;
53037     JSObject *p;
53038 
53039     buffer = argv[0];
53040     abuf = js_get_array_buffer(ctx, buffer);
53041     if (!abuf)
53042         return JS_EXCEPTION;
53043     offset = 0;
53044     if (argc > 1) {
53045         if (JS_ToIndex(ctx, &offset, argv[1]))
53046             return JS_EXCEPTION;
53047     }
53048     if (abuf->detached)
53049         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53050     if (offset > abuf->byte_length)
53051         return JS_ThrowRangeError(ctx, "invalid byteOffset");
53052     len = abuf->byte_length - offset;
53053     if (argc > 2 && !JS_IsUndefined(argv[2])) {
53054         uint64_t l;
53055         if (JS_ToIndex(ctx, &l, argv[2]))
53056             return JS_EXCEPTION;
53057         if (l > len)
53058             return JS_ThrowRangeError(ctx, "invalid byteLength");
53059         len = l;
53060     }
53061 
53062     obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW);
53063     if (JS_IsException(obj))
53064         return JS_EXCEPTION;
53065     if (abuf->detached) {
53066         /* could have been detached in js_create_from_ctor() */
53067         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53068         goto fail;
53069     }
53070     ta = js_malloc(ctx, sizeof(*ta));
53071     if (!ta) {
53072     fail:
53073         JS_FreeValue(ctx, obj);
53074         return JS_EXCEPTION;
53075     }
53076     p = JS_VALUE_GET_OBJ(obj);
53077     ta->obj = p;
53078     ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer));
53079     ta->offset = offset;
53080     ta->length = len;
53081     list_add_tail(&ta->link, &abuf->array_list);
53082     p->u.typed_array = ta;
53083     return obj;
53084 }
53085 
js_dataview_getValue(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv,int class_id)53086 static JSValue js_dataview_getValue(JSContext *ctx,
53087                                     JSValueConst this_obj,
53088                                     int argc, JSValueConst *argv, int class_id)
53089 {
53090     JSTypedArray *ta;
53091     JSArrayBuffer *abuf;
53092     int is_swap, size;
53093     uint8_t *ptr;
53094     uint32_t v;
53095     uint64_t pos;
53096 
53097     ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
53098     if (!ta)
53099         return JS_EXCEPTION;
53100     size = 1 << typed_array_size_log2(class_id);
53101     if (JS_ToIndex(ctx, &pos, argv[0]))
53102         return JS_EXCEPTION;
53103     is_swap = FALSE;
53104     if (argc > 1)
53105         is_swap = JS_ToBool(ctx, argv[1]);
53106 #ifndef WORDS_BIGENDIAN
53107     is_swap ^= 1;
53108 #endif
53109     abuf = ta->buffer->u.array_buffer;
53110     if (abuf->detached)
53111         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53112     if ((pos + size) > ta->length)
53113         return JS_ThrowRangeError(ctx, "out of bound");
53114     ptr = abuf->data + ta->offset + pos;
53115 
53116     switch(class_id) {
53117     case JS_CLASS_INT8_ARRAY:
53118         return JS_NewInt32(ctx, *(int8_t *)ptr);
53119     case JS_CLASS_UINT8_ARRAY:
53120         return JS_NewInt32(ctx, *(uint8_t *)ptr);
53121     case JS_CLASS_INT16_ARRAY:
53122         v = get_u16(ptr);
53123         if (is_swap)
53124             v = bswap16(v);
53125         return JS_NewInt32(ctx, (int16_t)v);
53126     case JS_CLASS_UINT16_ARRAY:
53127         v = get_u16(ptr);
53128         if (is_swap)
53129             v = bswap16(v);
53130         return JS_NewInt32(ctx, v);
53131     case JS_CLASS_INT32_ARRAY:
53132         v = get_u32(ptr);
53133         if (is_swap)
53134             v = bswap32(v);
53135         return JS_NewInt32(ctx, v);
53136     case JS_CLASS_UINT32_ARRAY:
53137         v = get_u32(ptr);
53138         if (is_swap)
53139             v = bswap32(v);
53140         return JS_NewUint32(ctx, v);
53141 #ifdef CONFIG_BIGNUM
53142     case JS_CLASS_BIG_INT64_ARRAY:
53143         {
53144             uint64_t v;
53145             v = get_u64(ptr);
53146             if (is_swap)
53147                 v = bswap64(v);
53148             return JS_NewBigInt64(ctx, v);
53149         }
53150         break;
53151     case JS_CLASS_BIG_UINT64_ARRAY:
53152         {
53153             uint64_t v;
53154             v = get_u64(ptr);
53155             if (is_swap)
53156                 v = bswap64(v);
53157             return JS_NewBigUint64(ctx, v);
53158         }
53159         break;
53160 #endif
53161     case JS_CLASS_FLOAT32_ARRAY:
53162         {
53163             union {
53164                 float f;
53165                 uint32_t i;
53166             } u;
53167             v = get_u32(ptr);
53168             if (is_swap)
53169                 v = bswap32(v);
53170             u.i = v;
53171             return __JS_NewFloat64(ctx, u.f);
53172         }
53173     case JS_CLASS_FLOAT64_ARRAY:
53174         {
53175             union {
53176                 double f;
53177                 uint64_t i;
53178             } u;
53179             u.i = get_u64(ptr);
53180             if (is_swap)
53181                 u.i = bswap64(u.i);
53182             return __JS_NewFloat64(ctx, u.f);
53183         }
53184     default:
53185         abort();
53186     }
53187 }
53188 
js_dataview_setValue(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv,int class_id)53189 static JSValue js_dataview_setValue(JSContext *ctx,
53190                                     JSValueConst this_obj,
53191                                     int argc, JSValueConst *argv, int class_id)
53192 {
53193     JSTypedArray *ta;
53194     JSArrayBuffer *abuf;
53195     int is_swap, size;
53196     uint8_t *ptr;
53197     uint64_t v64;
53198     uint32_t v;
53199     uint64_t pos;
53200     JSValueConst val;
53201 
53202     ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
53203     if (!ta)
53204         return JS_EXCEPTION;
53205     size = 1 << typed_array_size_log2(class_id);
53206     if (JS_ToIndex(ctx, &pos, argv[0]))
53207         return JS_EXCEPTION;
53208     val = argv[1];
53209     v = 0; /* avoid warning */
53210     v64 = 0; /* avoid warning */
53211     if (class_id <= JS_CLASS_UINT32_ARRAY) {
53212         if (JS_ToUint32(ctx, &v, val))
53213             return JS_EXCEPTION;
53214     } else
53215 #ifdef CONFIG_BIGNUM
53216     if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
53217         if (JS_ToBigInt64(ctx, (int64_t *)&v64, val))
53218             return JS_EXCEPTION;
53219     } else
53220 #endif
53221     {
53222         double d;
53223         if (JS_ToFloat64(ctx, &d, val))
53224             return JS_EXCEPTION;
53225         if (class_id == JS_CLASS_FLOAT32_ARRAY) {
53226             union {
53227                 float f;
53228                 uint32_t i;
53229             } u;
53230             u.f = d;
53231             v = u.i;
53232         } else {
53233             JSFloat64Union u;
53234             u.d = d;
53235             v64 = u.u64;
53236         }
53237     }
53238     is_swap = FALSE;
53239     if (argc > 2)
53240         is_swap = JS_ToBool(ctx, argv[2]);
53241 #ifndef WORDS_BIGENDIAN
53242     is_swap ^= 1;
53243 #endif
53244     abuf = ta->buffer->u.array_buffer;
53245     if (abuf->detached)
53246         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53247     if ((pos + size) > ta->length)
53248         return JS_ThrowRangeError(ctx, "out of bound");
53249     ptr = abuf->data + ta->offset + pos;
53250 
53251     switch(class_id) {
53252     case JS_CLASS_INT8_ARRAY:
53253     case JS_CLASS_UINT8_ARRAY:
53254         *ptr = v;
53255         break;
53256     case JS_CLASS_INT16_ARRAY:
53257     case JS_CLASS_UINT16_ARRAY:
53258         if (is_swap)
53259             v = bswap16(v);
53260         put_u16(ptr, v);
53261         break;
53262     case JS_CLASS_INT32_ARRAY:
53263     case JS_CLASS_UINT32_ARRAY:
53264     case JS_CLASS_FLOAT32_ARRAY:
53265         if (is_swap)
53266             v = bswap32(v);
53267         put_u32(ptr, v);
53268         break;
53269 #ifdef CONFIG_BIGNUM
53270     case JS_CLASS_BIG_INT64_ARRAY:
53271     case JS_CLASS_BIG_UINT64_ARRAY:
53272 #endif
53273     case JS_CLASS_FLOAT64_ARRAY:
53274         if (is_swap)
53275             v64 = bswap64(v64);
53276         put_u64(ptr, v64);
53277         break;
53278     default:
53279         abort();
53280     }
53281     return JS_UNDEFINED;
53282 }
53283 
53284 static const JSCFunctionListEntry js_dataview_proto_funcs[] = {
53285     JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1 ),
53286     JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 1 ),
53287     JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 1 ),
53288     JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ),
53289     JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ),
53290     JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ),
53291     JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ),
53292     JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ),
53293     JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ),
53294 #ifdef CONFIG_BIGNUM
53295     JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ),
53296     JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ),
53297 #endif
53298     JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ),
53299     JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ),
53300     JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ),
53301     JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY ),
53302     JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY ),
53303     JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ),
53304     JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ),
53305     JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ),
53306 #ifdef CONFIG_BIGNUM
53307     JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ),
53308     JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ),
53309 #endif
53310     JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ),
53311     JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ),
53312     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ),
53313 };
53314 
53315 /* Atomics */
53316 #ifdef CONFIG_ATOMICS
53317 
53318 typedef enum AtomicsOpEnum {
53319     ATOMICS_OP_ADD,
53320     ATOMICS_OP_AND,
53321     ATOMICS_OP_OR,
53322     ATOMICS_OP_SUB,
53323     ATOMICS_OP_XOR,
53324     ATOMICS_OP_EXCHANGE,
53325     ATOMICS_OP_COMPARE_EXCHANGE,
53326     ATOMICS_OP_LOAD,
53327 } AtomicsOpEnum;
53328 
js_atomics_get_ptr(JSContext * ctx,JSArrayBuffer ** pabuf,int * psize_log2,JSClassID * pclass_id,JSValueConst obj,JSValueConst idx_val,int is_waitable)53329 static void *js_atomics_get_ptr(JSContext *ctx,
53330                                 JSArrayBuffer **pabuf,
53331                                 int *psize_log2, JSClassID *pclass_id,
53332                                 JSValueConst obj, JSValueConst idx_val,
53333                                 int is_waitable)
53334 {
53335     JSObject *p;
53336     JSTypedArray *ta;
53337     JSArrayBuffer *abuf;
53338     void *ptr;
53339     uint64_t idx;
53340     BOOL err;
53341     int size_log2;
53342 
53343     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
53344         goto fail;
53345     p = JS_VALUE_GET_OBJ(obj);
53346 #ifdef CONFIG_BIGNUM
53347     if (is_waitable)
53348         err = (p->class_id != JS_CLASS_INT32_ARRAY &&
53349                p->class_id != JS_CLASS_BIG_INT64_ARRAY);
53350     else
53351         err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&
53352                 p->class_id <= JS_CLASS_BIG_UINT64_ARRAY);
53353 #else
53354     if (is_waitable)
53355         err = (p->class_id != JS_CLASS_INT32_ARRAY);
53356     else
53357         err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&
53358                 p->class_id <= JS_CLASS_UINT32_ARRAY);
53359 #endif
53360     if (err) {
53361     fail:
53362         JS_ThrowTypeError(ctx, "integer TypedArray expected");
53363         return NULL;
53364     }
53365     ta = p->u.typed_array;
53366     abuf = ta->buffer->u.array_buffer;
53367     if (!abuf->shared) {
53368         if (is_waitable == 2) {
53369             JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray");
53370             return NULL;
53371         }
53372         if (abuf->detached) {
53373             JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53374             return NULL;
53375         }
53376     }
53377     if (JS_ToIndex(ctx, &idx, idx_val)) {
53378         return NULL;
53379     }
53380     /* if the array buffer is detached, p->u.array.count = 0 */
53381     if (idx >= p->u.array.count) {
53382         JS_ThrowRangeError(ctx, "out-of-bound access");
53383         return NULL;
53384     }
53385     size_log2 = typed_array_size_log2(p->class_id);
53386     ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
53387     if (pabuf)
53388         *pabuf = abuf;
53389     if (psize_log2)
53390         *psize_log2 = size_log2;
53391     if (pclass_id)
53392         *pclass_id = p->class_id;
53393     return ptr;
53394 }
53395 
js_atomics_op(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv,int op)53396 static JSValue js_atomics_op(JSContext *ctx,
53397                              JSValueConst this_obj,
53398                              int argc, JSValueConst *argv, int op)
53399 {
53400     int size_log2;
53401 #ifdef CONFIG_BIGNUM
53402     uint64_t v, a, rep_val;
53403 #else
53404     uint32_t v, a, rep_val;
53405 #endif
53406     void *ptr;
53407     JSValue ret;
53408     JSClassID class_id;
53409     JSArrayBuffer *abuf;
53410 
53411     ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id,
53412                              argv[0], argv[1], 0);
53413     if (!ptr)
53414         return JS_EXCEPTION;
53415     rep_val = 0;
53416     if (op == ATOMICS_OP_LOAD) {
53417         v = 0;
53418     } else {
53419 #ifdef CONFIG_BIGNUM
53420         if (size_log2 == 3) {
53421             int64_t v64;
53422             if (JS_ToBigInt64(ctx, &v64, argv[2]))
53423                 return JS_EXCEPTION;
53424             v = v64;
53425             if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
53426                 if (JS_ToBigInt64(ctx, &v64, argv[3]))
53427                     return JS_EXCEPTION;
53428                 rep_val = v64;
53429             }
53430         } else
53431 #endif
53432         {
53433                 uint32_t v32;
53434                 if (JS_ToUint32(ctx, &v32, argv[2]))
53435                     return JS_EXCEPTION;
53436                 v = v32;
53437                 if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
53438                     if (JS_ToUint32(ctx, &v32, argv[3]))
53439                         return JS_EXCEPTION;
53440                     rep_val = v32;
53441                 }
53442         }
53443         if (abuf->detached)
53444             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53445    }
53446 
53447    switch(op | (size_log2 << 3)) {
53448 
53449 #ifdef CONFIG_BIGNUM
53450 #define OP(op_name, func_name)                          \
53451     case ATOMICS_OP_ ## op_name | (0 << 3):             \
53452        a = func_name((_Atomic(uint8_t) *)ptr, v);       \
53453        break;                                           \
53454     case ATOMICS_OP_ ## op_name | (1 << 3):             \
53455         a = func_name((_Atomic(uint16_t) *)ptr, v);     \
53456         break;                                          \
53457     case ATOMICS_OP_ ## op_name | (2 << 3):             \
53458         a = func_name((_Atomic(uint32_t) *)ptr, v);     \
53459         break;                                          \
53460     case ATOMICS_OP_ ## op_name | (3 << 3):             \
53461         a = func_name((_Atomic(uint64_t) *)ptr, v);     \
53462         break;
53463 #else
53464 #define OP(op_name, func_name)                          \
53465     case ATOMICS_OP_ ## op_name | (0 << 3):             \
53466        a = func_name((_Atomic(uint8_t) *)ptr, v);       \
53467        break;                                           \
53468     case ATOMICS_OP_ ## op_name | (1 << 3):             \
53469         a = func_name((_Atomic(uint16_t) *)ptr, v);     \
53470         break;                                          \
53471     case ATOMICS_OP_ ## op_name | (2 << 3):             \
53472         a = func_name((_Atomic(uint32_t) *)ptr, v);     \
53473         break;
53474 #endif
53475         OP(ADD, atomic_fetch_add)
53476         OP(AND, atomic_fetch_and)
53477         OP(OR, atomic_fetch_or)
53478         OP(SUB, atomic_fetch_sub)
53479         OP(XOR, atomic_fetch_xor)
53480         OP(EXCHANGE, atomic_exchange)
53481 #undef OP
53482 
53483     case ATOMICS_OP_LOAD | (0 << 3):
53484         a = atomic_load((_Atomic(uint8_t) *)ptr);
53485         break;
53486     case ATOMICS_OP_LOAD | (1 << 3):
53487         a = atomic_load((_Atomic(uint16_t) *)ptr);
53488         break;
53489     case ATOMICS_OP_LOAD | (2 << 3):
53490         a = atomic_load((_Atomic(uint32_t) *)ptr);
53491         break;
53492 #ifdef CONFIG_BIGNUM
53493     case ATOMICS_OP_LOAD | (3 << 3):
53494         a = atomic_load((_Atomic(uint64_t) *)ptr);
53495         break;
53496 #endif
53497 
53498     case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3):
53499         {
53500             uint8_t v1 = v;
53501             atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val);
53502             a = v1;
53503         }
53504         break;
53505     case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3):
53506         {
53507             uint16_t v1 = v;
53508             atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val);
53509             a = v1;
53510         }
53511         break;
53512     case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3):
53513         {
53514             uint32_t v1 = v;
53515             atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val);
53516             a = v1;
53517         }
53518         break;
53519 #ifdef CONFIG_BIGNUM
53520     case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3):
53521         {
53522             uint64_t v1 = v;
53523             atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val);
53524             a = v1;
53525         }
53526         break;
53527 #endif
53528     default:
53529         abort();
53530     }
53531 
53532     switch(class_id) {
53533     case JS_CLASS_INT8_ARRAY:
53534         a = (int8_t)a;
53535         goto done;
53536     case JS_CLASS_UINT8_ARRAY:
53537         a = (uint8_t)a;
53538         goto done;
53539     case JS_CLASS_INT16_ARRAY:
53540         a = (int16_t)a;
53541         goto done;
53542     case JS_CLASS_UINT16_ARRAY:
53543         a = (uint16_t)a;
53544         goto done;
53545     case JS_CLASS_INT32_ARRAY:
53546     done:
53547         ret = JS_NewInt32(ctx, a);
53548         break;
53549     case JS_CLASS_UINT32_ARRAY:
53550         ret = JS_NewUint32(ctx, a);
53551         break;
53552 #ifdef CONFIG_BIGNUM
53553     case JS_CLASS_BIG_INT64_ARRAY:
53554         ret = JS_NewBigInt64(ctx, a);
53555         break;
53556     case JS_CLASS_BIG_UINT64_ARRAY:
53557         ret = JS_NewBigUint64(ctx, a);
53558         break;
53559 #endif
53560     default:
53561         abort();
53562     }
53563     return ret;
53564 }
53565 
js_atomics_store(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv)53566 static JSValue js_atomics_store(JSContext *ctx,
53567                                 JSValueConst this_obj,
53568                                 int argc, JSValueConst *argv)
53569 {
53570     int size_log2;
53571     void *ptr;
53572     JSValue ret;
53573     JSArrayBuffer *abuf;
53574 
53575     ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL,
53576                              argv[0], argv[1], 0);
53577     if (!ptr)
53578         return JS_EXCEPTION;
53579 #ifdef CONFIG_BIGNUM
53580     if (size_log2 == 3) {
53581         int64_t v64;
53582         ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2]));
53583         if (JS_IsException(ret))
53584             return ret;
53585         if (JS_ToBigInt64(ctx, &v64, ret)) {
53586             JS_FreeValue(ctx, ret);
53587             return JS_EXCEPTION;
53588         }
53589         if (abuf->detached)
53590             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53591         atomic_store((_Atomic(uint64_t) *)ptr, v64);
53592     } else
53593 #endif
53594     {
53595         uint32_t v;
53596         /* XXX: spec, would be simpler to return the written value */
53597         ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2]));
53598         if (JS_IsException(ret))
53599             return ret;
53600         if (JS_ToUint32(ctx, &v, ret)) {
53601             JS_FreeValue(ctx, ret);
53602             return JS_EXCEPTION;
53603         }
53604         if (abuf->detached)
53605             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53606         switch(size_log2) {
53607         case 0:
53608             atomic_store((_Atomic(uint8_t) *)ptr, v);
53609             break;
53610         case 1:
53611             atomic_store((_Atomic(uint16_t) *)ptr, v);
53612             break;
53613         case 2:
53614             atomic_store((_Atomic(uint32_t) *)ptr, v);
53615             break;
53616         default:
53617             abort();
53618         }
53619     }
53620     return ret;
53621 }
53622 
js_atomics_isLockFree(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv)53623 static JSValue js_atomics_isLockFree(JSContext *ctx,
53624                                      JSValueConst this_obj,
53625                                      int argc, JSValueConst *argv)
53626 {
53627     int v, ret;
53628     if (JS_ToInt32Sat(ctx, &v, argv[0]))
53629         return JS_EXCEPTION;
53630     ret = (v == 1 || v == 2 || v == 4
53631 #ifdef CONFIG_BIGNUM
53632            || v == 8
53633 #endif
53634            );
53635     return JS_NewBool(ctx, ret);
53636 }
53637 
53638 typedef struct JSAtomicsWaiter {
53639     struct list_head link;
53640     BOOL linked;
53641     pthread_cond_t cond;
53642     int32_t *ptr;
53643 } JSAtomicsWaiter;
53644 
53645 static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER;
53646 static struct list_head js_atomics_waiter_list =
53647     LIST_HEAD_INIT(js_atomics_waiter_list);
53648 
js_atomics_wait(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv)53649 static JSValue js_atomics_wait(JSContext *ctx,
53650                                JSValueConst this_obj,
53651                                int argc, JSValueConst *argv)
53652 {
53653     int64_t v;
53654     int32_t v32;
53655     void *ptr;
53656     int64_t timeout;
53657     struct timespec ts;
53658     JSAtomicsWaiter waiter_s, *waiter;
53659     int ret, size_log2, res;
53660     double d;
53661 
53662     ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL,
53663                              argv[0], argv[1], 2);
53664     if (!ptr)
53665         return JS_EXCEPTION;
53666 #ifdef CONFIG_BIGNUM
53667     if (size_log2 == 3) {
53668         if (JS_ToBigInt64(ctx, &v, argv[2]))
53669             return JS_EXCEPTION;
53670     } else
53671 #endif
53672     {
53673         if (JS_ToInt32(ctx, &v32, argv[2]))
53674             return JS_EXCEPTION;
53675         v = v32;
53676     }
53677     if (JS_ToFloat64(ctx, &d, argv[3]))
53678         return JS_EXCEPTION;
53679     if (isnan(d) || d > INT64_MAX)
53680         timeout = INT64_MAX;
53681     else if (d < 0)
53682         timeout = 0;
53683     else
53684         timeout = (int64_t)d;
53685     if (!ctx->rt->can_block)
53686         return JS_ThrowTypeError(ctx, "cannot block in this thread");
53687 
53688     /* XXX: inefficient if large number of waiters, should hash on
53689        'ptr' value */
53690     /* XXX: use Linux futexes when available ? */
53691     pthread_mutex_lock(&js_atomics_mutex);
53692     if (size_log2 == 3) {
53693         res = *(int64_t *)ptr != v;
53694     } else {
53695         res = *(int32_t *)ptr != v;
53696     }
53697     if (res) {
53698         pthread_mutex_unlock(&js_atomics_mutex);
53699         return JS_AtomToString(ctx, JS_ATOM_not_equal);
53700     }
53701 
53702     waiter = &waiter_s;
53703     waiter->ptr = ptr;
53704     pthread_cond_init(&waiter->cond, NULL);
53705     waiter->linked = TRUE;
53706     list_add_tail(&waiter->link, &js_atomics_waiter_list);
53707 
53708     if (timeout == INT64_MAX) {
53709         pthread_cond_wait(&waiter->cond, &js_atomics_mutex);
53710         ret = 0;
53711     } else {
53712         /* XXX: use clock monotonic */
53713         clock_gettime(CLOCK_REALTIME, &ts);
53714         ts.tv_sec += timeout / 1000;
53715         ts.tv_nsec += (timeout % 1000) * 1000000;
53716         if (ts.tv_nsec >= 1000000000) {
53717             ts.tv_nsec -= 1000000000;
53718             ts.tv_sec++;
53719         }
53720         ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex,
53721                                      &ts);
53722     }
53723     if (waiter->linked)
53724         list_del(&waiter->link);
53725     pthread_mutex_unlock(&js_atomics_mutex);
53726     pthread_cond_destroy(&waiter->cond);
53727     if (ret == ETIMEDOUT) {
53728         return JS_AtomToString(ctx, JS_ATOM_timed_out);
53729     } else {
53730         return JS_AtomToString(ctx, JS_ATOM_ok);
53731     }
53732 }
53733 
js_atomics_notify(JSContext * ctx,JSValueConst this_obj,int argc,JSValueConst * argv)53734 static JSValue js_atomics_notify(JSContext *ctx,
53735                                  JSValueConst this_obj,
53736                                  int argc, JSValueConst *argv)
53737 {
53738     struct list_head *el, *el1, waiter_list;
53739     int32_t count, n;
53740     void *ptr;
53741     JSAtomicsWaiter *waiter;
53742     JSArrayBuffer *abuf;
53743 
53744     ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1);
53745     if (!ptr)
53746         return JS_EXCEPTION;
53747 
53748     if (JS_IsUndefined(argv[2])) {
53749         count = INT32_MAX;
53750     } else {
53751         if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0))
53752             return JS_EXCEPTION;
53753     }
53754     if (abuf->detached)
53755         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53756 
53757     n = 0;
53758     if (abuf->shared && count > 0) {
53759         pthread_mutex_lock(&js_atomics_mutex);
53760         init_list_head(&waiter_list);
53761         list_for_each_safe(el, el1, &js_atomics_waiter_list) {
53762             waiter = list_entry(el, JSAtomicsWaiter, link);
53763             if (waiter->ptr == ptr) {
53764                 list_del(&waiter->link);
53765                 waiter->linked = FALSE;
53766                 list_add_tail(&waiter->link, &waiter_list);
53767                 n++;
53768                 if (n >= count)
53769                     break;
53770             }
53771         }
53772         list_for_each(el, &waiter_list) {
53773             waiter = list_entry(el, JSAtomicsWaiter, link);
53774             pthread_cond_signal(&waiter->cond);
53775         }
53776         pthread_mutex_unlock(&js_atomics_mutex);
53777     }
53778     return JS_NewInt32(ctx, n);
53779 }
53780 
53781 static const JSCFunctionListEntry js_atomics_funcs[] = {
53782     JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ),
53783     JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ),
53784     JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ),
53785     JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ),
53786     JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ),
53787     JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ),
53788     JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ),
53789     JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ),
53790     JS_CFUNC_DEF("store", 3, js_atomics_store ),
53791     JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ),
53792     JS_CFUNC_DEF("wait", 4, js_atomics_wait ),
53793     JS_CFUNC_DEF("notify", 3, js_atomics_notify ),
53794     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ),
53795 };
53796 
53797 static const JSCFunctionListEntry js_atomics_obj[] = {
53798     JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
53799 };
53800 
JS_AddIntrinsicAtomics(JSContext * ctx)53801 void JS_AddIntrinsicAtomics(JSContext *ctx)
53802 {
53803     /* add Atomics as autoinit object */
53804     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj));
53805 }
53806 
53807 #endif /* CONFIG_ATOMICS */
53808 
JS_AddIntrinsicTypedArrays(JSContext * ctx)53809 void JS_AddIntrinsicTypedArrays(JSContext *ctx)
53810 {
53811     JSValue typed_array_base_proto, typed_array_base_func;
53812     JSValueConst array_buffer_func, shared_array_buffer_func;
53813     int i;
53814 
53815     ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx);
53816     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER],
53817                                js_array_buffer_proto_funcs,
53818                                countof(js_array_buffer_proto_funcs));
53819 
53820     array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer",
53821                                                  js_array_buffer_constructor, 1,
53822                                                  ctx->class_proto[JS_CLASS_ARRAY_BUFFER]);
53823     JS_SetPropertyFunctionList(ctx, array_buffer_func,
53824                                js_array_buffer_funcs,
53825                                countof(js_array_buffer_funcs));
53826 
53827     ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx);
53828     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER],
53829                                js_shared_array_buffer_proto_funcs,
53830                                countof(js_shared_array_buffer_proto_funcs));
53831 
53832     shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer",
53833                                                  js_shared_array_buffer_constructor, 1,
53834                                                  ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]);
53835     JS_SetPropertyFunctionList(ctx, shared_array_buffer_func,
53836                                js_shared_array_buffer_funcs,
53837                                countof(js_shared_array_buffer_funcs));
53838 
53839     typed_array_base_proto = JS_NewObject(ctx);
53840     JS_SetPropertyFunctionList(ctx, typed_array_base_proto,
53841                                js_typed_array_base_proto_funcs,
53842                                countof(js_typed_array_base_proto_funcs));
53843 
53844     /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */
53845     JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString);
53846     /* XXX: should use alias method in JSCFunctionListEntry */ //@@@
53847     JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj,
53848                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
53849 
53850     typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor,
53851                                             "TypedArray", 0);
53852     JS_SetPropertyFunctionList(ctx, typed_array_base_func,
53853                                js_typed_array_base_funcs,
53854                                countof(js_typed_array_base_funcs));
53855     JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto);
53856 
53857     for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) {
53858         JSValue func_obj;
53859         char buf[ATOM_GET_STR_BUF_SIZE];
53860         const char *name;
53861 
53862         ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto);
53863         JS_DefinePropertyValueStr(ctx, ctx->class_proto[i],
53864                                   "BYTES_PER_ELEMENT",
53865                                   JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),
53866                                   0);
53867         name = JS_AtomGetStr(ctx, buf, sizeof(buf),
53868                              JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY);
53869         func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_typed_array_constructor,
53870                                     name, 3, JS_CFUNC_constructor_magic, i,
53871                                     typed_array_base_func);
53872         JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]);
53873         JS_DefinePropertyValueStr(ctx, func_obj,
53874                                   "BYTES_PER_ELEMENT",
53875                                   JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),
53876                                   0);
53877     }
53878     JS_FreeValue(ctx, typed_array_base_proto);
53879     JS_FreeValue(ctx, typed_array_base_func);
53880 
53881     /* DataView */
53882     ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx);
53883     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW],
53884                                js_dataview_proto_funcs,
53885                                countof(js_dataview_proto_funcs));
53886     JS_NewGlobalCConstructorOnly(ctx, "DataView",
53887                                  js_dataview_constructor, 1,
53888                                  ctx->class_proto[JS_CLASS_DATAVIEW]);
53889     /* Atomics */
53890 #ifdef CONFIG_ATOMICS
53891     JS_AddIntrinsicAtomics(ctx);
53892 #endif
53893 }
53894 
53895 #ifdef ENABLE_JS_DEBUG
JS_UpdateBreakPoint(JSContext * ctx,JSFunctionBytecode * byte_code,uint32_t bp_len,JSValue breakpoints)53896 static int JS_UpdateBreakPoint(JSContext *ctx, JSFunctionBytecode *byte_code,
53897                                uint32_t bp_len, JSValue breakpoints)
53898 {
53899     if (ctx == NULL || byte_code == NULL) {
53900         return -1;
53901     }
53902 
53903     if (!byte_code->has_debug || !byte_code->debug.pc2line_buf) {
53904         return -1;
53905     }
53906 
53907     const uint8_t *pos = byte_code->debug.pc2line_buf;
53908     const uint8_t *pos_end = pos + byte_code->debug.pc2line_len;
53909     int pc = 0;
53910     int line_num = byte_code->debug.line_num;
53911 
53912     for (uint32_t i = 0; i < bp_len; i++) {
53913         uint32_t bp_line = 0;
53914         JS_ToUint32(ctx, &bp_line,
53915             JS_GetPropertyStr(ctx, JS_GetPropertyUint32(ctx, breakpoints, i), "line"));
53916 
53917         if (byte_code->debugger.last_line_num && bp_line > byte_code->debugger.last_line_num) {
53918             break;
53919         }
53920 
53921         if (bp_line < line_num) {
53922             continue;
53923         }
53924 
53925         int new_line_num;
53926         int last_line_num = line_num;
53927         int line_pc = pc;
53928 
53929         while (pos < pos_end) {
53930             if (line_num > bp_line) {
53931                 break;
53932             }
53933             unsigned int op = *pos++;
53934             if (op == 0) {
53935                 uint32_t val;
53936                 int value;
53937                 int ret = get_leb128(&val, pos, pos_end);
53938                 if (ret < 0) {
53939                     return -1;
53940                 }
53941 
53942                 pc += val;
53943                 pos += ret;
53944                 ret = get_sleb128(&value, pos, pos_end);
53945                 if (ret < 0) {
53946                     return -1;
53947                 }
53948 
53949                 pos += ret;
53950                 new_line_num = line_num + value;
53951             } else {
53952                 op -= PC2LINE_OP_FIRST;
53953                 pc += (op / PC2LINE_RANGE);
53954                 new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE;
53955             }
53956             line_num = new_line_num;
53957 
53958             if (line_num == last_line_num) {
53959                 continue;
53960             }
53961 
53962             if (last_line_num == bp_line && line_num > last_line_num) {
53963                 (void)memset_s(byte_code->debugger.breakpoints + line_pc,
53964                     pc - line_pc, 1, pc - line_pc);
53965             }
53966 
53967             line_pc = pc;
53968             last_line_num = line_num;
53969         }
53970 
53971         if (pos >= pos_end) {
53972             byte_code->debugger.last_line_num = line_num;
53973         }
53974     }
53975     return 1;
53976 }
53977 
JS_GetByteCode(JSContext * ctx)53978 static JSFunctionBytecode *JS_GetByteCode(JSContext *ctx)
53979 {
53980     if (ctx == NULL || ctx->rt->current_stack_frame == NULL) {
53981         return NULL;
53982     }
53983     JSObject *func = JS_VALUE_GET_OBJ(ctx->rt->current_stack_frame->cur_func);
53984     if (func == NULL || !js_class_has_bytecode(func->class_id)) {
53985         return NULL;
53986     }
53987 
53988     JSFunctionBytecode *byte_code = func->u.func.function_bytecode;
53989     if (byte_code == NULL || !byte_code->has_debug || !byte_code->debug.filename) {
53990         return NULL;
53991     }
53992     return byte_code;
53993 }
53994 
JS_JudgeConditionBreakPoint(JSContext * ctx,const uint8_t * pc)53995 int JS_JudgeConditionBreakPoint(JSContext *ctx, const uint8_t *pc)
53996 {
53997     if (ctx == NULL) {
53998         DEBUGGER_LOGE("JS_JudgeConditionBreakPoint ctx == NULL");
53999         return 0;
54000     }
54001     JSFunctionBytecode *byte_code = JS_GetByteCode(ctx);
54002     if (byte_code == NULL) {
54003         DEBUGGER_LOGE("JS_JudgeConditionBreakPoint get byte_code fail");
54004         return 0;
54005     }
54006     DebuggerInfo *debugger_info = JS_GetDebuggerInfo(ctx);
54007     if (debugger_info == NULL) {
54008         DEBUGGER_LOGE("JS_JudgeConditionBreakPoint get debugger info fail");
54009         return 0;
54010     }
54011     JSValue breakpoints_data =
54012         JS_GetPropertyStr(ctx, debugger_info->breakpoints,
54013             JS_AtomToCString(ctx, byte_code->debug.filename));
54014     JSValue breakpoints = JS_GetPropertyStr(ctx, breakpoints_data, "breakpoints");
54015     uint32_t bp_len = DBG_GetValueAsUint32Type(ctx, breakpoints, "length");
54016     if (bp_len < 0) {
54017         return 0;
54018     }
54019     LocInfo loc = JS_GetCurrentLocation(ctx, pc);
54020     int line_num = loc.line;
54021     for (uint32_t i = 0; i < bp_len; i++) {
54022         uint32_t bp_line = 0;
54023         JS_ToUint32(ctx, &bp_line,
54024             JS_GetPropertyStr(ctx, JS_GetPropertyUint32(ctx, breakpoints, i), "line"));
54025         if (bp_line == line_num) {
54026             JSValue condition =
54027                 JS_GetPropertyStr(ctx, JS_GetPropertyUint32(ctx, breakpoints, i), "condition");
54028             if (JS_IsUndefined(condition)) {
54029                 JS_FreeValue(ctx, condition);
54030                 return 1;
54031             }
54032             JSValue ret = JS_DebuggerEvaluate(ctx, 0, condition);
54033             if (JS_IsException(ret)) {
54034                 DEBUGGER_LOGE("JS_IsException ret");
54035             }
54036             int res = 0;
54037             JS_ToInt32(ctx, &res, ret);
54038             if (res == 1) {
54039                 return 1;
54040             } else {
54041                 return 0;
54042             }
54043 
54044             JS_FreeValue(ctx, ret);
54045         }
54046     }
54047     return 1;
54048 }
54049 
JS_HitBreakpoint(JSContext * ctx,const uint8_t * cur_pc)54050 int JS_HitBreakpoint(JSContext *ctx, const uint8_t *cur_pc)
54051 {
54052     if (ctx == NULL) {
54053         return 0;
54054     }
54055     JSFunctionBytecode *byte_code = JS_GetByteCode(ctx);
54056     if (byte_code == NULL) {
54057         return 0;
54058     }
54059 
54060     DebuggerInfo *debugger_info = JS_GetDebuggerInfo(ctx);
54061     if (debugger_info == NULL) {
54062         DEBUGGER_LOGE("JS_HitBreakpoint get debugger info fail");
54063         return 0;
54064     }
54065     JSValue breakpoints_data =
54066         JS_GetPropertyStr(ctx, debugger_info->breakpoints,
54067             JS_AtomToCString(ctx, byte_code->debug.filename));
54068     if (JS_IsUndefined(breakpoints_data)) {
54069         DEBUGGER_LOGE("JS_HitBreakpoint fail msg=JS_IsUndefined");
54070         JS_FreeValue(ctx, breakpoints_data);
54071         return 0;
54072     }
54073 
54074     JSValue breakpoints = JS_GetPropertyStr(ctx, breakpoints_data, "breakpoints");
54075     uint32_t bp_len = DBG_GetValueAsUint32Type(ctx, breakpoints, "length");
54076     if (bp_len < 0) {
54077         return 0;
54078     }
54079     if (!byte_code->debugger.breakpoints) {
54080         byte_code->debugger.breakpoints = js_malloc(ctx, byte_code->byte_code_len);
54081     }
54082     (void)memset_s(byte_code->debugger.breakpoints,
54083         byte_code->byte_code_len, 0, byte_code->byte_code_len);
54084 
54085     JS_FreeValue(ctx, breakpoints_data);
54086     if (JS_UpdateBreakPoint(ctx, byte_code, bp_len, breakpoints) < 0) {
54087         JS_FreeValue(ctx, breakpoints);
54088         DEBUGGER_LOGE("JS_HitBreakpoint update breakpoint fail");
54089         return 0;
54090     }
54091 
54092     if (!byte_code->debugger.breakpoints) {
54093         DEBUGGER_LOGE("JS_HitBreakpoint fail debugger.breakpoints is false");
54094         return 0;
54095     }
54096     int pc = (cur_pc ? cur_pc : ctx->rt->current_stack_frame->cur_pc) - byte_code->byte_code_buf - 1;
54097     if (pc < 0 || pc > byte_code->byte_code_len) {
54098         DEBUGGER_LOGE("JS_HitBreakpoint get pc fail");
54099         return 0;
54100     }
54101 
54102     return byte_code->debugger.breakpoints[pc];
54103 }
54104 
JS_BuildFileInfo(JSContext * ctx,JSStackFrame * sf,const uint8_t * cur_pc,JSValue cur_frame)54105 static JSValue JS_BuildFileInfo(JSContext *ctx, JSStackFrame *sf,
54106                                 const uint8_t *cur_pc, JSValue cur_frame)
54107 {
54108     if (ctx == NULL || sf == NULL) {
54109         return JS_UNDEFINED;
54110     }
54111 
54112     char atom_buf[ATOM_GET_STR_BUF_SIZE] = {'\0'};
54113     int line_num = -1;
54114     JSObject *cur_func = JS_VALUE_GET_OBJ(sf->cur_func);
54115     if (cur_func == NULL) {
54116         DEBUGGER_LOGE("JS_BuildFileInfo get cur_func fail");
54117         return JS_UNDEFINED;
54118     }
54119     JSFunctionBytecode *byte_code = cur_func->u.func.function_bytecode;
54120     if (byte_code && byte_code->has_debug) {
54121         if (sf == ctx->rt->current_stack_frame) {
54122             if (cur_pc == NULL) {
54123                 line_num = byte_code->debug.line_num;
54124             } else {
54125                 line_num = find_line_num(ctx, byte_code, cur_pc - byte_code->byte_code_buf - 1);
54126             }
54127         } else {
54128             line_num = find_line_num(ctx, byte_code, sf->cur_pc - byte_code->byte_code_buf - 1);
54129         }
54130         const char *filename = JS_AtomGetStr(ctx, atom_buf,
54131             sizeof(atom_buf), byte_code->debug.filename);
54132         JS_SetPropertyStr(ctx, cur_frame, "filename", JS_NewString(ctx, filename));
54133         if (line_num != -1) {
54134             JS_SetPropertyStr(ctx, cur_frame, "line", JS_NewUint32(ctx, line_num));
54135         }
54136         JS_FreeCString (ctx,filename);
54137     }
54138     return cur_frame;
54139 }
54140 
JS_BuildStackTrace(JSContext * ctx,const uint8_t * cur_pc)54141 JSValue JS_BuildStackTrace(JSContext *ctx, const uint8_t *cur_pc)
54142 {
54143     if (ctx == NULL) {
54144         return JS_EXCEPTION;
54145     }
54146 
54147     JSValue stack_trace = JS_NewArray(ctx);
54148     uint32_t stack_index = 0;
54149     JSStackFrame *sf = ctx->rt->current_stack_frame;
54150 
54151     while (sf != NULL) {
54152         JSValue cur_frame = JS_NewObject(ctx);
54153         JS_SetPropertyStr(ctx, cur_frame, "id", JS_NewUint32(ctx, stack_index));
54154         const char *func_name_str = get_func_name(ctx, sf->cur_func);
54155         if (!func_name_str || func_name_str[0] == '\0') {
54156             JS_SetPropertyStr(ctx, cur_frame, "name", JS_NewString(ctx, "<anonymous>"));
54157         } else {
54158             JS_SetPropertyStr(ctx, cur_frame, "name", JS_NewString(ctx, func_name_str));
54159         }
54160         JS_FreeCString(ctx, func_name_str);
54161 
54162         JSObject *cur_func = JS_VALUE_GET_OBJ(sf->cur_func);
54163         if (cur_func && js_class_has_bytecode(cur_func->class_id)) {
54164             cur_frame = JS_BuildFileInfo(ctx, sf, cur_pc, cur_frame);
54165         } else {
54166             JS_SetPropertyStr(ctx, cur_frame, "name", JS_NewString(ctx, " (native)"));
54167         }
54168         JS_SetPropertyUint32(ctx, stack_trace, stack_index, cur_frame);
54169         stack_index++;
54170         sf = sf->prev_frame;
54171     }
54172 
54173     return stack_trace;
54174 }
54175 
JS_GetStackDepth(const JSContext * ctx)54176 uint32_t JS_GetStackDepth(const JSContext *ctx)
54177 {
54178     if (ctx == NULL) {
54179         return 0;
54180     }
54181     uint32_t count = 0;
54182     JSStackFrame *stack_frame = ctx->rt->current_stack_frame;
54183     while (stack_frame != NULL) {
54184         count++;
54185         stack_frame = stack_frame->prev_frame;
54186     }
54187 
54188     return count;
54189 }
54190 
JS_GetCurrentLocation(JSContext * ctx,const uint8_t * pc)54191 LocInfo JS_GetCurrentLocation(JSContext *ctx, const uint8_t *pc)
54192 {
54193     LocInfo loc;
54194     (void)memset_s(&loc, sizeof(loc), 0, sizeof(loc));
54195     JSStackFrame *stack_frame = ctx->rt->current_stack_frame;
54196     if (stack_frame == NULL) {
54197         loc.filename = 0;
54198         DEBUGGER_LOGE("JS_GetCurrentLocation get stack_frame fail");
54199         return loc;
54200     }
54201     JSObject *cur_func = JS_VALUE_GET_OBJ(stack_frame->cur_func);
54202     if (cur_func == NULL) {
54203         loc.filename = 0;
54204         DEBUGGER_LOGE("JS_GetCurrentLocation get cur_func fail");
54205         return loc;
54206     }
54207     JSFunctionBytecode *byte_code = cur_func->u.func.function_bytecode;
54208     if (byte_code == NULL || !byte_code->has_debug) {
54209         loc.filename = 0;
54210         DEBUGGER_LOGE("JS_GetCurrentLocation byte_code fail");
54211         return loc;
54212     }
54213     if (pc == NULL) {
54214         pc = stack_frame->cur_pc;
54215     }
54216     loc.line = find_line_num(ctx, byte_code, pc - byte_code->byte_code_buf - 1);
54217     loc.filename = byte_code->debug.filename;
54218 
54219     return loc;
54220 }
54221 
JS_GetDebuggerInfo(JSContext * cx)54222 DebuggerInfo *JS_GetDebuggerInfo(JSContext *cx)
54223 {
54224     if (cx == NULL) {
54225         return NULL;
54226     }
54227     return cx->debugger_info;
54228 }
54229 
JS_FunctionDefineInit(JSContext * ctx,JSFunctionDef * fd,JSFunctionBytecode * byte_code)54230 static int JS_FunctionDefineInit(JSContext *ctx, JSFunctionDef *fd, JSFunctionBytecode *byte_code)
54231 {
54232     if (ctx == NULL || fd == NULL || byte_code == NULL) {
54233         return -1;
54234     }
54235 
54236     fd->has_this_binding = 0;
54237     fd->js_mode = byte_code->js_mode;
54238     fd->super_allowed = byte_code->super_allowed;
54239     fd->super_call_allowed = byte_code->super_call_allowed;
54240     fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_);
54241     fd->new_target_allowed = byte_code->new_target_allowed;
54242     fd->arguments_allowed = byte_code->arguments_allowed;
54243     fd->eval_type = JS_EVAL_TYPE_DIRECT;
54244 
54245     return 0;
54246 }
54247 
JS_FindCurStackFrame(JSContext * ctx,int stack_index)54248 static JSStackFrame *JS_FindCurStackFrame(JSContext *ctx, int stack_index)
54249 {
54250     if (ctx == NULL) {
54251         return NULL;
54252     }
54253 
54254     int cur_index = 0;
54255     JSStackFrame *sf = ctx->rt->current_stack_frame;
54256     if (sf == NULL) {
54257         DEBUGGER_LOGE("JS_FindCurStackFrame fail sf = NULL");
54258         return NULL;
54259     }
54260     while (cur_index < stack_index && sf != NULL) {
54261         cur_index++;
54262         sf = sf->prev_frame;
54263     }
54264 
54265     return sf;
54266 }
54267 
JS_GetScopeIdx(JSFunctionBytecode * byte_code,int scope_idx)54268 static int JS_GetScopeIdx(JSFunctionBytecode *byte_code, int scope_idx)
54269 {
54270     if (byte_code == NULL || !byte_code->var_count) {
54271         return -1;
54272     }
54273 
54274     return (byte_code->vardefs &&
54275         byte_code->vardefs[byte_code->arg_count + scope_idx].scope_next != scope_idx) ? 0 : -1;
54276 }
54277 
JS_GetFuncByteCode(JSStackFrame * sf)54278 static JSFunctionBytecode *JS_GetFuncByteCode(JSStackFrame *sf)
54279 {
54280     if (sf == NULL) {
54281         return NULL;
54282     }
54283 
54284     JSObject *cur_func = JS_VALUE_GET_OBJ(sf->cur_func);
54285     if (cur_func == NULL || !js_class_has_bytecode(cur_func->class_id)) {
54286         DEBUGGER_LOGE("JS_DebugFunctionInternal fail cur_func = NULL");
54287         return NULL;
54288     }
54289     return cur_func->u.func.function_bytecode;
54290 }
54291 
JS_DebugFunctionInternal(JSContext * ctx,JSParseState * parse_state,JSFunctionBytecode * byte_code,JSFunctionDef * fd,JSStackFrame * sf)54292 static JSValue JS_DebugFunctionInternal(JSContext *ctx, JSParseState *parse_state,
54293                                         JSFunctionBytecode *byte_code, JSFunctionDef *fd,
54294                                         JSStackFrame *sf)
54295 {
54296     if (ctx == NULL || parse_state == NULL || byte_code == NULL || fd == NULL || sf == NULL) {
54297         return JS_UNDEFINED;
54298     }
54299 
54300     JSObject *cur_func = JS_VALUE_GET_OBJ(sf->cur_func);
54301     if (cur_func == NULL) {
54302         DEBUGGER_LOGE("JS_DebugFunctionInternal get cur func fail");
54303         return JS_UNDEFINED;
54304     }
54305 
54306     JSVarRef **var_refs = cur_func->u.func.var_refs;
54307     parse_state->cur_func = fd;
54308     int scope_idx = byte_code->vardefs ? 0 : -1;
54309     if (byte_code) {
54310         if (add_closure_variables(ctx, fd, byte_code, JS_GetScopeIdx(byte_code, scope_idx))) {
54311             free_token(parse_state, &parse_state->token);
54312             js_free_function_def(ctx, fd);
54313             DEBUGGER_LOGE("JS_DebugFunctionInternal add closure variable fail");
54314             return JS_EXCEPTION;
54315         }
54316     }
54317     fd->module = NULL;
54318     parse_state->is_module = 0;
54319     parse_state->allow_html_comments = !parse_state->is_module;
54320     push_scope(parse_state);
54321 
54322     if (js_parse_program(parse_state)) {
54323         free_token(parse_state, &parse_state->token);
54324         js_free_function_def(ctx, fd);
54325         DEBUGGER_LOGE("JS_DebugFunctionInternal js parse program fail");
54326         return JS_EXCEPTION;
54327     }
54328 
54329     JSValue fun_obj = js_create_function(ctx, fd);
54330     if (JS_IsException(fun_obj)) {
54331         DEBUGGER_LOGE("JS_DebugFunctionInternal create function fail");
54332         return JS_EXCEPTION;
54333     }
54334 
54335     return JS_EvalFunctionInternal(ctx, fun_obj, sf->var_buf[byte_code->var_count], var_refs, sf);
54336 }
54337 
JS_DebuggerEvaluate(JSContext * ctx,int stack_index,JSValue expr)54338 JSValue JS_DebuggerEvaluate(JSContext *ctx, int stack_index, JSValue expr)
54339 {
54340     if (ctx == NULL) {
54341         return JS_UNDEFINED;
54342     }
54343     size_t input_len = 0;
54344     const char *input = JS_ToCStringLen(ctx, &input_len, expr);
54345     JSParseState parse_state1;
54346     JSParseState *parse_state = &parse_state1;
54347 
54348     js_parse_init(ctx, parse_state, input, input_len, "<debugger>");
54349     JS_FreeCString(ctx, input);
54350 
54351     skip_shebang(parse_state);
54352 
54353     JSStackFrame *sf = JS_FindCurStackFrame(ctx, stack_index);
54354     if (sf == NULL) {
54355         DEBUGGER_LOGE("JS_DebuggerEvaluate fail sf == NULL");
54356         return JS_UNDEFINED;
54357     }
54358 
54359     if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT) {
54360         DEBUGGER_LOGE("JS_DebuggerEvaluate fail cur_func is not JS_TAG_OBJECT");
54361         return JS_UNDEFINED;
54362     }
54363     JSFunctionBytecode *byte_code = JS_GetFuncByteCode(sf);
54364     if (byte_code == NULL) {
54365         DEBUGGER_LOGE("JS_DebuggerEvaluate fail byte_code=NULL");
54366         return JS_UNDEFINED;
54367     }
54368 
54369     JSFunctionDef *fd = js_new_function_def(ctx, NULL, TRUE, FALSE, "<debugger>", 1);
54370     if (fd == NULL) {
54371         DEBUGGER_LOGE("JS_DebuggerEvaluate fail fd = NULL");
54372         return JS_EXCEPTION;
54373     }
54374 
54375     if (JS_FunctionDefineInit(ctx, fd, byte_code) < 0) {
54376         DEBUGGER_LOGE("JS_DebuggerEvaluate function define init fail");
54377         return JS_EXCEPTION;
54378     }
54379 
54380     return JS_DebugFunctionInternal(ctx, parse_state, byte_code, fd, sf);
54381 }
54382 
JS_SetThisObject(JSContext * ctx,JSStackFrame * sf,JSValue ret)54383 JSValue JS_SetThisObject(JSContext *ctx, JSStackFrame *sf, JSValue ret)
54384 {
54385     if (ctx == NULL || sf == NULL) {
54386         DEBUGGER_LOGE("JS_SetThisObject fail ctx=NULL || sf == NULL");
54387         return ret;
54388     }
54389 
54390     JSFunctionBytecode *byte_code = JS_GetFuncByteCode(sf);
54391     if (byte_code != NULL) {
54392         JSValue this_obj = sf->var_buf[byte_code->var_count];
54393         if (JS_VALUE_GET_OBJ(this_obj) != JS_VALUE_GET_OBJ(ctx->global_obj)) {
54394             JS_SetPropertyStr(ctx, ret, "this", JS_DupValue(ctx, this_obj));
54395         }
54396     }
54397     return ret;
54398 }
54399 
JS_GetLocalVariables(JSContext * ctx,int stack_index)54400 JSValue JS_GetLocalVariables(JSContext *ctx, int stack_index)
54401 {
54402     if (ctx == NULL) {
54403         return JS_NULL;
54404     }
54405 
54406     JSValue ret = JS_NewObject(ctx);
54407     if (JS_IsException(ret)) {
54408         return JS_NULL;
54409     }
54410     if (stack_index == 0 && !JS_IsNull(ctx->rt->current_exception) &&
54411         !JS_IsUndefined(ctx->rt->current_exception)) {
54412         JS_SetPropertyStr(ctx, ret, "<exception>", JS_DupValue(ctx, ctx->rt->current_exception));
54413     }
54414 
54415     int cur_index = 0;
54416     for (JSStackFrame *sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {
54417         if (cur_index == stack_index - 1) {
54418             ret = JS_SetThisObject(ctx, sf, ret);
54419         }
54420 
54421         if (cur_index < stack_index) {
54422             cur_index++;
54423             continue;
54424         }
54425 
54426         JSFunctionBytecode *byte_code = JS_GetFuncByteCode(sf);
54427         if (byte_code == NULL) {
54428             DEBUGGER_LOGE("JS_GetLocalVariables fail byte_code=NULL");
54429             return ret;
54430         }
54431 
54432         for (uint32_t idx = 0; idx < byte_code->arg_count + byte_code->var_count; idx++) {
54433             JSValue var_val = idx < byte_code->arg_count ?
54434                 sf->arg_buf[idx] : sf->var_buf[idx - byte_code->arg_count];
54435             if (JS_IsUninitialized(var_val)) {
54436                 continue;
54437             }
54438             JSVarDef *vd = byte_code->vardefs + idx;
54439             if (vd == NULL) {
54440                 continue;
54441             }
54442             JS_SetProperty(ctx, ret, vd->var_name, JS_DupValue(ctx, var_val));
54443         }
54444 
54445         break;
54446     }
54447 
54448     return ret;
54449 }
54450 
JS_GetClosureVariables(JSContext * ctx,int stack_index)54451 JSValue JS_GetClosureVariables(JSContext *ctx, int stack_index)
54452 {
54453     if (ctx == NULL) {
54454         return JS_NULL;
54455     }
54456     JSValue ret = JS_NewObject(ctx);
54457     if (JS_IsException(ret)) {
54458         return JS_NULL;
54459     }
54460     JSStackFrame *sf = JS_FindCurStackFrame(ctx, stack_index);
54461     if (sf == NULL) {
54462         DEBUGGER_LOGE("JS_GetClosureVariables fail sf=NULL");
54463         return JS_NULL;
54464     }
54465 
54466     JSFunctionBytecode *byte_code = JS_GetFuncByteCode(sf);
54467     if (byte_code == NULL) {
54468         DEBUGGER_LOGE("JS_GetClosureVariables fail byte_code=NULL");
54469         return JS_NULL;
54470     }
54471     JSObject *func = JS_VALUE_GET_OBJ(sf->cur_func);
54472     if (func == NULL) {
54473         DEBUGGER_LOGE("JS_GetClosureVariables fail func=NULL");
54474         return JS_NULL;
54475     }
54476     for (uint32_t idx = 0; idx < byte_code->closure_var_count; idx++) {
54477         JSClosureVar *cvar = byte_code->closure_var + idx;
54478         JSVarRef *var_ref = NULL;
54479         if (func->u.func.var_refs) {
54480             var_ref = func->u.func.var_refs[idx];
54481         }
54482         if (var_ref == NULL || var_ref->pvalue == NULL) {
54483             continue;
54484         }
54485         JSValue var_val = *var_ref->pvalue;
54486         if (JS_IsUninitialized(var_val)) {
54487             continue;
54488         }
54489 
54490         JS_SetProperty(ctx, ret, cvar->var_name, JS_DupValue(ctx, var_val));
54491     }
54492 
54493     return ret;
54494 }
54495 
JS_JsonStringify(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)54496 JSValue JS_JsonStringify(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
54497 {
54498     if (ctx == NULL) {
54499         DEBUGGER_LOGE("JS_JsonStringify fail ctx=NULL");
54500         return JS_EXCEPTION;
54501     }
54502     return js_json_stringify(ctx, this_val, argc, argv);
54503 }
54504 #endif
54505 
54506 /* for ace 2.0 */
JS_CountClassInstances(JSRuntime * rt,uint16_t class_id)54507 int JS_CountClassInstances(JSRuntime* rt, uint16_t class_id)
54508 {
54509     int count = 0;
54510     // TODO: should implement in new version
54511     return count;
54512 }
54513 
JS_GetOpaqueA(JSValueConst obj,JSClassID * class_ids,size_t class_id_len)54514 void *JS_GetOpaqueA(JSValueConst obj,
54515                     JSClassID* class_ids, size_t class_id_len)
54516 {
54517     JSObject *p;
54518     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
54519         return NULL;
54520     p = JS_VALUE_GET_OBJ(obj);
54521     for (int i=class_id_len-1; i>=0; i--) {
54522         if (p->class_id == class_ids[i]) {
54523             return p->u.opaque;
54524         }
54525     }
54526 
54527     return NULL;
54528 }
54529 
JS_GetArrayLength(JSContext * ctx,JSValueConst arrayObj)54530 JSValue JS_GetArrayLength(JSContext *ctx, JSValueConst arrayObj) {
54531     return JS_GetProperty(ctx, arrayObj, JS_ATOM_length);
54532 }
54533 
JS_AceSetConstructor(JSContext * ctx,JSValueConst func_obj,JSValueConst proto)54534 void JS_AceSetConstructor(JSContext *ctx,
54535                           JSValueConst func_obj,
54536                           JSValueConst proto)
54537 {
54538     JS_SetConstructor(ctx, func_obj, proto);
54539 }
54540 
JS_GetNameSpace(JSContext * ctx,JSValueConst moduleVal)54541 JSValue JS_GetNameSpace(JSContext *ctx, JSValueConst moduleVal)
54542 {
54543     JSModuleDef *m;
54544     m = JS_VALUE_GET_PTR(moduleVal);
54545     return js_get_module_ns(ctx, m);
54546 }
54547 
JS_AceNewInstance(JSContext * ctx,int classId,int argc,JSValueConst * argv)54548 JSValue JS_AceNewInstance(JSContext *ctx, int classId, int argc, JSValueConst *argv)
54549 {
54550     JSValue obj = JS_NewObjectClass(ctx, classId);
54551     JSValue proto = JS_GetClassProto(ctx, classId);
54552     JSValue ctor = JS_GetProperty(ctx, proto, JS_ATOM_constructor);
54553     JSValue newInst = JS_CallConstructor2(ctx, ctor, obj, argc, argv);
54554     JS_SetPrototype(ctx, newInst, proto);
54555     JS_FreeValue(ctx, ctor);
54556     JS_FreeValue(ctx, proto);
54557     JS_FreeValue(ctx, obj);
54558     return newInst;
54559 }
54560 
JS_NewString16(JSContext * ctx,const uint16_t * buf,int len)54561 JSValue JS_NewString16(JSContext *ctx, const uint16_t *buf, int len)
54562 {
54563     return js_new_string16(ctx, buf, len);
54564 }
54565