Add Set.prototype.difference (#504)

This commit is contained in:
Ben Noordhuis 2024-09-07 09:44:55 +02:00 committed by GitHub
parent da591954a8
commit 7513260d9a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 152 additions and 80 deletions

170
quickjs.c
View file

@ -46168,46 +46168,160 @@ static int JS_WriteSet(BCWriterState *s, struct JSMapState *map_state)
return js_map_write(s, map_state, MAGIC_SET);
}
static int js_setlike_check(JSContext *ctx, JSValue setlike, int64_t *size)
static int js_setlike_get_size(JSContext *ctx, JSValue setlike, int64_t *pout)
{
JSValue val;
JSMapState *s;
JSValue v;
double d;
BOOL ok;
if (JS_GetOpaque(setlike, JS_CLASS_SET))
return 0;
val = JS_GetProperty(ctx, setlike, JS_ATOM_size);
if (JS_IsException(val))
s = JS_GetOpaque(setlike, JS_CLASS_SET);
if (s) {
*pout = s->record_count;
} else {
v = JS_GetProperty(ctx, setlike, JS_ATOM_size);
if (JS_IsException(v))
return -1;
if (JS_IsUndefined(val)) {
if (JS_IsUndefined(v)) {
JS_ThrowTypeError(ctx, ".size is undefined");
return -1;
}
if (JS_ToFloat64Free(ctx, &d, val) < 0)
if (JS_ToFloat64Free(ctx, &d, v) < 0)
return -1;
if (isnan(d)) {
JS_ThrowTypeError(ctx, ".size is not a number");
return -1;
}
val = JS_GetProperty(ctx, setlike, JS_ATOM_has);
if (JS_IsException(val))
*pout = d;
}
return 0;
}
static int js_setlike_get_has(JSContext *ctx, JSValue setlike, JSValue *pout)
{
JSValue v;
v = JS_GetProperty(ctx, setlike, JS_ATOM_has);
if (JS_IsException(v))
return -1;
if (JS_IsUndefined(val)) {
if (JS_IsUndefined(v)) {
JS_ThrowTypeError(ctx, ".has is undefined");
return -1;
}
ok = JS_IsFunction(ctx, val);
JS_FreeValue(ctx, val);
if (!ok) {
if (!JS_IsFunction(ctx, v)) {
JS_ThrowTypeError(ctx, ".has is not a function");
JS_FreeValue(ctx, v);
return -1;
}
*size = 0;
if (is_safe_integer(d))
*size = d;
*pout = v;
return 0;
}
static int js_setlike_get_keys(JSContext *ctx, JSValue setlike, JSValue *pout)
{
JSValue v;
v = JS_GetProperty(ctx, setlike, JS_ATOM_keys);
if (JS_IsException(v))
return -1;
if (JS_IsUndefined(v)) {
JS_ThrowTypeError(ctx, ".keys is undefined");
return -1;
}
if (!JS_IsFunction(ctx, v)) {
JS_ThrowTypeError(ctx, ".keys is not a function");
JS_FreeValue(ctx, v);
return -1;
}
*pout = v;
return 0;
}
static JSValue js_set_difference(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
JSValue newset, item, iter, keys, has, next, rv;
JSMapState *s, *t;
JSMapRecord *mr;
int64_t size;
BOOL done;
int ok;
has = JS_UNDEFINED;
iter = JS_UNDEFINED;
keys = JS_UNDEFINED;
next = JS_UNDEFINED;
newset = JS_UNDEFINED;
s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
if (!s)
goto exception;
// order matters!
if (js_setlike_get_size(ctx, argv[0], &size) < 0)
goto exception;
if (js_setlike_get_has(ctx, argv[0], &has) < 0)
goto exception;
if (js_setlike_get_keys(ctx, argv[0], &keys) < 0)
goto exception;
if (s->record_count > size) {
iter = JS_Call(ctx, keys, argv[0], 0, NULL);
if (JS_IsException(iter))
goto exception;
next = JS_GetProperty(ctx, iter, JS_ATOM_next);
if (JS_IsException(next))
goto exception;
newset = js_map_constructor(ctx, JS_UNDEFINED, 1, &this_val, MAGIC_SET);
if (JS_IsException(newset))
goto exception;
t = JS_GetOpaque(newset, JS_CLASS_SET);
for (;;) {
item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
if (JS_IsException(item))
goto exception;
if (done) // item is JS_UNDEFINED
break;
item = map_normalize_key(ctx, item);
mr = map_find_record(ctx, t, item);
if (mr)
map_delete_record(ctx->rt, t, mr);
JS_FreeValue(ctx, item);
}
} else {
iter = js_create_map_iterator(ctx, this_val, 0, NULL, MAGIC_SET);
if (JS_IsException(iter))
goto exception;
newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET);
if (JS_IsException(newset))
goto exception;
for (;;) {
item = js_map_iterator_next(ctx, iter, 0, NULL, &done, MAGIC_SET);
if (JS_IsException(item))
goto exception;
if (done) // item is JS_UNDEFINED
break;
rv = JS_Call(ctx, has, argv[0], 1, &item);
ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION
rv = JS_UNDEFINED;
if (ok == 0)
rv = js_map_set(ctx, newset, 1, &item, MAGIC_SET);
JS_FreeValue(ctx, item);
if (JS_IsException(rv))
goto exception;
JS_FreeValue(ctx, rv);
if (ok < 0)
goto exception;
}
}
goto fini;
exception:
JS_FreeValue(ctx, newset);
newset = JS_EXCEPTION;
fini:
JS_FreeValue(ctx, has);
JS_FreeValue(ctx, keys);
JS_FreeValue(ctx, iter);
JS_FreeValue(ctx, next);
return newset;
}
static JSValue js_set_union(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
@ -46221,14 +46335,17 @@ static JSValue js_set_union(JSContext *ctx, JSValue this_val,
s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
if (!s)
return JS_EXCEPTION;
// order matters! they're JS-observable side effects
if (js_setlike_get_size(ctx, argv[0], &size) < 0)
return JS_EXCEPTION;
if (js_setlike_get_has(ctx, argv[0], &rv) < 0)
return JS_EXCEPTION;
JS_FreeValue(ctx, rv);
newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET);
if (JS_IsException(newset))
return JS_EXCEPTION;
iter = JS_UNDEFINED;
next = JS_UNDEFINED;
// note: has JS-observable side effects
if (js_setlike_check(ctx, argv[0], &size) < 0)
goto exception;
list_for_each(el, &s->records) {
mr = list_entry(el, JSMapRecord, link);
if (mr->empty)
@ -46259,14 +46376,14 @@ static JSValue js_set_union(JSContext *ctx, JSValue this_val,
goto exception;
JS_FreeValue(ctx, rv);
}
goto fini;
exception:
JS_FreeValue(ctx, newset);
newset = JS_EXCEPTION;
fini:
JS_FreeValue(ctx, next);
JS_FreeValue(ctx, iter);
return newset;
exception:
JS_FreeValue(ctx, next);
JS_FreeValue(ctx, iter);
JS_FreeValue(ctx, newset);
return JS_EXCEPTION;
}
static const JSCFunctionListEntry js_map_funcs[] = {
@ -46301,6 +46418,7 @@ static const JSCFunctionListEntry js_set_proto_funcs[] = {
JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ),
JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ),
JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ),
JS_CFUNC_DEF("difference", 1, js_set_difference ),
JS_CFUNC_DEF("union", 1, js_set_union ),
JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ),
JS_ALIAS_DEF("keys", "values" ),

View file

@ -72,52 +72,6 @@ test262/test/built-ins/RegExp/property-escapes/generated/XID_Start.js:16: Test26
test262/test/built-ins/RegExp/property-escapes/generated/XID_Start.js:16: strict mode: Test262Error: `\p{XID_Start}` should match U+02EBF0 (`𮯰`)
test262/test/built-ins/RegExp/unicode_full_case_folding.js:20: Test262Error: \u0390 does not match \u1fd3
test262/test/built-ins/RegExp/unicode_full_case_folding.js:20: strict mode: Test262Error: \u0390 does not match \u1fd3
test262/test/built-ins/Set/prototype/difference/add-not-called.js:21: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/add-not-called.js:21: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/allows-set-like-class.js:32: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/allows-set-like-class.js:32: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/allows-set-like-object.js:30: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/allows-set-like-object.js:30: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/builtins.js:9: Test262Error: Built-in objects must be extensible. Expected SameValue(«false», «true») to be true
test262/test/built-ins/Set/prototype/difference/builtins.js:9: strict mode: Test262Error: Built-in objects must be extensible. Expected SameValue(«false», «true») to be true
test262/test/built-ins/Set/prototype/difference/combines-Map.js:16: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/combines-Map.js:16: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/combines-empty-sets.js:13: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/combines-empty-sets.js:13: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/combines-itself.js:12: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/combines-itself.js:12: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/combines-same-sets.js:13: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/combines-same-sets.js:13: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/combines-sets.js:13: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/combines-sets.js:13: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/converts-negative-zero.js:25: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/converts-negative-zero.js:25: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/difference.js:10: Test262Error: `typeof Set.prototype.difference` is `'function'` Expected SameValue(«undefined», «function») to be true
test262/test/built-ins/Set/prototype/difference/difference.js:10: strict mode: Test262Error: `typeof Set.prototype.difference` is `'function'` Expected SameValue(«undefined», «function») to be true
test262/test/built-ins/Set/prototype/difference/length.js:11: Test262Error: Expected SameValue(«undefined», «function») to be true
test262/test/built-ins/Set/prototype/difference/length.js:11: strict mode: Test262Error: Expected SameValue(«undefined», «function») to be true
test262/test/built-ins/Set/prototype/difference/name.js:11: Test262Error: Expected SameValue(«undefined», «function») to be true
test262/test/built-ins/Set/prototype/difference/name.js:11: strict mode: Test262Error: Expected SameValue(«undefined», «function») to be true
test262/test/built-ins/Set/prototype/difference/not-a-constructor.js:17: Test262Error: isConstructor invoked with a non-function value
test262/test/built-ins/Set/prototype/difference/not-a-constructor.js:17: strict mode: Test262Error: isConstructor invoked with a non-function value
test262/test/built-ins/Set/prototype/difference/require-internal-slot.js:17: Test262Error: Expected SameValue(«undefined», «function») to be true
test262/test/built-ins/Set/prototype/difference/require-internal-slot.js:17: strict mode: Test262Error: Expected SameValue(«undefined», «function») to be true
test262/test/built-ins/Set/prototype/difference/result-order.js:14: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/result-order.js:14: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/set-like-array.js:23: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/set-like-array.js:23: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/set-like-class-mutation.js:44: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/set-like-class-mutation.js:44: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/set-like-class-order.js:67: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/set-like-class-order.js:67: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/size-is-a-number.js:24: Test262Error: GetSetRecord coerces size Expected SameValue(«0», «1») to be true
test262/test/built-ins/Set/prototype/difference/size-is-a-number.js:24: strict mode: Test262Error: GetSetRecord coerces size Expected SameValue(«0», «1») to be true
test262/test/built-ins/Set/prototype/difference/subclass-receiver-methods.js:34: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/subclass-receiver-methods.js:34: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/subclass-symbol-species.js:20: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/subclass-symbol-species.js:20: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/subclass.js:15: TypeError: not a function
test262/test/built-ins/Set/prototype/difference/subclass.js:15: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/intersection/add-not-called.js:21: TypeError: not a function
test262/test/built-ins/Set/prototype/intersection/add-not-called.js:21: strict mode: TypeError: not a function
test262/test/built-ins/Set/prototype/intersection/allows-set-like-class.js:32: TypeError: not a function