@@ -353,7 +353,8 @@ typedef enum {
353353struct JSGCObjectHeader {
354354 int ref_count; /* must come first, 32-bit */
355355 JSGCObjectTypeEnum gc_obj_type : 4;
356- uint8_t mark : 4; /* used by the GC */
356+ uint8_t mark : 1; /* used by the GC */
357+ uint8_t dummy0: 3;
357358 uint8_t dummy1; /* not used by the GC */
358359 uint16_t dummy2; /* not used by the GC */
359360 struct list_head link;
@@ -446,7 +447,14 @@ struct JSContext {
446447
447448 uint16_t binary_object_count;
448449 int binary_object_size;
449-
450+ /* TRUE if the array prototype is "normal":
451+ - no small index properties which are get/set or non writable
452+ - its prototype is Object.prototype
453+ - Object.prototype has no small index properties which are get/set or non writable
454+ - the prototype of Object.prototype is null (always true as it is immutable)
455+ */
456+ uint8_t std_array_prototype;
457+
450458 JSShape *array_shape; /* initial shape for Array objects */
451459
452460 JSValue *class_proto;
@@ -904,10 +912,6 @@ struct JSShape {
904912 /* true if the shape is inserted in the shape hash table. If not,
905913 JSShape.hash is not valid */
906914 uint8_t is_hashed;
907- /* If true, the shape may have small array index properties 'n' with 0
908- <= n <= 2^31-1. If false, the shape is guaranteed not to have
909- small array index properties */
910- uint8_t has_small_array_index;
911915 uint32_t hash; /* current hash value */
912916 uint32_t prop_hash_mask;
913917 int prop_size; /* allocated properties */
@@ -923,7 +927,8 @@ struct JSObject {
923927 JSGCObjectHeader header;
924928 struct {
925929 int __gc_ref_count; /* corresponds to header.ref_count */
926- uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
930+ uint8_t __gc_mark : 7; /* corresponds to header.mark/gc_obj_type */
931+ uint8_t is_prototype : 1; /* object may be used as prototype */
927932
928933 uint8_t extensible : 1;
929934 uint8_t free_mark : 1; /* only used when freeing objects with cycles */
@@ -4765,7 +4770,6 @@ static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,
47654770 /* insert in the hash table */
47664771 sh->hash = shape_initial_hash(proto);
47674772 sh->is_hashed = TRUE;
4768- sh->has_small_array_index = FALSE;
47694773 js_shape_hash_link(ctx->rt, sh);
47704774 return sh;
47714775}
@@ -5010,7 +5014,6 @@ static int add_shape_property(JSContext *ctx, JSShape **psh,
50105014 pr = &prop[sh->prop_count++];
50115015 pr->atom = JS_DupAtom(ctx, atom);
50125016 pr->flags = prop_flags;
5013- sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom);
50145017 /* add in hash table */
50155018 hash_mask = sh->prop_hash_mask;
50165019 h = atom & hash_mask;
@@ -5125,6 +5128,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas
51255128 if (unlikely(!p))
51265129 goto fail;
51275130 p->class_id = class_id;
5131+ p->is_prototype = 0;
51285132 p->extensible = TRUE;
51295133 p->free_mark = 0;
51305134 p->is_exotic = 0;
@@ -7401,6 +7405,14 @@ static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj,
74017405 if (sh->proto)
74027406 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
74037407 sh->proto = proto;
7408+ if (proto)
7409+ proto->is_prototype = TRUE;
7410+ if (p->is_prototype) {
7411+ /* track modification of Array.prototype */
7412+ if (unlikely(p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]))) {
7413+ ctx->std_array_prototype = FALSE;
7414+ }
7415+ }
74047416 return TRUE;
74057417}
74067418
@@ -8587,6 +8599,14 @@ static JSProperty *add_property(JSContext *ctx,
85878599{
85888600 JSShape *sh, *new_sh;
85898601
8602+ if (unlikely(p->is_prototype)) {
8603+ /* track addition of small integer properties to Array.prototype and Object.prototype */
8604+ if (unlikely((p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]) ||
8605+ p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_OBJECT])) &&
8606+ __JS_AtomIsTaggedInt(prop))) {
8607+ ctx->std_array_prototype = FALSE;
8608+ }
8609+ }
85908610 sh = p->shape;
85918611 if (sh->is_hashed) {
85928612 /* try to find an existing shape */
@@ -8656,6 +8676,11 @@ static no_inline __exception int convert_fast_array_to_array(JSContext *ctx,
86568676 p->u.array.u.values = NULL; /* fail safe */
86578677 p->u.array.u1.size = 0;
86588678 p->fast_array = 0;
8679+
8680+ /* track modification of Array.prototype */
8681+ if (unlikely(p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]))) {
8682+ ctx->std_array_prototype = FALSE;
8683+ }
86598684 return 0;
86608685}
86618686
@@ -8880,8 +8905,8 @@ static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len)
88808905
88818906/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array =
88828907 TRUE and p->extensible = TRUE */
8883- static int add_fast_array_element(JSContext *ctx, JSObject *p,
8884- JSValue val, int flags)
8908+ static inline int add_fast_array_element(JSContext *ctx, JSObject *p,
8909+ JSValue val, int flags)
88858910{
88868911 uint32_t new_len, array_len;
88878912 /* extend the array by one */
@@ -9283,27 +9308,13 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
92839308 switch(p->class_id) {
92849309 case JS_CLASS_ARRAY:
92859310 if (unlikely(idx >= (uint32_t)p->u.array.count)) {
9286- JSObject *p1;
9287- JSShape *sh1;
9288-
92899311 /* fast path to add an element to the array */
9290- if (idx != (uint32_t)p->u.array.count ||
9291- !p->fast_array || !p->extensible)
9312+ if (unlikely(idx != (uint32_t)p->u.array.count ||
9313+ !p->fast_array ||
9314+ !p->extensible ||
9315+ p->shape->proto != JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]) ||
9316+ !ctx->std_array_prototype)) {
92929317 goto slow_path;
9293- /* check if prototype chain has a numeric property */
9294- p1 = p->shape->proto;
9295- while (p1 != NULL) {
9296- sh1 = p1->shape;
9297- if (p1->class_id == JS_CLASS_ARRAY) {
9298- if (unlikely(!p1->fast_array))
9299- goto slow_path;
9300- } else if (p1->class_id == JS_CLASS_OBJECT) {
9301- if (unlikely(sh1->has_small_array_index))
9302- goto slow_path;
9303- } else {
9304- goto slow_path;
9305- }
9306- p1 = sh1->proto;
93079318 }
93089319 /* add element */
93099320 return add_fast_array_element(ctx, p, val, flags);
@@ -18689,9 +18700,32 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
1868918700 idx = JS_VALUE_GET_INT(sp[-2]);
1869018701 if (unlikely(p->class_id != JS_CLASS_ARRAY))
1869118702 goto put_array_el_slow_path;
18692- if (unlikely(idx >= (uint32_t)p->u.array.count))
18693- goto put_array_el_slow_path;
18694- set_value(ctx, &p->u.array.u.values[idx], sp[-1]);
18703+ if (unlikely(idx >= (uint32_t)p->u.array.count)) {
18704+ uint32_t new_len, array_len;
18705+ if (unlikely(idx != (uint32_t)p->u.array.count ||
18706+ !p->fast_array ||
18707+ !p->extensible ||
18708+ p->shape->proto != JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]) ||
18709+ !ctx->std_array_prototype)) {
18710+ goto put_array_el_slow_path;
18711+ }
18712+ if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) != JS_TAG_INT))
18713+ goto put_array_el_slow_path;
18714+ /* cannot overflow otherwise the length would not be an integer */
18715+ new_len = idx + 1;
18716+ if (unlikely(new_len > p->u.array.u1.size))
18717+ goto put_array_el_slow_path;
18718+ array_len = JS_VALUE_GET_INT(p->prop[0].u.value);
18719+ if (new_len > array_len) {
18720+ if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE)))
18721+ goto put_array_el_slow_path;
18722+ p->prop[0].u.value = JS_NewInt32(ctx, new_len);
18723+ }
18724+ p->u.array.count = new_len;
18725+ p->u.array.u.values[idx] = sp[-1];
18726+ } else {
18727+ set_value(ctx, &p->u.array.u.values[idx], sp[-1]);
18728+ }
1869518729 JS_FreeValue(ctx, sp[-3]);
1869618730 sp -= 3;
1869718731 } else {
@@ -54343,7 +54377,8 @@ static void JS_AddIntrinsicBasicObjects(JSContext *ctx)
5434354377 JS_PROP_INITIAL_HASH_SIZE, 1);
5434454378 add_shape_property(ctx, &ctx->array_shape, NULL,
5434554379 JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH);
54346-
54380+ ctx->std_array_prototype = TRUE;
54381+
5434754382 /* XXX: could test it on first context creation to ensure that no
5434854383 new atoms are created in JS_AddIntrinsicBasicObjects(). It is
5434954384 necessary to avoid useless renumbering of atoms after
0 commit comments