From a19b07af377b2d376f770f743f6b7c4a6b0199bf Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 11 Nov 2023 21:51:02 +0100 Subject: [PATCH] Implement Array.prototype.toSorted (#44) --- quickjs.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++ test262_errors.txt | 34 ---------------------- 2 files changed, 72 insertions(+), 34 deletions(-) diff --git a/quickjs.c b/quickjs.c index c0b900b..5c67d16 100644 --- a/quickjs.c +++ b/quickjs.c @@ -37208,6 +37208,77 @@ fail: return JS_EXCEPTION; } +// Note: a.toSorted() is a.slice().sort() with the twist that a.slice() +// leaves holes in sparse arrays intact whereas a.toSorted() replaces them +// with undefined, thus in effect creating a dense array. +// Does not use Array[@@species], always returns a base Array. +static JSValue js_array_toSorted(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, obj, ret, *arrp, *pval; + JSObject *p; + int64_t i, len; + uint32_t count32; + int ok; + + ok = JS_IsUndefined(argv[0]) || JS_IsFunction(ctx, argv[0]); + if (!ok) + return JS_ThrowTypeError(ctx, "not a function"); + + ret = JS_EXCEPTION; + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (len > UINT32_MAX) { + JS_ThrowRangeError(ctx, "invalid array length"); + goto exception; + } + + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + goto exception; + + if (len > 0) { + p = JS_VALUE_GET_OBJ(arr); + if (expand_fast_array(ctx, p, len) < 0) + goto exception; + p->u.array.count = len; + + i = 0; + pval = p->u.array.u.values; + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + for (; i < len; i++, pval++) + *pval = JS_DupValue(ctx, arrp[i]); + } else { + for (; i < len; i++, pval++) { + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) { + for (; i < len; i++, pval++) + *pval = JS_UNDEFINED; + goto exception; + } + } + } + + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0) + goto exception; + } + + ret = js_array_sort(ctx, arr, argc, argv); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, ret); + + ret = arr; + arr = JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return ret; +} + typedef struct JSArrayIteratorData { JSValue obj; JSIteratorKindEnum kind; @@ -37385,6 +37456,7 @@ static const JSCFunctionListEntry js_array_proto_funcs[] = { JS_CFUNC_DEF("reverse", 0, js_array_reverse ), 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("copyWithin", 2, js_array_copyWithin ), diff --git a/test262_errors.txt b/test262_errors.txt index 9789b61..f7d7618 100644 --- a/test262_errors.txt +++ b/test262_errors.txt @@ -1,40 +1,6 @@ test262/test/annexB/language/eval-code/direct/script-decl-lex-collision-in-sloppy-mode.js:13: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all test262/test/built-ins/Array/prototype/Symbol.unscopables/change-array-by-copy.js:19: Test262Error: obj should have an own property toReversed test262/test/built-ins/Array/prototype/Symbol.unscopables/change-array-by-copy.js:19: strict mode: Test262Error: obj should have an own property toReversed -test262/test/built-ins/Array/prototype/toSorted/comparefn-called-after-get-elements.js:37: Test262Error: Expected a Test262Error but got a TypeError -test262/test/built-ins/Array/prototype/toSorted/comparefn-called-after-get-elements.js:37: strict mode: Test262Error: Expected a Test262Error but got a TypeError -test262/test/built-ins/Array/prototype/toSorted/comparefn-stop-after-error.js:22: Test262Error: Expected a Test262Error but got a TypeError -test262/test/built-ins/Array/prototype/toSorted/comparefn-stop-after-error.js:22: strict mode: Test262Error: Expected a Test262Error but got a TypeError -test262/test/built-ins/Array/prototype/toSorted/frozen-this-value.js:13: TypeError: not a function -test262/test/built-ins/Array/prototype/toSorted/frozen-this-value.js:13: strict mode: TypeError: not a function -test262/test/built-ins/Array/prototype/toSorted/holes-not-preserved.js:23: TypeError: not a function -test262/test/built-ins/Array/prototype/toSorted/holes-not-preserved.js:23: strict mode: TypeError: not a function -test262/test/built-ins/Array/prototype/toSorted/ignores-species.js:21: TypeError: not a function -test262/test/built-ins/Array/prototype/toSorted/ignores-species.js:21: strict mode: TypeError: not a function -test262/test/built-ins/Array/prototype/toSorted/immutable.js:13: TypeError: not a function -test262/test/built-ins/Array/prototype/toSorted/immutable.js:13: strict mode: TypeError: not a function -test262/test/built-ins/Array/prototype/toSorted/length-casted-to-zero.js:18: TypeError: cannot read property 'call' of undefined -test262/test/built-ins/Array/prototype/toSorted/length-casted-to-zero.js:18: strict mode: TypeError: cannot read property 'call' of undefined -test262/test/built-ins/Array/prototype/toSorted/length-decreased-while-iterating.js:31: TypeError: not a function -test262/test/built-ins/Array/prototype/toSorted/length-decreased-while-iterating.js:31: strict mode: TypeError: not a function -test262/test/built-ins/Array/prototype/toSorted/length-exceeding-array-length-limit.js:26: Test262Error: Expected a RangeError but got a TypeError -test262/test/built-ins/Array/prototype/toSorted/length-exceeding-array-length-limit.js:26: strict mode: Test262Error: Expected a RangeError but got a TypeError -test262/test/built-ins/Array/prototype/toSorted/length-increased-while-iterating.js:29: TypeError: not a function -test262/test/built-ins/Array/prototype/toSorted/length-increased-while-iterating.js:29: strict mode: TypeError: not a function -test262/test/built-ins/Array/prototype/toSorted/length-tolength.js:18: TypeError: cannot read property 'call' of undefined -test262/test/built-ins/Array/prototype/toSorted/length-tolength.js:18: strict mode: TypeError: cannot read property 'call' of undefined -test262/test/built-ins/Array/prototype/toSorted/metadata/length.js:30: TypeError: cannot convert to object -test262/test/built-ins/Array/prototype/toSorted/metadata/length.js:30: strict mode: TypeError: cannot convert to object -test262/test/built-ins/Array/prototype/toSorted/metadata/name.js:28: TypeError: cannot convert to object -test262/test/built-ins/Array/prototype/toSorted/metadata/name.js:28: strict mode: TypeError: cannot convert to object -test262/test/built-ins/Array/prototype/toSorted/metadata/property-descriptor.js:18: Test262Error: typeof Expected SameValue(«undefined», «function») to be true -test262/test/built-ins/Array/prototype/toSorted/metadata/property-descriptor.js:18: strict mode: Test262Error: typeof Expected SameValue(«undefined», «function») to be true -test262/test/built-ins/Array/prototype/toSorted/not-a-constructor.js:30: Test262Error: isConstructor invoked with a non-function value -test262/test/built-ins/Array/prototype/toSorted/not-a-constructor.js:30: strict mode: Test262Error: isConstructor invoked with a non-function value -test262/test/built-ins/Array/prototype/toSorted/this-value-boolean.js:18: TypeError: cannot read property 'call' of undefined -test262/test/built-ins/Array/prototype/toSorted/this-value-boolean.js:18: strict mode: TypeError: cannot read property 'call' of undefined -test262/test/built-ins/Array/prototype/toSorted/zero-or-one-element.js:13: TypeError: not a function -test262/test/built-ins/Array/prototype/toSorted/zero-or-one-element.js:13: strict mode: TypeError: not a function test262/test/built-ins/Array/prototype/toSpliced/deleteCount-clamped-between-zero-and-remaining-count.js:20: TypeError: not a function test262/test/built-ins/Array/prototype/toSpliced/deleteCount-clamped-between-zero-and-remaining-count.js:20: strict mode: TypeError: not a function test262/test/built-ins/Array/prototype/toSpliced/deleteCount-missing.js:18: TypeError: not a function