From: Fabrice Bellard Date: Sat, 13 Jun 2026 16:03:49 +0000 (+0200) Subject: optimized Array.prototype.slice and Array.prototype.splice X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/stylesheets/print.css?a=commitdiff_plain;h=42d08be5f28abfdf881110bba3713f6a256d8d97;p=quickjs.git optimized Array.prototype.slice and Array.prototype.splice --- diff --git a/quickjs.c b/quickjs.c index a8fb103..e3716a4 100644 --- a/quickjs.c +++ b/quickjs.c @@ -16795,18 +16795,6 @@ static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); -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) @@ -41869,10 +41857,10 @@ static JSValue js_get_this(JSContext *ctx, return JS_DupValue(ctx, this_val); } -static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj, - JSValueConst len_val) +/* XXX: optimize */ +static JSValue JS_ArraySpeciesGetCtor(JSContext *ctx, JSValueConst obj) { - JSValue ctor, ret, species; + JSValue ctor, species; int res; JSContext *realm; @@ -41880,7 +41868,7 @@ static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj, if (res < 0) return JS_EXCEPTION; if (!res) - return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); + return JS_UNDEFINED; ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); if (JS_IsException(ctor)) return ctor; @@ -41906,13 +41894,39 @@ static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj, if (JS_IsNull(ctor)) ctor = JS_UNDEFINED; } + if (!JS_IsUndefined(ctor) && + js_same_value(ctx, ctor, ctx->array_ctor)) { + JS_FreeValue(ctx, ctor); + ctor = JS_UNDEFINED; + } + return ctor; +} + +static JSValue JS_ArrayCreateFromCtor(JSContext *ctx, JSValueConst ctor, int64_t len) +{ + JSValue len_val, ret; + + len_val = JS_NewInt64(ctx, len); if (JS_IsUndefined(ctor)) { - return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); + ret = js_array_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&len_val); } else { - ret = JS_CallConstructor(ctx, ctor, 1, &len_val); - JS_FreeValue(ctx, ctor); - return ret; + ret = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)&len_val); } + JS_FreeValue(ctx, len_val); + return ret; +} + +/* len must be >= 0 */ +static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj, int64_t len) +{ + JSValue ctor, ret; + + ctor = JS_ArraySpeciesGetCtor(ctx, obj); + if (JS_IsException(ctor)) + return ctor; + ret = JS_ArrayCreateFromCtor(ctx, ctor, len); + JS_FreeValue(ctx, ctor); + return ret; } static const JSCFunctionListEntry js_array_funcs[] = { @@ -42042,7 +42056,7 @@ static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val, if (JS_IsException(obj)) goto exception; - arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); + arr = JS_ArraySpeciesCreate(ctx, obj, 0); if (JS_IsException(arr)) goto exception; n = 0; @@ -42145,13 +42159,12 @@ static JSValue js_array_every(JSContext *ctx, JSValueConst this_val, ret = JS_FALSE; break; case special_map: - /* XXX: JS_ArraySpeciesCreate should take int64_t */ - ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len)); + ret = JS_ArraySpeciesCreate(ctx, obj, len); if (JS_IsException(ret)) goto exception; break; case special_filter: - ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); + ret = JS_ArraySpeciesCreate(ctx, obj, 0); if (JS_IsException(ret)) goto exception; break; @@ -42923,13 +42936,13 @@ exception: } static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int splice) + int argc, JSValueConst *argv) { - JSValue obj, arr, val, len_val; - int64_t len, start, k, final, n, count, del_count, new_len; + JSValue obj, arr, val, ctor; + int64_t len, start, k, final, n, count; int kPresent; JSValue *arrp; - uint32_t count32, i, item_count; + uint32_t count32; arr = JS_UNDEFINED; obj = JS_ToObject(ctx, this_val); @@ -42939,69 +42952,150 @@ static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) goto exception; - if (splice) { - if (argc == 0) { - item_count = 0; - del_count = 0; - } else - if (argc == 1) { - item_count = 0; - del_count = len - start; - } else { - item_count = argc - 2; - if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0)) - goto exception; - } - if (len + item_count - del_count > MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array loo long"); + final = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len)) goto exception; - } - count = del_count; - } else { - item_count = 0; /* avoid warning */ - final = len; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len)) - goto exception; - } - count = max_int64(final - start, 0); } - len_val = JS_NewInt64(ctx, count); - arr = JS_ArraySpeciesCreate(ctx, obj, len_val); - JS_FreeValue(ctx, len_val); - if (JS_IsException(arr)) + count = max_int64(final - start, 0); + + ctor = JS_ArraySpeciesGetCtor(ctx, obj); + if (JS_IsException(ctor)) goto exception; - k = start; final = start + count; - n = 0; - /* The fast array test on arr ensures that - JS_CreateDataPropertyUint32() won't modify obj in case arr is - an exotic object */ - /* Special case fast arrays */ - if (js_get_fast_array(ctx, obj, &arrp, &count32) && - js_is_fast_array(ctx, arr)) { - /* XXX: should share code with fast array constructor */ - for (; k < final && k < count32; k++, n++) { - if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0) + if (JS_IsUndefined(ctor) && + js_get_fast_array(ctx, obj, &arrp, &count32) && + final <= count32) { + /* fast case */ + arr = js_create_array(ctx, count, (JSValueConst *)arrp + start); + } else { + arr = JS_ArrayCreateFromCtor(ctx, ctor, count); + JS_FreeValue(ctx, ctor); + if (JS_IsException(arr)) + goto exception; + + n = 0; + for (k = start; k < final; k++, n++) { + kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val); + if (kPresent < 0) goto exception; + if (kPresent) { + if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0) + goto exception; + } } + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) + goto exception; } - /* Copy the remaining elements if any (handle case of inherited properties) */ - for (; k < final; k++, n++) { - kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val); - if (kPresent < 0) + JS_FreeValue(ctx, obj); + return arr; + + exception: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +static JSValue js_array_splice(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, arr, val, ctor; + int64_t len, start, k, final, n, del_count, new_len; + int kPresent; + uint32_t i, item_count; + JSObject *p; + + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + + if (argc == 0) { + item_count = 0; + del_count = 0; + } else if (argc == 1) { + item_count = 0; + del_count = len - start; + } else { + item_count = argc - 2; + if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0)) goto exception; - if (kPresent) { - if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0) - goto exception; - } } - if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) + if (len + item_count - del_count > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + final = start + del_count; + /* warning: 'len' may be different from the actual array length + because it may have been modified */ + new_len = len + item_count - del_count; + + ctor = JS_ArraySpeciesGetCtor(ctx, obj); + if (JS_IsException(ctor)) goto exception; - if (splice) { - new_len = len + item_count - del_count; + p = JS_VALUE_GET_PTR(obj); + if (JS_IsUndefined(ctor) && + p->class_id == JS_CLASS_ARRAY && + p->fast_array && + final <= p->u.array.count && + (get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE) && /* writable array length */ + can_extend_fast_array(p)) { + uint32_t count32 = p->u.array.count; + JSValue *arrp = p->u.array.u.values; + + /* fast case */ + arr = js_create_array(ctx, del_count, (JSValueConst *)arrp + start); + if (JS_IsException(arr)) + goto exception; + + if (item_count != del_count) { + /* resize */ + uint32_t new_count32; + new_count32 = count32 + item_count - del_count; + if (del_count > item_count) { + for(i = 0; i < del_count - item_count; i++) + JS_FreeValue(ctx, arrp[start + item_count + i]); + memmove(arrp + start + item_count, arrp + final, + (count32 - final) * sizeof(arrp[0])); + } else { + if (unlikely(new_count32 > p->u.array.u1.size)) { + if (expand_fast_array(ctx, p, new_count32)) + goto exception; + arrp = p->u.array.u.values; + } + memmove(arrp + start + item_count, arrp + final, + (count32 - final) * sizeof(arrp[0])); + for(i = 0; i < item_count - del_count; i++) + arrp[start + del_count + i] = JS_UNDEFINED; + } + p->u.array.count = new_count32; + } + for(i = 0; i < item_count; i++) + set_value(ctx, &arrp[start + i], JS_DupValue(ctx, argv[i + 2])); + } else { + arr = JS_ArrayCreateFromCtor(ctx, ctor, del_count); + JS_FreeValue(ctx, ctor); + if (JS_IsException(arr)) + goto exception; + + n = 0; + for (k = start; k < final; k++, n++) { + kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val); + if (kPresent < 0) + goto exception; + if (kPresent) { + if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0) + goto exception; + } + } + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) + goto exception; + if (item_count != del_count) { if (JS_CopySubArray(ctx, obj, start + item_count, start + del_count, len - (start + del_count), @@ -43017,9 +43111,9 @@ static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0) goto exception; } - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0) - goto exception; } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0) + goto exception; JS_FreeValue(ctx, obj); return arr; @@ -43235,7 +43329,7 @@ static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val, goto exception; } } - arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); + arr = JS_ArraySpeciesCreate(ctx, obj, 0); if (JS_IsException(arr)) goto exception; if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum, @@ -44698,8 +44792,8 @@ static const JSCFunctionListEntry js_array_proto_funcs[] = { JS_CFUNC_DEF("toReversed", 0, js_array_toReversed ), JS_CFUNC_DEF("sort", 1, js_array_sort ), JS_CFUNC_DEF("toSorted", 1, js_array_toSorted ), - JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ), - JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ), + JS_CFUNC_DEF("slice", 2, js_array_slice ), + JS_CFUNC_DEF("splice", 2, js_array_splice ), JS_CFUNC_DEF("toSpliced", 2, js_array_toSpliced ), JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ), JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ),