added Object.groupBy and Map.groupBy (initial patch by bnoordhuis)

This commit is contained in:
Fabrice Bellard 2023-12-27 17:18:39 +01:00
parent 4876f72a1a
commit c2c773e135
3 changed files with 124 additions and 3 deletions

4
TODO
View File

@ -63,5 +63,5 @@ Optimization ideas:
Test262o: 0/11262 errors, 463 excluded Test262o: 0/11262 errors, 463 excluded
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch) Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
Result: 16/76419 errors, 1497 excluded, 8381 skipped Result: 16/76471 errors, 1497 excluded, 8355 skipped
Test262 commit: 31126581e7290f9233c29cefd93f66c6ac78f1c9 Test262 commit: 6cbb6da9473c56d95358d8e679c5a6d2b4574efb

121
quickjs.c
View File

@ -1271,6 +1271,8 @@ static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
JSAtom atom, void *opaque); JSAtom atom, void *opaque);
void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag); void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag);
static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int is_map);
static const JSClassExoticMethods js_arguments_exotic_methods; static const JSClassExoticMethods js_arguments_exotic_methods;
static const JSClassExoticMethods js_string_exotic_methods; static const JSClassExoticMethods js_string_exotic_methods;
@ -37711,6 +37713,7 @@ static const JSCFunctionListEntry js_object_funcs[] = {
JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ), JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ),
JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ), JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ),
JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ), JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ),
JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 0 ),
JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ), 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("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ),
JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ), JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ),
@ -46456,6 +46459,123 @@ static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,
return JS_UNDEFINED; return JS_UNDEFINED;
} }
static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int is_map)
{
JSValueConst cb, args[2];
JSValue res, iter, next, groups, key, v, prop;
JSAtom key_atom = JS_ATOM_NULL;
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;
iter = JS_GetIterator(ctx, argv[0], /*is_async*/FALSE);
if (JS_IsException(iter))
return JS_EXCEPTION;
key = JS_UNDEFINED;
key_atom = JS_ATOM_NULL;
v = JS_UNDEFINED;
prop = JS_UNDEFINED;
groups = JS_UNDEFINED;
next = JS_GetProperty(ctx, iter, JS_ATOM_next);
if (JS_IsException(next))
goto exception;
if (is_map) {
groups = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, 0);
} else {
groups = JS_NewObjectProto(ctx, JS_NULL);
}
if (JS_IsException(groups))
goto exception;
for (idx = 0; ; idx++) {
if (idx >= MAX_SAFE_INTEGER) {
JS_ThrowTypeError(ctx, "too many elements");
goto iterator_close_exception;
}
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);
key = JS_Call(ctx, cb, ctx->global_obj, 2, args);
if (JS_IsException(key))
goto iterator_close_exception;
if (is_map) {
prop = js_map_get(ctx, groups, 1, (JSValueConst *)&key, 0);
} else {
key_atom = JS_ValueToAtom(ctx, key);
JS_FreeValue(ctx, key);
key = JS_UNDEFINED;
if (key_atom == JS_ATOM_NULL)
goto iterator_close_exception;
prop = JS_GetProperty(ctx, groups, key_atom);
}
if (JS_IsException(prop))
goto exception;
if (JS_IsUndefined(prop)) {
prop = JS_NewArray(ctx);
if (JS_IsException(prop))
goto exception;
if (is_map) {
args[0] = key;
args[1] = prop;
res = js_map_set(ctx, groups, 2, args, 0);
if (JS_IsException(res))
goto exception;
JS_FreeValue(ctx, res);
} else {
prop = JS_DupValue(ctx, prop);
if (JS_DefinePropertyValue(ctx, groups, key_atom, prop,
JS_PROP_C_W_E) < 0) {
goto exception;
}
}
}
res = js_array_push(ctx, prop, 1, (JSValueConst *)&v, /*unshift*/0);
if (JS_IsException(res))
goto exception;
// res is an int64
JS_FreeValue(ctx, prop);
JS_FreeValue(ctx, key);
JS_FreeAtom(ctx, key_atom);
JS_FreeValue(ctx, v);
prop = JS_UNDEFINED;
key = JS_UNDEFINED;
key_atom = JS_ATOM_NULL;
v = JS_UNDEFINED;
}
JS_FreeValue(ctx, iter);
JS_FreeValue(ctx, next);
return groups;
iterator_close_exception:
JS_IteratorClose(ctx, iter, TRUE);
exception:
JS_FreeAtom(ctx, key_atom);
JS_FreeValue(ctx, prop);
JS_FreeValue(ctx, key);
JS_FreeValue(ctx, v);
JS_FreeValue(ctx, groups);
JS_FreeValue(ctx, iter);
JS_FreeValue(ctx, next);
return JS_EXCEPTION;
}
static void js_map_finalizer(JSRuntime *rt, JSValue val) static void js_map_finalizer(JSRuntime *rt, JSValue val)
{ {
JSObject *p; JSObject *p;
@ -46636,6 +46756,7 @@ static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,
} }
static const JSCFunctionListEntry js_map_funcs[] = { static const JSCFunctionListEntry js_map_funcs[] = {
JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 1 ),
JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
}; };

View File

@ -56,7 +56,7 @@ AggregateError
align-detached-buffer-semantics-with-web-reality align-detached-buffer-semantics-with-web-reality
arbitrary-module-namespace-names=skip arbitrary-module-namespace-names=skip
array-find-from-last array-find-from-last
array-grouping=skip array-grouping
Array.fromAsync=skip Array.fromAsync=skip
Array.prototype.at Array.prototype.at
Array.prototype.flat Array.prototype.flat