From addb38da658f79febbd64e39c0fe3e68decbf417 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Wed, 21 Jan 2026 19:38:38 -0600 Subject: [PATCH] intrinsic arrays --- source/cell.c | 24 +- source/quickjs.c | 1597 ++++++++++------------------------------------ source/quickjs.h | 30 +- 3 files changed, 358 insertions(+), 1293 deletions(-) diff --git a/source/cell.c b/source/cell.c index 2a2ca434..19eab92b 100644 --- a/source/cell.c +++ b/source/cell.c @@ -283,8 +283,30 @@ int uncaught_exception(JSContext *js, JSValue v) return 1; } - JSValue exp = JS_GetException(js); + + JSValue message = JS_GetPropertyStr(js, exp, "message"); + const char *msg_str = JS_ToCString(js, message); + if (msg_str) { + printf("Exception: %s\n", msg_str); + JS_FreeCString(js, msg_str); + } + JS_FreeValue(js, message); + + JSValue stack = JS_GetPropertyStr(js, exp, "stack"); + const char *stack_str = JS_ToCString(js, stack); + if (stack_str) { + printf("Stack:\n%s\n", stack_str); + JS_FreeCString(js, stack_str); + } + JS_FreeValue(js, stack); + + JS_FreeValue(js, exp); + JS_FreeValue(js, v); + return 0; + + + exp = JS_GetException(js); if (JS_IsNull(rt->on_exception)) { const char *str = JS_ToCString(js, exp); if (str) { diff --git a/source/quickjs.c b/source/quickjs.c index 0a79ae65..9e3fde3c 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -170,7 +170,6 @@ static inline JS_BOOL JS_VALUE_IS_NUMBER(JSValue v) enum { /* classid tag */ /* union usage | properties */ JS_CLASS_OBJECT = 1, /* must be first */ - JS_CLASS_ARRAY, /* u.array | length */ JS_CLASS_ERROR, JS_CLASS_C_FUNCTION, /* u.cfunc */ JS_CLASS_BYTECODE_FUNCTION, /* u.func */ @@ -296,8 +295,6 @@ struct JSClass { JSClassFinalizer *finalizer; JSClassGCMark *gc_mark; JSClassCall *call; - /* pointers for exotic behavior, can be NULL if none are present */ - const JSClassExoticMethods *exotic; }; #define JS_MODE_BACKTRACE_BARRIER (1 << 3) /* stop backtrace before this frame */ @@ -368,6 +365,7 @@ typedef enum { JS_GC_OBJ_TYPE_SHAPE, JS_GC_OBJ_TYPE_VAR_REF, JS_GC_OBJ_TYPE_JS_CONTEXT, + JS_GC_OBJ_TYPE_ARRAY, } JSGCObjectTypeEnum; /* header for GC objects. GC objects are C data structures with a @@ -516,6 +514,24 @@ typedef struct JSStringRope { JSValue right; /* might be the empty string */ } JSStringRope; +/* Intrinsic array type - not an object, a real cell type */ +typedef struct JSArray { + JSGCObjectHeader header; /* must come first */ + uint32_t len; /* current length */ + uint32_t cap; /* allocated capacity */ + JSValue *values; /* array of values */ +} JSArray; + +typedef struct JSRecord { + +} JSRecord; + +typedef struct JSFunction { + +} JSFunction; + +#define JS_VALUE_GET_ARRAY(v) ((JSArray *)JS_VALUE_GET_PTR(v)) + typedef struct JSClosureVar { uint8_t is_local : 1; uint8_t is_arg : 1; @@ -753,8 +769,6 @@ struct JSObject { uint8_t extensible : 1; uint8_t free_mark : 1; /* only used when freeing objects with cycles */ - uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ - uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY and typed arrays) */ uint8_t has_immutable_prototype : 1; /* cannot modify the prototype */ uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ uint16_t class_id; /* see JS_CLASS_x */ @@ -778,16 +792,6 @@ struct JSObject { uint8_t cproto; int16_t magic; } cfunc; - /* array part for fast arrays */ - struct { /* JS_CLASS_ARRAY */ - union { - uint32_t size; /* JS_CLASS_ARRAY */ - } u1; - union { - JSValue *values; /* JS_CLASS_ARRAY */ - } u; - uint32_t count; /* <= 2^31-1 */ - } array; /* 12/20 bytes */ JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ } u; }; @@ -874,6 +878,7 @@ static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); static void js_array_finalizer(JSRuntime *rt, JSValue val); static void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); +static void free_array(JSRuntime *rt, JSArray *arr); static void js_c_function_finalizer(JSRuntime *rt, JSValue val); static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val); @@ -968,7 +973,6 @@ static void remove_gc_object(JSGCObjectHeader *h); static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); static void JS_RunGCInternal(JSRuntime *rt); -static int js_string_find_invalid_codepoint(JSString *p); static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); @@ -1158,7 +1162,6 @@ 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, NULL, NULL }, /* JS_CLASS_ERROR */ { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */ { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */ @@ -2990,7 +2993,6 @@ static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, cl->finalizer = class_def->finalizer; cl->gc_mark = class_def->gc_mark; cl->call = class_def->call; - cl->exotic = class_def->exotic; return 0; } @@ -4680,8 +4682,6 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas p->class_id = class_id; p->extensible = TRUE; p->free_mark = 0; - p->is_exotic = 0; - p->fast_array = 0; p->has_immutable_prototype = 0; p->tmp_mark = 0; p->object_key_atom = JS_ATOM_NULL; @@ -4698,40 +4698,14 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas switch(class_id) { case JS_CLASS_OBJECT: break; - case JS_CLASS_ARRAY: - { - JSProperty *pr; - p->is_exotic = 1; - p->fast_array = 1; - p->u.array.u.values = NULL; - p->u.array.count = 0; - p->u.array.u1.size = 0; - /* the length property is always the first one */ - if (likely(sh == ctx->array_shape)) { - pr = &p->prop[0]; - } else { - /* only used for the first array */ - /* cannot fail */ - pr = add_property(ctx, p, JS_ATOM_length, - JS_PROP_WRITABLE | JS_PROP_LENGTH); - } - pr->u.value = JS_NewInt32(ctx, 0); - } - break; case JS_CLASS_C_FUNCTION: p->prop[0].u.value = JS_NULL; break; case JS_CLASS_REGEXP: p->u.regexp.pattern = NULL; p->u.regexp.bytecode = NULL; - if (ctx->rt->class_array[class_id].exotic) { - p->is_exotic = 1; - } break; default: - if (ctx->rt->class_array[class_id].exotic) { - p->is_exotic = 1; - } break; } p->header.ref_count = 1; @@ -4776,10 +4750,38 @@ JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto) return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); } +/* Create an intrinsic array with specified capacity */ +static JSValue JS_NewArrayLen(JSContext *ctx, uint32_t len) +{ + JSRuntime *rt = ctx->rt; + JSArray *arr; + + arr = js_mallocz(ctx, sizeof(JSArray)); + if (!arr) + return JS_EXCEPTION; + arr->header.ref_count = 1; + arr->len = len; + arr->cap = len > 0 ? len : JS_ARRAY_INITIAL_SIZE; + if (arr->cap > 0) { + arr->values = js_mallocz(ctx, sizeof(JSValue) * arr->cap); + if (!arr->values) { + js_free(ctx, arr); + return JS_EXCEPTION; + } + /* Initialize all values to null */ + for (uint32_t i = 0; i < len; i++) { + arr->values[i] = JS_NULL; + } + } else { + arr->values = NULL; + } + add_gc_object(rt, &arr->header, JS_GC_OBJ_TYPE_ARRAY); + return JS_MKPTR(JS_TAG_ARRAY, arr); +} + JSValue JS_NewArray(JSContext *ctx) { - return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape), - JS_CLASS_ARRAY); + return JS_NewArrayLen(ctx, 0); } JSValue JS_NewObject(JSContext *ctx) @@ -5113,6 +5115,35 @@ static void js_array_mark(JSRuntime *rt, JSValueConst val, } } +/* Free intrinsic array (JS_TAG_ARRAY) */ +static void free_array(JSRuntime *rt, JSArray *arr) +{ + uint32_t i; + for (i = 0; i < arr->len; i++) { + JS_FreeValueRT(rt, arr->values[i]); + } + js_free_rt(rt, arr->values); + remove_gc_object(&arr->header); + js_free_rt(rt, arr); +} + +/* Push element to intrinsic array, growing if needed. Returns -1 on error, 0 on success. */ +static int js_intrinsic_array_push(JSContext *ctx, JSArray *arr, JSValue val) +{ + if (arr->len >= arr->cap) { + uint32_t new_cap = arr->cap ? arr->cap * 2 : JS_ARRAY_INITIAL_SIZE; + JSValue *new_values = js_realloc(ctx, arr->values, sizeof(JSValue) * new_cap); + if (!new_values) { + JS_FreeValue(ctx, val); + return -1; + } + arr->values = new_values; + arr->cap = new_cap; + } + arr->values[arr->len++] = val; + return 0; +} + static void js_c_function_finalizer(JSRuntime *rt, JSValue val) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -5250,6 +5281,9 @@ static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: free_function_bytecode(rt, (JSFunctionBytecode *)gp); break; + case JS_GC_OBJ_TYPE_ARRAY: + free_array(rt, (JSArray *)gp); + break; default: abort(); } @@ -5312,6 +5346,7 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v) js_free_rt(rt, p); } break; + case JS_TAG_ARRAY: case JS_TAG_OBJECT: case JS_TAG_FUNCTION_BYTECODE: { @@ -5361,6 +5396,7 @@ void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { if (JS_VALUE_HAS_REF_COUNT(val)) { switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_ARRAY: case JS_TAG_OBJECT: case JS_TAG_FUNCTION_BYTECODE: mark_func(rt, JS_VALUE_GET_PTR(val)); @@ -5450,6 +5486,15 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, JS_MarkContext(rt, ctx, mark_func); } break; + case JS_GC_OBJ_TYPE_ARRAY: + { + JSArray *arr = (JSArray *)gp; + uint32_t i; + for (i = 0; i < arr->len; i++) { + JS_MarkValue(rt, arr->values[i], mark_func); + } + } + break; default: abort(); } @@ -5566,7 +5611,8 @@ static void gc_free_cycles(JSRuntime *rt) list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) { p = list_entry(el, JSGCObjectHeader, link); assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || - p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE || + p->gc_obj_type == JS_GC_OBJ_TYPE_ARRAY); js_free_rt(rt, p); } @@ -5744,21 +5790,6 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) } switch(p->class_id) { - case JS_CLASS_ARRAY: /* u.array | length */ - s->array_count++; - if (p->fast_array) { - s->fast_array_count++; - if (p->u.array.u.values) { - s->memory_used_count++; - s->memory_used_size += p->u.array.count * - sizeof(*p->u.array.u.values); - s->fast_array_elements += p->u.array.count; - for (i = 0; i < p->u.array.count; i++) { - compute_value_size(p->u.array.u.values[i], hp); - } - } - } - break; case JS_CLASS_C_FUNCTION: /* u.cfunc */ s->c_func_count++; break; @@ -5973,13 +6004,6 @@ void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) } if (s->array_count) { fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count); - if (s->fast_array_count) { - fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count); - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n", - " elements", s->fast_array_elements, - s->fast_array_elements * (int)sizeof(JSValue), - (double)s->fast_array_elements / s->fast_array_count); - } } if (s->binary_object_count) { fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", @@ -6735,43 +6759,6 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, return JS_DupValue(ctx, pr->u.value); } } - if (unlikely(p->is_exotic)) { - /* exotic behaviors */ - if (p->fast_array) { - if (__JS_AtomIsTaggedInt(prop)) { - uint32_t idx = __JS_AtomToUInt32(prop); - if (idx < p->u.array.count) { - /* we avoid duplicating the code */ - return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); - } - } - } else { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em) { - if (em->get_own_property) { - JSPropertyDescriptor desc; - int ret; - JSValue obj1; - - /* 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) { - if (desc.flags & JS_PROP_GETSET) { - JS_FreeValue(ctx, desc.setter); - return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL); - } else { - return desc.value; - } - } - } - } - } - } p = p->shape->proto; if (!p) break; @@ -6822,10 +6809,10 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, int i, j; JSShape *sh; JSShapeProperty *prs; - JSPropertyEnum *tab_atom, *tab_exotic; + JSPropertyEnum *tab_atom; JSAtom atom; uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count; - uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count; + uint32_t num_index, str_index, sym_index; BOOL is_enumerable, num_sorted; uint32_t num_key; JSAtomKindEnum kind; @@ -6838,9 +6825,6 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, num_keys_count = 0; str_keys_count = 0; sym_keys_count = 0; - exotic_keys_count = 0; - exotic_count = 0; - tab_exotic = NULL; sh = p->shape; for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { atom = prs->atom; @@ -6873,14 +6857,6 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, } } - if (p->is_exotic) { - if (p->fast_array) { - if (flags & JS_GPN_STRING_MASK) { - num_keys_count += p->u.array.count; - } - } - } - /* fill them */ atom_count = num_keys_count + str_keys_count; @@ -6889,11 +6865,9 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, atom_count += sym_keys_count; if (atom_count < sym_keys_count) goto add_overflow; - atom_count += exotic_keys_count; - if (atom_count < exotic_keys_count || atom_count > INT32_MAX) { + if (atom_count > INT32_MAX) { add_overflow: JS_ThrowOutOfMemory(ctx); - JS_FreePropertyEnum(ctx, tab_exotic, exotic_count); return -1; } /* XXX: need generic way to test for js_malloc(ctx, a * b) overflow */ @@ -6901,7 +6875,6 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, /* avoid allocating 0 bytes */ tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); if (!tab_atom) { - JS_FreePropertyEnum(ctx, tab_exotic, exotic_count); return -1; } @@ -6935,39 +6908,6 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, } } - if (p->is_exotic) { - if (p->fast_array) { - if (flags & JS_GPN_STRING_MASK) { - int len = p->u.array.count; - for(i = 0; i < len; i++) { - tab_atom[num_index].atom = __JS_AtomFromUInt32(i); - if (tab_atom[num_index].atom == JS_ATOM_NULL) { - JS_FreePropertyEnum(ctx, tab_atom, num_index); - return -1; - } - tab_atom[num_index].is_enumerable = TRUE; - num_index++; - } - } - } else { - /* Note: exotic keys are not reordered and comes after the object own properties. */ - for(i = 0; i < exotic_count; i++) { - atom = tab_exotic[i].atom; - is_enumerable = tab_exotic[i].is_enumerable; - kind = JS_AtomGetKind(ctx, atom); - if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && - ((flags >> kind) & 1) != 0) { - tab_atom[sym_index].atom = atom; - tab_atom[sym_index].is_enumerable = is_enumerable; - sym_index++; - } else { - JS_FreeAtom(ctx, atom); - } - } - js_free(ctx, tab_exotic); - } - } - assert(num_index == num_keys_count); assert(str_index == num_keys_count + str_keys_count); assert(sym_index == atom_count); @@ -6984,6 +6924,28 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen, JSValueConst obj, int flags) { + /* Handle intrinsic arrays */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_ARRAY) { + JSArray *arr = JS_VALUE_GET_ARRAY(obj); + uint32_t i, len = arr->len; + JSPropertyEnum *tab; + + if (len == 0) { + *ptab = NULL; + *plen = 0; + return 0; + } + tab = js_malloc(ctx, sizeof(JSPropertyEnum) * len); + if (!tab) + return -1; + for (i = 0; i < len; i++) { + tab[i].atom = __JS_AtomFromUInt32(i); + tab[i].is_enumerable = TRUE; + } + *ptab = tab; + *plen = len; + return 0; + } if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { JS_ThrowTypeErrorNotAnObject(ctx); return -1; @@ -7045,37 +7007,30 @@ retry: } return TRUE; } - if (p->is_exotic) { - if (p->fast_array) { - /* specific case for fast arrays */ - if (__JS_AtomIsTaggedInt(prop)) { - uint32_t idx; - idx = __JS_AtomToUInt32(prop); - if (idx < p->u.array.count) { - if (desc) { - desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | - JS_PROP_CONFIGURABLE; - desc->getter = JS_NULL; - desc->setter = JS_NULL; - desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); - } - return TRUE; - } - } - } else { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->get_own_property) { - return em->get_own_property(ctx, desc, - JS_MKPTR(JS_TAG_OBJECT, p), prop); - } - } - } return FALSE; } int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, JSValueConst obj, JSAtom prop) { + /* Handle intrinsic arrays */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_ARRAY) { + JSArray *arr = JS_VALUE_GET_ARRAY(obj); + uint32_t idx; + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx < arr->len) { + if (desc) { + desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE; + desc->value = JS_DupValue(ctx, arr->values[idx]); + desc->getter = JS_NULL; + desc->setter = JS_NULL; + } + return TRUE; + } + } + return FALSE; + } if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { JS_ThrowTypeErrorNotAnObject(ctx); return -1; @@ -7112,6 +7067,16 @@ int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) JSObject *p; int ret; + /* Handle intrinsic arrays */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_ARRAY) { + JSArray *arr = JS_VALUE_GET_ARRAY(obj); + uint32_t idx; + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + return idx < arr->len; + } + return FALSE; + } if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) return FALSE; p = JS_VALUE_GET_OBJ(obj); @@ -7170,83 +7135,49 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, JSAtom atom; JSValue ret; uint32_t prop_tag = JS_VALUE_GET_TAG(prop); + int this_tag = JS_VALUE_GET_TAG(this_obj); - if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && - prop_tag == JS_TAG_INT)) { - JSObject *p; - uint32_t idx; - int32_t signed_idx; - /* fast path for array access */ - p = JS_VALUE_GET_OBJ(this_obj); - signed_idx = JS_VALUE_GET_INT(prop); - switch(p->class_id) { - case JS_CLASS_ARRAY: - /* arrays require non-negative numeric index - return null for invalid */ - if (signed_idx < 0) { - JS_FreeValue(ctx, prop); + if (this_tag == JS_TAG_NULL) { + JS_FreeValue(js, prop); + return JS_NULL; + } + + if (this_tag == JS_TAG_ARRAY) { + if (prop_tag == JS_TAG_INT) { + int idx = JS_VALUE_GET_INT(prop); + if (idx < 0 || idx >= JS_VALUE_GET_ARRAY(this_obj)->len) { return JS_NULL; } - idx = (uint32_t)signed_idx; - if (unlikely(idx >= p->u.array.count)) goto slow_path; - return JS_DupValue(ctx, p->u.array.u.values[idx]); - default: - goto slow_path; + return JS_DupValue(ctx, JS_VALUE_GET_ARRAY(this_obj)->values[idx]); } - } else { - slow_path: - /* Type checking for array vs object indexing - return null for invalid retrieval */ - if (JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_obj); - if (p->class_id == JS_CLASS_ARRAY) { - /* Arrays require numeric index - return null for non-numeric */ - if (prop_tag != JS_TAG_INT && !JS_TAG_IS_FLOAT64(prop_tag)) { - JS_FreeValue(ctx, prop); - return JS_NULL; - } - /* Return null for negative index */ - if (prop_tag == JS_TAG_INT) { - if (JS_VALUE_GET_INT(prop) < 0) { - JS_FreeValue(ctx, prop); - return JS_NULL; - } - } else { - double d = JS_VALUE_GET_FLOAT64(prop); - if (d < 0) { - JS_FreeValue(ctx, prop); - return JS_NULL; - } - } - } else { - /* Objects require text or non-array object (symbol) key - return null for invalid */ - if (prop_tag == JS_TAG_OBJECT) { - /* Check if it's an array - arrays not allowed as object keys */ - JSObject *key_obj = JS_VALUE_GET_OBJ(prop); - if (key_obj->class_id == JS_CLASS_ARRAY) { - JS_FreeValue(ctx, prop); - return JS_NULL; - } - } else if (prop_tag != JS_TAG_STRING && prop_tag != JS_TAG_STRING_ROPE && - prop_tag != JS_TAG_SYMBOL) { - JS_FreeValue(ctx, prop); - return JS_NULL; - } + + if (prop_tag == JS_TAG_FLOAT64) { + double d = JS_VALUE_GET_FLOAT64(prop); + if (d < 0 || d >= JS_VALUE_GET_ARRAY(this_obj)->len) { + return JS_NULL; } + return JS_DupValue(ctx, JS_VALUE_GET_ARRAY(this_obj)->values[(int)d]); } - /* ToObject() must be done before ToPropertyKey() */ - atom = JS_ValueToAtom(ctx, prop); - if (JS_IsNull(this_obj)) { + + return JS_EXCEPTION; + } + + if (this_tag == JS_TAG_OBJECT) { + + } + + if (this_tag == JS_TAG_OBJECT) { + if (prop_tag == JS_TAG_INT || prop_tag == JS_TAG_FLOAT64) { + return JS_EXCEPTION; + } + + if (prop_tag == JS_TAG_STRING || prop_tag == JS_TAG_STRING_ROPE) { + atom = JS_ValueToAtom(ctx, prop); JS_FreeValue(ctx, prop); - JSValue ret = JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", atom); + ret = JS_GetProperty(ctx, this_obj, atom); JS_FreeAtom(ctx, atom); return ret; } - - JS_FreeValue(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - ret = JS_GetProperty(ctx, this_obj, atom); - JS_FreeAtom(ctx, atom); - return ret; } } @@ -7366,42 +7297,6 @@ static JSProperty *add_property(JSContext *ctx, return &p->prop[p->shape->prop_count - 1]; } -/* can be called on Array or Arguments objects. return < 0 if - memory alloc error. */ -static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, - JSObject *p) -{ - JSProperty *pr; - JSShape *sh; - JSValue *tab; - uint32_t i, len, new_count; - - if (js_shape_prepare_update(ctx, p, NULL)) - return -1; - len = p->u.array.count; - /* resize the properties once to simplify the error handling */ - sh = p->shape; - new_count = sh->prop_count + len; - if (new_count > sh->prop_size) { - if (resize_properties(ctx, &p->shape, p, new_count)) - return -1; - } - - tab = p->u.array.u.values; - for(i = 0; i < len; i++) { - /* add_property cannot fail here but - __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ - pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E); - pr->u.value = *tab++; - } - js_free(ctx, p->u.array.u.values); - p->u.array.count = 0; - p->u.array.u.values = NULL; /* fail safe */ - p->u.array.u1.size = 0; - p->fast_array = 0; - return 0; -} - static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) { JSShape *sh; @@ -7456,33 +7351,6 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) lpr = pr; h = pr->hash_next; } - - if (p->is_exotic) { - if (p->fast_array) { - uint32_t idx; - if (JS_AtomIsArrayIndex(ctx, &idx, atom) && - idx < p->u.array.count) { - if (p->class_id == JS_CLASS_ARRAY) { - /* Special case deleting the last element of a fast Array */ - if (idx == p->u.array.count - 1) { - JS_FreeValue(ctx, p->u.array.u.values[idx]); - p->u.array.count = idx; - return TRUE; - } - if (convert_fast_array_to_array(ctx, p)) - return -1; - goto redo; - } else { - return FALSE; - } - } - } else { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->delete_property) { - return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom); - } - } - } /* not found */ return TRUE; } @@ -7512,170 +7380,6 @@ static int call_setter(JSContext *ctx, JSObject *setter, } } -/* set the array length and remove the array elements if necessary. */ -static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, - int flags) -{ - uint32_t len, idx, cur_len; - int i, ret; - - /* Note: this call can reallocate the properties of 'p' */ - ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE); - if (ret) - return -1; - /* JS_ToArrayLengthFree() must be done before the read-only test */ - if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE))) - return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); - - if (likely(p->fast_array)) { - uint32_t old_len = p->u.array.count; - if (len < old_len) { - for(i = len; i < old_len; i++) { - JS_FreeValue(ctx, p->u.array.u.values[i]); - } - p->u.array.count = len; - } - p->prop[0].u.value = JS_NewUint32(ctx, len); - } else { - /* Note: length is always a uint32 because the object is an - array */ - JS_ToUint32(ctx, &cur_len, p->prop[0].u.value); - if (len < cur_len) { - uint32_t d; - JSShape *sh; - JSShapeProperty *pr; - - d = cur_len - len; - sh = p->shape; - if (d <= sh->prop_count) { - JSAtom atom; - - /* faster to iterate */ - while (cur_len > len) { - atom = JS_NewAtomUInt32(ctx, cur_len - 1); - ret = delete_property(ctx, p, atom); - JS_FreeAtom(ctx, atom); - if (unlikely(!ret)) { - /* unlikely case: property is not - configurable */ - break; - } - cur_len--; - } - } else { - /* faster to iterate thru all the properties. Need two - passes in case one of the property is not - configurable */ - cur_len = len; - for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; - i++, pr++) { - if (pr->atom != JS_ATOM_NULL && - JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { - if (idx >= cur_len && - !(pr->flags & JS_PROP_CONFIGURABLE)) { - cur_len = idx + 1; - } - } - } - - for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; - i++, pr++) { - if (pr->atom != JS_ATOM_NULL && - JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { - if (idx >= cur_len) { - /* remove the property */ - delete_property(ctx, p, pr->atom); - /* WARNING: the shape may have been modified */ - sh = p->shape; - pr = get_shape_prop(sh) + i; - } - } - } - } - } else { - cur_len = len; - } - set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len)); - if (unlikely(cur_len > len)) { - return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable"); - } - } - return TRUE; -} - -/* return -1 if exception */ -static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len) -{ - uint32_t new_size; - size_t slack; - JSValue *new_array_prop; - /* XXX: potential arithmetic overflow */ - new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); - new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); - if (!new_array_prop) - return -1; - new_size += slack / sizeof(*new_array_prop); - p->u.array.u.values = new_array_prop; - p->u.array.u1.size = new_size; - return 0; -} - -/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array = - TRUE and p->extensible = TRUE */ -static int add_fast_array_element(JSContext *ctx, JSObject *p, - JSValue val, int flags) -{ - uint32_t new_len, array_len; - /* extend the array by one */ - /* XXX: convert to slow array if new_len > 2^31-1 elements */ - new_len = p->u.array.count + 1; - /* update the length if necessary. We assume that if the length is - not an integer, then if it >= 2^31. */ - if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { - array_len = JS_VALUE_GET_INT(p->prop[0].u.value); - if (new_len > array_len) { - if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); - } - p->prop[0].u.value = JS_NewInt32(ctx, new_len); - } - } - if (unlikely(new_len > p->u.array.u1.size)) { - if (expand_fast_array(ctx, p, new_len)) { - JS_FreeValue(ctx, val); - return -1; - } - } - p->u.array.u.values[new_len - 1] = val; - p->u.array.count = new_len; - return TRUE; -} - -/* Allocate a new fast array. Its 'length' property is set to zero. It - maximum size is 2^31-1 elements. For convenience, 'len' is a 64 bit - integer. WARNING: the content of the array is not initialized. */ -static JSValue js_allocate_fast_array(JSContext *ctx, int64_t len) -{ - JSValue arr; - JSObject *p; - - if (len > INT32_MAX) - return JS_ThrowRangeError(ctx, "invalid array length"); - arr = JS_NewArray(ctx); - if (JS_IsException(arr)) - return arr; - if (len > 0) { - p = JS_VALUE_GET_OBJ(arr); - if (expand_fast_array(ctx, p, len) < 0) { - JS_FreeValue(ctx, arr); - return JS_EXCEPTION; - } - p->u.array.count = len; - } - return arr; -} - static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) { JS_FreeValue(ctx, desc->getter); @@ -7731,10 +7435,6 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, /* fast case */ set_value(ctx, &pr->u.value, val); return TRUE; - } else if (prs->flags & JS_PROP_LENGTH) { - assert(p->class_id == JS_CLASS_ARRAY); - assert(prop == JS_ATOM_length); - return set_array_length(ctx, p, val, flags); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { @@ -7753,61 +7453,6 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, } for(;;) { - if (p1->is_exotic) { - if (p1->fast_array) { - if (__JS_AtomIsTaggedInt(prop)) { - uint32_t idx = __JS_AtomToUInt32(prop); - if (idx < p1->u.array.count) { - if (unlikely(p == p1)) - return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags); - else - break; - } - } - } else { - const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic; - if (em) { - JSValue obj1; - 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, - obj1, prop); - JS_FreeValue(ctx, obj1); - if (ret < 0) { - JS_FreeValue(ctx, val); - return ret; - } - if (ret) { - if (desc.flags & JS_PROP_GETSET) { - JSObject *setter; - if (JS_IsNull(desc.setter)) - setter = NULL; - else - setter = JS_VALUE_GET_OBJ(desc.setter); - ret = call_setter(ctx, setter, this_obj, val, flags); - JS_FreeValue(ctx, desc.getter); - JS_FreeValue(ctx, desc.setter); - return ret; - } else { - JS_FreeValue(ctx, desc.value); - if (!(desc.flags & JS_PROP_WRITABLE)) - goto read_only_prop; - if (likely(p == p1)) { - ret = JS_DefineProperty(ctx, this_obj, prop, val, - JS_NULL, JS_NULL, - JS_PROP_HAS_VALUE); - JS_FreeValue(ctx, val); - return ret; - } else { - break; - } - } - } - } - } - } - } p1 = p1->shape->proto; prototype_lookup: if (!p1) @@ -7848,20 +7493,6 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, } if (likely(p == JS_VALUE_GET_OBJ(obj))) { - if (p->is_exotic) { - if (p->class_id == JS_CLASS_ARRAY && p->fast_array && - __JS_AtomIsTaggedInt(prop)) { - uint32_t idx = __JS_AtomToUInt32(prop); - if (idx == p->u.array.count) { - /* fast case */ - return add_fast_array_element(ctx, p, val, flags); - } else { - goto generic_create_prop; - } - } else { - goto generic_create_prop; - } - } else { pr = add_property(ctx, p, prop, JS_PROP_C_W_E); if (unlikely(!pr)) { JS_FreeValue(ctx, val); @@ -7869,7 +7500,6 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, } pr->u.value = val; return TRUE; - } } else { /* generic case: modify the property in this_obj if it already exists */ ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); @@ -7916,110 +7546,56 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags) { uint32_t prop_tag = JS_VALUE_GET_TAG(prop); + int this_tag = JS_VALUE_GET_TAG(this_obj); - if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && - prop_tag == JS_TAG_INT)) { - JSObject *p; - uint32_t idx; - int32_t signed_idx; + /* Fast path for intrinsic array (JS_TAG_ARRAY) */ + if (likely(this_tag == JS_TAG_ARRAY && prop_tag == JS_TAG_INT)) { + JSArray *arr = JS_VALUE_GET_ARRAY(this_obj); + int32_t signed_idx = JS_VALUE_GET_INT(prop); + /* Throw for negative index or out of bounds */ + if (signed_idx < 0 || (uint32_t)signed_idx >= arr->len) { + JS_FreeValue(ctx, val); + JS_ThrowRangeError(ctx, "array index %d out of bounds (length %u)", + signed_idx, arr->len); + return -1; + } + set_value(ctx, &arr->values[signed_idx], val); + return TRUE; + } - /* fast path for array access */ - p = JS_VALUE_GET_OBJ(this_obj); - signed_idx = JS_VALUE_GET_INT(prop); - switch(p->class_id) { - case JS_CLASS_ARRAY: - /* arrays require non-negative numeric index */ - if (signed_idx < 0) { - JS_FreeValue(ctx, prop); + /* Intrinsic array slow path - handle non-int index */ + if (this_tag == JS_TAG_ARRAY) { + /* Arrays require numeric index */ + if (prop_tag != JS_TAG_INT && !JS_TAG_IS_FLOAT64(prop_tag)) { + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, val); + JS_ThrowTypeError(ctx, "array index must be a number"); + return -1; + } + /* Handle float index */ + if (JS_TAG_IS_FLOAT64(prop_tag)) { + double d = JS_VALUE_GET_FLOAT64(prop); + JSArray *arr = JS_VALUE_GET_ARRAY(this_obj); + JS_FreeValue(ctx, prop); + if (d < 0 || d >= arr->len || d != (double)(uint32_t)d) { JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "array index must be non-negative"); + JS_ThrowRangeError(ctx, "array index out of bounds"); return -1; } - idx = (uint32_t)signed_idx; - if (unlikely(idx >= (uint32_t)p->u.array.count)) { - JSObject *p1; - JSShape *sh1; + set_value(ctx, &arr->values[(uint32_t)d], val); + return TRUE; + } + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, val); + return -1; + } - /* fast path to add an element to the array */ - if (idx != (uint32_t)p->u.array.count || - !p->fast_array || !p->extensible) - goto slow_path; - /* check if prototype chain has a numeric property */ - p1 = p->shape->proto; - while (p1 != NULL) { - sh1 = p1->shape; - if (p1->class_id == JS_CLASS_ARRAY) { - if (unlikely(!p1->fast_array)) - goto slow_path; - } else if (p1->class_id == JS_CLASS_OBJECT) { - if (unlikely(sh1->has_small_array_index)) - goto slow_path; - } else { - goto slow_path; - } - p1 = sh1->proto; - } - /* add element */ - return add_fast_array_element(ctx, p, val, flags); - } - set_value(ctx, &p->u.array.u.values[idx], val); - break; - default: - goto slow_path; - } - return TRUE; - } else { - JSAtom atom; - int ret; - slow_path: - /* Type checking for array vs object indexing */ - if (JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_obj); - if (p->class_id == JS_CLASS_ARRAY) { - /* Arrays require numeric index */ - if (prop_tag != JS_TAG_INT && !JS_TAG_IS_FLOAT64(prop_tag)) { - JS_FreeValue(ctx, prop); - JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "array index must be a number"); - return -1; - } - /* Check for negative index */ - if (prop_tag == JS_TAG_INT) { - if (JS_VALUE_GET_INT(prop) < 0) { - JS_FreeValue(ctx, prop); - JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "array index must be non-negative"); - return -1; - } - } else { - double d = JS_VALUE_GET_FLOAT64(prop); - if (d < 0) { - JS_FreeValue(ctx, prop); - JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "array index must be non-negative"); - return -1; - } - } - } else { - /* Objects require text or non-array object (symbol) key - NOT numbers */ - if (prop_tag == JS_TAG_OBJECT) { - /* Check if it's an array - arrays not allowed as object keys */ - JSObject *key_obj = JS_VALUE_GET_OBJ(prop); - if (key_obj->class_id == JS_CLASS_ARRAY) { - JS_FreeValue(ctx, prop); - JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "object key must be text or object, not array"); - return -1; - } - } else if (prop_tag != JS_TAG_STRING && prop_tag != JS_TAG_STRING_ROPE && - prop_tag != JS_TAG_SYMBOL) { - JS_FreeValue(ctx, prop); - JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "object key must be text or object"); - return -1; - } - } + if (this_tag == JS_TAG_OBJECT) { + if (prop_tag != JS_TAG_STRING && prop_tag != JS_TAG_STRING_ROPE && prop_tag != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "record index must be a text or record"); + return -1; } + atom = JS_ValueToAtom(ctx, prop); JS_FreeValue(ctx, prop); if (unlikely(atom == JS_ATOM_NULL)) { @@ -8122,66 +7698,6 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p, int ret, prop_flags; /* add a new property or modify an existing exotic one */ - if (p->is_exotic) { - if (p->class_id == JS_CLASS_ARRAY) { - uint32_t idx, len; - - if (p->fast_array) { - if (__JS_AtomIsTaggedInt(prop)) { - idx = __JS_AtomToUInt32(prop); - if (idx == p->u.array.count) { - if (!p->extensible) - goto not_extensible; - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) - goto convert_to_array; - prop_flags = get_prop_flags(flags, 0); - if (prop_flags != JS_PROP_C_W_E) - goto convert_to_array; - return add_fast_array_element(ctx, p, - JS_DupValue(ctx, val), flags); - } else { - goto convert_to_array; - } - } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { - /* convert the fast array to normal array */ - convert_to_array: - if (convert_fast_array_to_array(ctx, p)) - return -1; - goto generic_array; - } - } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { - JSProperty *plen; - JSShapeProperty *pslen; - generic_array: - /* update the length field */ - plen = &p->prop[0]; - JS_ToUint32(ctx, &len, plen->u.value); - if ((idx + 1) > len) { - pslen = get_shape_prop(p->shape); - if (unlikely(!(pslen->flags & JS_PROP_WRITABLE))) - return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); - /* XXX: should update the length after defining - the property */ - len = idx + 1; - set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len)); - } - } - } else if (!(flags & JS_PROP_NO_EXOTIC)) { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em) { - if (em->define_own_property) { - return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), - prop, val, getter, setter, flags); - } - ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - if (ret < 0) - return -1; - if (!ret) - goto not_extensible; - } - } - } - if (!p->extensible) { not_extensible: return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); @@ -8305,6 +7821,36 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, JSProperty *pr; int mask, res; + /* Handle intrinsic arrays - only numeric indices allowed */ + if (JS_VALUE_GET_TAG(this_obj) == JS_TAG_ARRAY) { + JSArray *arr = JS_VALUE_GET_ARRAY(this_obj); + uint32_t idx; + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + /* For array creation, we allow setting within allocated length */ + if (idx < arr->len) { + JS_FreeValueRT(ctx->rt, arr->values[idx]); + arr->values[idx] = JS_DupValue(ctx, val); + return 1; + } + /* Allow extending by exactly 1 (for push-like operations during construction) */ + if (idx == arr->len) { + if (js_intrinsic_array_push(ctx, arr, JS_DupValue(ctx, val)) < 0) + return -1; + return 1; + } + JS_ThrowRangeError(ctx, "array index %u out of bounds (length %u)", idx, arr->len); + return -1; + } + /* length property - intrinsic arrays have fixed length */ + if (prop == JS_ATOM_length) { + JS_ThrowTypeError(ctx, "cannot set length of intrinsic array"); + return -1; + } + JS_ThrowTypeError(ctx, "intrinsic arrays only support numeric indices"); + return -1; + } + if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) { JS_ThrowTypeErrorNotAnObject(ctx); return -1; @@ -8482,33 +8028,6 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, return TRUE; } - /* handle modification of fast array elements */ - if (p->fast_array) { - uint32_t idx; - uint32_t prop_flags; - if (p->class_id == JS_CLASS_ARRAY) { - if (__JS_AtomIsTaggedInt(prop)) { - idx = __JS_AtomToUInt32(prop); - if (idx < p->u.array.count) { - prop_flags = get_prop_flags(flags, JS_PROP_C_W_E); - if (prop_flags != JS_PROP_C_W_E) - goto convert_to_slow_array; - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - convert_to_slow_array: - if (convert_fast_array_to_array(ctx, p)) - return -1; - else - goto redo_prop_update; - } - if (flags & JS_PROP_HAS_VALUE) { - set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val)); - } - return TRUE; - } - } - } - } - return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags); } @@ -8682,7 +8201,6 @@ static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop) } /* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ -/* XXX: could support exotic global object. */ static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) { JSObject *p; @@ -8724,7 +8242,6 @@ static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) /* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */ -/* XXX: could support exotic global object. */ static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags) { JSObject *p; @@ -8757,7 +8274,6 @@ static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags) } /* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */ -/* XXX: could support exotic global object. */ static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop, JSValueConst func, int def_flags) { @@ -8786,7 +8302,6 @@ static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop, JSShapeProperty *prs; JSProperty *pr; - /* no exotic behavior is possible in global_var_obj */ p = JS_VALUE_GET_OBJ(ctx->global_var_obj); prs = find_own_property(&pr, p, prop); if (prs) { @@ -8806,7 +8321,6 @@ static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp) JSShapeProperty *prs; JSProperty *pr; - /* no exotic behavior is possible in global_var_obj */ p = JS_VALUE_GET_OBJ(ctx->global_var_obj); prs = find_own_property(&pr, p, prop); if (prs) { @@ -8843,7 +8357,6 @@ static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop) JSShapeProperty *prs; int ret; - /* no exotic behavior is possible in global_var_obj */ p = JS_VALUE_GET_OBJ(ctx->global_var_obj); prs = find_own_property1(p, prop); if (prs) { @@ -8868,7 +8381,6 @@ static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, JSProperty *pr; int flags; - /* no exotic behavior is possible in global_var_obj */ p = JS_VALUE_GET_OBJ(ctx->global_var_obj); prs = find_own_property(&pr, p, prop); if (prs) { @@ -9065,6 +8577,7 @@ static int JS_ToBoolFree(JSContext *ctx, JSValue val) JS_FreeValue(ctx, val); return ret; } + case JS_TAG_ARRAY: case JS_TAG_OBJECT: JS_FreeValue(ctx, val); return 1; @@ -10137,28 +9650,7 @@ static void js_print_object(JSPrintValueState *s, JSObject *p) comma_state = 0; is_array = FALSE; - if (p->class_id == JS_CLASS_ARRAY) { - is_array = TRUE; - js_printf(s, "[ "); - /* XXX: print array like properties even if not fast array */ - if (p->fast_array) { - uint32_t len, n, len1; - len = js_print_array_get_length(p); - - len1 = min_uint32(p->u.array.count, s->options.max_item_count); - for(i = 0; i < len1; i++) { - js_print_comma(s, &comma_state); - js_print_value(s, p->u.array.u.values[i]); - } - if (len1 < p->u.array.count) - js_print_more_items(s, &comma_state, p->u.array.count - len1); - if (p->u.array.count < len) { - n = len - p->u.array.count; - js_print_comma(s, &comma_state); - js_printf(s, "<%u empty item%s>", n, n > 1 ? "s" : ""); - } - } - } else if (p->class_id == JS_CLASS_BYTECODE_FUNCTION || rt->class_array[p->class_id].call != NULL) { + if (p->class_id == JS_CLASS_BYTECODE_FUNCTION || rt->class_array[p->class_id].call != NULL) { js_printf(s, "[Function"); /* XXX: allow dump without ctx */ if (!s->options.raw_dump && s->ctx) { @@ -10522,15 +10014,12 @@ static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) } /* return -1 if exception (proxy case) or TRUE/FALSE */ -// TODO: should take flags to make proxy resolution and exceptions optional int JS_IsArray(JSContext *ctx, JSValueConst val) { - if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(val); - return p->class_id == JS_CLASS_ARRAY; - } else { - return FALSE; - } + int tag = JS_VALUE_GET_TAG(val); + if (tag == JS_TAG_ARRAY) + return TRUE; + return FALSE; } static double js_pow(double a, double b) @@ -11035,30 +10524,16 @@ static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst * return val; } -static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj) -{ - /* Try and handle fast arrays explicitly */ - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(obj); - if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { - return TRUE; - } - } - return FALSE; -} - /* Access an Array's internal JSValue array if available */ static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, JSValue **arrpp, uint32_t *countp) { - /* Try and handle fast arrays explicitly */ - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(obj); - if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { - *countp = p->u.array.count; - *arrpp = p->u.array.u.values; - return TRUE; - } + /* Fast path for intrinsic arrays */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_ARRAY) { + JSArray *arr = JS_VALUE_GET_ARRAY(obj); + *countp = arr->len; + *arrpp = arr->values; + return TRUE; } return FALSE; } @@ -25008,9 +24483,6 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) 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; @@ -25999,9 +25471,6 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj, case 0: val = JS_GetProperty(ctx, ctx->global_obj, atom1); break; - case 1: - val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1); - break; default: abort(); } @@ -26257,6 +25726,12 @@ static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val, static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, JSValueConst obj) { + /* Fast path for intrinsic arrays */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_ARRAY) { + JSArray *arr = JS_VALUE_GET_ARRAY(obj); + *pres = arr->len; + return 0; + } JSValue len_val; len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); if (JS_IsException(len_val)) { @@ -26269,6 +25744,12 @@ static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, static __exception int js_get_length64(JSContext *ctx, int64_t *pres, JSValueConst obj) { + /* Fast path for intrinsic arrays */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_ARRAY) { + JSArray *arr = JS_VALUE_GET_ARRAY(obj); + *pres = arr->len; + return 0; + } JSValue len_val; len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); if (JS_IsException(len_val)) { @@ -26281,6 +25762,11 @@ static __exception int js_get_length64(JSContext *ctx, int64_t *pres, static __exception int js_set_length64(JSContext *ctx, JSValueConst obj, int64_t len) { + /* Intrinsic arrays have fixed length - cannot set */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_ARRAY) { + JS_ThrowTypeError(ctx, "cannot change length of array"); + return -1; + } return JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, len)); } @@ -26308,44 +25794,29 @@ static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, uint32_t len, i; int64_t len64; JSValue *tab, ret; - JSObject *p; + int tag = JS_VALUE_GET_TAG(array_arg); - if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) { - JS_ThrowTypeError(ctx, "not a object"); - return NULL; - } - if (js_get_length64(ctx, &len64, array_arg)) - return NULL; - if (len64 > JS_MAX_LOCAL_VARS) { - // XXX: check for stack overflow? - JS_ThrowRangeError(ctx, "too many arguments in function call (only %d allowed)", - JS_MAX_LOCAL_VARS); - return NULL; - } - len = len64; - /* avoid allocating 0 bytes */ - tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len)); - if (!tab) - return NULL; - p = JS_VALUE_GET_OBJ(array_arg); - if ((p->class_id == JS_CLASS_ARRAY) && - p->fast_array && - len == p->u.array.count) { - for(i = 0; i < len; i++) { - tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]); + /* Fast path for intrinsic arrays */ + if (tag == JS_TAG_ARRAY) { + JSArray *arr = JS_VALUE_GET_ARRAY(array_arg); + len = arr->len; + if (len > JS_MAX_LOCAL_VARS) { + JS_ThrowRangeError(ctx, "too many arguments in function call (only %d allowed)", + JS_MAX_LOCAL_VARS); + return NULL; } - } else { - for(i = 0; i < len; i++) { - ret = JS_GetPropertyUint32(ctx, array_arg, i); - if (JS_IsException(ret)) { - free_arg_list(ctx, tab, i); - return NULL; - } - tab[i] = ret; + tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len)); + if (!tab) + return NULL; + for (i = 0; i < len; i++) { + tab[i] = JS_DupValue(ctx, arr->values[i]); } + *plen = len; + return tab; } - *plen = len; - return tab; + + JS_ThrowTypeError(ctx, "not an array"); + return NULL; } /* magic value: 0 = normal apply, 2 = Reflect.apply */ @@ -26482,322 +25953,16 @@ static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val, return JS_ConcatString(ctx, name, msg); } -/* Array */ - -static int JS_CopySubArray(JSContext *ctx, - JSValueConst obj, int64_t to_pos, - int64_t from_pos, int64_t count, int dir) -{ - JSObject *p; - int64_t i, from, to, len; - JSValue val; - int fromPresent; - - p = NULL; - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(obj); - if (p->class_id != JS_CLASS_ARRAY || !p->fast_array) { - p = NULL; - } - } - - for (i = 0; i < count; ) { - if (dir < 0) { - from = from_pos + count - i - 1; - to = to_pos + count - i - 1; - } else { - from = from_pos + i; - to = to_pos + i; - } - if (p && p->fast_array && - from >= 0 && from < (len = p->u.array.count) && - to >= 0 && to < len) { - int64_t l, j; - /* Fast path for fast arrays. Since we don't look at the - prototype chain, we can optimize only the cases where - all the elements are present in the array. */ - l = count - i; - if (dir < 0) { - l = min_int64(l, from + 1); - l = min_int64(l, to + 1); - for(j = 0; j < l; j++) { - set_value(ctx, &p->u.array.u.values[to - j], - JS_DupValue(ctx, p->u.array.u.values[from - j])); - } - } else { - l = min_int64(l, len - from); - l = min_int64(l, len - to); - for(j = 0; j < l; j++) { - set_value(ctx, &p->u.array.u.values[to + j], - JS_DupValue(ctx, p->u.array.u.values[from + j])); - } - } - i += l; - } else { - fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val); - if (fromPresent < 0) - goto exception; - - if (fromPresent) { - if (JS_SetPropertyInt64(ctx, obj, to, val) < 0) - goto exception; - } else { - if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0) - goto exception; - } - i++; - } - } - return 0; - - exception: - return -1; -} - -static JSValue js_array_constructor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj; - int i; - - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - if (argc == 1 && JS_IsNumber(argv[0])) { - uint32_t len; - if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE)) - goto fail; - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0) - goto fail; - } else { - for(i = 0; i < argc; i++) { - if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) - goto fail; - } - } - return obj; -fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - JSValue obj, val; - int64_t len, n; - JSValue *arrp; - uint32_t count; - int res; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - res = FALSE; - if (len > 0) { - n = 0; - if (argc > 1) { - if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) - goto exception; - } - if (js_get_fast_array(ctx, obj, &arrp, &count)) { - for (; n < count; n++) { - if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), - JS_DupValue(ctx, arrp[n]), - JS_EQ_SAME_VALUE_ZERO)) { - res = TRUE; - goto done; - } - } - } - for (; n < len; n++) { - val = JS_GetPropertyInt64(ctx, obj, n); - if (JS_IsException(val)) - goto exception; - if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, - JS_EQ_SAME_VALUE_ZERO)) { - res = TRUE; - break; - } - } - } - done: - JS_FreeValue(ctx, obj); - return JS_NewBool(ctx, res); - - exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int shift) -{ - JSValue obj, res = JS_NULL; - int64_t len, newLen; - JSValue *arrp; - uint32_t count32; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - newLen = 0; - if (len > 0) { - newLen = len - 1; - /* Special case fast arrays */ - if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { - JSObject *p = JS_VALUE_GET_OBJ(obj); - if (shift) { - res = arrp[0]; - memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp)); - p->u.array.count--; - } else { - res = arrp[count32 - 1]; - p->u.array.count--; - } - } else { - if (shift) { - res = JS_GetPropertyInt64(ctx, obj, 0); - if (JS_IsException(res)) - goto exception; - if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1)) - goto exception; - } else { - res = JS_GetPropertyInt64(ctx, obj, newLen); - if (JS_IsException(res)) - goto exception; - } - if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0) - goto exception; - } - } - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) - goto exception; - - JS_FreeValue(ctx, obj); - return res; - - exception: - JS_FreeValue(ctx, res); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int unshift) -{ - JSValue obj; - int i; - int64_t len, from, newLen; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - newLen = len + argc; - if (newLen > MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array loo long"); - goto exception; - } - from = len; - if (unshift && argc > 0) { - if (JS_CopySubArray(ctx, obj, argc, 0, len, -1)) - goto exception; - from = 0; - } - for(i = 0; i < argc; i++) { - if (JS_SetPropertyInt64(ctx, obj, from + i, - JS_DupValue(ctx, argv[i])) < 0) - goto exception; - } - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) - goto exception; - - JS_FreeValue(ctx, obj); - return JS_NewInt64(ctx, newLen); - - exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target, - JSValueConst source, int64_t sourceLen, - int64_t targetIndex, int depth, - JSValueConst mapperFunction, - JSValueConst thisArg) -{ - JSValue element; - int64_t sourceIndex, elementLen; - int present, is_array; - - if (js_check_stack_overflow(ctx->rt, 0)) { - JS_ThrowStackOverflow(ctx); - return -1; - } - - for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { - present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element); - if (present < 0) - return -1; - if (!present) - continue; - if (!JS_IsNull(mapperFunction)) { - JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source }; - element = JS_Call(ctx, mapperFunction, thisArg, 3, args); - JS_FreeValue(ctx, (JSValue)args[0]); - JS_FreeValue(ctx, (JSValue)args[1]); - if (JS_IsException(element)) - return -1; - } - if (depth > 0) { - is_array = JS_IsArray(ctx, element); - if (is_array < 0) - goto fail; - if (is_array) { - if (js_get_length64(ctx, &elementLen, element) < 0) - goto fail; - targetIndex = JS_FlattenIntoArray(ctx, target, element, - elementLen, targetIndex, - depth - 1, - JS_NULL, JS_NULL); - if (targetIndex < 0) - goto fail; - JS_FreeValue(ctx, element); - continue; - } - } - if (targetIndex >= MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array too long"); - goto fail; - } - if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element, - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - return -1; - targetIndex++; - } - return targetIndex; - -fail: - JS_FreeValue(ctx, element); - return -1; -} - -static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab) -{ - JSValue obj; - int i; - - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) + JSValue found = js_cell_array_find(ctx, this_val, argc, argv); + if (JS_IsException(found)) return JS_EXCEPTION; - for(i = 0; i < len; i++) { - if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - } - return obj; + + if (JS_IsNull(found)) + return JS_NewBool(ctx, FALSE); + return JS_NewBool(ctx, TRUE); } static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len) @@ -26810,27 +25975,6 @@ static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len) return 0; } -/* return the position of the first invalid character in the string or - -1 if none */ -static int js_string_find_invalid_codepoint(JSString *p) -{ - int i; - if (!p->is_wide_char) - return -1; - for(i = 0; i < p->len; i++) { - uint32_t c = p->u.str16[i]; - if (is_surrogate(c)) { - if (is_hi_surrogate(c) && (i + 1) < p->len - && is_lo_surrogate(p->u.str16[i + 1])) { - i++; - } else { - return i; - } - } - } - return -1; -} - /* return < 0 if exception or TRUE/FALSE */ static int js_is_regexp(JSContext *ctx, JSValueConst obj); @@ -27837,10 +26981,10 @@ static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder, val = JS_GetProperty(ctx, holder, name); if (JS_IsException(val)) return val; - if (JS_IsObject(val)) { - is_array = JS_IsArray(ctx, val); - if (is_array < 0) - goto fail; + is_array = JS_IsArray(ctx, val); + if (is_array < 0) + goto fail; + if (is_array || JS_IsObject(val)) { if (is_array) { if (js_get_length32(ctx, &len, val)) goto fail; @@ -27972,6 +27116,7 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, } switch (JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_ARRAY: case JS_TAG_OBJECT: if (JS_IsFunction(ctx, val)) break; @@ -28015,7 +27160,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, goto exception; } - if (JS_IsObject(val)) { + if (JS_IsObject(val) || JS_VALUE_GET_TAG(val) == JS_TAG_ARRAY) { v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val); if (JS_IsException(v)) goto exception; @@ -28037,7 +27182,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, sep = JS_DupValue(ctx, jsc->empty); sep1 = JS_DupValue(ctx, jsc->empty); } - v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0); + v = js_cell_push(ctx, jsc->stack, 1, (JSValueConst *)&val); if (check_exception_free(ctx, v)) goto exception; ret = JS_IsArray(ctx, val); @@ -28118,7 +27263,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, } string_buffer_putc8(jsc->b, '}'); } - if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0))) + if (check_exception_free(ctx, js_cell_pop(ctx, jsc->stack, 0, NULL))) goto exception; JS_FreeValue(ctx, val); JS_FreeValue(ctx, tab); @@ -31900,32 +31045,14 @@ static JSValue js_cell_reverse(JSContext *ctx, JSValueConst this_val, if (!JS_IsArray(ctx, obj)) return JS_NULL; - JSValue length_val = JS_GetPropertyStr(ctx, obj, "length"); - int64_t len; - if (JS_ToInt64(ctx, &len, length_val) < 0) { - JS_FreeValue(ctx, length_val); - return JS_EXCEPTION; - } - JS_FreeValue(ctx, length_val); + JSArray *arr = JS_VALUE_GET_ARRAY(obj); - if (len <= 0) + if (arr->len == 0) return JS_NULL; - JSValue val = JS_GetPropertyInt64(ctx, obj, len - 1); - if (JS_IsException(val)) - return val; - - if (JS_DeletePropertyInt64(ctx, obj, len - 1, JS_PROP_THROW) < 0) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - - if (JS_SetPropertyStr(ctx, obj, "length", JS_NewInt64(ctx, len - 1)) < 0) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - - return val; + JSValue last = JS_DupValue(ctx, arr->values[arr->len-1]); + arr->len--; + return last; } static JSValue js_cell_push(JSContext *ctx, JSValueConst this_val, @@ -31935,24 +31062,15 @@ static JSValue js_cell_push(JSContext *ctx, JSValueConst this_val, return JS_NULL; JSValue obj = argv[0]; - if (!JS_IsArray(ctx, obj)) + if (JS_VALUE_GET_TAG(obj) != JS_TAG_ARRAY) return JS_NULL; - JSValue length_val = JS_GetPropertyStr(ctx, obj, "length"); - int64_t len; - if (JS_ToInt64(ctx, &len, length_val) < 0) { - JS_FreeValue(ctx, length_val); - return JS_EXCEPTION; - } - JS_FreeValue(ctx, length_val); - - for (int i = 1; i < argc; i++) { - if (JS_SetPropertyInt64(ctx, obj, len, JS_DupValue(ctx, argv[i])) < 0) - return JS_EXCEPTION; - len++; - } - - return JS_NewInt64(ctx, len); + JSArray *arr = JS_VALUE_GET_ARRAY(obj); + for (int i = 1; i < argc; i++) { + if (js_intrinsic_array_push(ctx, arr, JS_DupValue(ctx, argv[i])) < 0) + return JS_EXCEPTION; + } + return JS_NewInt64(ctx, arr->len); } static JSValue js_cell_proto(JSContext *ctx, JSValueConst this_val, @@ -32455,23 +31573,6 @@ static void JS_AddIntrinsicBasicObjects(JSContext *ctx) JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); ctx->native_error_proto[i] = proto; } - - /* the array prototype is an array */ - ctx->class_proto[JS_CLASS_ARRAY] = - JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], - JS_CLASS_ARRAY); - - ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]), - JS_PROP_INITIAL_HASH_SIZE, 1); - add_shape_property(ctx, &ctx->array_shape, NULL, - JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH); - - /* XXX: could test it on first context creation to ensure that no - new atoms are created in JS_AddIntrinsicBasicObjects(). It is - necessary to avoid useless renumbering of atoms after - JS_EvalBinary() if it is done just after - JS_AddIntrinsicBasicObjects(). */ - // assert(ctx->rt->atom_count == JS_ATOM_END); } void JS_AddIntrinsicBaseObjects(JSContext *ctx) @@ -32513,47 +31614,6 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) JS_FreeValue(ctx, func_obj); } - /* Array */ - obj = JS_NewCFunction2(ctx, js_array_constructor, "Array", 1, - JS_CFUNC_generic, 0); - JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Array", - JS_DupValue(ctx, obj), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - ctx->array_ctor = obj; - - /* XXX: create auto_initializer */ - { - /* initialize Array.prototype[Symbol.unscopables] */ - static const char unscopables[] = - "at" "\0" - "copyWithin" "\0" - "entries" "\0" - "fill" "\0" - "find" "\0" - "findIndex" "\0" - "findLast" "\0" - "findLastIndex" "\0" - "flat" "\0" - "flatMap" "\0" - "includes" "\0" - "keys" "\0" - "toReversed" "\0" - "toSorted" "\0" - "toSpliced" "\0" - "values" "\0"; - const char *p = unscopables; - obj1 = JS_NewObjectProto(ctx, JS_NULL); - for(p = unscopables; *p; p += strlen(p) + 1) { - JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E); - } - JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY], - JS_ATOM_Symbol_unscopables, obj1, - JS_PROP_CONFIGURABLE); - } - - ctx->array_proto_values = - JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values); - /* global properties */ JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis, JS_DupValue(ctx, ctx->global_obj), @@ -33852,3 +32912,4 @@ JSValue js_math_cycles_use(JSContext *ctx) JSContext *JS_GetContext(JSRuntime *rt) { return rt->js; } + diff --git a/source/quickjs.h b/source/quickjs.h index bccb946f..db84c565 100644 --- a/source/quickjs.h +++ b/source/quickjs.h @@ -74,11 +74,12 @@ typedef uint32_t JSAtom; enum { /* all tags with a reference count are negative */ - JS_TAG_FIRST = -9, /* first negative tag */ - JS_TAG_BIG_INT = -9, - JS_TAG_SYMBOL = -8, - JS_TAG_STRING = -7, - JS_TAG_STRING_ROPE = -6, + JS_TAG_FIRST = -10, /* first negative tag */ + JS_TAG_BIG_INT = -10, + JS_TAG_SYMBOL = -9, + JS_TAG_STRING = -8, + JS_TAG_STRING_ROPE = -7, + JS_TAG_ARRAY = -6, /* intrinsic array type */ JS_TAG_MODULE = -3, /* used internally */ JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ JS_TAG_OBJECT = -1, @@ -449,22 +450,6 @@ typedef struct JSPropertyDescriptor { JSValue setter; } JSPropertyDescriptor; -typedef struct JSClassExoticMethods { - /* Return -1 if exception (can only happen in case of Proxy object), - FALSE if the property does not exists, TRUE if it exists. If 1 is - returned, the property descriptor 'desc' is filled if != NULL. */ - int (*get_own_property)(JSContext *ctx, JSPropertyDescriptor *desc, - JSValueConst obj, JSAtom prop); - - /* return < 0 if exception, or TRUE/FALSE */ - int (*delete_property)(JSContext *ctx, JSValueConst obj, JSAtom prop); - /* return < 0 if exception or TRUE/FALSE */ - int (*define_own_property)(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValueConst val, - JSValueConst getter, JSValueConst setter, - int flags); -} JSClassExoticMethods; - typedef void JSClassFinalizer(JSRuntime *rt, JSValue val); typedef void JSClassGCMark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); @@ -478,9 +463,6 @@ typedef struct JSClassDef { JSClassGCMark *gc_mark; /* if call != NULL, the object is a function */ JSClassCall *call; - /* XXX: suppress this indirection ? It is here only to save memory - because only a few classes need these methods */ - JSClassExoticMethods *exotic; } JSClassDef; #define JS_INVALID_CLASS_ID 0