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
This commit is contained in:
Tom Schuster 2018-07-01 13:03:11 +02:00
parent 476f752665
commit 604d57faf2
3 changed files with 114 additions and 58 deletions

View File

@ -21,4 +21,3 @@ function test(arr) {
} }
test([123]); test([123]);
test(new Int32Array([123]));

View File

@ -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);

View File

@ -1919,6 +1919,36 @@ js::NativeDefineDataProperty(JSContext* cx, HandleNativeObject obj, PropertyName
return NativeDefineDataProperty(cx, obj, id, value, attrs); return NativeDefineDataProperty(cx, obj, id, value, attrs);
} }
// ES2019 draft rev e7dc63fb5d1c26beada9ffc12dc78aa6548f1fb5
// 9.4.5.9 IntegerIndexedElementSet
static bool
DefineNonexistentTypedArrayElement(JSContext* cx, Handle<TypedArrayObject*> 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 static bool
DefineNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, DefineNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
HandleValue v, ObjectOpResult& result) 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. // 9.4.5.5 step 2. Indexed properties of typed arrays are special.
uint64_t index; uint64_t index;
if (IsTypedArrayIndex(id, &index)) { if (IsTypedArrayIndex(id, &index)) {
// ES2019 draft rev e7dc63fb5d1c26beada9ffc12dc78aa6548f1fb5 Rooted<TypedArrayObject*> tobj(cx, &obj->as<TypedArrayObject>());
// 9.4.5.9 IntegerIndexedElementSet return DefineNonexistentTypedArrayElement(cx, tobj, index, v, 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->as<TypedArrayObject>().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<TypedArrayObject>().hasDetachedBuffer())
return result.failSoft(JSMSG_TYPED_ARRAY_DETACHED);
// Steps 6-9.
// We (wrongly) ignore out of range defines.
return result.failSoft(JSMSG_BAD_INDEX);
} }
} else if (obj->is<ArgumentsObject>()) { } else if (obj->is<ArgumentsObject>()) {
// If this method is called with either |length| or |@@iterator|, the // 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); return DefineNonexistentProperty(cx, obj, id, v, result);
} }
if (IsQualified && obj->is<TypedArrayObject>()) {
// 9.4.5.5 step 2. Indexed properties of typed arrays are special.
uint64_t index;
if (IsTypedArrayIndex(id, &index)) {
Rooted<TypedArrayObject*> tobj(cx, &obj->as<TypedArrayObject>());
return DefineNonexistentTypedArrayElement(cx, tobj, index, v, result);
}
}
return SetPropertyByDefining(cx, id, v, receiver, result); return SetPropertyByDefining(cx, id, v, receiver, result);
} }
/* // ES2019 draft rev e7dc63fb5d1c26beada9ffc12dc78aa6548f1fb5
* Set an existing own property obj[index] that's a dense element or typed // 9.4.5.9 IntegerIndexedElementSet
* array element. // Set an existing own property obj[index] that's a typed array element.
*/
static bool static bool
SetDenseOrTypedArrayElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue v, SetTypedArrayElement(JSContext* cx, Handle<TypedArrayObject*> obj, uint32_t index, HandleValue v,
ObjectOpResult& result) ObjectOpResult& result)
{ {
if (obj->is<TypedArrayObject>()) { // Steps 1-2 are enforced by the caller.
// ES2019 draft rev e7dc63fb5d1c26beada9ffc12dc78aa6548f1fb5
// 9.4.5.9 IntegerIndexedElementSet
// Steps 1-2 are enforced by the caller. // Step 3.
double d;
if (!ToNumber(cx, v, &d))
return false;
// Step 3. // Steps 6-7 don't apply for existing typed array elements.
double d;
if (!ToNumber(cx, v, &d))
return false;
// 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
// Steps 8-16. // current behavior. (ES6 currently says to throw for this in
// Silently do nothing for out-of-bounds sets, for consistency with // strict mode code, so we may eventually need to change.)
// current behavior. (ES6 currently says to throw for this in uint32_t len = obj->as<TypedArrayObject>().length();
// strict mode code, so we may eventually need to change.) if (index < len) {
uint32_t len = obj->as<TypedArrayObject>().length(); TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, d);
if (index < len) { return result.succeed();
TypedArrayObject::setElement(obj->as<TypedArrayObject>(), 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<TypedArrayObject>().hasDetachedBuffer());
return result.failSoft(JSMSG_TYPED_ARRAY_DETACHED);
} }
// 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<TypedArrayObject>().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<TypedArrayObject>());
MOZ_ASSERT(obj->containsDenseElement(index)); MOZ_ASSERT(obj->containsDenseElement(index));
if (!obj->maybeCopyElementsForWrite(cx)) if (!obj->maybeCopyElementsForWrite(cx))
@ -2716,13 +2734,19 @@ SetExistingProperty(JSContext* cx, HandleId id, HandleValue v, HandleValue recei
{ {
// Step 5 for dense elements. // Step 5 for dense elements.
if (prop.isDenseOrTypedArrayElement()) { if (prop.isDenseOrTypedArrayElement()) {
// TypedArray [[Set]] ignores the receiver completely.
if (pobj->is<TypedArrayObject>()) {
Rooted<TypedArrayObject*> tobj(cx, &pobj->as<TypedArrayObject>());
return SetTypedArrayElement(cx, tobj, JSID_TO_INT(id), v, result);
}
// Step 5.a. // Step 5.a.
if (pobj->denseElementsAreFrozen()) if (pobj->denseElementsAreFrozen())
return result.fail(JSMSG_READ_ONLY); return result.fail(JSMSG_READ_ONLY);
// Pure optimization for the common case: // Pure optimization for the common case:
if (receiver.isObject() && pobj == &receiver.toObject()) 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. // Steps 5.b-f.
return SetPropertyByDefining(cx, id, v, receiver, result); return SetPropertyByDefining(cx, id, v, receiver, result);