From 604d57faf284d343c077488913c60fe2407589ab Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Sun, 1 Jul 2018 13:03:11 +0200 Subject: [PATCH] Bug 1413794 - Typed array [[Set]] wrongly inspects the receiver when a canonical numeric string is passed as property name. r=anba --HG-- extra : rebase_source : f28f2528aa258b0ac3eea15bbaf0690bd1a19ace --- .../tests/proxy/testDirectProxySetArray4.js | 1 - .../non262/TypedArray/set-with-receiver.js | 33 +++++ js/src/vm/NativeObject.cpp | 138 ++++++++++-------- 3 files changed, 114 insertions(+), 58 deletions(-) create mode 100644 js/src/tests/non262/TypedArray/set-with-receiver.js diff --git a/js/src/jit-test/tests/proxy/testDirectProxySetArray4.js b/js/src/jit-test/tests/proxy/testDirectProxySetArray4.js index 524ddca456ce..2c40df1e2654 100644 --- a/js/src/jit-test/tests/proxy/testDirectProxySetArray4.js +++ b/js/src/jit-test/tests/proxy/testDirectProxySetArray4.js @@ -21,4 +21,3 @@ function test(arr) { } test([123]); -test(new Int32Array([123])); diff --git a/js/src/tests/non262/TypedArray/set-with-receiver.js b/js/src/tests/non262/TypedArray/set-with-receiver.js new file mode 100644 index 000000000000..6e75f544b033 --- /dev/null +++ b/js/src/tests/non262/TypedArray/set-with-receiver.js @@ -0,0 +1,33 @@ +for (var constructor of anyTypedArrayConstructors) { + var receiver = new Proxy({}, { + getOwnPropertyDescriptor(p) { + throw "fail"; + }, + + defineProperty() { + throw "fail"; + } + }); + + var ta = new constructor(1); + assertEq(Reflect.set(ta, 0, 47, receiver), true); + assertEq(ta[0], 47); + + // Out-of-bounds + assertEq(Reflect.set(ta, 10, 47, receiver), false); + assertEq(ta[10], undefined); + + // Detached + if (typeof detachArrayBuffer === "function" && + !isSharedConstructor(constructor)) + { + detachArrayBuffer(ta.buffer) + + assertEq(ta[0], undefined); + assertEq(Reflect.set(ta, 0, 47, receiver), false); + assertEq(ta[0], undefined); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index 4b7a720be965..ff69d1a314b4 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -1919,6 +1919,36 @@ js::NativeDefineDataProperty(JSContext* cx, HandleNativeObject obj, PropertyName return NativeDefineDataProperty(cx, obj, id, value, attrs); } + +// ES2019 draft rev e7dc63fb5d1c26beada9ffc12dc78aa6548f1fb5 +// 9.4.5.9 IntegerIndexedElementSet +static bool +DefineNonexistentTypedArrayElement(JSContext* cx, Handle obj, uint64_t index, + HandleValue v, ObjectOpResult& result) +{ + // This method is only called for non-existent properties, which + // means any absent indexed property must be out of range. + MOZ_ASSERT(index >= obj->length()); + + // Steps 1-2 are enforced by the caller. + + // Step 3. + // We still need to call ToNumber, because of its possible side + // effects. + double d; + if (!ToNumber(cx, v, &d)) + return false; + + // Steps 4-5. + // ToNumber may have detached the array buffer. + if (obj->hasDetachedBuffer()) + return result.failSoft(JSMSG_TYPED_ARRAY_DETACHED); + + // Steps 6-9. + // We (wrongly) ignore out of range defines. + return result.failSoft(JSMSG_BAD_INDEX); +} + static bool DefineNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v, ObjectOpResult& result) @@ -1941,30 +1971,8 @@ DefineNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, // 9.4.5.5 step 2. Indexed properties of typed arrays are special. uint64_t index; if (IsTypedArrayIndex(id, &index)) { - // ES2019 draft rev e7dc63fb5d1c26beada9ffc12dc78aa6548f1fb5 - // 9.4.5.9 IntegerIndexedElementSet - - // This method is only called for non-existent properties, which - // means any absent indexed property must be out of range. - MOZ_ASSERT(index >= obj->as().length()); - - // Steps 1-2 are enforced by the caller. - - // Step 3. - // We still need to call ToNumber, because of its possible side - // effects. - double d; - if (!ToNumber(cx, v, &d)) - return false; - - // Steps 4-5. - // ToNumber may have detached the array buffer. - if (obj->as().hasDetachedBuffer()) - return result.failSoft(JSMSG_TYPED_ARRAY_DETACHED); - - // Steps 6-9. - // We (wrongly) ignore out of range defines. - return result.failSoft(JSMSG_BAD_INDEX); + Rooted tobj(cx, &obj->as()); + return DefineNonexistentTypedArrayElement(cx, tobj, index, v, result); } } else if (obj->is()) { // If this method is called with either |length| or |@@iterator|, the @@ -2651,48 +2659,58 @@ SetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, Handl return DefineNonexistentProperty(cx, obj, id, v, result); } + if (IsQualified && obj->is()) { + // 9.4.5.5 step 2. Indexed properties of typed arrays are special. + uint64_t index; + if (IsTypedArrayIndex(id, &index)) { + Rooted tobj(cx, &obj->as()); + return DefineNonexistentTypedArrayElement(cx, tobj, index, v, result); + } + } + return SetPropertyByDefining(cx, id, v, receiver, result); } -/* - * Set an existing own property obj[index] that's a dense element or typed - * array element. - */ +// ES2019 draft rev e7dc63fb5d1c26beada9ffc12dc78aa6548f1fb5 +// 9.4.5.9 IntegerIndexedElementSet +// Set an existing own property obj[index] that's a typed array element. static bool -SetDenseOrTypedArrayElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue v, - ObjectOpResult& result) +SetTypedArrayElement(JSContext* cx, Handle obj, uint32_t index, HandleValue v, + ObjectOpResult& result) { - if (obj->is()) { - // ES2019 draft rev e7dc63fb5d1c26beada9ffc12dc78aa6548f1fb5 - // 9.4.5.9 IntegerIndexedElementSet + // Steps 1-2 are enforced by the caller. - // Steps 1-2 are enforced by the caller. + // Step 3. + double d; + if (!ToNumber(cx, v, &d)) + return false; - // Step 3. - double d; - if (!ToNumber(cx, v, &d)) - return false; + // Steps 6-7 don't apply for existing typed array elements. - // Steps 6-7 don't apply for existing typed array elements. - - // Steps 8-16. - // Silently do nothing for out-of-bounds sets, for consistency with - // current behavior. (ES6 currently says to throw for this in - // strict mode code, so we may eventually need to change.) - uint32_t len = obj->as().length(); - if (index < len) { - TypedArrayObject::setElement(obj->as(), index, d); - return result.succeed(); - } - - // Steps 4-5. - // A previously existing typed array element can only be out-of-bounds - // if the above ToNumber call detached the typed array's buffer. - MOZ_ASSERT(obj->as().hasDetachedBuffer()); - - return result.failSoft(JSMSG_TYPED_ARRAY_DETACHED); + // Steps 8-16. + // Silently do nothing for out-of-bounds sets, for consistency with + // current behavior. (ES6 currently says to throw for this in + // strict mode code, so we may eventually need to change.) + uint32_t len = obj->as().length(); + if (index < len) { + TypedArrayObject::setElement(obj->as(), index, d); + return result.succeed(); } + // Steps 4-5. + // A previously existing typed array element can only be out-of-bounds + // if the above ToNumber call detached the typed array's buffer. + MOZ_ASSERT(obj->as().hasDetachedBuffer()); + + return result.failSoft(JSMSG_TYPED_ARRAY_DETACHED); +} + +// Set an existing own property obj[index] that's a dense element. +static bool +SetDenseElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue v, + ObjectOpResult& result) +{ + MOZ_ASSERT(!obj->is()); MOZ_ASSERT(obj->containsDenseElement(index)); if (!obj->maybeCopyElementsForWrite(cx)) @@ -2716,13 +2734,19 @@ SetExistingProperty(JSContext* cx, HandleId id, HandleValue v, HandleValue recei { // Step 5 for dense elements. if (prop.isDenseOrTypedArrayElement()) { + // TypedArray [[Set]] ignores the receiver completely. + if (pobj->is()) { + Rooted tobj(cx, &pobj->as()); + return SetTypedArrayElement(cx, tobj, JSID_TO_INT(id), v, result); + } + // Step 5.a. if (pobj->denseElementsAreFrozen()) return result.fail(JSMSG_READ_ONLY); // Pure optimization for the common case: if (receiver.isObject() && pobj == &receiver.toObject()) - return SetDenseOrTypedArrayElement(cx, pobj, JSID_TO_INT(id), v, result); + return SetDenseElement(cx, pobj, JSID_TO_INT(id), v, result); // Steps 5.b-f. return SetPropertyByDefining(cx, id, v, receiver, result);