Skip to content

Commit b51b510

Browse files
authored
Handle negative zero typed array indices correctly (#212)
`ta["-0"] = 42` is a thing and not just any thing but a decidedly weird thing: it completes successful, sets no property, but still evaluates the value for side effects.
1 parent 5168db1 commit b51b510

File tree

3 files changed

+31
-10
lines changed

3 files changed

+31
-10
lines changed

quickjs-atom.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ DEF(await, "await")
7777

7878
/* empty string */
7979
DEF(empty_string, "")
80+
DEF(negative_zero, "-0")
8081
/* identifiers */
8182
DEF(length, "length")
8283
DEF(fileName, "fileName")

quickjs.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,11 @@ static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);
10701070
static void js_promise_resolve_function_mark(JSRuntime *rt, JSValue val,
10711071
JS_MarkFunc *mark_func);
10721072

1073+
#define HINT_STRING 0
1074+
#define HINT_NUMBER 1
1075+
#define HINT_NONE 2
1076+
#define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive
1077+
static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint);
10731078
static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
10741079
static int JS_ToBoolFree(JSContext *ctx, JSValue val);
10751080
static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
@@ -8224,6 +8229,7 @@ static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)
82248229

82258230
/* generic (and slower) version of JS_SetProperty() for
82268231
* Reflect.set(). 'obj' must be an object. */
8232+
// TODO(bnoordhuis) merge with JS_SetPropertyInternal2
82278233
static int JS_SetPropertyGeneric(JSContext *ctx,
82288234
JSValue obj, JSAtom prop,
82298235
JSValue val, JSValue this_obj,
@@ -8289,6 +8295,18 @@ static int JS_SetPropertyGeneric(JSContext *ctx,
82898295
}
82908296

82918297
p = JS_VALUE_GET_OBJ(this_obj);
8298+
if (prop == JS_ATOM_negative_zero &&
8299+
p->class_id >= JS_CLASS_UINT8C_ARRAY &&
8300+
p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
8301+
// per spec: CanonicalNumericIndexString("-0") evaluates to -0 and
8302+
// returns true but sets no property; side effects of evaluating
8303+
// the value should still be observable
8304+
val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
8305+
if (JS_IsException(val))
8306+
return -1;
8307+
JS_FreeValue(ctx, val);
8308+
return TRUE;
8309+
}
82928310

82938311
/* modify the property in this_obj if it already exists */
82948312
ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
@@ -8363,6 +8381,18 @@ int JS_SetPropertyInternal2(JSContext *ctx, JSValue this_obj,
83638381
}
83648382
}
83658383
p = JS_VALUE_GET_OBJ(this_obj);
8384+
if (prop == JS_ATOM_negative_zero &&
8385+
p->class_id >= JS_CLASS_UINT8C_ARRAY &&
8386+
p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
8387+
// per spec: CanonicalNumericIndexString("-0") evaluates to -0 and
8388+
// returns true but sets no property; side effects of evaluating
8389+
// the value should still be observable
8390+
val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
8391+
if (JS_IsException(val))
8392+
return -1;
8393+
JS_FreeValue(ctx, val);
8394+
return TRUE;
8395+
}
83668396
retry:
83678397
prs = find_own_property_ic(&pr, p, prop, &offset);
83688398
if (prs) {
@@ -9770,12 +9800,6 @@ void *JS_GetAnyOpaque(JSValue obj, JSClassID *class_id)
97709800
return p->u.opaque;
97719801
}
97729802

9773-
#define HINT_STRING 0
9774-
#define HINT_NUMBER 1
9775-
#define HINT_NONE 2
9776-
/* don't try Symbol.toPrimitive */
9777-
#define HINT_FORCE_ORDINARY (1 << 4)
9778-
97799803
static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
97809804
{
97819805
int i;

test262_errors.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,12 @@ test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/tonumb
1313
test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/tonumber-value-detached-buffer.js:42: strict mode: Test262Error: Reflect.defineProperty(ta, 0, {value: {valueOf() {$DETACHBUFFER(ta.buffer); return 42;}}} ) must return true Expected SameValue(«false», «true») to be true (Testing with Float64Array.)
1414
test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/detached-buffer.js:34: TypeError: cannot convert bigint to number (Testing with BigInt64Array.)
1515
test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/detached-buffer.js:34: strict mode: TypeError: cannot convert bigint to number (Testing with BigInt64Array.)
16-
test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-minus-zero.js:20: Test262Error: Reflect.set("new TA([42n])", "-0", 1n) must return true Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.)
17-
test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-minus-zero.js:20: strict mode: Test262Error: Reflect.set("new TA([42n])", "-0", 1n) must return true Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.)
1816
test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-not-integer.js:21: Test262Error: Reflect.set("new TA([42n])", "1.1", 1n) must return true Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.)
1917
test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-not-integer.js:21: strict mode: Test262Error: Reflect.set("new TA([42n])", "1.1", 1n) must return true Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.)
2018
test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-out-of-bounds.js:27: Test262Error: Reflect.set("new TA([42n])", "-1", 1n) must return false Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.)
2119
test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-out-of-bounds.js:27: strict mode: Test262Error: Reflect.set("new TA([42n])", "-1", 1n) must return false Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.)
2220
test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/tonumber-value-detached-buffer.js:24: Test262Error: Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.)
2321
test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/tonumber-value-detached-buffer.js:24: strict mode: Test262Error: Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.)
24-
test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-minus-zero.js:22: Test262Error: Reflect.set(sample, "-0", 1) must return true Expected SameValue(«false», «true») to be true (Testing with Float64Array.)
25-
test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-minus-zero.js:22: strict mode: Test262Error: Reflect.set(sample, "-0", 1) must return true Expected SameValue(«false», «true») to be true (Testing with Float64Array.)
2622
test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-not-integer.js:22: Test262Error: Reflect.set(sample, "1.1", 1) must return true Expected SameValue(«false», «true») to be true (Testing with Float64Array.)
2723
test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-not-integer.js:22: strict mode: Test262Error: Reflect.set(sample, "1.1", 1) must return true Expected SameValue(«false», «true») to be true (Testing with Float64Array.)
2824
test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-out-of-bounds.js:22: Test262Error: Reflect.set(sample, "-1", 1) must return true Expected SameValue(«false», «true») to be true (Testing with Float64Array.)

0 commit comments

Comments
 (0)