diff --git a/include/internal.h b/include/internal.h index ec9ac7d..69bbf76 100644 --- a/include/internal.h +++ b/include/internal.h @@ -378,7 +378,6 @@ ant_offset_t vstr(ant_t *js, ant_value_t value, ant_offset_t *len); ant_offset_t vstrlen(ant_t *js, ant_value_t value); ant_offset_t str_len_fast(ant_t *js, ant_value_t str); -ant_value_t mkarr(ant_t *js); ant_value_t mkval(uint8_t type, uint64_t data); ant_value_t mkobj(ant_t *js, ant_offset_t parent); ant_value_t js_mkobj_with_inobj_limit(ant_t *js, uint8_t inobj_limit); diff --git a/src/ant.c b/src/ant.c index b79ba74..af11f89 100644 --- a/src/ant.c +++ b/src/ant.c @@ -2349,16 +2349,17 @@ ant_value_t js_mkobj_with_inobj_limit(ant_t *js, uint8_t inobj_limit) { return mkobj_with_inobj_limit(js, 0, inobj_limit); } -ant_value_t mkarr(ant_t *js) { +static ant_value_t alloc_array_with_proto(ant_t *js, ant_value_t proto) { ant_object_t *obj = obj_alloc(js, T_ARR, (uint8_t)ANT_INOBJ_MAX_SLOTS); if (!obj) return js_mkerr(js, "oom"); + ant_value_t arr = mkval(T_ARR, (uintptr_t)obj); - ant_value_t array_proto = js->array_proto; - if (vtype(array_proto) == T_OBJ) js_set_proto_init(arr, array_proto); + if (is_object_type(proto)) js_set_proto_init(arr, proto); obj->u.array.cap = MAX_DENSE_INITIAL_CAP; obj->u.array.len = 0; obj->u.array.data = malloc(sizeof(*obj->u.array.data) * (size_t)obj->u.array.cap); + if (obj->u.array.data) { for (uint32_t i = 0; i < obj->u.array.cap; i++) obj->u.array.data[i] = T_EMPTY; obj->fast_array = 1; @@ -2371,6 +2372,10 @@ ant_value_t mkarr(ant_t *js) { return arr; } +static inline ant_value_t mkarr(ant_t *js) { + return alloc_array_with_proto(js, js->array_proto); +} + ant_value_t js_mkarr(ant_t *js) { return mkarr(js); } @@ -12488,8 +12493,7 @@ ant_t *js_create(void *buf, size_t len) { ant_value_t function_proto = js_obj_to_func(function_proto_obj); set_slot(glob, SLOT_FUNC_PROTO, function_proto); - ant_value_t array_proto = js_mkobj(js); - set_proto(js, array_proto, object_proto); + ant_value_t array_proto = alloc_array_with_proto(js, object_proto); defmethod(js, array_proto, "push", 4, js_mkfun(builtin_array_push)); defmethod(js, array_proto, "pop", 3, js_mkfun(builtin_array_pop)); @@ -12814,7 +12818,7 @@ ant_t *js_create(void *buf, size_t len) { js_set_descriptor(js, js_as_obj(function_proto), "constructor", 11, JS_DESC_W | JS_DESC_C); js_setprop(js, array_proto, js_mkstr(js, "constructor", 11), arr_ctor_func); - js_set_descriptor(js, array_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); + js_set_descriptor(js, js_as_obj(array_proto), "constructor", 11, JS_DESC_W | JS_DESC_C); js_setprop(js, string_proto, js_mkstr(js, "constructor", 11), str_ctor_func); js_set_descriptor(js, string_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); diff --git a/src/errors.c b/src/errors.c index 0c4d011..ff9b877 100644 --- a/src/errors.c +++ b/src/errors.c @@ -917,7 +917,7 @@ typedef struct { static bool callsite_visit_frame(ant_t *js, const js_vm_frame_view_t *view, void *ctx) { callsite_build_ctx_t *c = (callsite_build_ctx_t *)ctx; - ant_value_t data = mkarr(js); + ant_value_t data = js_mkarr(js); js_arr_push(js, data, js_mkstr(js, view->file, strlen(view->file))); js_arr_push(js, data, js_mknum((double)view->line)); js_arr_push(js, data, js_mknum((double)view->col)); @@ -948,7 +948,7 @@ ant_value_t js_build_callsite_array(ant_t *js) { js_set(js, proto, "getThis", js_mkfun(callsite_getThis)); js_set(js, proto, "toString", js_mkfun(callsite_toString)); - ant_value_t arr = mkarr(js); + ant_value_t arr = js_mkarr(js); callsite_build_ctx_t ctx = { js, arr, proto }; const char *file = (js->errsite.valid && js->errsite.filename) diff --git a/src/modules/collections.c b/src/modules/collections.c index 0ec988c..43878c8 100644 --- a/src/modules/collections.c +++ b/src/modules/collections.c @@ -713,7 +713,7 @@ static ant_value_t builtin_FinalizationRegistry(ant_t *js, ant_value_t *args, in ant_value_t fr_proto = js_get_ctor_proto(js, "FinalizationRegistry", 20); if (is_special_object(fr_proto)) js_set_proto_init(fr_obj, fr_proto); - js_set_slot(fr_obj, SLOT_MAP, mkarr(js)); + js_set_slot(fr_obj, SLOT_MAP, js_mkarr(js)); js_set_slot(fr_obj, SLOT_DATA, args[0]); return fr_obj; @@ -738,7 +738,7 @@ static ant_value_t finreg_register(ant_t *js, ant_value_t *args, int nargs) { ant_value_t registrations = js_get_slot(this_val, SLOT_MAP); if (vtype(registrations) != T_ARR) return js_mkundef(); - ant_value_t entry = mkarr(js); + ant_value_t entry = js_mkarr(js); ant_offset_t len = js_arr_len(js, registrations); char idx[16]; diff --git a/src/modules/reflect.c b/src/modules/reflect.c index 290d7fb..73f99b6 100644 --- a/src/modules/reflect.c +++ b/src/modules/reflect.c @@ -249,13 +249,12 @@ static ant_value_t reflect_set_prototype_of(ant_t *js, ant_value_t *args, int na ant_value_t target = args[0]; ant_value_t proto = args[1]; - int t = vtype(target); - if (t != T_OBJ && t != T_FUNC) return js_false; - - int pt = vtype(proto); - if (pt != T_OBJ && pt != T_FUNC && pt != T_NULL) return js_false; + if (!is_object_type(target)) return js_false; + if (!is_object_type(proto) && vtype(proto) != T_NULL) return js_false; + if (vtype(proto) != T_NULL && proto_chain_contains(js, proto, target)) return js_false; js_set_proto_wb(js, target, proto); + return js_true; } diff --git a/src/modules/regex.c b/src/modules/regex.c index e71b4a1..b9fe79b 100644 --- a/src/modules/regex.c +++ b/src/modules/regex.c @@ -1010,7 +1010,7 @@ static ant_value_t builtin_regexp_symbol_match(ant_t *js, ant_value_t *args, int bool full_unicode = js_truthy(js, unicode_val); js_setprop(js, rx, js_mkstr(js, "lastIndex", 9), tov(0)); - ant_value_t A = mkarr(js); + ant_value_t A = js_mkarr(js); if (is_err(A)) return A; ant_offset_t n = 0; @@ -1179,7 +1179,7 @@ static ant_value_t builtin_regexp_symbol_replace(ant_t *js, ant_value_t *args, i js_setprop(js, rx, js_mkstr(js, "lastIndex", 9), tov(0)); } - ant_value_t results = mkarr(js); + ant_value_t results = js_mkarr(js); if (is_err(results)) return results; ant_offset_t nresults = 0; @@ -1374,7 +1374,7 @@ static ant_value_t builtin_regexp_symbol_split(ant_t *js, ant_value_t *args, int ant_value_t splitter = regexp_species_construct(js, rx, C, ctor_args, 2); if (is_err(splitter)) return splitter; - ant_value_t A = mkarr(js); + ant_value_t A = js_mkarr(js); if (is_err(A)) return A; ant_offset_t lengthA = 0; diff --git a/src/silver/ops/objects.h b/src/silver/ops/objects.h index 259e61e..1068438 100644 --- a/src/silver/ops/objects.h +++ b/src/silver/ops/objects.h @@ -260,7 +260,8 @@ static inline void sv_op_define_class( if (vtype(ctor) == T_FUNC) js_mark_constructor(js_func_obj(ctor), true); setprop_interned(js, proto, "constructor", 11, ctor); - setprop_interned(js, ctor, "prototype", 9, proto); + ant_value_t ctor_obj = (vtype(ctor) == T_FUNC) ? js_func_obj(ctor) : ctor; + js_mkprop_fast(js, ctor_obj, "prototype", 9, proto); if (a && a->len > 0) setprop_cstr(js, ctor, "name", 4, js_mkstr(js, a->str, a->len)); diff --git a/tests/test_array_proto_subclassing.cjs b/tests/test_array_proto_subclassing.cjs new file mode 100644 index 0000000..26c851c --- /dev/null +++ b/tests/test_array_proto_subclassing.cjs @@ -0,0 +1,22 @@ +function assert(cond, msg) { + if (!cond) throw new Error(msg); +} + +console.log("array prototype and subclassing regression"); + +assert(Array.isArray(Array.prototype), "Array.prototype should be an array exotic"); +assert(Object.getPrototypeOf(Array.prototype) === Object.prototype, "Array.prototype should inherit from Object.prototype"); + +class C extends Array {} +const c = new C(1, 2, 3); + +assert(c instanceof C, "Array subclass instances should preserve subclass identity"); +assert(c instanceof Array, "Array subclass instances should still be arrays"); +assert(c.concat(4) instanceof C, "concat should preserve Array subclass"); +assert(c.map(x => x) instanceof C, "map should preserve Array subclass"); +assert(c.filter(() => true) instanceof C, "filter should preserve Array subclass"); +assert(c.slice(0) instanceof C, "slice should preserve Array subclass"); +assert(C.from([1, 2]) instanceof C, "Array.from on subclass should preserve subclass"); +assert(C.of(1, 2) instanceof C, "Array.of on subclass should preserve subclass"); + +console.log("PASS");