From 3e54403b08a886e9af36f964bfd2f27f5b5b2b28 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 24 Nov 2023 00:48:40 +0100 Subject: [PATCH] Implement Object.groupBy --- quickjs.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++ test262_errors.txt | 22 ----------- 2 files changed, 91 insertions(+), 22 deletions(-) diff --git a/quickjs.c b/quickjs.c index f56ab21..9660d9d 100644 --- a/quickjs.c +++ b/quickjs.c @@ -34377,6 +34377,96 @@ static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY); } +static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue cb, res, iter, next, groups, k, v, prop; + JSValue args[2]; + int64_t idx; + BOOL done; + + // "is function?" check must be observed before argv[0] is accessed + cb = argv[1]; + if (check_function(ctx, cb)) + return JS_EXCEPTION; + + // TODO(bnoordhuis) add fast path for arrays but as groupBy() is + // defined in terms of iterators, the fast path must check that + // this[Symbol.iterator] is the built-in array iterator + iter = JS_GetIterator(ctx, argv[0], /*is_async*/FALSE); + if (JS_IsException(iter)) + return JS_EXCEPTION; + + k = JS_UNDEFINED; + v = JS_UNDEFINED; + prop = JS_UNDEFINED; + groups = JS_UNDEFINED; + + next = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next)) + goto exception; + + groups = JS_NewObjectProto(ctx, JS_NULL); + if (JS_IsException(groups)) + goto exception; + + for (idx = 0; ; idx++) { + v = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); + if (JS_IsException(v)) + goto exception; + if (done) + break; // v is JS_UNDEFINED + + args[0] = v; + args[1] = JS_NewInt64(ctx, idx); + k = JS_Call(ctx, cb, ctx->global_obj, 2, args); + if (JS_IsException(k)) + goto exception; + + k = JS_DupValue(ctx, k); + prop = JS_GetPropertyValue(ctx, groups, k); + if (JS_IsException(prop)) + goto exception; + + if (JS_IsUndefined(prop)) { + prop = JS_NewArray(ctx); + if (JS_IsException(prop)) + goto exception; + k = JS_DupValue(ctx, k); + prop = JS_DupValue(ctx, prop); + if (JS_SetPropertyValue(ctx, groups, k, prop, + JS_PROP_C_W_E|JS_PROP_THROW) < 0) { + goto exception; + } + } + + res = js_array_push(ctx, prop, 1, &v, /*unshift*/0); + if (JS_IsException(res)) + goto exception; + // res is an int64 + + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, k); + JS_FreeValue(ctx, v); + prop = JS_UNDEFINED; + k = JS_UNDEFINED; + v = JS_UNDEFINED; + } + + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return groups; + +exception: + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, k); + JS_FreeValue(ctx, v); + JS_FreeValue(ctx, groups); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return JS_EXCEPTION; +} + static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int kind) { @@ -34947,6 +35037,7 @@ static const JSCFunctionListEntry js_object_funcs[] = { JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ), JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ), JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ), + JS_CFUNC_DEF("groupBy", 2, js_object_groupBy ), JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ), JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ), JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ), diff --git a/test262_errors.txt b/test262_errors.txt index 60d32d7..b8d6a6c 100644 --- a/test262_errors.txt +++ b/test262_errors.txt @@ -23,28 +23,6 @@ test262/test/built-ins/Map/groupBy/negativeZero.js:27: TypeError: not a function test262/test/built-ins/Map/groupBy/negativeZero.js:27: strict mode: TypeError: not a function test262/test/built-ins/Map/groupBy/toPropertyKey.js:24: TypeError: not a function test262/test/built-ins/Map/groupBy/toPropertyKey.js:24: strict mode: TypeError: not a function -test262/test/built-ins/Object/groupBy/callback-arg.js:30: TypeError: not a function -test262/test/built-ins/Object/groupBy/callback-arg.js:30: strict mode: TypeError: not a function -test262/test/built-ins/Object/groupBy/callback-throws.js:20: Test262Error: Expected a Test262Error but got a TypeError -test262/test/built-ins/Object/groupBy/callback-throws.js:20: strict mode: Test262Error: Expected a Test262Error but got a TypeError -test262/test/built-ins/Object/groupBy/emptyList.js:24: TypeError: not a function -test262/test/built-ins/Object/groupBy/emptyList.js:24: strict mode: TypeError: not a function -test262/test/built-ins/Object/groupBy/evenOdd.js:18: TypeError: not a function -test262/test/built-ins/Object/groupBy/evenOdd.js:18: strict mode: TypeError: not a function -test262/test/built-ins/Object/groupBy/groupLength.js:23: TypeError: not a function -test262/test/built-ins/Object/groupBy/groupLength.js:23: strict mode: TypeError: not a function -test262/test/built-ins/Object/groupBy/invalid-property-key.js:22: Test262Error: Expected a Test262Error but got a TypeError -test262/test/built-ins/Object/groupBy/invalid-property-key.js:22: strict mode: Test262Error: Expected a Test262Error but got a TypeError -test262/test/built-ins/Object/groupBy/iterator-next-throws.js:26: Test262Error: Expected a Test262Error but got a TypeError -test262/test/built-ins/Object/groupBy/iterator-next-throws.js:26: strict mode: Test262Error: Expected a Test262Error but got a TypeError -test262/test/built-ins/Object/groupBy/length.js:25: TypeError: cannot convert to object -test262/test/built-ins/Object/groupBy/length.js:25: strict mode: TypeError: cannot convert to object -test262/test/built-ins/Object/groupBy/name.js:25: TypeError: cannot convert to object -test262/test/built-ins/Object/groupBy/name.js:25: strict mode: TypeError: cannot convert to object -test262/test/built-ins/Object/groupBy/null-prototype.js:24: TypeError: not a function -test262/test/built-ins/Object/groupBy/null-prototype.js:24: strict mode: TypeError: not a function -test262/test/built-ins/Object/groupBy/toPropertyKey.js:33: TypeError: not a function -test262/test/built-ins/Object/groupBy/toPropertyKey.js:33: strict mode: TypeError: not a function test262/test/built-ins/RegExp/lookahead-quantifier-match-groups.js:27: Test262Error: Expected [a, abc] and [a, undefined] to have the same contents. ? quantifier test262/test/built-ins/RegExp/lookahead-quantifier-match-groups.js:27: strict mode: Test262Error: Expected [a, abc] and [a, undefined] to have the same contents. ? quantifier test262/test/built-ins/TypedArray/prototype/set/array-arg-targetbuffer-detached-on-get-src-value-no-throw.js:30: TypeError: out-of-bound numeric index (Testing with Float64Array.)