diff options
author | bellard <6490144+bellard@users.noreply.github.com> | 2020-09-06 19:07:30 +0200 |
---|---|---|
committer | bellard <6490144+bellard@users.noreply.github.com> | 2020-09-06 19:07:30 +0200 |
commit | 89007660998db0ee55f0ab3b34bb1a84f86fd3c4 (patch) | |
tree | 84311e5628175a91035ac2a62b16726650f2b62e /quickjs.c | |
parent | 1722758717730ac0838418f142d82ca3cff4ad4b (diff) | |
download | quickjs-89007660998db0ee55f0ab3b34bb1a84f86fd3c4.tar.gz quickjs-89007660998db0ee55f0ab3b34bb1a84f86fd3c4.zip |
2020-07-05 release
Diffstat (limited to 'quickjs.c')
-rw-r--r-- | quickjs.c | 3311 |
1 files changed, 2058 insertions, 1253 deletions
@@ -71,6 +71,12 @@ #define CONFIG_ATOMICS #endif +#if !defined(EMSCRIPTEN) +/* enable stack limitation */ +#define CONFIG_STACK_CHECK +#endif + + /* dump object free */ //#define DUMP_FREE //#define DUMP_CLOSURE @@ -281,7 +287,9 @@ struct JSRuntime { void *module_loader_opaque; BOOL can_block : 8; /* TRUE if Atomics.wait can block */ - + /* used to allocate, free and clone SharedArrayBuffers */ + JSSharedArrayBufferFunctions sab_funcs; + /* Shape hash table */ int shape_hash_bits; int shape_hash_size; @@ -392,7 +400,7 @@ typedef struct JSBigDecimal { typedef enum { JS_AUTOINIT_ID_PROTOTYPE, JS_AUTOINIT_ID_MODULE_NS, - JS_AUTOINIT_ID_PROP, /* must be last */ + JS_AUTOINIT_ID_PROP, } JSAutoInitIDEnum; /* must be large enough to have a negligible runtime cost and small @@ -625,7 +633,6 @@ typedef struct JSRegExp { typedef struct JSProxyData { JSValue target; JSValue handler; - JSValue proto; uint8_t is_func; uint8_t is_revoked; } JSProxyData; @@ -770,6 +777,7 @@ struct JSModuleDef { JSValue func_obj; /* only used for JS modules */ JSModuleInitFunc *init_func; /* only used for C modules */ BOOL resolved : 8; + BOOL func_created : 8; BOOL instantiated : 8; BOOL evaluated : 8; BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */ @@ -799,10 +807,8 @@ typedef struct JSProperty { struct { /* JS_PROP_AUTOINIT */ /* in order to use only 2 pointers, we compress the realm and the init function pointer */ - union { - JSContext *realm; /* for JS_AUTOINIT_ID_PROP */ - uintptr_t init_id; /* JS_AUTOINIT_ID_x */ - } u; + uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x) + in the 2 low bits */ void *opaque; } init; } u; @@ -832,11 +838,13 @@ struct JSShape { uint32_t hash; /* current hash value */ uint32_t prop_hash_mask; int prop_size; /* allocated properties */ - int prop_count; + int prop_count; /* include deleted properties */ + int deleted_prop_count; JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ JSObject *proto; JSShapeProperty prop[0]; /* prop_size elements */ }; + struct JSObject { union { JSGCObjectHeader header; @@ -850,7 +858,6 @@ struct JSObject { uint8_t fast_array : 1; /* TRUE if u.array is used for get/put */ uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */ - uint8_t is_class : 1; /* TRUE if object is a class constructor */ uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ uint16_t class_id; /* see JS_CLASS_x */ }; @@ -1124,7 +1131,7 @@ static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val); #endif JSValue JS_ThrowOutOfMemory(JSContext *ctx); static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); -static JSValueConst js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); +static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, JSValueConst proto_val, BOOL throw_flag); static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); @@ -1136,6 +1143,17 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p, int flags); static int js_string_memcmp(const JSString *p1, const JSString *p2, int len); static void reset_weak_ref(JSRuntime *rt, JSObject *p); +static JSValue 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); +static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj); +static JSValue js_typed_array_constructor(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int classid); static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p); static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p); static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); @@ -1212,6 +1230,7 @@ static int js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); +void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag); static const JSClassExoticMethods js_arguments_exotic_methods; static const JSClassExoticMethods js_string_exotic_methods; @@ -1361,6 +1380,33 @@ char *js_strdup(JSContext *ctx, const char *str) return js_strndup(ctx, str, strlen(str)); } +static no_inline int js_realloc_array(JSContext *ctx, void **parray, + int elem_size, int *psize, int req_size) +{ + int new_size; + size_t slack; + void *new_array; + /* XXX: potential arithmetic overflow */ + new_size = max_int(req_size, *psize * 3 / 2); + new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack); + if (!new_array) + return -1; + new_size += slack / elem_size; + *psize = new_size; + *parray = new_array; + return 0; +} + +/* resize the array and update its size if req_size > *psize */ +static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size, + int *psize, int req_size) +{ + if (unlikely(req_size > *psize)) + return js_realloc_array(ctx, parray, elem_size, psize, req_size); + else + return 0; +} + static inline void js_dbuf_init(JSContext *ctx, DynBuf *s) { dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt); @@ -1379,7 +1425,7 @@ typedef struct JSClassShortDef { static JSClassShortDef const js_std_class_def[] = { { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */ { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */ - { JS_ATOM_Error, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_ERROR */ + { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */ { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */ { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */ { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */ @@ -1426,7 +1472,7 @@ static JSClassShortDef const js_std_class_def[] = { { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */ { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */ { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */ - { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */ + { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */ { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */ }; @@ -1505,8 +1551,8 @@ static void set_dummy_numeric_ops(JSNumericOperations *ops) #endif /* CONFIG_BIGNUM */ -#if defined(EMSCRIPTEN) -/* currently no stack limitation */ +#if !defined(CONFIG_STACK_CHECK) +/* no stack limitation */ static inline uint8_t *js_get_stack_pointer(void) { return NULL; @@ -1729,6 +1775,12 @@ void JS_SetCanBlock(JSRuntime *rt, BOOL can_block) rt->can_block = can_block; } +void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, + const JSSharedArrayBufferFunctions *sf) +{ + rt->sab_funcs = *sf; +} + /* return 0 if OK, < 0 if exception */ int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *argv) @@ -4262,9 +4314,10 @@ static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, memset(sh->prop_hash_end - hash_size, 0, sizeof(sh->prop_hash_end[0]) * hash_size); sh->prop_hash_mask = hash_size - 1; - sh->prop_count = 0; sh->prop_size = prop_size; - + sh->prop_count = 0; + sh->deleted_prop_count = 0; + /* insert in the hash table */ sh->hash = shape_initial_hash(proto); sh->is_hashed = TRUE; @@ -4415,6 +4468,74 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh, return 0; } +/* remove the deleted properties. */ +static int compact_properties(JSContext *ctx, JSObject *p) +{ + JSShape *sh, *old_sh; + void *sh_alloc; + intptr_t h; + uint32_t new_hash_size, i, j, new_hash_mask, new_size; + JSShapeProperty *old_pr, *pr; + JSProperty *prop, *new_prop; + + sh = p->shape; + assert(!sh->is_hashed); + + new_size = max_int(JS_PROP_INITIAL_SIZE, + sh->prop_count - sh->deleted_prop_count); + assert(new_size <= sh->prop_size); + + new_hash_size = sh->prop_hash_mask + 1; + while ((new_hash_size / 2) >= new_size) + new_hash_size = new_hash_size / 2; + new_hash_mask = new_hash_size - 1; + + /* resize the hash table and the properties */ + old_sh = sh; + sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); + if (!sh_alloc) + return -1; + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_del(&old_sh->header.link); + memcpy(sh, old_sh, sizeof(JSShape)); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + + memset(sh->prop_hash_end - new_hash_size, 0, + sizeof(sh->prop_hash_end[0]) * new_hash_size); + + j = 0; + old_pr = old_sh->prop; + pr = sh->prop; + prop = p->prop; + for(i = 0; i < sh->prop_count; i++) { + if (old_pr->atom != JS_ATOM_NULL) { + pr->atom = old_pr->atom; + pr->flags = old_pr->flags; + h = ((uintptr_t)old_pr->atom & new_hash_mask); + pr->hash_next = sh->prop_hash_end[-h - 1]; + sh->prop_hash_end[-h - 1] = j + 1; + prop[j] = prop[i]; + j++; + pr++; + } + old_pr++; + } + assert(j == (sh->prop_count - sh->deleted_prop_count)); + sh->prop_hash_mask = new_hash_mask; + sh->prop_size = new_size; + sh->deleted_prop_count = 0; + sh->prop_count = j; + + p->shape = sh; + js_free(ctx, get_alloc_from_shape(old_sh)); + + /* reduce the size of the object properties */ + new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); + if (new_prop) + p->prop = new_prop; + return 0; +} + static int add_shape_property(JSContext *ctx, JSShape **psh, JSObject *p, JSAtom atom, int prop_flags) { @@ -4571,7 +4692,6 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas p->fast_array = 0; p->is_constructor = 0; p->is_uncatchable_error = 0; - p->is_class = 0; p->tmp_mark = 0; p->first_weak_ref = NULL; p->u.opaque = NULL; @@ -4621,7 +4741,6 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas p->u.array.u.ptr = NULL; p->u.array.count = 0; break; - case JS_CLASS_ERROR: case JS_CLASS_NUMBER: case JS_CLASS_STRING: case JS_CLASS_BOOLEAN: @@ -4967,19 +5086,25 @@ JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, return func_obj; } +static JSContext *js_autoinit_get_realm(JSProperty *pr) +{ + return (JSContext *)(pr->u.init.realm_and_id & ~3); +} + +static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr) +{ + return pr->u.init.realm_and_id & 3; +} + static void js_autoinit_free(JSRuntime *rt, JSProperty *pr) { - if (pr->u.init.u.init_id >= JS_AUTOINIT_ID_PROP) { - JS_FreeContext(pr->u.init.u.realm); - } + JS_FreeContext(js_autoinit_get_realm(pr)); } static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr, JS_MarkFunc *mark_func) { - if (pr->u.init.u.init_id >= JS_AUTOINIT_ID_PROP) { - mark_func(rt, &pr->u.init.u.realm->header); - } + mark_func(rt, &js_autoinit_get_realm(pr)->header); } static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) @@ -6331,7 +6456,7 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_obj, backtrace_barrier = FALSE; if (js_class_has_bytecode(p->class_id)) { JSFunctionBytecode *b; - char atom_buf[ATOM_GET_STR_BUF_SIZE]; + const char *atom_str; int line_num1; b = p->u.func.function_bytecode; @@ -6339,9 +6464,10 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_obj, if (b->has_debug) { line_num1 = find_line_num(ctx, b, sf->cur_pc - b->byte_code_buf - 1); + atom_str = JS_AtomToCString(ctx, b->debug.filename); dbuf_printf(&dbuf, " (%s", - JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), - b->debug.filename)); + atom_str ? atom_str : "<null>"); + JS_FreeCString(ctx, atom_str); if (line_num1 != -1) dbuf_printf(&dbuf, ":%d", line_num1); dbuf_putc(&dbuf, ')'); @@ -6459,13 +6585,32 @@ static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSCont } } +/* never use it directly */ +static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowTypeError(ctx, fmt, + JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); +} + +/* never use it directly */ +static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowSyntaxError(ctx, fmt, + JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); +} + +/* %s is replaced by 'atom'. The macro is used so that gcc can check + the format string. */ +#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "") +#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "") + static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) { if ((flags & JS_PROP_THROW) || ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { - char buf[ATOM_GET_STR_BUF_SIZE]; - JS_ThrowTypeError(ctx, "%s is read-only", - JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); + JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom); return -1; } else { return FALSE; @@ -6534,7 +6679,7 @@ static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx) static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name) { char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowReferenceError(ctx, "%s is not defined", + return JS_ThrowReferenceError(ctx, "'%s' is not defined", JS_AtomGetStr(ctx, buf, sizeof(buf), name)); } @@ -6549,11 +6694,33 @@ static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name) static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) { JSRuntime *rt = ctx->rt; - char buf[ATOM_GET_STR_BUF_SIZE]; JSAtom name; name = rt->class_array[class_id].class_name; - return JS_ThrowTypeError(ctx, "%s object expected", - JS_AtomGetStr(ctx, buf, sizeof(buf), name)); + return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); +} + +static no_inline __exception int __js_poll_interrupts(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; + if (rt->interrupt_handler) { + if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { + /* XXX: should set a specific flag to avoid catching */ + JS_ThrowInternalError(ctx, "interrupted"); + JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE); + return -1; + } + } + return 0; +} + +static inline __exception int js_poll_interrupts(JSContext *ctx) +{ + if (unlikely(--ctx->interrupt_counter <= 0)) { + return __js_poll_interrupts(ctx); + } else { + return 0; + } } /* return -1 (exception) or TRUE/FALSE */ @@ -6633,11 +6800,9 @@ int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val) return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE); } -/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */ -JSValueConst JS_GetPrototype(JSContext *ctx, JSValueConst val) +/* Only works for primitive types, otherwise return JS_NULL. */ +static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val) { - JSObject *p; - switch(JS_VALUE_GET_NORM_TAG(val)) { #ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: @@ -6664,26 +6829,45 @@ JSValueConst JS_GetPrototype(JSContext *ctx, JSValueConst val) val = ctx->class_proto[JS_CLASS_SYMBOL]; break; case JS_TAG_OBJECT: - p = JS_VALUE_GET_OBJ(val); + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + default: + val = JS_NULL; + break; + } + return val; +} + +/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */ +JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj) +{ + JSValue val; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p; + p = JS_VALUE_GET_OBJ(obj); if (unlikely(p->class_id == JS_CLASS_PROXY)) { - val = js_proxy_getPrototypeOf(ctx, val); + val = js_proxy_getPrototypeOf(ctx, obj); } else { p = p->shape->proto; if (!p) val = JS_NULL; else - val = JS_MKPTR(JS_TAG_OBJECT, p); + val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); } - break; - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - default: - val = JS_NULL; - break; + } else { + val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj)); } return val; } +static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj) +{ + JSValue obj1; + obj1 = JS_GetPrototype(ctx, obj); + JS_FreeValue(ctx, obj); + return obj1; +} + /* return TRUE, FALSE or (-1) in case of exception */ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) @@ -6704,7 +6888,6 @@ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, /* Only explicitly boxed values are instances of constructors */ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) return FALSE; - ret = FALSE; obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype); if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) { if (!JS_IsException(obj_proto)) @@ -6717,19 +6900,36 @@ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, for(;;) { proto1 = p->shape->proto; if (!proto1) { - if (p->class_id == JS_CLASS_PROXY) { - JSValueConst proto_val; - proto_val = JS_GetPrototype(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p)); - if (JS_IsException(proto_val)) { - ret = -1; - goto done; + /* slow case if proxy in the prototype chain */ + if (unlikely(p->class_id == JS_CLASS_PROXY)) { + JSValue obj1; + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p)); + for(;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsException(obj1)) { + ret = -1; + break; + } + if (JS_IsNull(obj1)) { + ret = FALSE; + break; + } + if (proto == JS_VALUE_GET_OBJ(obj1)) { + JS_FreeValue(ctx, obj1); + ret = TRUE; + break; + } + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + ret = -1; + break; + } } - proto1 = JS_VALUE_GET_OBJ(proto_val); - if (!proto1) - break; } else { - break; + ret = FALSE; } + break; } p = proto1; if (proto == p) { @@ -6767,28 +6967,24 @@ int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) return JS_OrdinaryIsInstanceOf(ctx, val, obj); } -static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, JSProperty *pr) +typedef int JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); + +static JSAutoInitFunc *js_autoinit_func_table[] = { + js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */ + js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */ + JS_InstantiateFunctionListItem, /* JS_AUTOINIT_ID_PROP */ +}; + +static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, + JSProperty *pr) { int ret; JSContext *realm; - - if (pr->u.init.u.init_id >= JS_AUTOINIT_ID_PROP) { - realm = pr->u.init.u.realm; - ret = JS_InstantiateFunctionListItem(realm, p, prop, pr->u.init.opaque); - if (ret) - return ret; - } else { - switch(pr->u.init.u.init_id) { - case JS_AUTOINIT_ID_PROTOTYPE: - ret = js_instantiate_prototype(ctx, p, prop, pr->u.init.opaque); - break; - case JS_AUTOINIT_ID_MODULE_NS: - ret = js_module_ns_autoinit(ctx, p, prop, pr->u.init.opaque); - break; - default: - abort(); - } - } + JSAutoInitFunc *func; + + realm = js_autoinit_get_realm(pr); + func = js_autoinit_func_table[js_autoinit_get_id(pr)]; + ret = func(realm, p, prop, pr->u.init.opaque); return ret; } @@ -6805,8 +7001,9 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, if (unlikely(tag != JS_TAG_OBJECT)) { switch(tag) { case JS_TAG_NULL: + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop); case JS_TAG_UNDEFINED: - return JS_ThrowTypeError(ctx, "value has no property"); + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop); case JS_TAG_EXCEPTION: return JS_EXCEPTION; case JS_TAG_STRING: @@ -6831,7 +7028,7 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, break; } /* cannot raise an exception */ - p = JS_VALUE_GET_OBJ(JS_GetPrototype(ctx, obj)); + p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); if (!p) return JS_UNDEFINED; } else { @@ -6896,15 +7093,25 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; if (em) { if (em->get_property) { + JSValue obj1, retval; /* XXX: should pass throw_ref_error */ - return em->get_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), - prop, this_obj); + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + retval = em->get_property(ctx, obj1, prop, this_obj); + JS_FreeValue(ctx, obj1); + return retval; } if (em->get_own_property) { JSPropertyDescriptor desc; int ret; + JSValue obj1; - ret = em->get_own_property(ctx, &desc, JS_MKPTR(JS_TAG_OBJECT, p), prop); + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->get_own_property(ctx, &desc, obj1, prop); + JS_FreeValue(ctx, obj1); if (ret < 0) return JS_EXCEPTION; if (ret) { @@ -6932,9 +7139,8 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) { - char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowTypeError(ctx, "private class field %s does not exist", - JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); + return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist", + atom); } /* Private fields can be added even on non extensible objects or @@ -6960,9 +7166,8 @@ static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, p = JS_VALUE_GET_OBJ(obj); prs = find_own_property(&pr, p, prop); if (prs) { - char buf[ATOM_GET_STR_BUF_SIZE]; - JS_ThrowTypeError(ctx, "private class field %s already exists", - JS_AtomGetStr(ctx, buf, sizeof(buf), prop)); + JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists", + prop); goto fail; } pr = add_property(ctx, p, prop, JS_PROP_C_W_E); @@ -7211,7 +7416,9 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, JS_ThrowTypeErrorDetachedArrayBuffer(ctx); return -1; } - num_keys_count += p->u.array.count; + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += p->u.array.count; + } } else { const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; if (em && em->get_own_property_names) { @@ -7293,15 +7500,17 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, if (p->is_exotic) { if (p->fast_array) { - for(i = 0; i < p->u.array.count; i++) { - tab_atom[num_index].atom = __JS_AtomFromUInt32(i); - if (tab_atom[num_index].atom == JS_ATOM_NULL) { - js_free_prop_enum(ctx, tab_exotic, exotic_count); - js_free_prop_enum(ctx, tab_atom, num_index); - return -1; + if (flags & JS_GPN_STRING_MASK) { + for(i = 0; i < p->u.array.count; i++) { + tab_atom[num_index].atom = __JS_AtomFromUInt32(i); + if (tab_atom[num_index].atom == JS_ATOM_NULL) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + js_free_prop_enum(ctx, tab_atom, num_index); + return -1; + } + tab_atom[num_index].is_enumerable = TRUE; + num_index++; } - tab_atom[num_index].is_enumerable = TRUE; - num_index++; } } if (exotic_count > 0) { @@ -7492,6 +7701,7 @@ int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) { JSObject *p; int ret; + JSValue obj1; if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) return FALSE; @@ -7499,10 +7709,18 @@ int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) for(;;) { if (p->is_exotic) { const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->has_property) - return em->has_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), prop); + if (em && em->has_property) { + /* has_property can free the prototype */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->has_property(ctx, obj1, prop); + JS_FreeValue(ctx, obj1); + return ret; + } } + /* JS_GetOwnPropertyInternal can free the prototype */ + JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); if (ret != 0) return ret; if (p->class_id >= JS_CLASS_UINT8C_ARRAY && @@ -7799,6 +8017,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) } else { sh->prop_hash_end[-h1 - 1] = pr->hash_next; } + sh->deleted_prop_count++; /* free the entry */ pr1 = &p->prop[h - 1]; free_property(ctx->rt, pr1, pr->flags); @@ -7807,6 +8026,12 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) pr->flags = 0; pr->atom = JS_ATOM_NULL; pr1->u.value = JS_UNDEFINED; + + /* compact the properties if too many deleted properties */ + if (sh->deleted_prop_count >= 8 && + sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { + compact_properties(ctx, p); + } return TRUE; } lpr = pr; @@ -8016,8 +8241,12 @@ static int JS_SetPropertyGeneric(JSContext *ctx, if (p->is_exotic) { const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; if (em && em->set_property) { - ret = em->set_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), prop, + JSValue obj1; + /* set_property can free the prototype */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->set_property(ctx, obj1, prop, val, this_obj, flags); + JS_FreeValue(ctx, obj1); JS_FreeValue(ctx, val); return ret; } @@ -8110,14 +8339,17 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj, if (unlikely(tag != JS_TAG_OBJECT)) { switch(tag) { case JS_TAG_NULL: + JS_FreeValue(ctx, val); + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop); + return -1; case JS_TAG_UNDEFINED: JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "value has no property"); + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop); return -1; default: /* even on a primitive type we can have setters on the prototype */ p = NULL; - p1 = JS_VALUE_GET_OBJ(JS_GetPrototype(ctx, this_obj)); + p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj)); goto prototype_lookup; } } @@ -8195,15 +8427,22 @@ retry: } else { const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic; if (em) { + JSValue obj1; if (em->set_property) { - ret = em->set_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p1), prop, + /* set_property can free the prototype */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); + ret = em->set_property(ctx, obj1, prop, val, this_obj, flags); + JS_FreeValue(ctx, obj1); JS_FreeValue(ctx, val); return ret; } if (em->get_own_property) { + /* get_own_property can free the prototype */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); ret = em->get_own_property(ctx, &desc, - JS_MKPTR(JS_TAG_OBJECT, p1), prop); + obj1, prop); + JS_FreeValue(ctx, obj1); if (ret < 0) { JS_FreeValue(ctx, val); return ret; @@ -8973,11 +9212,10 @@ static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj, pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT); if (unlikely(!pr)) return -1; - if (id == JS_AUTOINIT_ID_PROP) { - pr->u.init.u.realm = JS_DupContext(ctx); - } else { - pr->u.init.u.init_id = id; - } + pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx); + assert((pr->u.init.realm_and_id & 3) == 0); + assert(id <= 3); + pr->u.init.realm_and_id |= id; pr->u.init.opaque = opaque; return TRUE; } @@ -9114,9 +9352,7 @@ static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj, static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop) { - char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowSyntaxError(ctx, "redeclaration of %s", - JS_AtomGetStr(ctx, buf, sizeof(buf), prop)); + return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop); } /* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ @@ -9125,7 +9361,6 @@ static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) { JSObject *p; JSShapeProperty *prs; - char buf[ATOM_GET_STR_BUF_SIZE]; p = JS_VALUE_GET_OBJ(ctx->global_obj); prs = find_own_property1(p, prop); @@ -9143,8 +9378,8 @@ static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) != (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) { define_error: - JS_ThrowTypeError(ctx, "cannot define variable %s", - JS_AtomGetStr(ctx, buf, sizeof(buf), prop)); + JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'", + prop); return -1; } } @@ -11395,7 +11630,9 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { printf("[varref %p]", (void *)pr->u.var_ref); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - printf("[autoinit %p %p]", (void *)pr->u.init.u.realm, + printf("[autoinit %p %d %p]", + (void *)js_autoinit_get_realm(pr), + js_autoinit_get_id(pr), (void *)pr->u.init.opaque); } else { JS_DumpValueShort(rt, pr->u.value); @@ -15726,30 +15963,6 @@ static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, } } -static no_inline __exception int __js_poll_interrupts(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; - if (rt->interrupt_handler) { - if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { - /* XXX: should set a specific flag to avoid catching */ - JS_ThrowInternalError(ctx, "interrupted"); - JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE); - return -1; - } - } - return 0; -} - -static inline __exception int js_poll_interrupts(JSContext *ctx) -{ - if (unlikely(--ctx->interrupt_counter <= 0)) { - return __js_poll_interrupts(ctx); - } else { - return 0; - } -} - /* argument of OP_special_object */ typedef enum { OP_SPECIAL_OBJECT_ARGUMENTS, @@ -16440,7 +16653,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, CASE(OP_get_super): { JSValue proto; - proto = JS_DupValue(ctx, JS_GetPrototype(ctx, sp[-1])); + proto = JS_GetPrototype(ctx, sp[-1]); if (JS_IsException(proto)) goto exception; JS_FreeValue(ctx, sp[-1]); @@ -18543,6 +18756,9 @@ static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) { JSValue func_obj; + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); + /* the tag does not matter provided it is not an object */ func_obj = JS_MKPTR(JS_TAG_INT, s); return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, @@ -19670,8 +19886,8 @@ typedef struct JSFunctionDef { /* constant pool (strings, functions, numbers) */ JSValue *cpool; - uint32_t cpool_count; - uint32_t cpool_size; + int cpool_count; + int cpool_size; /* list of variables in the closure */ int closure_var_count; @@ -19741,6 +19957,7 @@ typedef struct JSParseState { JSFunctionDef *cur_func; BOOL is_module; /* parsing a module */ BOOL allow_html_comments; + BOOL ext_json; /* true if accepting JSON superset */ } JSParseState; typedef struct JSOpCode { @@ -20308,23 +20525,18 @@ static __exception int next_token(JSParseState *s) c = *p; switch(c) { case 0: - s->token.val = TOK_EOF; - break; - case '`': - if (!s->cur_func) { - /* JSON does not accept templates */ + if (p >= s->buf_end) { + s->token.val = TOK_EOF; + } else { goto def_token; } + break; + case '`': if (js_parse_template_part(s, p + 1)) goto fail; p = s->buf_ptr; break; case '\'': - if (!s->cur_func) { - /* JSON does not accept single quoted strings */ - goto def_token; - } - /* fall through */ case '\"': if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p)) goto fail; @@ -20342,11 +20554,6 @@ static __exception int next_token(JSParseState *s) goto redo; case '\f': case '\v': - if (!s->cur_func) { - /* JSONWhitespace does not match <VT>, nor <FF> */ - goto def_token; - } - /* fall through */ case ' ': case '\t': p++; @@ -20443,16 +20650,15 @@ static __exception int next_token(JSParseState *s) s->token.u.ident.is_reserved = FALSE; if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD || (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD && - s->cur_func && (s->cur_func->js_mode & JS_MODE_STRICT)) || - (s->token.u.ident.atom == JS_ATOM_yield && s->cur_func && + (s->cur_func->js_mode & JS_MODE_STRICT)) || + (s->token.u.ident.atom == JS_ATOM_yield && ((s->cur_func->func_kind & JS_FUNC_GENERATOR) || (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && !s->cur_func->in_function_body && s->cur_func->parent && (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) || (s->token.u.ident.atom == JS_ATOM_await && (s->is_module || - (s->cur_func && - ((s->cur_func->func_kind & JS_FUNC_ASYNC) || + (((s->cur_func->func_kind & JS_FUNC_ASYNC) || (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && !s->cur_func->in_function_body && s->cur_func->parent && (s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) { @@ -20505,9 +20711,8 @@ static __exception int next_token(JSParseState *s) } break; case '0': - /* in strict or JSON parsing mode, octal literals are not accepted */ - if (is_digit(p[1]) && (!s->cur_func || - (s->cur_func->js_mode & JS_MODE_STRICT))) { + /* in strict mode, octal literals are not accepted */ + if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) { js_parse_error(s, "octal literals are deprecated in strict mode"); goto fail; } @@ -20519,22 +20724,17 @@ static __exception int next_token(JSParseState *s) JSValue ret; const uint8_t *p1; int flags, radix; - if (!s->cur_func) { - flags = 0; - radix = 10; - } else { - flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL | - ATOD_ACCEPT_UNDERSCORES; + flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL | + ATOD_ACCEPT_UNDERSCORES; #ifdef CONFIG_BIGNUM - flags |= ATOD_ACCEPT_SUFFIX; - if (s->cur_func->js_mode & JS_MODE_MATH) { - flags |= ATOD_MODE_BIGINT; - if (s->cur_func->js_mode & JS_MODE_MATH) - flags |= ATOD_TYPE_BIG_FLOAT; - } -#endif - radix = 0; + flags |= ATOD_ACCEPT_SUFFIX; + if (s->cur_func->js_mode & JS_MODE_MATH) { + flags |= ATOD_MODE_BIGINT; + if (s->cur_func->js_mode & JS_MODE_MATH) + flags |= ATOD_TYPE_BIG_FLOAT; } +#endif + radix = 0; #ifdef CONFIG_BIGNUM s->token.u.num.exponent = 0; ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix, @@ -20699,7 +20899,7 @@ static __exception int next_token(JSParseState *s) case '^': if (p[1] == '=') { p += 2; - if (s->cur_func && (s->cur_func->js_mode & JS_MODE_MATH)) + if (s->cur_func->js_mode & JS_MODE_MATH) s->token.val = TOK_MATH_POW_ASSIGN; else s->token.val = TOK_XOR_ASSIGN; @@ -20713,7 +20913,7 @@ static __exception int next_token(JSParseState *s) } } else { p++; - if (s->cur_func && (s->cur_func->js_mode & JS_MODE_MATH)) + if (s->cur_func->js_mode & JS_MODE_MATH) s->token.val = TOK_MATH_POW; else s->token.val = '^'; @@ -20758,22 +20958,12 @@ static __exception int next_token(JSParseState *s) switch(c) { case CP_PS: case CP_LS: - if (!s->cur_func) { - /* <PS> and <LS> are not JSONWhitespace */ - goto def_token; - } else { - /* XXX: should avoid incrementing line_number, but - needed to handle HTML comments */ - goto line_terminator; - } + /* XXX: should avoid incrementing line_number, but + needed to handle HTML comments */ + goto line_terminator; default: if (lre_is_space(c)) { - if (!s->cur_func) { - /* category z spaces are not JSONWhitespace */ - goto def_token; - } else { - goto redo; - } + goto redo; } else if (lre_js_is_ident_first(c)) { ident_has_escape = FALSE; goto has_ident; @@ -20798,6 +20988,220 @@ static __exception int next_token(JSParseState *s) return -1; } +/* 'c' is the first character. Return JS_ATOM_NULL in case of error */ +static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c) +{ + const uint8_t *p; + char ident_buf[128], *buf; + size_t ident_size, ident_pos; + JSAtom atom; + + p = *pp; + buf = ident_buf; + ident_size = sizeof(ident_buf); + ident_pos = 0; + for(;;) { + buf[ident_pos++] = c; + c = *p; + if (c >= 128 || + !((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1)) + break; + p++; + if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { + if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) { + atom = JS_ATOM_NULL; + goto done; + } + } + } + atom = JS_NewAtomLen(s->ctx, buf, ident_pos); + done: + if (unlikely(buf != ident_buf)) + js_free(s->ctx, buf); + *pp = p; + return atom; +} + +static __exception int json_next_token(JSParseState *s) +{ + const uint8_t *p; + int c; + JSAtom atom; + + if (js_check_stack_overflow(s->ctx->rt, 0)) { + return js_parse_error(s, "stack overflow"); + } + + free_token(s, &s->token); + + p = s->last_ptr = s->buf_ptr; + s->last_line_num = s->token.line_num; + redo: + s->token.line_num = s->line_num; + s->token.ptr = p; + c = *p; + switch(c) { + case 0: + if (p >= s->buf_end) { + s->token.val = TOK_EOF; + } else { + goto def_token; + } + break; + case '\'': + if (!s->ext_json) { + /* JSON does not accept single quoted strings */ + goto def_token; + } + /* fall through */ + case '\"': + if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p)) + goto fail; + break; + case '\r': /* accept DOS and MAC newline sequences */ + if (p[1] == '\n') { + p++; + } + /* fall thru */ + case '\n': + p++; + s->line_num++; + goto redo; + case '\f': + case '\v': + if (!s->ext_json) { + /* JSONWhitespace does not match <VT>, nor <FF> */ + goto def_token; + } + /* fall through */ + case ' ': + case '\t': + p++; + goto redo; + case '/': + if (!s->ext_json) { + /* JSON does not accept comments */ + goto def_token; + } + if (p[1] == '*') { + /* comment */ + p += 2; + for(;;) { + if (*p == '\0' && p >= s->buf_end) { + js_parse_error(s, "unexpected end of comment"); + goto fail; + } + if (p[0] == '*' && p[1] == '/') { + p += 2; + break; + } + if (*p == '\n') { + s->line_num++; + p++; + } else if (*p == '\r') { + p++; + } else if (*p >= 0x80) { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); + if (c == -1) { + p++; /* skip invalid UTF-8 */ + } + } else { + p++; + } + } + goto redo; + } else if (p[1] == '/') { + /* line comment */ + p += 2; + for(;;) { + if (*p == '\0' && p >= s->buf_end) + break; + if (*p == '\r' || *p == '\n') + break; + if (*p >= 0x80) { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); + /* LS or PS are considered as line terminator */ + if (c == CP_LS || c == CP_PS) { + break; + } else if (c == -1) { + p++; /* skip invalid UTF-8 */ + } + } else { + p++; + } + } + goto redo; + } else { + goto def_token; + } + break; + case 'a' ... 'z': + case 'A' ... 'Z': + case '_': + case '$': + /* identifier : only pure ascii characters are accepted */ + p++; + atom = json_parse_ident(s, &p, c); + if (atom == JS_ATOM_NULL) + goto fail; + s->token.u.ident.atom = atom; + s->token.u.ident.has_escape = FALSE; + s->token.u.ident.is_reserved = FALSE; + s->token.val = TOK_IDENT; + break; + case '+': + if (!s->ext_json || !is_digit(p[1])) + goto def_token; + goto parse_number; + case '0': + if (is_digit(p[1])) + goto def_token; + goto parse_number; + case '-': + if (!is_digit(p[1])) + goto def_token; + goto parse_number; + case '1' ... '9': + /* number */ + parse_number: + { + JSValue ret; + int flags, radix; + if (!s->ext_json) { + flags = 0; + radix = 10; + } else { + flags = ATOD_ACCEPT_BIN_OCT; + radix = 0; + } + ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix, + flags); + if (JS_IsException(ret)) + goto fail; + s->token.val = TOK_NUMBER; + s->token.u.num.val = ret; + } + break; + default: + if (c >= 128) { + js_parse_error(s, "unexpected character"); + goto fail; + } + def_token: + s->token.val = c; + p++; + break; + } + s->buf_ptr = p; + + // dump_token(s, &s->token); + return 0; + + fail: + s->token.val = TOK_ERROR; + return -1; +} + /* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is only set if TOK_IMPORT is returned */ /* XXX: handle all unicode cases */ @@ -20858,6 +21262,12 @@ static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator) } } else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) { return TOK_OF; + } else if (c == 'e' && + p[0] == 'x' && p[1] == 'p' && p[2] == 'o' && + p[3] == 'r' && p[4] == 't' && + !lre_js_is_ident_next(p[5])) { + *pp = p + 5; + return TOK_EXPORT; } else if (c == 'f' && p[0] == 'u' && p[1] == 'n' && p[2] == 'c' && p[3] == 't' && p[4] == 'i' && p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) { @@ -20881,16 +21291,21 @@ static int peek_token(JSParseState *s, BOOL no_line_terminator) (heuristic). 'input' must be a zero terminated. Heuristic: skip comments and expect 'import' keyword not followed - by '(' or '.' + by '(' or '.' or export keyword. */ BOOL JS_DetectModule(const char *input, size_t input_len) { const uint8_t *p = (const uint8_t *)input; int tok; - if (simple_next_token(&p, FALSE) != TOK_IMPORT) + switch(simple_next_token(&p, FALSE)) { + case TOK_IMPORT: + tok = simple_next_token(&p, FALSE); + return (tok != '.' && tok != '('); + case TOK_EXPORT: + return TRUE; + default: return FALSE; - tok = simple_next_token(&p, FALSE); - return (tok != '.' && tok != '('); + } } static inline int get_prev_opcode(JSFunctionDef *fd) { @@ -20974,20 +21389,10 @@ static int new_label_fd(JSFunctionDef *fd, int label) LabelSlot *ls; if (label < 0) { - if (fd->label_count >= fd->label_size) { - int new_size; - size_t slack; - LabelSlot *new_tab; - - /* XXX: potential arithmetic overflow */ - new_size = fd->label_size * 3 / 2 + 4; - new_tab = js_realloc2(fd->ctx, fd->label_slots, new_size * sizeof(*new_tab), &slack); - if (!new_tab) - return -1; - new_size += slack / sizeof(*new_tab); - fd->label_slots = new_tab; - fd->label_size = new_size; - } + if (js_resize_array(fd->ctx, (void *)&fd->label_slots, + sizeof(fd->label_slots[0]), + &fd->label_size, fd->label_count + 1)) + return -1; label = fd->label_count++; ls = &fd->label_slots[label]; ls->ref_count = 0; @@ -21035,19 +21440,10 @@ static int emit_goto(JSParseState *s, int opcode, int label) static int cpool_add(JSParseState *s, JSValue val) { JSFunctionDef *fd = s->cur_func; - if (fd->cpool_count >= fd->cpool_size) { - int new_size; - size_t slack; - JSValue *new_tab; - /* XXX: potential arithmetic overflow */ - new_size = max_int(fd->cpool_count + 1, fd->cpool_size * 3 / 2); - new_tab = js_realloc2(s->ctx, fd->cpool, new_size * sizeof(JSValue), &slack); - if (!new_tab) - return -1; - new_size += slack / sizeof(*new_tab); - fd->cpool = new_tab; - fd->cpool_size = new_size; - } + + if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]), + &fd->cpool_size, fd->cpool_count + 1)) + return -1; fd->cpool[fd->cpool_count++] = val; return fd->cpool_count - 1; } @@ -21246,18 +21642,9 @@ static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) JS_ThrowInternalError(ctx, "too many local variables"); return -1; } - if ((fd->var_count + 1) > fd->var_size) { - int new_size; - size_t slack; - JSVarDef *new_buf; - new_size = max_int(fd->var_count + 1, fd->var_size * 3 / 2); - new_buf = js_realloc2(ctx, fd->vars, new_size * sizeof(*fd->vars), &slack); - if (!new_buf) - return -1; - new_size += slack / sizeof(*new_buf); - fd->vars = new_buf; - fd->var_size = new_size; - } + if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]), + &fd->var_size, fd->var_count + 1)) + return -1; vd = &fd->vars[fd->var_count++]; memset(vd, 0, sizeof(*vd)); vd->var_name = JS_DupAtom(ctx, name); @@ -21309,18 +21696,9 @@ static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) JS_ThrowInternalError(ctx, "too many arguments"); return -1; } - if ((fd->arg_count + 1) > fd->arg_size) { - int new_size; - size_t slack; - JSVarDef *new_buf; - new_size = max_int(fd->arg_count + 1, fd->arg_size * 3 / 2); - new_buf = js_realloc2(ctx, fd->args, new_size * sizeof(*fd->args), &slack); - if (!new_buf) - return -1; - new_size += slack / sizeof(*new_buf); - fd->args = new_buf; - fd->arg_size = new_size; - } + if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]), + &fd->arg_size, fd->arg_count + 1)) + return -1; vd = &fd->args[fd->arg_count++]; memset(vd, 0, sizeof(*vd)); vd->var_name = JS_DupAtom(ctx, name); @@ -21335,19 +21713,10 @@ static JSHoistedDef *add_hoisted_def(JSContext *ctx, { JSHoistedDef *hf; - if (s->hoisted_def_count >= s->hoisted_def_size) { - int new_size; - size_t slack; - JSHoistedDef *new_tab; - new_size = max_int(s->hoisted_def_count + 1, - s->hoisted_def_size * 3 / 2); - new_tab = js_realloc2(ctx, s->hoisted_def, new_size * sizeof(s->hoisted_def[0]), &slack); - if (!new_tab) - return NULL; - new_size += slack / sizeof(*new_tab); - s->hoisted_def = new_tab; - s->hoisted_def_size = new_size; - } + if (js_resize_array(ctx, (void **)&s->hoisted_def, + sizeof(s->hoisted_def[0]), + &s->hoisted_def_size, s->hoisted_def_count + 1)) + return NULL; hf = &s->hoisted_def[s->hoisted_def_count++]; hf->cpool_idx = cpool_idx; hf->force_init = 0; @@ -21639,7 +22008,7 @@ static __exception int js_parse_template(JSParseState *s, int call, int *argc) goto done; if (next_token(s)) return -1; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_expr(s)) return -1; depth++; if (s->token.val != '}') { @@ -21647,7 +22016,8 @@ static __exception int js_parse_template(JSParseState *s, int call, int *argc) } /* XXX: should convert to string at this stage? */ free_token(s, &s->token); - /* Resume TOK_TEMPLATE parsing (s->token.line_num and s->token.ptr are OK) */ + /* Resume TOK_TEMPLATE parsing (s->token.line_num and + * s->token.ptr are OK) */ s->got_lf = FALSE; s->last_line_num = s->token.line_num; if (js_parse_template_part(s, s->buf_ptr)) @@ -21875,7 +22245,7 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ size_t level = 0; JSParsePos pos; int last_tok, tok = TOK_EOF; - int tok_len, bits = 0; + int c, tok_len, bits = 0; /* protect from underflow */ state[level++] = 0; @@ -21900,8 +22270,30 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ goto done; break; case '}': - if (state[--level] != '{') + c = state[--level]; + if (c == '`') { + /* continue the parsing of the template */ + free_token(s, &s->token); + /* Resume TOK_TEMPLATE parsing (s->token.line_num and + * s->token.ptr are OK) */ + s->got_lf = FALSE; + s->last_line_num = s->token.line_num; + if (js_parse_template_part(s, s->buf_ptr)) + goto done; + goto handle_template; + } else if (c != '{') { goto done; + } + break; + case TOK_TEMPLATE: + handle_template: + if (s->token.u.str.sep != '`') { + /* '${' inside the template : closing '}' and continue + parsing the template */ + if (level >= sizeof(state)) + goto done; + state[level++] = '`'; + } break; case TOK_EOF: goto done; @@ -24005,7 +24397,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen int scope; name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); - if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL) { + if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) { /* direct 'eval' */ opcode = OP_eval; } else { @@ -26318,26 +26710,6 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m) js_free(ctx, m); } -static int js_resize_array(JSContext *ctx, void **parray, int elem_size, - int *psize, int *pcount, int new_count) -{ - if (unlikely(new_count > *psize)) { - int new_size; - size_t slack; - void *new_array; - /* XXX: potential arithmetic overflow */ - new_size = max_int(new_count, *psize * 3 / 2); - new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack); - if (!new_array) - return -1; - new_size += slack / elem_size; - *psize = new_size; - *parray = new_array; - } - *pcount = new_count; - return 0; -} - static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, JSAtom module_name) { @@ -26353,11 +26725,10 @@ static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, if (js_resize_array(ctx, (void **)&m->req_module_entries, sizeof(JSReqModuleEntry), - &m->req_module_entries_size, &m->req_module_entries_count, + &m->req_module_entries_size, m->req_module_entries_count + 1)) return -1; - i = m->req_module_entries_count - 1; - rme = &m->req_module_entries[i]; + rme = &m->req_module_entries[m->req_module_entries_count++]; rme->module_name = JS_DupAtom(ctx, module_name); rme->module = NULL; return i; @@ -26389,18 +26760,17 @@ static JSExportEntry *add_export_entry2(JSContext *ctx, js_parse_error(s, "duplicate exported name '%s'", JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name)); } else { - JS_ThrowSyntaxError(ctx, "duplicate exported name '%s'", - JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name)); + JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name); } return NULL; } if (js_resize_array(ctx, (void **)&m->export_entries, sizeof(JSExportEntry), - &m->export_entries_size, &m->export_entries_count, + &m->export_entries_size, m->export_entries_count + 1)) return NULL; - me = &m->export_entries[m->export_entries_count - 1]; + me = &m->export_entries[m->export_entries_count++]; memset(me, 0, sizeof(*me)); me->local_name = JS_DupAtom(ctx, local_name); me->export_name = JS_DupAtom(ctx, export_name); @@ -26423,10 +26793,10 @@ static int add_star_export_entry(JSContext *ctx, JSModuleDef *m, if (js_resize_array(ctx, (void **)&m->star_export_entries, sizeof(JSStarExportEntry), - &m->star_export_entries_size, &m->star_export_entries_count, + &m->star_export_entries_size, m->star_export_entries_count + 1)) return -1; - se = &m->star_export_entries[m->star_export_entries_count - 1]; + se = &m->star_export_entries[m->star_export_entries_count++]; se->req_module_idx = req_module_idx; return 0; } @@ -26652,10 +27022,9 @@ static int add_resolve_entry(JSContext *ctx, JSResolveState *s, if (js_resize_array(ctx, (void **)&s->array, sizeof(JSResolveEntry), - &s->size, &s->count, - s->count + 1)) + &s->size, s->count + 1)) return -1; - re = &s->array[s->count - 1]; + re = &s->array[s->count++]; re->module = m; re->name = JS_DupAtom(ctx, name); return 0; @@ -26780,17 +27149,17 @@ static void js_resolve_export_throw_error(JSContext *ctx, break; default: case JS_RESOLVE_RES_NOT_FOUND: - JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous", + JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'", JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); break; case JS_RESOLVE_RES_CIRCULAR: - JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'", + JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'", JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); break; case JS_RESOLVE_RES_AMBIGUOUS: - JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'", + JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous", JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); break; @@ -26847,9 +27216,9 @@ static __exception int get_exported_names(JSContext *ctx, return 0; } if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]), - &s->modules_size, &s->modules_count, s->modules_count + 1)) + &s->modules_size, s->modules_count + 1)) return -1; - s->modules[s->modules_count - 1] = m; + s->modules[s->modules_count++] = m; for(i = 0; i < m->export_entries_count; i++) { JSExportEntry *me = &m->export_entries[i]; @@ -26858,9 +27227,10 @@ static __exception int get_exported_names(JSContext *ctx, j = find_exported_name(s, me->export_name); if (j < 0) { if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]), - &s->exported_names_size, &s->exported_names_count, s->exported_names_count + 1)) + &s->exported_names_size, + s->exported_names_count + 1)) return -1; - en = &s->exported_names[s->exported_names_count - 1]; + en = &s->exported_names[s->exported_names_count++]; en->export_name = me->export_name; /* avoid a second lookup for simple module exports */ if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL) @@ -27096,7 +27466,7 @@ static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical) } /* Create the <eval> function associated with the module */ -static int js_create_module_function(JSContext *ctx, JSModuleDef *m) +static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m) { JSFunctionBytecode *b; int i; @@ -27147,20 +27517,15 @@ static int js_create_module_function(JSContext *ctx, JSModuleDef *m) return -1; } -/* Prepare a module to be executed by resolving all the imported - variables. */ -static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) +/* must be done before js_instantiate_module() because of cyclic references */ +static int js_create_module_function(JSContext *ctx, JSModuleDef *m) { - int i; - JSImportEntry *mi; - JSModuleDef *m1; - JSVarRef **var_refs, *var_ref; - JSObject *p; BOOL is_c_module; - - if (m->instantiated) + int i; + JSVarRef *var_ref; + + if (m->func_created) return 0; - m->instantiated = TRUE; is_c_module = (m->init_func != NULL); @@ -27171,14 +27536,49 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) if (me->export_type == JS_EXPORT_TYPE_LOCAL) { var_ref = js_create_module_var(ctx, FALSE); if (!var_ref) - goto fail; + return -1; me->u.local.var_ref = var_ref; } } } else { - if (js_create_module_function(ctx, m) < 0) - goto fail; + if (js_create_module_bytecode_function(ctx, m)) + return -1; + } + m->func_created = TRUE; + + /* do it on the dependencies */ + + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + if (js_create_module_function(ctx, rme->module) < 0) + return -1; + } + + return 0; +} + + +/* Prepare a module to be executed by resolving all the imported + variables. */ +static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) +{ + int i; + JSImportEntry *mi; + JSModuleDef *m1; + JSVarRef **var_refs, *var_ref; + JSObject *p; + BOOL is_c_module; + + if (m->instantiated) + return 0; + m->instantiated = TRUE; + +#ifdef DUMP_MODULE_RESOLVE + { + char buf1[ATOM_GET_STR_BUF_SIZE]; + printf("start instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); } +#endif for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; @@ -27221,6 +27621,8 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) } #endif + is_c_module = (m->init_func != NULL); + if (!is_c_module) { p = JS_VALUE_GET_OBJ(m->func_obj); var_refs = p->u.func.var_refs; @@ -27740,10 +28142,10 @@ static int add_import(JSParseState *s, JSModuleDef *m, return -1; if (js_resize_array(ctx, (void **)&m->import_entries, sizeof(JSImportEntry), - &m->import_entries_size, &m->import_entries_count, + &m->import_entries_size, m->import_entries_count + 1)) return -1; - mi = &m->import_entries[m->import_entries_count - 1]; + mi = &m->import_entries[m->import_entries_count++]; mi->import_name = JS_DupAtom(ctx, import_name); mi->var_idx = var_idx; return 0; @@ -28474,20 +28876,10 @@ static int add_closure_var(JSContext *ctx, JSFunctionDef *s, return -1; } - if (s->closure_var_count >= s->closure_var_size) { - JSClosureVar *new_tab; - int new_size; - size_t slack; - new_size = max_int(s->closure_var_count + 1, - s->closure_var_size * 3 / 2); - new_tab = js_realloc2(ctx, s->closure_var, - new_size * sizeof(JSClosureVar), &slack); - if (!new_tab) - return -1; - new_size += slack / sizeof(*new_tab); - s->closure_var = new_tab; - s->closure_var_size = new_size; - } + if (js_resize_array(ctx, (void **)&s->closure_var, + sizeof(s->closure_var[0]), + &s->closure_var_size, s->closure_var_count + 1)) + return -1; cv = &s->closure_var[s->closure_var_count++]; cv->is_local = is_local; cv->is_arg = is_arg; @@ -29243,8 +29635,6 @@ static int resolve_scope_private_field1(JSContext *ctx, } scope_level = fd->parent_scope_level; if (!fd->parent) { - char buf[ATOM_GET_STR_BUF_SIZE]; - if (fd->is_eval) { /* closure of the eval function (top level) */ for (idx = 0; idx < fd->closure_var_count; idx++) { @@ -29267,8 +29657,8 @@ static int resolve_scope_private_field1(JSContext *ctx, } } /* XXX: no line number info */ - JS_ThrowSyntaxError(ctx, "undefined private field %s", - JS_AtomGetStr(ctx, buf, sizeof(buf), var_name)); + JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'", + var_name); return -1; } else { fd = fd->parent; @@ -31535,9 +31925,8 @@ static int add_module_variables(JSContext *ctx, JSFunctionDef *fd) if (me->export_type == JS_EXPORT_TYPE_LOCAL) { idx = find_closure_var(ctx, fd, me->local_name); if (idx < 0) { - char buf1[ATOM_GET_STR_BUF_SIZE]; - JS_ThrowSyntaxError(ctx, "exported variable '%s' does not exist", - JS_AtomGetStr(ctx, buf1, sizeof(buf1), me->local_name)); + JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist", + me->local_name); return -1; } me->u.local.var_idx = idx; @@ -32559,6 +32948,8 @@ static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj, m = JS_VALUE_GET_PTR(fun_obj); /* the module refcount should be >= 2 */ JS_FreeValue(ctx, fun_obj); + if (js_create_module_function(ctx, m) < 0) + goto fail; if (js_instantiate_module(ctx, m) < 0) goto fail; ret_val = js_evaluate_module(ctx, m); @@ -32770,6 +33161,110 @@ int JS_ResolveModule(JSContext *ctx, JSValueConst obj) } /*******************************************************************/ +/* object list */ + +typedef struct { + JSObject *obj; + uint32_t hash_next; /* -1 if no next entry */ +} JSObjectListEntry; + +/* XXX: reuse it to optimize weak references */ +typedef struct { + JSObjectListEntry *object_tab; + int object_count; + int object_size; + uint32_t *hash_table; + uint32_t hash_size; +} JSObjectList; + +static void js_object_list_init(JSObjectList *s) +{ + memset(s, 0, sizeof(*s)); +} + +static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size) +{ + return ((uintptr_t)p * 3163) & (hash_size - 1); +} + +static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s, + uint32_t new_hash_size) +{ + JSObjectListEntry *e; + uint32_t i, h, *new_hash_table; + + new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size); + if (!new_hash_table) + return -1; + js_free(ctx, s->hash_table); + s->hash_table = new_hash_table; + s->hash_size = new_hash_size; + + for(i = 0; i < s->hash_size; i++) { + s->hash_table[i] = -1; + } + for(i = 0; i < s->object_count; i++) { + e = &s->object_tab[i]; + h = js_object_list_get_hash(e->obj, s->hash_size); + e->hash_next = s->hash_table[h]; + s->hash_table[h] = i; + } + return 0; +} + +/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if + memory error */ +static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj) +{ + JSObjectListEntry *e; + uint32_t h, new_hash_size; + + if (js_resize_array(ctx, (void *)&s->object_tab, + sizeof(s->object_tab[0]), + &s->object_size, s->object_count + 1)) + return -1; + if (unlikely((s->object_count + 1) >= s->hash_size)) { + new_hash_size = max_uint32(s->hash_size, 4); + while (new_hash_size <= s->object_count) + new_hash_size *= 2; + if (js_object_list_resize_hash(ctx, s, new_hash_size)) + return -1; + } + e = &s->object_tab[s->object_count++]; + h = js_object_list_get_hash(obj, s->hash_size); + e->obj = obj; + e->hash_next = s->hash_table[h]; + s->hash_table[h] = s->object_count - 1; + return 0; +} + +/* return -1 if not present or the object index */ +static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj) +{ + JSObjectListEntry *e; + uint32_t h, p; + + /* must test empty size because there is no hash table */ + if (s->object_count == 0) + return -1; + h = js_object_list_get_hash(obj, s->hash_size); + p = s->hash_table[h]; + while (p != -1) { + e = &s->object_tab[p]; + if (e->obj == obj) + return p; + p = e->hash_next; + } + return -1; +} + +static void js_object_list_end(JSContext *ctx, JSObjectList *s) +{ + js_free(ctx, s->object_tab); + js_free(ctx, s->hash_table); +} + +/*******************************************************************/ /* binary object writer & reader */ typedef enum BCTagEnum { @@ -32788,6 +33283,12 @@ typedef enum BCTagEnum { BC_TAG_TEMPLATE_OBJECT, BC_TAG_FUNCTION_BYTECODE, BC_TAG_MODULE, + BC_TAG_TYPED_ARRAY, + BC_TAG_ARRAY_BUFFER, + BC_TAG_SHARED_ARRAY_BUFFER, + BC_TAG_DATE, + BC_TAG_OBJECT_VALUE, + BC_TAG_OBJECT_REFERENCE, } BCTagEnum; #ifdef CONFIG_BIGNUM @@ -32805,14 +33306,21 @@ typedef enum BCTagEnum { typedef struct BCWriterState { JSContext *ctx; DynBuf dbuf; - BOOL byte_swap; - BOOL allow_bytecode; + BOOL byte_swap : 8; + BOOL allow_bytecode : 8; + BOOL allow_sab : 8; + BOOL allow_reference : 8; uint32_t first_atom; uint32_t *atom_to_idx; int atom_to_idx_size; JSAtom *idx_to_atom; int idx_to_atom_count; int idx_to_atom_size; + uint8_t **sab_tab; + int sab_tab_len; + int sab_tab_size; + /* list of referenced objects (used if allow_reference = TRUE) */ + JSObjectList object_list; } BCWriterState; #ifdef DUMP_READ_OBJECT @@ -32833,6 +33341,12 @@ static const char * const bc_tag_str[] = { "template", "function", "module", + "TypedArray", + "ArrayBuffer", + "SharedArrayBuffer", + "Date", + "ObjectValue", + "ObjectReference", }; #endif @@ -32892,36 +33406,20 @@ static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom) return 0; } if (atom >= s->atom_to_idx_size) { - size_t new_size, i, slack; - uint32_t *new_tab; - /* XXX: potential arithmetic overflow */ - new_size = s->atom_to_idx_size * 3 / 2; - if ((atom + 1) > new_size) - new_size = atom + 1; - new_tab = js_realloc2(s->ctx, s->atom_to_idx, - new_size * sizeof(s->atom_to_idx[0]), &slack); - if (!new_tab) - goto fail; - new_size += slack / sizeof(*new_tab); - for(i = s->atom_to_idx_size; i < new_size; i++) - new_tab[i] = 0; - s->atom_to_idx = new_tab; - s->atom_to_idx_size = new_size; - } - if ((s->idx_to_atom_count + 1) > s->idx_to_atom_size) { - size_t new_size, slack; - JSAtom *new_tab; - new_size = s->idx_to_atom_size * 3 / 2; - if ((s->idx_to_atom_count + 1) > new_size) - new_size = s->idx_to_atom_count + 1; - new_tab = js_realloc2(s->ctx, s->idx_to_atom, - new_size * sizeof(s->idx_to_atom[0]), &slack); - if (!new_tab) - goto fail; - new_size += slack / sizeof(*new_tab); - s->idx_to_atom = new_tab; - s->idx_to_atom_size = new_size; + int old_size, i; + old_size = s->atom_to_idx_size; + if (js_resize_array(s->ctx, (void **)&s->atom_to_idx, + sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size, + atom + 1)) + return -1; + /* XXX: could add a specific js_resize_array() function to do it */ + for(i = old_size; i < s->atom_to_idx_size; i++) + s->atom_to_idx[i] = 0; } + if (js_resize_array(s->ctx, (void **)&s->idx_to_atom, + sizeof(s->idx_to_atom[0]), + &s->idx_to_atom_size, s->idx_to_atom_count + 1)) + goto fail; v = s->idx_to_atom_count++; s->idx_to_atom[v] = atom + s->first_atom; @@ -33204,10 +33702,276 @@ static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj) } #endif /* CONFIG_BIGNUM */ +static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj); + +static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) +{ + JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj); + uint32_t flags; + int idx, i; + + bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE); + flags = idx = 0; + bc_set_flags(&flags, &idx, b->has_prototype, 1); + bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1); + bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1); + bc_set_flags(&flags, &idx, b->need_home_object, 1); + bc_set_flags(&flags, &idx, b->func_kind, 2); + bc_set_flags(&flags, &idx, b->new_target_allowed, 1); + bc_set_flags(&flags, &idx, b->super_call_allowed, 1); + bc_set_flags(&flags, &idx, b->super_allowed, 1); + bc_set_flags(&flags, &idx, b->arguments_allowed, 1); + bc_set_flags(&flags, &idx, b->has_debug, 1); + bc_set_flags(&flags, &idx, b->backtrace_barrier, 1); + assert(idx <= 16); + bc_put_u16(s, flags); + bc_put_u8(s, b->js_mode); + bc_put_atom(s, b->func_name); + + bc_put_leb128(s, b->arg_count); + bc_put_leb128(s, b->var_count); + bc_put_leb128(s, b->defined_arg_count); + bc_put_leb128(s, b->stack_size); + bc_put_leb128(s, b->closure_var_count); + bc_put_leb128(s, b->cpool_count); + bc_put_leb128(s, b->byte_code_len); + if (b->vardefs) { + bc_put_leb128(s, b->arg_count + b->var_count); + for(i = 0; i < b->arg_count + b->var_count; i++) { + JSVarDef *vd = &b->vardefs[i]; + bc_put_atom(s, vd->var_name); + bc_put_leb128(s, vd->scope_level); + bc_put_leb128(s, vd->scope_next + 1); + flags = idx = 0; + bc_set_flags(&flags, &idx, vd->var_kind, 4); + bc_set_flags(&flags, &idx, vd->is_func_var, 1); + bc_set_flags(&flags, &idx, vd->is_const, 1); + bc_set_flags(&flags, &idx, vd->is_lexical, 1); + bc_set_flags(&flags, &idx, vd->is_captured, 1); + assert(idx <= 8); + bc_put_u8(s, flags); + } + } else { + bc_put_leb128(s, 0); + } + + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + bc_put_atom(s, cv->var_name); + bc_put_leb128(s, cv->var_idx); + flags = idx = 0; + bc_set_flags(&flags, &idx, cv->is_local, 1); + bc_set_flags(&flags, &idx, cv->is_arg, 1); + bc_set_flags(&flags, &idx, cv->is_const, 1); + bc_set_flags(&flags, &idx, cv->is_lexical, 1); + bc_set_flags(&flags, &idx, cv->var_kind, 4); + assert(idx <= 8); + bc_put_u8(s, flags); + } + + if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len)) + goto fail; + + if (b->has_debug) { + bc_put_atom(s, b->debug.filename); + bc_put_leb128(s, b->debug.line_num); + bc_put_leb128(s, b->debug.pc2line_len); + dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len); + } + + for(i = 0; i < b->cpool_count; i++) { + if (JS_WriteObjectRec(s, b->cpool[i])) + goto fail; + } + return 0; + fail: + return -1; +} + +static int JS_WriteModule(BCWriterState *s, JSValueConst obj) +{ + JSModuleDef *m = JS_VALUE_GET_PTR(obj); + int i; + + bc_put_u8(s, BC_TAG_MODULE); + bc_put_atom(s, m->module_name); + + bc_put_leb128(s, m->req_module_entries_count); + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + bc_put_atom(s, rme->module_name); + } + + bc_put_leb128(s, m->export_entries_count); + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + bc_put_u8(s, me->export_type); + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + bc_put_leb128(s, me->u.local.var_idx); + } else { + bc_put_leb128(s, me->u.req_module_idx); + bc_put_atom(s, me->local_name); + } + bc_put_atom(s, me->export_name); + } + + bc_put_leb128(s, m->star_export_entries_count); + for(i = 0; i < m->star_export_entries_count; i++) { + JSStarExportEntry *se = &m->star_export_entries[i]; + bc_put_leb128(s, se->req_module_idx); + } + + bc_put_leb128(s, m->import_entries_count); + for(i = 0; i < m->import_entries_count; i++) { + JSImportEntry *mi = &m->import_entries[i]; + bc_put_leb128(s, mi->var_idx); + bc_put_atom(s, mi->import_name); + bc_put_leb128(s, mi->req_module_idx); + } + + if (JS_WriteObjectRec(s, m->func_obj)) + goto fail; + return 0; + fail: + return -1; +} + +static int JS_WriteArray(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + uint32_t i, len; + JSValue val; + int ret; + BOOL is_template; + + if (s->allow_bytecode && !p->extensible) { + /* not extensible array: we consider it is a + template when we are saving bytecode */ + bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT); + is_template = TRUE; + } else { + bc_put_u8(s, BC_TAG_ARRAY); + is_template = FALSE; + } + if (js_get_length32(s->ctx, &len, obj)) + goto fail1; + bc_put_leb128(s, len); + for(i = 0; i < len; i++) { + val = JS_GetPropertyUint32(s->ctx, obj, i); + if (JS_IsException(val)) + goto fail1; + ret = JS_WriteObjectRec(s, val); + JS_FreeValue(s->ctx, val); + if (ret) + goto fail1; + } + if (is_template) { + val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw); + if (JS_IsException(val)) + goto fail1; + ret = JS_WriteObjectRec(s, val); + JS_FreeValue(s->ctx, val); + if (ret) + goto fail1; + } + return 0; + fail1: + return -1; +} + +static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + uint32_t i, prop_count; + JSShape *sh; + JSShapeProperty *pr; + int pass; + JSAtom atom; + + bc_put_u8(s, BC_TAG_OBJECT); + prop_count = 0; + sh = p->shape; + for(pass = 0; pass < 2; pass++) { + if (pass == 1) + bc_put_leb128(s, prop_count); + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { + atom = pr->atom; + if (atom != JS_ATOM_NULL && + JS_AtomIsString(s->ctx, atom) && + (pr->flags & JS_PROP_ENUMERABLE)) { + if (pr->flags & JS_PROP_TMASK) { + JS_ThrowTypeError(s->ctx, "only value properties are supported"); + goto fail; + } + if (pass == 0) { + prop_count++; + } else { + bc_put_atom(s, atom); + if (JS_WriteObjectRec(s, p->prop[i].u.value)) + goto fail; + } + } + } + } + return 0; + fail: + return -1; +} + +static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + JSTypedArray *ta = p->u.typed_array; + + bc_put_u8(s, BC_TAG_TYPED_ARRAY); + bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY); + bc_put_leb128(s, p->u.array.count); + bc_put_leb128(s, ta->offset); + if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) + return -1; + return 0; +} + +static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + JSArrayBuffer *abuf = p->u.array_buffer; + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx); + return -1; + } + bc_put_u8(s, BC_TAG_ARRAY_BUFFER); + bc_put_leb128(s, abuf->byte_length); + dbuf_put(&s->dbuf, abuf->data, abuf->byte_length); + return 0; +} + +static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + JSArrayBuffer *abuf = p->u.array_buffer; + assert(!abuf->detached); /* SharedArrayBuffer are never detached */ + bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER); + bc_put_leb128(s, abuf->byte_length); + bc_put_u64(s, (uintptr_t)abuf->data); + if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]), + &s->sab_tab_size, s->sab_tab_len + 1)) + return -1; + /* keep the SAB pointer so that the user can clone it or free it */ + s->sab_tab[s->sab_tab_len++] = abuf->data; + return 0; +} + static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) { - uint32_t tag = JS_VALUE_GET_NORM_TAG(obj); + uint32_t tag; + + if (js_check_stack_overflow(s->ctx->rt, 0)) { + JS_ThrowStackOverflow(s->ctx); + return -1; + } + tag = JS_VALUE_GET_NORM_TAG(obj); switch(tag) { case JS_TAG_NULL: bc_put_u8(s, BC_TAG_NULL); @@ -33238,218 +34002,82 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) } break; case JS_TAG_FUNCTION_BYTECODE: - { - JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj); - uint32_t flags; - int idx, i; - - if (!s->allow_bytecode) - goto invalid_tag; - bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE); - flags = idx = 0; - bc_set_flags(&flags, &idx, b->has_prototype, 1); - bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1); - bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1); - bc_set_flags(&flags, &idx, b->need_home_object, 1); - bc_set_flags(&flags, &idx, b->func_kind, 2); - bc_set_flags(&flags, &idx, b->new_target_allowed, 1); - bc_set_flags(&flags, &idx, b->super_call_allowed, 1); - bc_set_flags(&flags, &idx, b->super_allowed, 1); - bc_set_flags(&flags, &idx, b->arguments_allowed, 1); - bc_set_flags(&flags, &idx, b->has_debug, 1); - bc_set_flags(&flags, &idx, b->backtrace_barrier, 1); - assert(idx <= 16); - bc_put_u16(s, flags); - bc_put_u8(s, b->js_mode); - bc_put_atom(s, b->func_name); - - bc_put_leb128(s, b->arg_count); - bc_put_leb128(s, b->var_count); - bc_put_leb128(s, b->defined_arg_count); - bc_put_leb128(s, b->stack_size); - bc_put_leb128(s, b->closure_var_count); - bc_put_leb128(s, b->cpool_count); - bc_put_leb128(s, b->byte_code_len); - if (b->vardefs) { - bc_put_leb128(s, b->arg_count + b->var_count); - for(i = 0; i < b->arg_count + b->var_count; i++) { - JSVarDef *vd = &b->vardefs[i]; - bc_put_atom(s, vd->var_name); - bc_put_leb128(s, vd->scope_level); - bc_put_leb128(s, vd->scope_next + 1); - flags = idx = 0; - bc_set_flags(&flags, &idx, vd->var_kind, 4); - bc_set_flags(&flags, &idx, vd->is_func_var, 1); - bc_set_flags(&flags, &idx, vd->is_const, 1); - bc_set_flags(&flags, &idx, vd->is_lexical, 1); - bc_set_flags(&flags, &idx, vd->is_captured, 1); - assert(idx <= 8); - bc_put_u8(s, flags); - } - } else { - bc_put_leb128(s, 0); - } - - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - bc_put_atom(s, cv->var_name); - bc_put_leb128(s, cv->var_idx); - flags = idx = 0; - bc_set_flags(&flags, &idx, cv->is_local, 1); - bc_set_flags(&flags, &idx, cv->is_arg, 1); - bc_set_flags(&flags, &idx, cv->is_const, 1); - bc_set_flags(&flags, &idx, cv->is_lexical, 1); - bc_set_flags(&flags, &idx, cv->var_kind, 4); - assert(idx <= 8); - bc_put_u8(s, flags); - } - - if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len)) - goto fail; - - if (b->has_debug) { - bc_put_atom(s, b->debug.filename); - bc_put_leb128(s, b->debug.line_num); - bc_put_leb128(s, b->debug.pc2line_len); - dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len); - } - - for(i = 0; i < b->cpool_count; i++) { - if (JS_WriteObjectRec(s, b->cpool[i])) - goto fail; - } - } + if (!s->allow_bytecode) + goto invalid_tag; + if (JS_WriteFunctionTag(s, obj)) + goto fail; break; case JS_TAG_MODULE: - { - JSModuleDef *m = JS_VALUE_GET_PTR(obj); - int i; - - if (!s->allow_bytecode) - goto invalid_tag; - bc_put_u8(s, BC_TAG_MODULE); - bc_put_atom(s, m->module_name); - - bc_put_leb128(s, m->req_module_entries_count); - for(i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry *rme = &m->req_module_entries[i]; - bc_put_atom(s, rme->module_name); - } - - bc_put_leb128(s, m->export_entries_count); - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - bc_put_u8(s, me->export_type); - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - bc_put_leb128(s, me->u.local.var_idx); - } else { - bc_put_leb128(s, me->u.req_module_idx); - bc_put_atom(s, me->local_name); - } - bc_put_atom(s, me->export_name); - } - - bc_put_leb128(s, m->star_export_entries_count); - for(i = 0; i < m->star_export_entries_count; i++) { - JSStarExportEntry *se = &m->star_export_entries[i]; - bc_put_leb128(s, se->req_module_idx); - } - - bc_put_leb128(s, m->import_entries_count); - for(i = 0; i < m->import_entries_count; i++) { - JSImportEntry *mi = &m->import_entries[i]; - bc_put_leb128(s, mi->var_idx); - bc_put_atom(s, mi->import_name); - bc_put_leb128(s, mi->req_module_idx); - } - - if (JS_WriteObjectRec(s, m->func_obj)) - goto fail; - } + if (!s->allow_bytecode) + goto invalid_tag; + if (JS_WriteModule(s, obj)) + goto fail; break; case JS_TAG_OBJECT: { JSObject *p = JS_VALUE_GET_OBJ(obj); - uint32_t i, prop_count, len; - JSShape *sh; - JSShapeProperty *pr; - JSValue val; - int ret, pass; - BOOL is_template; - JSAtom atom; - - if (p->class_id != JS_CLASS_ARRAY && - p->class_id != JS_CLASS_OBJECT) { - JS_ThrowTypeError(s->ctx, "unsupported object class"); - goto fail; - } - if (p->tmp_mark) { - JS_ThrowTypeError(s->ctx, "circular reference"); - goto fail; - } - p->tmp_mark = 1; - if (p->class_id == JS_CLASS_ARRAY) { - if (s->allow_bytecode && !p->extensible) { - /* not extensible array: we consider it is a - template when we are saving bytecode */ - bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT); - is_template = TRUE; + int ret, idx; + + if (s->allow_reference) { + idx = js_object_list_find(s->ctx, &s->object_list, p); + if (idx >= 0) { + bc_put_u8(s, BC_TAG_OBJECT_REFERENCE); + bc_put_leb128(s, idx); + break; } else { - bc_put_u8(s, BC_TAG_ARRAY); - is_template = FALSE; - } - if (js_get_length32(s->ctx, &len, obj)) - goto fail1; - bc_put_leb128(s, len); - for(i = 0; i < len; i++) { - val = JS_GetPropertyUint32(s->ctx, obj, i); - if (JS_IsException(val)) - goto fail1; - ret = JS_WriteObjectRec(s, val); - JS_FreeValue(s->ctx, val); - if (ret) - goto fail1; - } - if (is_template) { - val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw); - if (JS_IsException(val)) - goto fail1; - ret = JS_WriteObjectRec(s, val); - JS_FreeValue(s->ctx, val); - if (ret) - goto fail1; + if (js_object_list_add(s->ctx, &s->object_list, p)) + goto fail; } } else { - bc_put_u8(s, BC_TAG_OBJECT); - prop_count = 0; - sh = p->shape; - for(pass = 0; pass < 2; pass++) { - if (pass == 1) - bc_put_leb128(s, prop_count); - for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { - atom = pr->atom; - if (atom != JS_ATOM_NULL && - JS_AtomIsString(s->ctx, atom) && - (pr->flags & JS_PROP_ENUMERABLE)) { - if (pr->flags & JS_PROP_TMASK) { - JS_ThrowTypeError(s->ctx, "only value properties are supported"); - goto fail; - } - if (pass == 0) { - prop_count++; - } else { - bc_put_atom(s, atom); - if (JS_WriteObjectRec(s, p->prop[i].u.value)) { - fail1: - p->tmp_mark = 0; - goto fail; - } - } - } - } + if (p->tmp_mark) { + JS_ThrowTypeError(s->ctx, "circular reference"); + goto fail; + } + p->tmp_mark = 1; + } + switch(p->class_id) { + case JS_CLASS_ARRAY: + ret = JS_WriteArray(s, obj); + break; + case JS_CLASS_OBJECT: + ret = JS_WriteObjectTag(s, obj); + break; + case JS_CLASS_ARRAY_BUFFER: + ret = JS_WriteArrayBuffer(s, obj); + break; + case JS_CLASS_SHARED_ARRAY_BUFFER: + if (!s->allow_sab) + goto invalid_tag; + ret = JS_WriteSharedArrayBuffer(s, obj); + break; + case JS_CLASS_DATE: + bc_put_u8(s, BC_TAG_DATE); + ret = JS_WriteObjectRec(s, p->u.object_data); + break; + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT: + case JS_CLASS_BIG_FLOAT: + case JS_CLASS_BIG_DECIMAL: +#endif + bc_put_u8(s, BC_TAG_OBJECT_VALUE); + ret = JS_WriteObjectRec(s, p->u.object_data); + break; + default: + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_WriteTypedArray(s, obj); + } else { + JS_ThrowTypeError(s->ctx, "unsupported object class"); + ret = -1; } + break; } p->tmp_mark = 0; + if (ret) + goto fail; } break; #ifdef CONFIG_BIGNUM @@ -33511,8 +34139,8 @@ static int JS_WriteObjectAtoms(BCWriterState *s) return -1; } -uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, - int flags) +uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, + int flags, uint8_t ***psab_tab, size_t *psab_tab_len) { BCWriterState ss, *s = &ss; @@ -33521,29 +34149,48 @@ uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, /* XXX: byte swapped output is untested */ s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0); s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0); + s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0); + s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0); /* XXX: could use a different version when bytecode is included */ if (s->allow_bytecode) s->first_atom = JS_ATOM_END; else s->first_atom = 1; js_dbuf_init(ctx, &s->dbuf); - + js_object_list_init(&s->object_list); + if (JS_WriteObjectRec(s, obj)) goto fail; if (JS_WriteObjectAtoms(s)) goto fail; + js_object_list_end(ctx, &s->object_list); js_free(ctx, s->atom_to_idx); js_free(ctx, s->idx_to_atom); *psize = s->dbuf.size; + if (psab_tab) + *psab_tab = s->sab_tab; + if (psab_tab_len) + *psab_tab_len = s->sab_tab_len; return s->dbuf.buf; fail: + js_object_list_end(ctx, &s->object_list); js_free(ctx, s->atom_to_idx); js_free(ctx, s->idx_to_atom); dbuf_free(&s->dbuf); *psize = 0; + if (psab_tab) + *psab_tab = NULL; + if (psab_tab_len) + *psab_tab_len = 0; return NULL; } +uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, + int flags) +{ + return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL); +} + typedef struct BCReaderState { JSContext *ctx; const uint8_t *buf_start, *ptr, *buf_end; @@ -33551,8 +34198,15 @@ typedef struct BCReaderState { uint32_t idx_to_atom_count; JSAtom *idx_to_atom; int error_state; - BOOL allow_bytecode; - BOOL is_rom_data; + BOOL allow_sab : 8; + BOOL allow_bytecode : 8; + BOOL is_rom_data : 8; + BOOL allow_reference : 8; + /* object references */ + JSObject **objects; + int objects_count; + int objects_size; + #ifdef DUMP_READ_OBJECT const uint8_t *ptr_last; int level; @@ -33756,7 +34410,7 @@ static JSString *JS_ReadString(BCReaderState *s) p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */ } #ifdef DUMP_READ_OBJECT - bc_read_trace(s, "string: "); JS_DumpString(s->ctx->rt, p); printf("\n"); + JS_DumpString(s->ctx->rt, p); printf("\n"); #endif return p; } @@ -33838,8 +34492,6 @@ static JSValue JS_ReadBigNum(BCReaderState *s, int tag) bf_t *a; int bpos, d; - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); - p = js_new_bf(s->ctx); if (!p) goto fail; @@ -33961,28 +34613,560 @@ static JSValue JS_ReadBigNum(BCReaderState *s, int tag) } #endif /* CONFIG_BIGNUM */ +static JSValue JS_ReadObjectRec(BCReaderState *s); + +static int BC_add_object_ref1(BCReaderState *s, JSObject *p) +{ + if (s->allow_reference) { + if (js_resize_array(s->ctx, (void *)&s->objects, + sizeof(s->objects[0]), + &s->objects_size, s->objects_count + 1)) + return -1; + s->objects[s->objects_count++] = p; + } + return 0; +} + +static int BC_add_object_ref(BCReaderState *s, JSValueConst obj) +{ + return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj)); +} + +static JSValue JS_ReadFunctionTag(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSFunctionBytecode bc, *b; + JSValue obj = JS_UNDEFINED; + uint16_t v16; + uint8_t v8; + int idx, i, local_count; + int function_size, cpool_offset, byte_code_offset; + int closure_var_offset, vardefs_offset; + + memset(&bc, 0, sizeof(bc)); + bc.header.ref_count = 1; + //bc.gc_header.mark = 0; + + if (bc_get_u16(s, &v16)) + goto fail; + idx = 0; + bc.has_prototype = bc_get_flags(v16, &idx, 1); + bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1); + bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1); + bc.need_home_object = bc_get_flags(v16, &idx, 1); + bc.func_kind = bc_get_flags(v16, &idx, 2); + bc.new_target_allowed = bc_get_flags(v16, &idx, 1); + bc.super_call_allowed = bc_get_flags(v16, &idx, 1); + bc.super_allowed = bc_get_flags(v16, &idx, 1); + bc.arguments_allowed = bc_get_flags(v16, &idx, 1); + bc.has_debug = bc_get_flags(v16, &idx, 1); + bc.backtrace_barrier = bc_get_flags(v16, &idx, 1); + bc.read_only_bytecode = s->is_rom_data; + if (bc_get_u8(s, &v8)) + goto fail; + bc.js_mode = v8; + if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure + goto fail; + if (bc_get_leb128_u16(s, &bc.arg_count)) + goto fail; + if (bc_get_leb128_u16(s, &bc.var_count)) + goto fail; + if (bc_get_leb128_u16(s, &bc.defined_arg_count)) + goto fail; + if (bc_get_leb128_u16(s, &bc.stack_size)) + goto fail; + if (bc_get_leb128_int(s, &bc.closure_var_count)) + goto fail; + if (bc_get_leb128_int(s, &bc.cpool_count)) + goto fail; + if (bc_get_leb128_int(s, &bc.byte_code_len)) + goto fail; + if (bc_get_leb128_int(s, &local_count)) + goto fail; + + if (bc.has_debug) { + function_size = sizeof(*b); + } else { + function_size = offsetof(JSFunctionBytecode, debug); + } + cpool_offset = function_size; + function_size += bc.cpool_count * sizeof(*bc.cpool); + vardefs_offset = function_size; + function_size += local_count * sizeof(*bc.vardefs); + closure_var_offset = function_size; + function_size += bc.closure_var_count * sizeof(*bc.closure_var); + byte_code_offset = function_size; + if (!bc.read_only_bytecode) { + function_size += bc.byte_code_len; + } + + b = js_mallocz(ctx, function_size); + if (!b) + return JS_EXCEPTION; + + memcpy(b, &bc, offsetof(JSFunctionBytecode, debug)); + b->header.ref_count = 1; + add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + + obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); + +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n"); +#endif + bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n", + b->arg_count, b->var_count, b->defined_arg_count, + b->closure_var_count, b->cpool_count); + bc_read_trace(s, "stack=%d bclen=%d locals=%d\n", + b->stack_size, b->byte_code_len, local_count); + + if (local_count != 0) { + bc_read_trace(s, "vars {\n"); + b->vardefs = (void *)((uint8_t*)b + vardefs_offset); + for(i = 0; i < local_count; i++) { + JSVarDef *vd = &b->vardefs[i]; + if (bc_get_atom(s, &vd->var_name)) + goto fail; + if (bc_get_leb128_int(s, &vd->scope_level)) + goto fail; + if (bc_get_leb128_int(s, &vd->scope_next)) + goto fail; + vd->scope_next--; + if (bc_get_u8(s, &v8)) + goto fail; + idx = 0; + vd->var_kind = bc_get_flags(v8, &idx, 4); + vd->is_func_var = bc_get_flags(v8, &idx, 1); + vd->is_const = bc_get_flags(v8, &idx, 1); + vd->is_lexical = bc_get_flags(v8, &idx, 1); + vd->is_captured = bc_get_flags(v8, &idx, 1); +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n"); +#endif + } + bc_read_trace(s, "}\n"); + } + if (b->closure_var_count != 0) { + bc_read_trace(s, "closure vars {\n"); + b->closure_var = (void *)((uint8_t*)b + closure_var_offset); + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + int var_idx; + if (bc_get_atom(s, &cv->var_name)) + goto fail; + if (bc_get_leb128_int(s, &var_idx)) + goto fail; + cv->var_idx = var_idx; + if (bc_get_u8(s, &v8)) + goto fail; + idx = 0; + cv->is_local = bc_get_flags(v8, &idx, 1); + cv->is_arg = bc_get_flags(v8, &idx, 1); + cv->is_const = bc_get_flags(v8, &idx, 1); + cv->is_lexical = bc_get_flags(v8, &idx, 1); + cv->var_kind = bc_get_flags(v8, &idx, 4); +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n"); +#endif + } + bc_read_trace(s, "}\n"); + } + { + bc_read_trace(s, "bytecode {\n"); + if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len)) + goto fail; + bc_read_trace(s, "}\n"); + } + if (b->has_debug) { + /* read optional debug information */ + bc_read_trace(s, "debug {\n"); + if (bc_get_atom(s, &b->debug.filename)) + goto fail; + if (bc_get_leb128_int(s, &b->debug.line_num)) + goto fail; + if (bc_get_leb128_int(s, &b->debug.pc2line_len)) + goto fail; + if (b->debug.pc2line_len) { + b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len); + if (!b->debug.pc2line_buf) + goto fail; + if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len)) + goto fail; + } +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n"); +#endif + bc_read_trace(s, "}\n"); + } + if (b->cpool_count != 0) { + bc_read_trace(s, "cpool {\n"); + b->cpool = (void *)((uint8_t*)b + cpool_offset); + for(i = 0; i < b->cpool_count; i++) { + JSValue val; + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + b->cpool[i] = val; + } + bc_read_trace(s, "}\n"); + } + b->realm = JS_DupContext(ctx); + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadModule(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue obj; + JSModuleDef *m = NULL; + JSAtom module_name; + int i; + uint8_t v8; + + if (bc_get_atom(s, &module_name)) + goto fail; +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n"); +#endif + m = js_new_module_def(ctx, module_name); + if (!m) + goto fail; + obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); + if (bc_get_leb128_int(s, &m->req_module_entries_count)) + goto fail; + if (m->req_module_entries_count != 0) { + m->req_module_entries_size = m->req_module_entries_count; + m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size); + if (!m->req_module_entries) + goto fail; + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + if (bc_get_atom(s, &rme->module_name)) + goto fail; + } + } + + if (bc_get_leb128_int(s, &m->export_entries_count)) + goto fail; + if (m->export_entries_count != 0) { + m->export_entries_size = m->export_entries_count; + m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size); + if (!m->export_entries) + goto fail; + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (bc_get_u8(s, &v8)) + goto fail; + me->export_type = v8; + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + if (bc_get_leb128_int(s, &me->u.local.var_idx)) + goto fail; + } else { + if (bc_get_leb128_int(s, &me->u.req_module_idx)) + goto fail; + if (bc_get_atom(s, &me->local_name)) + goto fail; + } + if (bc_get_atom(s, &me->export_name)) + goto fail; + } + } + + if (bc_get_leb128_int(s, &m->star_export_entries_count)) + goto fail; + if (m->star_export_entries_count != 0) { + m->star_export_entries_size = m->star_export_entries_count; + m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size); + if (!m->star_export_entries) + goto fail; + for(i = 0; i < m->star_export_entries_count; i++) { + JSStarExportEntry *se = &m->star_export_entries[i]; + if (bc_get_leb128_int(s, &se->req_module_idx)) + goto fail; + } + } + + if (bc_get_leb128_int(s, &m->import_entries_count)) + goto fail; + if (m->import_entries_count != 0) { + m->import_entries_size = m->import_entries_count; + m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size); + if (!m->import_entries) + goto fail; + for(i = 0; i < m->import_entries_count; i++) { + JSImportEntry *mi = &m->import_entries[i]; + if (bc_get_leb128_int(s, &mi->var_idx)) + goto fail; + if (bc_get_atom(s, &mi->import_name)) + goto fail; + if (bc_get_leb128_int(s, &mi->req_module_idx)) + goto fail; + } + } + + m->func_obj = JS_ReadObjectRec(s); + if (JS_IsException(m->func_obj)) + goto fail; + return obj; + fail: + if (m) { + js_free_module_def(ctx, m); + } + return JS_EXCEPTION; +} + +static JSValue JS_ReadObjectTag(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue obj; + uint32_t prop_count, i; + JSAtom atom; + JSValue val; + int ret; + + obj = JS_NewObject(ctx); + if (BC_add_object_ref(s, obj)) + goto fail; + if (bc_get_leb128(s, &prop_count)) + goto fail; + for(i = 0; i < prop_count; i++) { + if (bc_get_atom(s, &atom)) + goto fail; +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n"); +#endif + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) { + JS_FreeAtom(ctx, atom); + goto fail; + } + ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E); + JS_FreeAtom(ctx, atom); + if (ret < 0) + goto fail; + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadArray(BCReaderState *s, int tag) +{ + JSContext *ctx = s->ctx; + JSValue obj; + uint32_t len, i; + JSValue val; + int ret, prop_flags; + BOOL is_template; + + obj = JS_NewArray(ctx); + if (BC_add_object_ref(s, obj)) + goto fail; + is_template = (tag == BC_TAG_TEMPLATE_OBJECT); + if (bc_get_leb128(s, &len)) + goto fail; + for(i = 0; i < len; i++) { + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + if (is_template) + prop_flags = JS_PROP_ENUMERABLE; + else + prop_flags = JS_PROP_C_W_E; + ret = JS_DefinePropertyValueUint32(ctx, obj, i, val, + prop_flags); + if (ret < 0) + goto fail; + } + if (is_template) { + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + if (!JS_IsUndefined(val)) { + ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0); + if (ret < 0) + goto fail; + } + JS_PreventExtensions(ctx, obj); + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadTypedArray(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED; + uint8_t array_tag; + JSValueConst args[3]; + uint32_t offset, len, idx; + + if (bc_get_u8(s, &array_tag)) + return JS_EXCEPTION; + if (array_tag >= JS_TYPED_ARRAY_COUNT) + return JS_ThrowTypeError(ctx, "invalid typed array"); + if (bc_get_leb128(s, &len)) + return JS_EXCEPTION; + if (bc_get_leb128(s, &offset)) + return JS_EXCEPTION; + /* XXX: this hack could be avoided if the typed array could be + created before the array buffer */ + idx = s->objects_count; + if (BC_add_object_ref1(s, NULL)) + goto fail; + array_buffer = JS_ReadObjectRec(s); + if (JS_IsException(array_buffer)) + return JS_EXCEPTION; + if (!js_get_array_buffer(ctx, array_buffer)) { + JS_FreeValue(ctx, array_buffer); + return JS_EXCEPTION; + } + args[0] = array_buffer; + args[1] = JS_NewInt64(ctx, offset); + args[2] = JS_NewInt64(ctx, len); + obj = js_typed_array_constructor(ctx, JS_UNDEFINED, + 3, args, + JS_CLASS_UINT8C_ARRAY + array_tag); + if (JS_IsException(obj)) + goto fail; + if (s->allow_reference) { + s->objects[idx] = JS_VALUE_GET_OBJ(obj); + } + JS_FreeValue(ctx, array_buffer); + return obj; + fail: + JS_FreeValue(ctx, array_buffer); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadArrayBuffer(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + uint32_t byte_length; + JSValue obj; + + if (bc_get_leb128(s, &byte_length)) + return JS_EXCEPTION; + if (unlikely(s->buf_end - s->ptr < byte_length)) { + bc_read_error_end(s); + return JS_EXCEPTION; + } + obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + s->ptr += byte_length; + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + uint32_t byte_length; + uint8_t *data_ptr; + JSValue obj; + uint64_t u64; + + if (bc_get_leb128(s, &byte_length)) + return JS_EXCEPTION; + if (bc_get_u64(s, &u64)) + return JS_EXCEPTION; + data_ptr = (uint8_t *)(uintptr_t)u64; + /* the SharedArrayBuffer is cloned */ + obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length, + JS_CLASS_SHARED_ARRAY_BUFFER, + data_ptr, + NULL, NULL, FALSE); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadDate(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue val, obj = JS_UNDEFINED; + + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + if (!JS_IsNumber(val)) { + JS_ThrowTypeError(ctx, "Number tag expected for date"); + goto fail; + } + obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE], + JS_CLASS_DATE); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + JS_SetObjectData(ctx, obj, val); + return obj; + fail: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadObjectValue(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue val, obj = JS_UNDEFINED; + + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + obj = JS_ToObject(ctx, val); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + JS_FreeValue(ctx, val); + return obj; + fail: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + static JSValue JS_ReadObjectRec(BCReaderState *s) { JSContext *ctx = s->ctx; uint8_t tag; JSValue obj = JS_UNDEFINED; - JSModuleDef *m = NULL; + + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); if (bc_get_u8(s, &tag)) return JS_EXCEPTION; + bc_read_trace(s, "%s {\n", bc_tag_str[tag]); + switch(tag) { case BC_TAG_NULL: - bc_read_trace(s, "null\n"); obj = JS_NULL; break; case BC_TAG_UNDEFINED: - bc_read_trace(s, "undefined\n"); obj = JS_UNDEFINED; break; case BC_TAG_BOOL_FALSE: case BC_TAG_BOOL_TRUE: - bc_read_trace(s, "%s\n", bc_tag_str[tag]); obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE); break; case BC_TAG_INT32: @@ -33990,7 +35174,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) int32_t val; if (bc_get_sleb128(s, &val)) return JS_EXCEPTION; - bc_read_trace(s, "int32 %d\n", val); + bc_read_trace(s, "%d\n", val); obj = JS_NewInt32(ctx, val); } break; @@ -33999,7 +35183,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) JSFloat64Union u; if (bc_get_u64(s, &u.u64)) return JS_EXCEPTION; - bc_read_trace(s, "float64 %g\n", u.d); + bc_read_trace(s, "%g\n", u.d); obj = __JS_NewFloat64(ctx, u.d); } break; @@ -34013,378 +35197,68 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) } break; case BC_TAG_FUNCTION_BYTECODE: - { - JSFunctionBytecode bc, *b; - uint16_t v16; - uint8_t v8; - int idx, i, local_count; - int function_size, cpool_offset, byte_code_offset; - int closure_var_offset, vardefs_offset; - - if (!s->allow_bytecode) - goto invalid_tag; - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); - - memset(&bc, 0, sizeof(bc)); - bc.header.ref_count = 1; - //bc.gc_header.mark = 0; - - if (bc_get_u16(s, &v16)) - goto fail; - idx = 0; - bc.has_prototype = bc_get_flags(v16, &idx, 1); - bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1); - bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1); - bc.need_home_object = bc_get_flags(v16, &idx, 1); - bc.func_kind = bc_get_flags(v16, &idx, 2); - bc.new_target_allowed = bc_get_flags(v16, &idx, 1); - bc.super_call_allowed = bc_get_flags(v16, &idx, 1); - bc.super_allowed = bc_get_flags(v16, &idx, 1); - bc.arguments_allowed = bc_get_flags(v16, &idx, 1); - bc.has_debug = bc_get_flags(v16, &idx, 1); - bc.backtrace_barrier = bc_get_flags(v16, &idx, 1); - bc.read_only_bytecode = s->is_rom_data; - if (bc_get_u8(s, &v8)) - goto fail; - bc.js_mode = v8; - if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure - goto fail; - if (bc_get_leb128_u16(s, &bc.arg_count)) - goto fail; - if (bc_get_leb128_u16(s, &bc.var_count)) - goto fail; - if (bc_get_leb128_u16(s, &bc.defined_arg_count)) - goto fail; - if (bc_get_leb128_u16(s, &bc.stack_size)) - goto fail; - if (bc_get_leb128_int(s, &bc.closure_var_count)) - goto fail; - if (bc_get_leb128_int(s, &bc.cpool_count)) - goto fail; - if (bc_get_leb128_int(s, &bc.byte_code_len)) - goto fail; - if (bc_get_leb128_int(s, &local_count)) - goto fail; - - if (bc.has_debug) { - function_size = sizeof(*b); - } else { - function_size = offsetof(JSFunctionBytecode, debug); - } - cpool_offset = function_size; - function_size += bc.cpool_count * sizeof(*bc.cpool); - vardefs_offset = function_size; - function_size += local_count * sizeof(*bc.vardefs); - closure_var_offset = function_size; - function_size += bc.closure_var_count * sizeof(*bc.closure_var); - byte_code_offset = function_size; - if (!bc.read_only_bytecode) { - function_size += bc.byte_code_len; - } - - b = js_mallocz(ctx, function_size); - if (!b) - return JS_EXCEPTION; - - memcpy(b, &bc, offsetof(JSFunctionBytecode, debug)); - b->header.ref_count = 1; - add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); - - obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); - -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n"); -#endif - bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n", - b->arg_count, b->var_count, b->defined_arg_count, - b->closure_var_count, b->cpool_count); - bc_read_trace(s, "stack=%d bclen=%d locals=%d\n", - b->stack_size, b->byte_code_len, local_count); - - if (local_count != 0) { - bc_read_trace(s, "vars {\n"); - b->vardefs = (void *)((uint8_t*)b + vardefs_offset); - for(i = 0; i < local_count; i++) { - JSVarDef *vd = &b->vardefs[i]; - if (bc_get_atom(s, &vd->var_name)) - goto fail; - if (bc_get_leb128_int(s, &vd->scope_level)) - goto fail; - if (bc_get_leb128_int(s, &vd->scope_next)) - goto fail; - vd->scope_next--; - if (bc_get_u8(s, &v8)) - goto fail; - idx = 0; - vd->var_kind = bc_get_flags(v8, &idx, 4); - vd->is_func_var = bc_get_flags(v8, &idx, 1); - vd->is_const = bc_get_flags(v8, &idx, 1); - vd->is_lexical = bc_get_flags(v8, &idx, 1); - vd->is_captured = bc_get_flags(v8, &idx, 1); -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n"); -#endif - } - bc_read_trace(s, "}\n"); - } - if (b->closure_var_count != 0) { - bc_read_trace(s, "closure vars {\n"); - b->closure_var = (void *)((uint8_t*)b + closure_var_offset); - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - int var_idx; - if (bc_get_atom(s, &cv->var_name)) - goto fail; - if (bc_get_leb128_int(s, &var_idx)) - goto fail; - cv->var_idx = var_idx; - if (bc_get_u8(s, &v8)) - goto fail; - idx = 0; - cv->is_local = bc_get_flags(v8, &idx, 1); - cv->is_arg = bc_get_flags(v8, &idx, 1); - cv->is_const = bc_get_flags(v8, &idx, 1); - cv->is_lexical = bc_get_flags(v8, &idx, 1); - cv->var_kind = bc_get_flags(v8, &idx, 4); -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n"); -#endif - } - bc_read_trace(s, "}\n"); - } - { - bc_read_trace(s, "bytecode {\n"); - if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len)) - goto fail; - bc_read_trace(s, "}\n"); - } - if (b->has_debug) { - /* read optional debug information */ - bc_read_trace(s, "debug {\n"); - if (bc_get_atom(s, &b->debug.filename)) - goto fail; - if (bc_get_leb128_int(s, &b->debug.line_num)) - goto fail; - if (bc_get_leb128_int(s, &b->debug.pc2line_len)) - goto fail; - if (b->debug.pc2line_len) { - b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len); - if (!b->debug.pc2line_buf) - goto fail; - if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len)) - goto fail; - } -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n"); -#endif - bc_read_trace(s, "}\n"); - } - if (b->cpool_count != 0) { - bc_read_trace(s, "cpool {\n"); - b->cpool = (void *)((uint8_t*)b + cpool_offset); - for(i = 0; i < b->cpool_count; i++) { - JSValue val; - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) - goto fail; - b->cpool[i] = val; - } - bc_read_trace(s, "}\n"); - } - bc_read_trace(s, "}\n"); - b->realm = JS_DupContext(ctx); - } + if (!s->allow_bytecode) + goto invalid_tag; + obj = JS_ReadFunctionTag(s); break; case BC_TAG_MODULE: - { - JSAtom module_name; - int i; - uint8_t v8; - - if (!s->allow_bytecode) - goto invalid_tag; - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); - if (bc_get_atom(s, &module_name)) - goto fail; -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n"); -#endif - m = js_new_module_def(ctx, module_name); - if (!m) - goto fail; - obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); - if (bc_get_leb128_int(s, &m->req_module_entries_count)) - goto fail; - if (m->req_module_entries_count != 0) { - m->req_module_entries_size = m->req_module_entries_count; - m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size); - if (!m->req_module_entries) - goto fail; - for(i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry *rme = &m->req_module_entries[i]; - if (bc_get_atom(s, &rme->module_name)) - goto fail; - } - } - - if (bc_get_leb128_int(s, &m->export_entries_count)) - goto fail; - if (m->export_entries_count != 0) { - m->export_entries_size = m->export_entries_count; - m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size); - if (!m->export_entries) - goto fail; - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (bc_get_u8(s, &v8)) - goto fail; - me->export_type = v8; - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - if (bc_get_leb128_int(s, &me->u.local.var_idx)) - goto fail; - } else { - if (bc_get_leb128_int(s, &me->u.req_module_idx)) - goto fail; - if (bc_get_atom(s, &me->local_name)) - goto fail; - } - if (bc_get_atom(s, &me->export_name)) - goto fail; - } - } - - if (bc_get_leb128_int(s, &m->star_export_entries_count)) - goto fail; - if (m->star_export_entries_count != 0) { - m->star_export_entries_size = m->star_export_entries_count; - m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size); - if (!m->star_export_entries) - goto fail; - for(i = 0; i < m->star_export_entries_count; i++) { - JSStarExportEntry *se = &m->star_export_entries[i]; - if (bc_get_leb128_int(s, &se->req_module_idx)) - goto fail; - } - } - - if (bc_get_leb128_int(s, &m->import_entries_count)) - goto fail; - if (m->import_entries_count != 0) { - m->import_entries_size = m->import_entries_count; - m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size); - if (!m->import_entries) - goto fail; - for(i = 0; i < m->import_entries_count; i++) { - JSImportEntry *mi = &m->import_entries[i]; - if (bc_get_leb128_int(s, &mi->var_idx)) - goto fail; - if (bc_get_atom(s, &mi->import_name)) - goto fail; - if (bc_get_leb128_int(s, &mi->req_module_idx)) - goto fail; - } - } - - m->func_obj = JS_ReadObjectRec(s); - if (JS_IsException(m->func_obj)) - goto fail; - bc_read_trace(s, "}\n"); - } + if (!s->allow_bytecode) + goto invalid_tag; + obj = JS_ReadModule(s); break; case BC_TAG_OBJECT: - { - uint32_t prop_count, i; - JSAtom atom; - JSValue val; - int ret; - - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); - - obj = JS_NewObject(ctx); - if (bc_get_leb128(s, &prop_count)) - goto fail; - for(i = 0; i < prop_count; i++) { - if (bc_get_atom(s, &atom)) - goto fail; -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n"); -#endif - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) { - JS_FreeAtom(ctx, atom); - goto fail; - } - ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E); - JS_FreeAtom(ctx, atom); - if (ret < 0) - goto fail; - } - bc_read_trace(s, "}\n"); - } + obj = JS_ReadObjectTag(s); break; case BC_TAG_ARRAY: case BC_TAG_TEMPLATE_OBJECT: - { - uint32_t len, i; - JSValue val; - int ret, prop_flags; - BOOL is_template; - - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); - - obj = JS_NewArray(ctx); - is_template = (tag == BC_TAG_TEMPLATE_OBJECT); - if (bc_get_leb128(s, &len)) - goto fail; - for(i = 0; i < len; i++) { - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) - goto fail; - if (is_template) - prop_flags = JS_PROP_ENUMERABLE; - else - prop_flags = JS_PROP_C_W_E; - ret = JS_DefinePropertyValueUint32(ctx, obj, i, val, - prop_flags); - if (ret < 0) - goto fail; - } - if (is_template) { - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) - goto fail; - if (!JS_IsUndefined(val)) { - ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0); - if (ret < 0) - goto fail; - } - JS_PreventExtensions(ctx, obj); - } - bc_read_trace(s, "}\n"); - } + obj = JS_ReadArray(s, tag); + break; + case BC_TAG_TYPED_ARRAY: + obj = JS_ReadTypedArray(s); + break; + case BC_TAG_ARRAY_BUFFER: + obj = JS_ReadArrayBuffer(s); + break; + case BC_TAG_SHARED_ARRAY_BUFFER: + if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup) + goto invalid_tag; + obj = JS_ReadSharedArrayBuffer(s); + break; + case BC_TAG_DATE: + obj = JS_ReadDate(s); + break; + case BC_TAG_OBJECT_VALUE: + obj = JS_ReadObjectValue(s); break; #ifdef CONFIG_BIGNUM case BC_TAG_BIG_INT: case BC_TAG_BIG_FLOAT: case BC_TAG_BIG_DECIMAL: obj = JS_ReadBigNum(s, tag); - if (JS_IsException(obj)) - goto fail; break; #endif + case BC_TAG_OBJECT_REFERENCE: + { + uint32_t val; + if (!s->allow_reference) + return JS_ThrowSyntaxError(ctx, "object references are not allowed"); + if (bc_get_leb128(s, &val)) + return JS_EXCEPTION; + bc_read_trace(s, "%u\n", val); + if (val >= s->objects_count) { + return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)", + val, s->objects_count); + } + obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val])); + } + break; default: invalid_tag: return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)", tag, (unsigned int)(s->ptr - s->buf_start)); } + bc_read_trace(s, "}\n"); return obj; - fail: - if (m) { - js_free_module_def(ctx, m); - } else { - JS_FreeValue(ctx, obj); - } - return JS_EXCEPTION; } static int JS_ReadObjectAtoms(BCReaderState *s) @@ -34437,10 +35311,11 @@ static void bc_reader_free(BCReaderState *s) } js_free(s->ctx, s->idx_to_atom); } + js_free(s->ctx, s->objects); } JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, - int flags) + int flags) { BCReaderState ss, *s = &ss; JSValue obj; @@ -34455,6 +35330,8 @@ JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, s->ptr = buf; s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0); s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0); + s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0); + s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0); if (s->allow_bytecode) s->first_atom = JS_ATOM_END; else @@ -34944,7 +35821,7 @@ static __exception int JS_ObjectDefineProperties(JSContext *ctx, if (JS_IsException(props)) return -1; p = JS_VALUE_GET_OBJ(props); - if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK) < 0) + if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0) goto exception; for(i = 0; i < len; i++) { JS_FreeValue(ctx, desc); @@ -35021,7 +35898,7 @@ static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val, JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED) return JS_ThrowTypeErrorNotAnObject(ctx); } - return JS_DupValue(ctx, JS_GetPrototype(ctx, val)); + return JS_GetPrototype(ctx, val); } static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val, @@ -35815,7 +36692,7 @@ static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val) val = JS_ToObject(ctx, this_val); if (JS_IsException(val)) return val; - ret = JS_DupValue(ctx, JS_GetPrototype(ctx, val)); + ret = JS_GetPrototype(ctx, val); JS_FreeValue(ctx, val); return ret; } @@ -35836,9 +36713,9 @@ static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val, static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - JSValue obj; + JSValue obj, v1; JSValueConst v; - int max_depth = 1000, res = -1; + int res; v = argv[0]; if (!JS_IsObject(v)) @@ -35846,27 +36723,29 @@ static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val, obj = JS_ToObject(ctx, this_val); if (JS_IsException(obj)) return JS_EXCEPTION; - while (--max_depth > 0) { - v = JS_GetPrototype(ctx, v); - if (JS_IsException(v)) + v1 = JS_DupValue(ctx, v); + for(;;) { + v1 = JS_GetPrototypeFree(ctx, v1); + if (JS_IsException(v1)) goto exception; - if (JS_IsNull(v)) { + if (JS_IsNull(v1)) { res = FALSE; break; } - if (js_strict_eq2(ctx, JS_DupValue(ctx, obj), JS_DupValue(ctx, v), - JS_EQ_STRICT)) { + if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) { res = TRUE; break; } + /* avoid infinite loop (possible with proxies) */ + if (js_poll_interrupts(ctx)) + goto exception; } + JS_FreeValue(ctx, v1); JS_FreeValue(ctx, obj); - if (res < 0) - return JS_ThrowInternalError(ctx, "prototype chain cycle"); - else - return JS_NewBool(ctx, res); + return JS_NewBool(ctx, res); exception: + JS_FreeValue(ctx, v1); JS_FreeValue(ctx, obj); return JS_EXCEPTION; } @@ -35906,7 +36785,6 @@ static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int setter) { JSValue obj, res = JS_EXCEPTION; - JSValueConst v; JSAtom prop = JS_ATOM_NULL; JSPropertyDescriptor desc; int has_prop; @@ -35918,8 +36796,8 @@ static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val, if (unlikely(prop == JS_ATOM_NULL)) goto exception; - for (v = obj;;) { - has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(v), prop); + for (;;) { + has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); if (has_prop < 0) goto exception; if (has_prop) { @@ -35930,13 +36808,16 @@ static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val, js_free_desc(ctx, &desc); break; } - v = JS_GetPrototype(ctx, v); - if (JS_IsException(v)) + obj = JS_GetPrototypeFree(ctx, obj); + if (JS_IsException(obj)) goto exception; - if (JS_IsNull(v)) { + if (JS_IsNull(obj)) { res = JS_UNDEFINED; break; } + /* avoid infinite loop (possible with proxies) */ + if (js_poll_interrupts(ctx)) + goto exception; } exception: @@ -36002,59 +36883,12 @@ static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val, return JS_UNDEFINED; } -/* insert a '\n' at unicode character position 'pos' in the source of - 'func_obj' */ -static int patch_function_constructor_source(JSContext *ctx, - JSValueConst func_obj, - size_t pos) -{ - JSObject *p; - JSFunctionBytecode *b; - char *r, *r_end, *new_source; - int c; - size_t idx, len; - - if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) - return 0; - p = JS_VALUE_GET_OBJ(func_obj); - if (!js_class_has_bytecode(p->class_id)) - return 0; - b = p->u.func.function_bytecode; - if (!b->has_debug) - return 0; - r = b->debug.source; - r_end = b->debug.source + b->debug.source_len; - idx = 0; - while (r < r_end) { - if (idx == pos) { - /* add the '\n' */ - new_source = js_realloc(ctx, b->debug.source, - b->debug.source_len + 2); - if (!new_source) - return -1; - len = r - b->debug.source; - memmove(new_source + len + 1, new_source + len, - b->debug.source_len + 1 - len); - new_source[len] = '\n'; - b->debug.source = new_source; - b->debug.source_len++; - break; - } - c = unicode_from_utf8((const uint8_t *)r, UTF8_CHAR_LEN_MAX, - (const uint8_t **)&r); - if (c < 0) - break; - idx++; - } - return 0; -} - /* XXX: add a specific eval mode so that Function("}), ({") is rejected */ static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic) { JSFunctionKindEnum func_kind = magic; - int i, n, ret, func_start_pos; + int i, n, ret; JSValue s, proto, obj = JS_UNDEFINED; StringBuffer b_s, *b = &b_s; @@ -36079,13 +36913,7 @@ static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, if (string_buffer_concat_value(b, argv[i])) goto fail; } - string_buffer_puts8(b, "\n) {"); - /* Annex B HTML comments: We don't add a '\n' after "{" so that - "-->" is not considered as an HTML comment. It is necessary - because in the spec the function body is parsed separately. */ - /* XXX: find a simpler way or be deliberately incompatible to - simplify the code ? */ - func_start_pos = b->len - 1; /* the leading '(' is not in the source */ + string_buffer_puts8(b, "\n) {\n"); if (n >= 0) { if (string_buffer_concat_value(b, argv[n])) goto fail; @@ -36099,8 +36927,6 @@ static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, JS_FreeValue(ctx, s); if (JS_IsException(obj)) goto fail1; - if (patch_function_constructor_source(ctx, obj, func_start_pos) < 0) - goto fail1; if (!JS_IsUndefined(new_target)) { /* set the prototype */ proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); @@ -36315,27 +37141,22 @@ static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val, JSValue name; const char *pref, *suff; - if (p->is_class) { - pref = "class "; - suff = " {\n [native code]\n}"; - } else { - switch(func_kind) { - default: - case JS_FUNC_NORMAL: - pref = "function "; - break; - case JS_FUNC_GENERATOR: - pref = "function *"; - break; - case JS_FUNC_ASYNC: - pref = "async function "; - break; - case JS_FUNC_ASYNC_GENERATOR: - pref = "async function *"; - break; - } - suff = "() {\n [native code]\n}"; + switch(func_kind) { + default: + case JS_FUNC_NORMAL: + pref = "function "; + break; + case JS_FUNC_GENERATOR: + pref = "function *"; + break; + case JS_FUNC_ASYNC: + pref = "async function "; + break; + case JS_FUNC_ASYNC_GENERATOR: + pref = "async function *"; + break; } + suff = "() {\n [native code]\n}"; name = JS_GetProperty(ctx, this_val, JS_ATOM_name); if (JS_IsUndefined(name)) name = JS_AtomToString(ctx, JS_ATOM_empty_string); @@ -36435,12 +37256,6 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, if (JS_IsException(obj)) return obj; if (magic == JS_AGGREGATE_ERROR) { - JSObject *p; - JSValue error_list = iterator_to_array(ctx, argv[0]); - if (JS_IsException(error_list)) - goto exception; - p = JS_VALUE_GET_OBJ(obj); - p->u.object_data = error_list; message = argv[1]; } else { message = argv[0]; @@ -36448,17 +37263,26 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, if (!JS_IsUndefined(message)) { msg = JS_ToString(ctx, message); - if (unlikely(JS_IsException(msg))) { - exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } + if (unlikely(JS_IsException(msg))) + goto exception; JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); } + + if (magic == JS_AGGREGATE_ERROR) { + JSValue error_list = iterator_to_array(ctx, argv[0]); + if (JS_IsException(error_list)) + goto exception; + JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } + /* skip the Error() function in the backtrace */ build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL); return obj; + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; } static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val, @@ -36498,56 +37322,22 @@ static const JSCFunctionListEntry js_error_proto_funcs[] = { /* AggregateError */ -/* used by C code. 'errors' must be a fast array. */ +/* used by C code. */ static JSValue js_aggregate_error_constructor(JSContext *ctx, JSValueConst errors) { JSValue obj; - JSObject *p; obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[JS_AGGREGATE_ERROR], JS_CLASS_ERROR); if (JS_IsException(obj)) return obj; - p = JS_VALUE_GET_OBJ(obj); - p->u.object_data = JS_DupValue(ctx, errors); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); return obj; } -static JSValue js_aggregate_error_errors(JSContext *ctx, JSValueConst this_val) -{ - JSObject *p; - JSValue r, *arr; - uint32_t len, i; - - if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) - goto invalid_type; - p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id != JS_CLASS_ERROR) - goto invalid_type; - if (!js_get_fast_array(ctx, p->u.object_data, &arr, &len)) { - invalid_type: - return JS_ThrowTypeError(ctx, "not an AggregateError"); - } - r = JS_NewArray(ctx); - if (JS_IsException(r)) - goto exception; - for(i = 0; i < len; i++) { - if (JS_DefinePropertyValueInt64(ctx, r, i, JS_DupValue(ctx, arr[i]), - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - } - return r; - exception: - JS_FreeValue(ctx, r); - return JS_EXCEPTION; -} - -static const JSCFunctionListEntry js_aggregate_error_proto_funcs[] = { - JS_CGETSET_DEF("errors", js_aggregate_error_errors, NULL), -}; - /* Array */ static int JS_CopySubArray(JSContext *ctx, @@ -39130,22 +39920,25 @@ static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val, p1 = JS_VALUE_GET_STRING(v); len = p->len; v_len = p1->len; - pos = (magic & 2) ? len : 0; + pos = (magic == 2) ? len : 0; if (argc > 1 && !JS_IsUndefined(argv[1])) { if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) goto fail; } len -= v_len; - start = pos; - stop = len; - if (magic & 1) { - stop = pos; - } - if (magic & 2) { - pos -= v_len; + ret = 0; + if (magic == 0) { + start = pos; + stop = len; + } else { + if (magic == 1) { + if (pos > len) + goto done; + } else { + pos -= v_len; + } start = stop = pos; } - ret = 0; if (start >= 0 && start <= stop) { for (i = start;; i++) { if (!string_cmp(p, p1, i, 0, v_len)) { @@ -39156,6 +39949,7 @@ static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val, break; } } + done: JS_FreeValue(ctx, str); JS_FreeValue(ctx, v); return JS_NewBool(ctx, ret); @@ -40372,46 +41166,23 @@ static double js_math_round(double a) static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - double r, a, b; + double r, a; int i; - if (argc == 2) { - /* use the more precise built-in function when possible */ - if (JS_ToFloat64(ctx, &a, argv[0])) - return JS_EXCEPTION; - if (JS_ToFloat64(ctx, &b, argv[1])) - return JS_EXCEPTION; - r = hypot(a, b); - } else if (argc == 0) { - r = 0; - } else { - double *tab, a_max; - tab = js_malloc(ctx, sizeof(tab[0]) * argc); - if (!tab) + r = 0; + if (argc > 0) { + if (JS_ToFloat64(ctx, &r, argv[0])) return JS_EXCEPTION; - /* avoid overflow by taking the maximum */ - a_max = 0; - for(i = 0; i < argc; i++) { - if (JS_ToFloat64(ctx, &a, argv[i])) { - js_free(ctx, tab); - return JS_EXCEPTION; - } - a = fabs(a); - tab[i] = a; - if (a > a_max) - a_max = a; - } - if (a_max == 0 || !isfinite(a_max)) { - r = a_max; + if (argc == 1) { + r = fabs(r); } else { - r = 0; - for(i = 0; i < argc; i++) { - a = tab[i] / a_max; - r += a * a; + /* use the built-in function to minimize precision loss */ + for (i = 1; i < argc; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + r = hypot(r, a); } - r = a_max * sqrt(r); } - js_free(ctx, tab); } return JS_NewFloat64(ctx, r); } @@ -42093,58 +42864,69 @@ void JS_AddIntrinsicRegExp(JSContext *ctx) /* JSON */ -/* XXX: this parser is less strict than the JSON standard because we - reuse the Javascript tokenizer. It could be improved by adding a - specific JSON parse flag. */ +static int json_parse_expect(JSParseState *s, int tok) +{ + if (s->token.val != tok) { + /* XXX: dump token correctly in all cases */ + return js_parse_error(s, "expecting '%c'", tok); + } + return json_next_token(s); +} + static JSValue json_parse_value(JSParseState *s) { JSContext *ctx = s->ctx; JSValue val = JS_NULL; - BOOL is_neg; int ret; switch(s->token.val) { case '{': { - JSValue prop_val, prop_str; - - if (next_token(s)) + JSValue prop_val; + JSAtom prop_name; + + if (json_next_token(s)) goto fail; val = JS_NewObject(ctx); if (JS_IsException(val)) goto fail; if (s->token.val != '}') { for(;;) { - if (s->token.val != TOK_STRING) { + if (s->token.val == TOK_STRING) { + prop_name = JS_ValueToAtom(ctx, s->token.u.str.str); + if (prop_name == JS_ATOM_NULL) + goto fail; + } else if (s->ext_json && s->token.val == TOK_IDENT) { + prop_name = JS_DupAtom(ctx, s->token.u.ident.atom); + } else { js_parse_error(s, "expecting property name"); goto fail; } - prop_str = JS_DupValue(ctx, s->token.u.str.str); - if (next_token(s)) { - JS_FreeValue(ctx, prop_str); - goto fail; - } - if (js_parse_expect(s, ':')) { - JS_FreeValue(ctx, prop_str); - goto fail; - } + if (json_next_token(s)) + goto fail1; + if (json_parse_expect(s, ':')) + goto fail1; prop_val = json_parse_value(s); if (JS_IsException(prop_val)) { - JS_FreeValue(ctx, prop_str); + fail1: + JS_FreeAtom(ctx, prop_name); goto fail; } - ret = JS_DefinePropertyValueValue(ctx, val, prop_str, - prop_val, JS_PROP_C_W_E); + ret = JS_DefinePropertyValue(ctx, val, prop_name, + prop_val, JS_PROP_C_W_E); + JS_FreeAtom(ctx, prop_name); if (ret < 0) goto fail; if (s->token.val != ',') break; - if (next_token(s)) + if (json_next_token(s)) goto fail; + if (s->ext_json && s->token.val == '}') + break; } } - if (js_parse_expect(s, '}')) + if (json_parse_expect(s, '}')) goto fail; } break; @@ -42153,7 +42935,7 @@ static JSValue json_parse_value(JSParseState *s) JSValue el; uint32_t idx; - if (next_token(s)) + if (json_next_token(s)) goto fail; val = JS_NewArray(ctx); if (JS_IsException(val)) @@ -42169,52 +42951,41 @@ static JSValue json_parse_value(JSParseState *s) goto fail; if (s->token.val != ',') break; - if (next_token(s)) + if (json_next_token(s)) goto fail; idx++; + if (s->ext_json && s->token.val == ']') + break; } } - if (js_parse_expect(s, ']')) + if (json_parse_expect(s, ']')) goto fail; } break; case TOK_STRING: val = JS_DupValue(ctx, s->token.u.str.str); - if (next_token(s)) + if (json_next_token(s)) goto fail; break; case TOK_NUMBER: - is_neg = 0; - goto number; - case '-': - if (next_token(s)) - goto fail; - if (s->token.val != TOK_NUMBER) { - js_parse_error(s, "number expected"); - goto fail; - } - is_neg = 1; - number: val = s->token.u.num.val; - if (is_neg) { - double d; - JS_ToFloat64(ctx, &d, val); /* no exception possible */ - val = JS_NewFloat64(ctx, -d); - } - if (next_token(s)) - goto fail; - break; - case TOK_FALSE: - case TOK_TRUE: - val = JS_NewBool(ctx, s->token.val - TOK_FALSE); - if (next_token(s)) + if (json_next_token(s)) goto fail; break; - case TOK_NULL: - if (next_token(s)) + case TOK_IDENT: + if (s->token.u.ident.atom == JS_ATOM_false || + s->token.u.ident.atom == JS_ATOM_true) { + val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true)); + } else if (s->token.u.ident.atom == JS_ATOM_null) { + val = JS_NULL; + } else { + goto def_token; + } + if (json_next_token(s)) goto fail; break; default: + def_token: if (s->token.val == TOK_EOF) { js_parse_error(s, "unexpected end of input"); } else { @@ -42229,15 +43000,15 @@ static JSValue json_parse_value(JSParseState *s) return JS_EXCEPTION; } -JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, - const char *filename) +JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename, int flags) { JSParseState s1, *s = &s1; - JSValue val; + JSValue val = JS_UNDEFINED; js_parse_init(ctx, s, buf, buf_len, filename); - - if (next_token(s)) + s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0); + if (json_next_token(s)) goto fail; val = json_parse_value(s); if (JS_IsException(val)) @@ -42248,10 +43019,17 @@ JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, } return val; fail: + JS_FreeValue(ctx, val); free_token(s, &s->token); return JS_EXCEPTION; } +JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename) +{ + return JS_ParseJSON2(ctx, buf, buf_len, filename, 0); +} + static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder, JSAtom name, JSValueConst reviver) { @@ -42960,7 +43738,6 @@ static void js_proxy_finalizer(JSRuntime *rt, JSValue val) if (s) { JS_FreeValueRT(rt, s->target); JS_FreeValueRT(rt, s->handler); - JS_FreeValueRT(rt, s->proto); js_free_rt(rt, s); } } @@ -42972,7 +43749,6 @@ static void js_proxy_mark(JSRuntime *rt, JSValueConst val, if (s) { JS_MarkValue(rt, s->target, mark_func); JS_MarkValue(rt, s->handler, mark_func); - JS_MarkValue(rt, s->proto, mark_func); } } @@ -43007,17 +43783,12 @@ static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod, return s; } -static JSValueConst js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) +static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) { JSProxyData *s; - JSValue method, ret; - JSValueConst proto1; + JSValue method, ret, proto1; int res; - /* must check for timeout to avoid infinite loop in instanceof */ - if (js_poll_interrupts(ctx)) - return JS_EXCEPTION; - s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf); if (!s) return JS_EXCEPTION; @@ -43043,22 +43814,22 @@ static JSValueConst js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) return JS_EXCEPTION; } if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) { + JS_FreeValue(ctx, proto1); fail: JS_FreeValue(ctx, ret); return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); } + JS_FreeValue(ctx, proto1); } - /* store the prototype in the proxy so that its refcount is at least 1 */ - set_value(ctx, &s->proto, ret); - return (JSValueConst)ret; + return ret; } static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, JSValueConst proto_val, BOOL throw_flag) { JSProxyData *s; - JSValue method, ret; - JSValueConst args[2], proto1; + JSValue method, ret, proto1; + JSValueConst args[2]; BOOL res; int res2; @@ -43089,9 +43860,11 @@ static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, if (JS_IsException(proto1)) return -1; if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) { + JS_FreeValue(ctx, proto1); JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); return -1; } + JS_FreeValue(ctx, proto1); } return TRUE; } @@ -43829,7 +44602,6 @@ static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val, } s->target = JS_DupValue(ctx, target); s->handler = JS_DupValue(ctx, handler); - s->proto = JS_NULL; s->is_func = JS_IsFunction(ctx, target); s->is_revoked = FALSE; JS_SetOpaque(obj, s); @@ -45414,10 +46186,10 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx, static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { - JSValue result_promise, resolving_funcs[2], iter, item, next_promise, ret; + JSValue result_promise, resolving_funcs[2], item, next_promise, ret; JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED; JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element; - JSValue promise_resolve = JS_UNDEFINED; + JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED; JSValueConst then_args[2], resolve_element_data[5]; BOOL done; int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any); @@ -45427,6 +46199,10 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); if (JS_IsException(result_promise)) return result_promise; + promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); + if (JS_IsException(promise_resolve) || + check_function(ctx, promise_resolve)) + goto fail_reject; iter = JS_GetIterator(ctx, argv[0], FALSE); if (JS_IsException(iter)) { JSValue error; @@ -45454,11 +46230,6 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) goto fail_reject; - promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); - if (JS_IsException(promise_resolve) || - check_function(ctx, promise_resolve)) - goto fail_reject1; - index = 0; for(;;) { /* XXX: conformance: should close the iterator if error on 'done' @@ -45560,8 +46331,8 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - JSValue result_promise, resolving_funcs[2], iter, item, next_promise, ret; - JSValue next_method = JS_UNDEFINED; + JSValue result_promise, resolving_funcs[2], item, next_promise, ret; + JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED; JSValue promise_resolve = JS_UNDEFINED; BOOL done; @@ -45570,6 +46341,10 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); if (JS_IsException(result_promise)) return result_promise; + promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); + if (JS_IsException(promise_resolve) || + check_function(ctx, promise_resolve)) + goto fail_reject; iter = JS_GetIterator(ctx, argv[0], FALSE); if (JS_IsException(iter)) { JSValue error; @@ -45586,11 +46361,6 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, if (JS_IsException(next_method)) goto fail_reject; - promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); - if (JS_IsException(promise_resolve) || - check_function(ctx, promise_resolve)) - goto fail_reject1; - for(;;) { /* XXX: conformance: should close the iterator if error on 'done' access, but not on 'value' access */ @@ -49385,9 +50155,6 @@ static void JS_AddIntrinsicBasicObjects(JSContext *ctx) JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); ctx->native_error_proto[i] = proto; } - JS_SetPropertyFunctionList(ctx, ctx->native_error_proto[JS_AGGREGATE_ERROR], - js_aggregate_error_proto_funcs, - countof(js_aggregate_error_proto_funcs)); /* the array prototype is an array */ ctx->class_proto[JS_CLASS_ARRAY] = @@ -49621,6 +50388,7 @@ static JSValue js_array_buffer_constructor3(JSContext *ctx, JSFreeArrayBufferDataFunc *free_func, void *opaque, BOOL alloc_flag) { + JSRuntime *rt = ctx->rt; JSValue obj; JSArrayBuffer *abuf = NULL; @@ -49637,11 +50405,24 @@ static JSValue js_array_buffer_constructor3(JSContext *ctx, goto fail; abuf->byte_length = len; if (alloc_flag) { - /* the allocation must be done after the object creation */ - abuf->data = js_mallocz(ctx, max_int(len, 1)); - if (!abuf->data) - goto fail; + if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && + rt->sab_funcs.sab_alloc) { + abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque, + max_int(len, 1)); + if (!abuf->data) + goto fail; + memset(abuf->data, 0, len); + } else { + /* the allocation must be done after the object creation */ + abuf->data = js_mallocz(ctx, max_int(len, 1)); + if (!abuf->data) + goto fail; + } } else { + if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && + rt->sab_funcs.sab_dup) { + rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf); + } abuf->data = buf; } init_list_head(&abuf->array_list); @@ -49731,8 +50512,12 @@ static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val) array finalizers using it, so abuf->array_list is not necessarily empty. */ // assert(list_empty(&abuf->array_list)); - if (abuf->free_func) - abuf->free_func(rt, abuf->opaque, abuf->data); + if (abuf->shared && rt->sab_funcs.sab_free) { + rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data); + } else { + if (abuf->free_func) + abuf->free_func(rt, abuf->opaque, abuf->data); + } js_free_rt(rt, abuf); } } @@ -50208,11 +50993,6 @@ static JSValue js_typed_array___getLength(JSContext *ctx, } #endif -static JSValue js_typed_array_constructor(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int classid); - static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor, int argc, JSValueConst *argv) { @@ -51967,9 +52747,10 @@ typedef enum AtomicsOpEnum { } AtomicsOpEnum; static void *js_atomics_get_ptr(JSContext *ctx, + JSArrayBuffer **pabuf, int *psize_log2, JSClassID *pclass_id, JSValueConst obj, JSValueConst idx_val, - BOOL is_waitable) + int is_waitable) { JSObject *p; JSTypedArray *ta; @@ -52004,18 +52785,27 @@ static void *js_atomics_get_ptr(JSContext *ctx, ta = p->u.typed_array; abuf = ta->buffer->u.array_buffer; if (!abuf->shared) { - JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray"); - return NULL; + if (is_waitable == 2) { + JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray"); + return NULL; + } + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return NULL; + } } if (JS_ToIndex(ctx, &idx, idx_val)) { return NULL; } + /* if the array buffer is detached, p->u.array.count = 0 */ if (idx >= p->u.array.count) { JS_ThrowRangeError(ctx, "out-of-bound access"); return NULL; } size_log2 = typed_array_size_log2(p->class_id); ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2); + if (pabuf) + *pabuf = abuf; if (psize_log2) *psize_log2 = size_log2; if (pclass_id) @@ -52036,17 +52826,18 @@ static JSValue js_atomics_op(JSContext *ctx, void *ptr; JSValue ret; JSClassID class_id; + JSArrayBuffer *abuf; - ptr = js_atomics_get_ptr(ctx, &size_log2, &class_id, - argv[0], argv[1], FALSE); + ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id, + argv[0], argv[1], 0); if (!ptr) return JS_EXCEPTION; rep_val = 0; if (op == ATOMICS_OP_LOAD) { v = 0; - } else + } else { #ifdef CONFIG_BIGNUM - if (size_log2 == 3) { + if (size_log2 == 3) { int64_t v64; if (JS_ToBigInt64(ctx, &v64, argv[2])) return JS_EXCEPTION; @@ -52056,19 +52847,23 @@ static JSValue js_atomics_op(JSContext *ctx, return JS_EXCEPTION; rep_val = v64; } - } else + } else #endif - { - uint32_t v32; - if (JS_ToUint32(ctx, &v32, argv[2])) - return JS_EXCEPTION; - v = v32; - if (op == ATOMICS_OP_COMPARE_EXCHANGE) { - if (JS_ToUint32(ctx, &v32, argv[3])) - return JS_EXCEPTION; - rep_val = v32; - } + { + uint32_t v32; + if (JS_ToUint32(ctx, &v32, argv[2])) + return JS_EXCEPTION; + v = v32; + if (op == ATOMICS_OP_COMPARE_EXCHANGE) { + if (JS_ToUint32(ctx, &v32, argv[3])) + return JS_EXCEPTION; + rep_val = v32; + } + } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); } + switch(op | (size_log2 << 3)) { #ifdef CONFIG_BIGNUM @@ -52195,8 +52990,10 @@ static JSValue js_atomics_store(JSContext *ctx, int size_log2; void *ptr; JSValue ret; + JSArrayBuffer *abuf; - ptr = js_atomics_get_ptr(ctx, &size_log2, NULL, argv[0], argv[1], FALSE); + ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL, + argv[0], argv[1], 0); if (!ptr) return JS_EXCEPTION; #ifdef CONFIG_BIGNUM @@ -52209,6 +53006,8 @@ static JSValue js_atomics_store(JSContext *ctx, JS_FreeValue(ctx, ret); return JS_EXCEPTION; } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); atomic_store((_Atomic(uint64_t) *)ptr, v64); } else #endif @@ -52222,6 +53021,8 @@ static JSValue js_atomics_store(JSContext *ctx, JS_FreeValue(ctx, ret); return JS_EXCEPTION; } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); switch(size_log2) { case 0: atomic_store((_Atomic(uint8_t) *)ptr, v); @@ -52278,7 +53079,8 @@ static JSValue js_atomics_wait(JSContext *ctx, int ret, size_log2, res; double d; - ptr = js_atomics_get_ptr(ctx, &size_log2, NULL, argv[0], argv[1], TRUE); + ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL, + argv[0], argv[1], 2); if (!ptr) return JS_EXCEPTION; #ifdef CONFIG_BIGNUM @@ -52357,8 +53159,9 @@ static JSValue js_atomics_notify(JSContext *ctx, int32_t count, n; void *ptr; JSAtomicsWaiter *waiter; + JSArrayBuffer *abuf; - ptr = js_atomics_get_ptr(ctx, NULL, NULL, argv[0], argv[1], TRUE); + ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1); if (!ptr) return JS_EXCEPTION; @@ -52368,9 +53171,11 @@ static JSValue js_atomics_notify(JSContext *ctx, if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0)) return JS_EXCEPTION; } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); n = 0; - if (count > 0) { + if (abuf->shared && count > 0) { pthread_mutex_lock(&js_atomics_mutex); init_list_head(&waiter_list); list_for_each_safe(el, el1, &js_atomics_waiter_list) { |