mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1121554 - Include receiver argument in setProperty hooks, r=jorendorff.
This commit is contained in:
parent
30b0849a33
commit
8d8840048c
@ -173,7 +173,7 @@ typedef bool
|
||||
(* GetPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id,
|
||||
JS::MutableHandleValue vp);
|
||||
typedef bool
|
||||
(* SetPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
|
||||
(* SetPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id,
|
||||
JS::MutableHandleValue vp, bool strict);
|
||||
typedef bool
|
||||
(* GetOwnPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
|
||||
|
@ -1768,7 +1768,8 @@ bool
|
||||
TypedObject::obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
|
||||
PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
|
||||
{
|
||||
return ReportPropertyError(cx, JSMSG_UNDEFINED_PROP, id);
|
||||
Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
|
||||
return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1816,7 +1817,6 @@ bool
|
||||
TypedObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
HandleId id, MutableHandleValue vp)
|
||||
{
|
||||
MOZ_ASSERT(obj->is<TypedObject>());
|
||||
Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
|
||||
|
||||
// Dispatch elements to obj_getElement:
|
||||
@ -1917,7 +1917,7 @@ TypedObject::obj_getArrayElement(JSContext *cx,
|
||||
}
|
||||
|
||||
bool
|
||||
TypedObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleId id,
|
||||
TypedObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
|
||||
MutableHandleValue vp, bool strict)
|
||||
{
|
||||
MOZ_ASSERT(obj->is<TypedObject>());
|
||||
@ -1925,7 +1925,7 @@ TypedObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleId id,
|
||||
|
||||
uint32_t index;
|
||||
if (js_IdIsIndex(id, &index))
|
||||
return obj_setElement(cx, obj, index, vp, strict);
|
||||
return obj_setElement(cx, obj, receiver, index, vp, strict);
|
||||
|
||||
switch (typedObj->typeDescr().kind()) {
|
||||
case type::Scalar:
|
||||
@ -1937,9 +1937,12 @@ TypedObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleId id,
|
||||
|
||||
case type::Array:
|
||||
if (JSID_IS_ATOM(id, cx->names().length)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage,
|
||||
nullptr, JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
|
||||
return false;
|
||||
if (obj == receiver) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage,
|
||||
nullptr, JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
|
||||
return false;
|
||||
}
|
||||
return SetNonWritableProperty(cx, id, strict);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1950,6 +1953,9 @@ TypedObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleId id,
|
||||
if (!descr->fieldIndex(id, &fieldIndex))
|
||||
break;
|
||||
|
||||
if (obj != receiver)
|
||||
return SetPropertyByDefining(cx, obj, receiver, id, vp, strict, false);
|
||||
|
||||
size_t offset = descr->fieldOffset(fieldIndex);
|
||||
Rooted<TypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
|
||||
RootedAtom fieldName(cx, &descr->fieldName(fieldIndex));
|
||||
@ -1957,17 +1963,26 @@ TypedObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleId id,
|
||||
}
|
||||
}
|
||||
|
||||
return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
|
||||
return SetPropertyOnProto(cx, obj, receiver, id, vp, strict);
|
||||
}
|
||||
|
||||
bool
|
||||
TypedObject::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
|
||||
MutableHandleValue vp, bool strict)
|
||||
TypedObject::obj_setElement(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
uint32_t index, MutableHandleValue vp, bool strict)
|
||||
{
|
||||
MOZ_ASSERT(obj->is<TypedObject>());
|
||||
Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
|
||||
Rooted<TypeDescr *> descr(cx, &typedObj->typeDescr());
|
||||
|
||||
if (obj != receiver) {
|
||||
RootedId id(cx);
|
||||
if (!IndexToId(cx, index, &id))
|
||||
return false;
|
||||
if (descr->is<ArrayTypeDescr>())
|
||||
return SetPropertyByDefining(cx, obj, receiver, id, vp, strict, false);
|
||||
return SetPropertyOnProto(cx, obj, receiver, id, vp, strict);
|
||||
}
|
||||
|
||||
switch (descr->kind()) {
|
||||
case type::Scalar:
|
||||
case type::Reference:
|
||||
@ -1979,15 +1994,18 @@ TypedObject::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
|
||||
return obj_setArrayElement(cx, typedObj, descr, index, vp);
|
||||
}
|
||||
|
||||
return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
|
||||
RootedId id(cx);
|
||||
if (!IndexToId(cx, index, &id))
|
||||
return false;
|
||||
return SetPropertyOnProto(cx, obj, receiver, id, vp, strict);
|
||||
}
|
||||
|
||||
/*static*/ bool
|
||||
TypedObject::obj_setArrayElement(JSContext *cx,
|
||||
Handle<TypedObject*> typedObj,
|
||||
Handle<TypeDescr*> descr,
|
||||
uint32_t index,
|
||||
MutableHandleValue vp)
|
||||
Handle<TypedObject*> typedObj,
|
||||
Handle<TypeDescr*> descr,
|
||||
uint32_t index,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
if (index >= (size_t) typedObj->length()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage,
|
||||
|
@ -543,11 +543,10 @@ class TypedObject : public JSObject
|
||||
static bool obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
uint32_t index, MutableHandleValue vp);
|
||||
|
||||
static bool obj_setProperty(JSContext *cx, HandleObject obj, HandleId id,
|
||||
MutableHandleValue vp, bool strict);
|
||||
|
||||
static bool obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
|
||||
MutableHandleValue vp, bool strict);
|
||||
static bool obj_setProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
HandleId id, MutableHandleValue vp, bool strict);
|
||||
static bool obj_setElement(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
uint32_t index, MutableHandleValue vp, bool strict);
|
||||
|
||||
static bool obj_getOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc);
|
||||
|
@ -0,0 +1,72 @@
|
||||
|
||||
if (typeof TypedObject === "undefined")
|
||||
quit();
|
||||
|
||||
// Test the behavior of property sets on typed objects when they are a
|
||||
// prototype or their prototype has a setter.
|
||||
var TO = TypedObject;
|
||||
|
||||
function assertThrows(fun, errorType) {
|
||||
try {
|
||||
fun();
|
||||
assertEq(true, false, "Expected error, but none was thrown");
|
||||
} catch (e) {
|
||||
assertEq(e instanceof errorType, true, "Wrong error type thrown");
|
||||
}
|
||||
}
|
||||
|
||||
var PointType = new TO.StructType({x: TO.int32, y: TO.int32 });
|
||||
|
||||
function testPoint() {
|
||||
var p = new PointType();
|
||||
var sub = Object.create(p);
|
||||
var found;
|
||||
Object.defineProperty(PointType.prototype, "z", {set: function(a) { this.d = a; }});
|
||||
Object.defineProperty(PointType.prototype, "innocuous", {set: function(a) { found = a; }});
|
||||
|
||||
sub.x = 5;
|
||||
assertEq(sub.x, 5);
|
||||
assertEq(p.x, 0);
|
||||
|
||||
sub.z = 5;
|
||||
assertEq(sub.d, 5);
|
||||
assertEq(sub.z, undefined);
|
||||
|
||||
sub[3] = 5;
|
||||
assertEq(sub[3], 5);
|
||||
|
||||
p.innocuous = 10;
|
||||
assertEq(found, 10);
|
||||
|
||||
assertThrows(function() {
|
||||
p.z = 10;
|
||||
assertEq(true, false);
|
||||
}, TypeError);
|
||||
}
|
||||
testPoint();
|
||||
|
||||
var IntArrayType = new TO.ArrayType(TO.int32, 3);
|
||||
|
||||
function testArray() {
|
||||
var arr = new IntArrayType();
|
||||
var found;
|
||||
Object.defineProperty(IntArrayType.prototype, 5, {set: function(a) { found = a; }});
|
||||
|
||||
assertThrows(function() {
|
||||
arr[5] = 5;
|
||||
}, RangeError);
|
||||
|
||||
assertThrows(function() {
|
||||
arr[4] = 5;
|
||||
}, RangeError);
|
||||
|
||||
var p = Object.create(arr);
|
||||
p.length = 100;
|
||||
assertEq(p.length, 3);
|
||||
|
||||
assertThrows(function() {
|
||||
"use strict";
|
||||
p.length = 100;
|
||||
}, TypeError);
|
||||
}
|
||||
testArray();
|
31
js/src/jit-test/tests/basic/unboxed-object-set-property.js
Normal file
31
js/src/jit-test/tests/basic/unboxed-object-set-property.js
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
// Use the correct receiver when non-native objects are prototypes of other objects.
|
||||
|
||||
function Thing(a, b) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
function foo() {
|
||||
var array = [];
|
||||
for (var i = 0; i < 10000; i++)
|
||||
array.push(new Thing(i, i + 1));
|
||||
|
||||
var proto = new Thing(1, 2);
|
||||
var obj = Object.create(proto);
|
||||
|
||||
Object.defineProperty(Thing.prototype, "c", {set:function() { this.d = 0; }});
|
||||
obj.c = 3;
|
||||
assertEq(obj.c, undefined);
|
||||
assertEq(obj.d, 0);
|
||||
assertEq(obj.hasOwnProperty("d"), true);
|
||||
assertEq(proto.d, undefined);
|
||||
assertEq(proto.hasOwnProperty("d"), false);
|
||||
|
||||
obj.a = 3;
|
||||
assertEq(obj.a, 3);
|
||||
assertEq(proto.a, 1);
|
||||
assertEq(obj.hasOwnProperty("a"), true);
|
||||
}
|
||||
|
||||
foo();
|
@ -338,7 +338,7 @@ extern JS_FRIEND_API(bool)
|
||||
proxy_GetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id,
|
||||
JS::MutableHandleValue vp);
|
||||
extern JS_FRIEND_API(bool)
|
||||
proxy_SetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
|
||||
proxy_SetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id,
|
||||
JS::MutableHandleValue bp, bool strict);
|
||||
extern JS_FRIEND_API(bool)
|
||||
proxy_GetOwnPropertyDescriptor(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
|
||||
|
@ -1675,9 +1675,7 @@ JSObject::nonNativeSetProperty(JSContext *cx, HandleObject obj, HandleObject rec
|
||||
if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
|
||||
return false;
|
||||
}
|
||||
if (obj->is<ProxyObject>())
|
||||
return Proxy::set(cx, obj, receiver, id, strict, vp);
|
||||
return obj->getOps()->setProperty(cx, obj, id, vp, strict);
|
||||
return obj->getOps()->setProperty(cx, obj, receiver, id, vp, strict);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
@ -572,10 +572,10 @@ js::proxy_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, Ha
|
||||
}
|
||||
|
||||
bool
|
||||
js::proxy_SetProperty(JSContext *cx, HandleObject obj, HandleId id,
|
||||
js::proxy_SetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
|
||||
MutableHandleValue vp, bool strict)
|
||||
{
|
||||
return Proxy::set(cx, obj, obj, id, strict, vp);
|
||||
return Proxy::set(cx, obj, receiver, id, strict, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1948,9 +1948,9 @@ MaybeReportUndeclaredVarAssignment(JSContext *cx, JSString *propname)
|
||||
* This implements ES6 draft rev 28, 9.1.9 [[Set]] steps 5.c-f, but it
|
||||
* is really old code and there are a few barnacles.
|
||||
*/
|
||||
static bool
|
||||
SetPropertyByDefining(JSContext *cx, HandleNativeObject obj, HandleObject receiver,
|
||||
HandleId id, HandleValue v, bool strict, bool objHasOwn)
|
||||
bool
|
||||
js::SetPropertyByDefining(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
HandleId id, HandleValue v, bool strict, bool objHasOwn)
|
||||
{
|
||||
// Step 5.c-d: Test whether receiver has an existing own property
|
||||
// receiver[id]. The spec calls [[GetOwnProperty]]; js::HasOwnProperty is
|
||||
@ -2015,6 +2015,33 @@ SetPropertyByDefining(JSContext *cx, HandleNativeObject obj, HandleObject receiv
|
||||
return DefinePropertyOrElement(cx, nativeReceiver, id, getter, setter, attrs, v, true, strict);
|
||||
}
|
||||
|
||||
// When setting |id| for |receiver| and |obj| has no property for id, continue
|
||||
// the search up the prototype chain.
|
||||
bool
|
||||
js::SetPropertyOnProto(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
HandleId id, MutableHandleValue vp, bool strict)
|
||||
{
|
||||
MOZ_ASSERT(!obj->is<ProxyObject>());
|
||||
|
||||
RootedObject proto(cx, obj->getProto());
|
||||
if (proto)
|
||||
return SetProperty(cx, proto, receiver, id, vp, strict);
|
||||
return SetPropertyByDefining(cx, obj, receiver, id, vp, strict, false);
|
||||
}
|
||||
|
||||
bool
|
||||
js::SetNonWritableProperty(JSContext *cx, HandleId id, bool strict)
|
||||
{
|
||||
// Setting a non-writable property is an error in strict mode code, a
|
||||
// warning with the extra warnings option, and otherwise does nothing.
|
||||
|
||||
if (strict)
|
||||
return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
|
||||
if (cx->compartment()->options().extraWarnings(cx))
|
||||
return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implement "the rest of" assignment to a property when no property receiver[id]
|
||||
* was found anywhere on the prototype chain.
|
||||
@ -2154,18 +2181,8 @@ SetExistingProperty(JSContext *cx, HandleNativeObject obj, HandleObject receiver
|
||||
} else {
|
||||
MOZ_ASSERT(shape->isDataDescriptor());
|
||||
|
||||
if (!shape->writable()) {
|
||||
/*
|
||||
* Error in strict mode code, warn with extra warnings
|
||||
* options, otherwise do nothing.
|
||||
*/
|
||||
|
||||
if (strict)
|
||||
return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
|
||||
if (cx->compartment()->options().extraWarnings(cx))
|
||||
return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
|
||||
return true;
|
||||
}
|
||||
if (!shape->writable())
|
||||
return SetNonWritableProperty(cx, id, strict);
|
||||
}
|
||||
|
||||
if (pobj == receiver) {
|
||||
|
@ -1282,6 +1282,18 @@ NativeGetElement(JSContext *cx, HandleNativeObject obj, uint32_t index, MutableH
|
||||
return NativeGetElement(cx, obj, obj, index, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
SetPropertyByDefining(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
HandleId id, HandleValue v, bool strict, bool objHasOwn);
|
||||
|
||||
bool
|
||||
SetPropertyOnProto(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
HandleId id, MutableHandleValue vp, bool strict);
|
||||
|
||||
// Report any error or warning when writing to a non-writable property.
|
||||
bool
|
||||
SetNonWritableProperty(JSContext *cx, HandleId id, bool strict);
|
||||
|
||||
/*
|
||||
* Indicates whether an assignment operation is qualified (`x.y = 0`) or
|
||||
* unqualified (`y = 0`). In strict mode, the latter is an error if no such
|
||||
|
@ -492,11 +492,14 @@ with_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleI
|
||||
}
|
||||
|
||||
static bool
|
||||
with_SetProperty(JSContext *cx, HandleObject obj, HandleId id,
|
||||
with_SetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
|
||||
MutableHandleValue vp, bool strict)
|
||||
{
|
||||
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
|
||||
return SetProperty(cx, actual, actual, id, vp, strict);
|
||||
RootedObject actualReceiver(cx, receiver);
|
||||
if (receiver == obj)
|
||||
actualReceiver = actual;
|
||||
return SetProperty(cx, actual, actualReceiver, id, vp, strict);
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -935,7 +938,7 @@ uninitialized_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver
|
||||
}
|
||||
|
||||
static bool
|
||||
uninitialized_SetProperty(JSContext *cx, HandleObject obj, HandleId id,
|
||||
uninitialized_SetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
|
||||
MutableHandleValue vp, bool strict)
|
||||
{
|
||||
ReportUninitializedLexicalId(cx, id);
|
||||
|
@ -309,28 +309,25 @@ UnboxedPlainObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObjec
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UnboxedPlainObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleId id,
|
||||
MutableHandleValue vp, bool strict)
|
||||
UnboxedPlainObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
HandleId id, MutableHandleValue vp, bool strict)
|
||||
{
|
||||
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
|
||||
|
||||
if (const UnboxedLayout::Property *property = layout.lookup(id)) {
|
||||
if (obj->as<UnboxedPlainObject>().setValue(cx, *property, vp))
|
||||
return true;
|
||||
if (obj == receiver) {
|
||||
if (obj->as<UnboxedPlainObject>().setValue(cx, *property, vp))
|
||||
return true;
|
||||
|
||||
if (!obj->as<UnboxedPlainObject>().convertToNative(cx))
|
||||
return false;
|
||||
return SetProperty(cx, obj, obj, id, vp, strict);
|
||||
if (!obj->as<UnboxedPlainObject>().convertToNative(cx))
|
||||
return false;
|
||||
return SetProperty(cx, obj, receiver, id, vp, strict);
|
||||
}
|
||||
|
||||
return SetPropertyByDefining(cx, obj, receiver, id, vp, strict, false);
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->getProto());
|
||||
if (!proto) {
|
||||
if (!obj->as<UnboxedPlainObject>().convertToNative(cx))
|
||||
return false;
|
||||
return SetProperty(cx, obj, obj, id, vp, strict);
|
||||
}
|
||||
|
||||
return SetProperty(cx, proto, obj, id, vp, strict);
|
||||
return SetPropertyOnProto(cx, obj, receiver, id, vp, strict);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
@ -144,8 +144,8 @@ class UnboxedPlainObject : public JSObject
|
||||
static bool obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
HandleId id, MutableHandleValue vp);
|
||||
|
||||
static bool obj_setProperty(JSContext *cx, HandleObject obj, HandleId id,
|
||||
MutableHandleValue vp, bool strict);
|
||||
static bool obj_setProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
HandleId id, MutableHandleValue vp, bool strict);
|
||||
|
||||
static bool obj_getOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc);
|
||||
|
Loading…
Reference in New Issue
Block a user