diff --git a/quickjs.c b/quickjs.c index d13c0b0..2824637 100644 --- a/quickjs.c +++ b/quickjs.c @@ -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)) - return -1; - if (JS_IsUndefined(val)) { - JS_ThrowTypeError(ctx, ".size is undefined"); - return -1; + 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(v)) { + JS_ThrowTypeError(ctx, ".size is undefined"); + return -1; + } + if (JS_ToFloat64Free(ctx, &d, v) < 0) + return -1; + if (isnan(d)) { + JS_ThrowTypeError(ctx, ".size is not a number"); + return -1; + } + *pout = d; } - if (JS_ToFloat64Free(ctx, &d, val) < 0) + 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 (isnan(d)) { - JS_ThrowTypeError(ctx, ".size is not a number"); - return -1; - } - val = JS_GetProperty(ctx, setlike, JS_ATOM_has); - if (JS_IsException(val)) - 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" ), diff --git a/test262_errors.txt b/test262_errors.txt index e5a3699..4357ac8 100644 --- a/test262_errors.txt +++ b/test262_errors.txt @@ -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