diff --git a/quickjs.c b/quickjs.c index af30fc09e..38f41d1a4 100644 --- a/quickjs.c +++ b/quickjs.c @@ -49502,11 +49502,11 @@ static int JS_WriteSet(BCWriterState *s, struct JSMapState *map_state) } static int js_setlike_get_props(JSContext *ctx, JSValueConst setlike, - int64_t *psize, JSValue *phas, JSValue *pkeys) + uint64_t *psize, JSValue *phas, JSValue *pkeys) { JSValue has, keys, v; JSMapState *s; - int64_t size; + uint64_t size; double d; keys = JS_UNDEFINED; @@ -49524,14 +49524,19 @@ static int js_setlike_get_props(JSContext *ctx, JSValueConst setlike, } if (JS_ToFloat64Free(ctx, &d, v) < 0) return -1; + if (d < 0) { + JS_ThrowRangeError(ctx, ".size is not a legal size"); + return -1; + } if (isnan(d)) { - JS_ThrowTypeError(ctx, ".size is not a number"); + JS_ThrowTypeError(ctx, ".size is not a legal size"); return -1; } - // TODO(bnoordhuis) add precision check, can be double - // that cannot be accurately represented as an int64, - // like Infinity and numbers outside MAX_SAFE_INTEGER - size = d; + if (isinf(d) || d > (double)MAX_SAFE_INTEGER) { + size = UINT64_MAX; + } else { + size = (uint64_t)d; // cast for expository reasons + } } has = JS_GetProperty(ctx, setlike, JS_ATOM_has); if (JS_IsException(has)) @@ -49565,7 +49570,7 @@ static JSValue js_set_isDisjointFrom(JSContext *ctx, JSValueConst this_val, int done; bool found; JSMapState *s; - int64_t size; + uint64_t size; int ok; s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); @@ -49635,7 +49640,7 @@ static JSValue js_set_isSubsetOf(JSContext *ctx, JSValueConst this_val, JSValueConst setlike; bool found; JSMapState *s; - int64_t size; + uint64_t size; int done, ok; s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); @@ -49685,7 +49690,7 @@ static JSValue js_set_isSupersetOf(JSContext *ctx, JSValueConst this_val, int done; bool found; JSMapState *s; - int64_t size; + uint64_t size; s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); if (!s) @@ -49738,7 +49743,7 @@ static JSValue js_set_intersection(JSContext *ctx, JSValueConst this_val, JSValueConst setlike; JSMapState *s, *t; JSMapRecord *mr; - int64_t size; + uint64_t size; int done, ok; s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); @@ -49831,7 +49836,7 @@ static JSValue js_set_difference(JSContext *ctx, JSValueConst this_val, JSValueConst setlike; JSMapState *s, *t; JSMapRecord *mr; - int64_t size; + uint64_t size; int done; int ok; @@ -49920,7 +49925,7 @@ static JSValue js_set_symmetricDifference(JSContext *ctx, JSValueConst this_val, struct list_head *el; JSMapState *s, *t; JSMapRecord *mr; - int64_t size; + uint64_t size; int done; bool present; @@ -50005,7 +50010,7 @@ static JSValue js_set_union(JSContext *ctx, JSValueConst this_val, struct list_head *el; JSMapState *s, *t; JSMapRecord *mr; - int64_t size; + uint64_t size; int done; iter = JS_UNDEFINED; diff --git a/tests/test_builtin.js b/tests/test_builtin.js index 74c9ec347..255886adf 100644 --- a/tests/test_builtin.js +++ b/tests/test_builtin.js @@ -933,22 +933,51 @@ function test_set() } // set must be bigger than iter.a to hit iter.next and iter.return assert(new Set([4,5,6,7]).isSupersetOf(setlike), true) - assert(iter.nextCalls, 4); - assert(iter.returnCalls, 0); + assert(iter.nextCalls, 4) + assert(iter.returnCalls, 0) iter.nextCalls = iter.returnCalls = 0 assert(new Set([0,1,2,3]).isSupersetOf(setlike), false) - assert(iter.nextCalls, 1); - assert(iter.returnCalls, 1); + assert(iter.nextCalls, 1) + assert(iter.returnCalls, 1) iter.nextCalls = iter.returnCalls = 0 // set must be bigger than iter.a to hit iter.next and iter.return assert(new Set([4,5,6,7]).isDisjointFrom(setlike), false) - assert(iter.nextCalls, 1); - assert(iter.returnCalls, 1); + assert(iter.nextCalls, 1) + assert(iter.returnCalls, 1) iter.nextCalls = iter.returnCalls = 0 assert(new Set([0,1,2,3]).isDisjointFrom(setlike), true) - assert(iter.nextCalls, 4); - assert(iter.returnCalls, 0); + assert(iter.nextCalls, 4) + assert(iter.returnCalls, 0) iter.nextCalls = iter.returnCalls = 0 + function expectException(klass, sizes) { + for (const size of sizes) { + let ex + try { + new Set([]).union({size}) + } catch (e) { + ex = e + } + assert(ex instanceof klass) + assert(typeof ex.message, "string") + assert(ex.message.includes(".size")) + } + } + expectException(RangeError, [-1, -(Number.MAX_SAFE_INTEGER+1), -Infinity]) + expectException(TypeError, [NaN]) + const legal = [ + 0, -0, 1, 2, + Number.MAX_SAFE_INTEGER + 1, + Number.MAX_SAFE_INTEGER + 2, + Number.MAX_SAFE_INTEGER + 3, + Infinity + ] + for (const size of legal) { + new Set([]).union({ + size, + has() { return false }, + keys() { return [].values() }, + }) + } } function test_weak_set()