Skip to content

Commit f526708

Browse files
committed
Improve handling of set-like .size properties
test262 doesn't test it but the spec mandates that negative .size properties should result in a RangeError being raised, and that is indeed what V8 and SpiderMonkey do. Now we do, too. Adjust for another quirk, which is that Infinity is a legal size, and, more generally, that sizes > Number.MAX_SAFE_INTEGER are not illegal - which is okay, because the size is not actually used for anything, because of course it isn't.
1 parent 2b29c33 commit f526708

File tree

2 files changed

+56
-22
lines changed

2 files changed

+56
-22
lines changed

quickjs.c

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49502,11 +49502,11 @@ static int JS_WriteSet(BCWriterState *s, struct JSMapState *map_state)
4950249502
}
4950349503

4950449504
static int js_setlike_get_props(JSContext *ctx, JSValueConst setlike,
49505-
int64_t *psize, JSValue *phas, JSValue *pkeys)
49505+
uint64_t *psize, JSValue *phas, JSValue *pkeys)
4950649506
{
4950749507
JSValue has, keys, v;
4950849508
JSMapState *s;
49509-
int64_t size;
49509+
uint64_t size;
4951049510
double d;
4951149511

4951249512
keys = JS_UNDEFINED;
@@ -49524,14 +49524,19 @@ static int js_setlike_get_props(JSContext *ctx, JSValueConst setlike,
4952449524
}
4952549525
if (JS_ToFloat64Free(ctx, &d, v) < 0)
4952649526
return -1;
49527+
if (d < 0) {
49528+
JS_ThrowRangeError(ctx, ".size is not a legal size");
49529+
return -1;
49530+
}
4952749531
if (isnan(d)) {
49528-
JS_ThrowTypeError(ctx, ".size is not a number");
49532+
JS_ThrowTypeError(ctx, ".size is not a legal size");
4952949533
return -1;
4953049534
}
49531-
// TODO(bnoordhuis) add precision check, can be double
49532-
// that cannot be accurately represented as an int64,
49533-
// like Infinity and numbers outside MAX_SAFE_INTEGER
49534-
size = d;
49535+
if (isinf(d) || d > (double)MAX_SAFE_INTEGER) {
49536+
size = UINT64_MAX;
49537+
} else {
49538+
size = (uint64_t)d; // cast for expository reasons
49539+
}
4953549540
}
4953649541
has = JS_GetProperty(ctx, setlike, JS_ATOM_has);
4953749542
if (JS_IsException(has))
@@ -49565,7 +49570,7 @@ static JSValue js_set_isDisjointFrom(JSContext *ctx, JSValueConst this_val,
4956549570
int done;
4956649571
bool found;
4956749572
JSMapState *s;
49568-
int64_t size;
49573+
uint64_t size;
4956949574
int ok;
4957049575

4957149576
s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
@@ -49635,7 +49640,7 @@ static JSValue js_set_isSubsetOf(JSContext *ctx, JSValueConst this_val,
4963549640
JSValueConst setlike;
4963649641
bool found;
4963749642
JSMapState *s;
49638-
int64_t size;
49643+
uint64_t size;
4963949644
int done, ok;
4964049645

4964149646
s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
@@ -49685,7 +49690,7 @@ static JSValue js_set_isSupersetOf(JSContext *ctx, JSValueConst this_val,
4968549690
int done;
4968649691
bool found;
4968749692
JSMapState *s;
49688-
int64_t size;
49693+
uint64_t size;
4968949694

4969049695
s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
4969149696
if (!s)
@@ -49738,7 +49743,7 @@ static JSValue js_set_intersection(JSContext *ctx, JSValueConst this_val,
4973849743
JSValueConst setlike;
4973949744
JSMapState *s, *t;
4974049745
JSMapRecord *mr;
49741-
int64_t size;
49746+
uint64_t size;
4974249747
int done, ok;
4974349748

4974449749
s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
@@ -49831,7 +49836,7 @@ static JSValue js_set_difference(JSContext *ctx, JSValueConst this_val,
4983149836
JSValueConst setlike;
4983249837
JSMapState *s, *t;
4983349838
JSMapRecord *mr;
49834-
int64_t size;
49839+
uint64_t size;
4983549840
int done;
4983649841
int ok;
4983749842

@@ -49920,7 +49925,7 @@ static JSValue js_set_symmetricDifference(JSContext *ctx, JSValueConst this_val,
4992049925
struct list_head *el;
4992149926
JSMapState *s, *t;
4992249927
JSMapRecord *mr;
49923-
int64_t size;
49928+
uint64_t size;
4992449929
int done;
4992549930
bool present;
4992649931

@@ -50005,7 +50010,7 @@ static JSValue js_set_union(JSContext *ctx, JSValueConst this_val,
5000550010
struct list_head *el;
5000650011
JSMapState *s, *t;
5000750012
JSMapRecord *mr;
50008-
int64_t size;
50013+
uint64_t size;
5000950014
int done;
5001050015

5001150016
iter = JS_UNDEFINED;

tests/test_builtin.js

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -933,22 +933,51 @@ function test_set()
933933
}
934934
// set must be bigger than iter.a to hit iter.next and iter.return
935935
assert(new Set([4,5,6,7]).isSupersetOf(setlike), true)
936-
assert(iter.nextCalls, 4);
937-
assert(iter.returnCalls, 0);
936+
assert(iter.nextCalls, 4)
937+
assert(iter.returnCalls, 0)
938938
iter.nextCalls = iter.returnCalls = 0
939939
assert(new Set([0,1,2,3]).isSupersetOf(setlike), false)
940-
assert(iter.nextCalls, 1);
941-
assert(iter.returnCalls, 1);
940+
assert(iter.nextCalls, 1)
941+
assert(iter.returnCalls, 1)
942942
iter.nextCalls = iter.returnCalls = 0
943943
// set must be bigger than iter.a to hit iter.next and iter.return
944944
assert(new Set([4,5,6,7]).isDisjointFrom(setlike), false)
945-
assert(iter.nextCalls, 1);
946-
assert(iter.returnCalls, 1);
945+
assert(iter.nextCalls, 1)
946+
assert(iter.returnCalls, 1)
947947
iter.nextCalls = iter.returnCalls = 0
948948
assert(new Set([0,1,2,3]).isDisjointFrom(setlike), true)
949-
assert(iter.nextCalls, 4);
950-
assert(iter.returnCalls, 0);
949+
assert(iter.nextCalls, 4)
950+
assert(iter.returnCalls, 0)
951951
iter.nextCalls = iter.returnCalls = 0
952+
function expectException(klass, sizes) {
953+
for (const size of sizes) {
954+
let ex
955+
try {
956+
new Set([]).union({size})
957+
} catch (e) {
958+
ex = e
959+
}
960+
assert(ex instanceof klass)
961+
assert(typeof ex.message, "string")
962+
assert(ex.message.includes(".size"))
963+
}
964+
}
965+
expectException(RangeError, [-1, -(Number.MAX_SAFE_INTEGER+1), -Infinity])
966+
expectException(TypeError, [NaN])
967+
const legal = [
968+
0, -0, 1, 2,
969+
Number.MAX_SAFE_INTEGER + 1,
970+
Number.MAX_SAFE_INTEGER + 2,
971+
Number.MAX_SAFE_INTEGER + 3,
972+
Infinity
973+
]
974+
for (const size of legal) {
975+
new Set([]).union({
976+
size,
977+
has() { return false },
978+
keys() { return [].values() },
979+
})
980+
}
952981
}
953982

954983
function test_weak_set()

0 commit comments

Comments
 (0)