Bug 603201 - Enable primitive receivers in [[Set]]. r=jorendorff

This commit is contained in:
Jason Orendorff 2015-03-01 13:37:42 -06:00
parent d3cc5fb902
commit 8d8cb64993
6 changed files with 58 additions and 62 deletions

View File

@ -1059,7 +1059,8 @@ function traverse (node, cb) {
cb(node); cb(node);
keys(node).map(key => { keys(node).map(key => {
if (key === 'parent' || !node[key]) return; if (key === 'parent' || !node[key]) return;
node[key].parent = node; if (typeof node[key] === "object")
node[key].parent = node;
traverse(node[key], cb); traverse(node[key], cb);
}); });
} }

View File

@ -2766,7 +2766,7 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs)) if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
return false; return false;
} else { } else {
if (!SetObjectElement(cx, obj, index, rhs, JSOp(*pc) == JSOP_STRICTSETELEM, script, pc)) if (!SetObjectElement(cx, obj, index, rhs, objv, JSOp(*pc) == JSOP_STRICTSETELEM, script, pc))
return false; return false;
} }
@ -4777,9 +4777,12 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_
} else { } else {
MOZ_ASSERT(op == JSOP_SETPROP || op == JSOP_STRICTSETPROP); MOZ_ASSERT(op == JSOP_SETPROP || op == JSOP_STRICTSETPROP);
RootedValue v(cx, rhs); ObjectOpResult result;
if (!PutProperty(cx, obj, id, v, op == JSOP_STRICTSETPROP)) if (!SetProperty(cx, obj, id, rhs, lhs, result) ||
!result.checkStrictErrorOrWarning(cx, obj, id, op == JSOP_STRICTSETPROP))
{
return false; return false;
}
} }
// Leave the RHS on the stack. // Leave the RHS on the stack.

View File

@ -7,34 +7,36 @@
#include "jsapi-tests/tests.h" #include "jsapi-tests/tests.h"
using namespace JS;
BEGIN_TEST(testForwardSetProperty) BEGIN_TEST(testForwardSetProperty)
{ {
JS::RootedValue v1(cx); RootedValue v1(cx);
EVAL("var foundValue; \n" EVAL("var foundValue; \n"
"var obj1 = { set prop(val) { foundValue = this; } }; \n" "var obj1 = { set prop(val) { foundValue = this; } }; \n"
"obj1;", "obj1;",
&v1); &v1);
JS::RootedValue v2(cx); RootedValue v2(cx);
EVAL("var obj2 = Object.create(obj1); \n" EVAL("var obj2 = Object.create(obj1); \n"
"obj2;", "obj2;",
&v2); &v2);
JS::RootedValue v3(cx); RootedValue v3(cx);
EVAL("var obj3 = {}; \n" EVAL("var obj3 = {}; \n"
"obj3;", "obj3;",
&v3); &v3);
JS::RootedObject obj1(cx, &v1.toObject()); RootedObject obj1(cx, &v1.toObject());
JS::RootedObject obj2(cx, &v2.toObject()); RootedObject obj2(cx, &v2.toObject());
JS::RootedObject obj3(cx, &v3.toObject()); RootedObject obj3(cx, &v3.toObject());
JS::RootedValue setval(cx, JS::Int32Value(42)); RootedValue setval(cx, Int32Value(42));
JS::RootedValue propkey(cx); RootedValue propkey(cx);
EVAL("'prop';", &propkey); EVAL("'prop';", &propkey);
JS::RootedId prop(cx); RootedId prop(cx);
CHECK(JS_ValueToId(cx, propkey, &prop)); CHECK(JS_ValueToId(cx, propkey, &prop));
EXEC("function assertEq(a, b, msg) \n" EXEC("function assertEq(a, b, msg) \n"
@ -68,42 +70,20 @@ BEGIN_TEST(testForwardSetProperty)
// Strict setter // Strict setter
EVAL("obj1 = { set prop(val) { 'use strict'; foundValue = this; } }; \n" RootedValue v4(cx);
"obj1;", EVAL("({ set prop(val) { 'use strict'; foundValue = this; } })", &v4);
&v1); RootedObject obj4(cx, &v4.toObject());
CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, setval, v3, result)); CHECK(JS_ForwardSetPropertyTo(cx, obj4, prop, setval, v3, result));
CHECK(result); CHECK(result);
EXEC("assertEq(foundValue, obj3, 'wrong receiver passed to strict setter');"); EXEC("assertEq(foundValue, obj3, 'wrong receiver passed to strict setter');");
CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, setval, setval, result)); CHECK(JS_ForwardSetPropertyTo(cx, obj4, prop, setval, setval, result));
CHECK(result); CHECK(result);
JS::RootedValue strictSetSupported(cx); EXEC("assertEq(foundValue, 42, \n"
EVAL("var strictSetSupported = false; \n" " '42 passed as receiver to strict setter was mangled');");
"Object.defineProperty(Object.prototype, \n"
" 'strictSetter', \n"
" { \n"
" set(v) { \n"
" 'use strict'; \n"
" strictSetSupported = \n"
" typeof this === 'number'; \n"
" } \n"
" }); \n"
"17..strictSetter = 42; \n"
"strictSetSupported;",
&strictSetSupported);
CHECK(strictSetSupported.isBoolean());
if (strictSetSupported.toBoolean()) {
// XXX Bug 603201 will fix this.
MOZ_ASSERT(false,
"remove the support-testing check when bug 603201 is fixt");
EXEC("assertEq(foundValue, 42, \n"
" '42 passed as receiver to strict setter ' + \n"
" 'was mangled');");
}
return true; return true;
} }

View File

@ -27,9 +27,8 @@ Object.defineProperty(Symbol.prototype, "prop", {
set: function (v) { set: function (v) {
"use strict"; "use strict";
sets++; sets++;
assertEq(typeof this, "object"); assertEq(typeof this, "symbol");
assertEq(this instanceof Symbol, true); assertEq(this, sym);
assertEq(this.valueOf(), sym);
assertEq(v, "newvalue"); assertEq(v, "newvalue");
} }
}); });

View File

@ -282,11 +282,8 @@ SetPropertyOperation(JSContext* cx, JSOp op, HandleValue lval, HandleId id, Hand
if (!obj) if (!obj)
return false; return false;
// Note: ES6 specifies that the value lval, not obj, is passed as receiver
// to obj's [[Set]] internal method. See bug 603201.
RootedValue receiver(cx, ObjectValue(*obj));
ObjectOpResult result; ObjectOpResult result;
return SetProperty(cx, obj, id, rval, receiver, result) && return SetProperty(cx, obj, id, rval, lval, result) &&
result.checkStrictErrorOrWarning(cx, obj, id, op == JSOP_STRICTSETPROP); result.checkStrictErrorOrWarning(cx, obj, id, op == JSOP_STRICTSETPROP);
} }
@ -1409,9 +1406,9 @@ ModOperation(JSContext* cx, HandleValue lhs, HandleValue rhs, MutableHandleValue
} }
static MOZ_ALWAYS_INLINE bool static MOZ_ALWAYS_INLINE bool
SetObjectElementOperation(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id, SetObjectElementOperation(JSContext* cx, HandleObject obj, HandleId id, HandleValue value,
const Value& value, bool strict, JSScript* script = nullptr, HandleValue receiver, bool strict,
jsbytecode* pc = nullptr) JSScript* script = nullptr, jsbytecode* pc = nullptr)
{ {
// receiver != obj happens only at super[expr], where we expect to find the property // receiver != obj happens only at super[expr], where we expect to find the property
// People probably aren't building hashtables with |super| anyway. // People probably aren't building hashtables with |super| anyway.
@ -1430,9 +1427,8 @@ SetObjectElementOperation(JSContext* cx, HandleObject obj, HandleValue receiver,
if (obj->isNative() && !JSID_IS_INT(id) && !obj->setHadElementsAccess(cx)) if (obj->isNative() && !JSID_IS_INT(id) && !obj->setHadElementsAccess(cx))
return false; return false;
RootedValue tmp(cx, value);
ObjectOpResult result; ObjectOpResult result;
return SetProperty(cx, obj, id, tmp, receiver, result) && return SetProperty(cx, obj, id, value, receiver, result) &&
result.checkStrictErrorOrWarning(cx, obj, id, strict); result.checkStrictErrorOrWarning(cx, obj, id, strict);
} }
@ -2677,13 +2673,15 @@ CASE(JSOP_STRICTSETELEM)
{ {
static_assert(JSOP_SETELEM_LENGTH == JSOP_STRICTSETELEM_LENGTH, static_assert(JSOP_SETELEM_LENGTH == JSOP_STRICTSETELEM_LENGTH,
"setelem and strictsetelem must be the same size"); "setelem and strictsetelem must be the same size");
HandleValue receiver = REGS.stackHandleAt(-3);
ReservedRooted<JSObject*> obj(&rootObject0); ReservedRooted<JSObject*> obj(&rootObject0);
FETCH_OBJECT(cx, -3, obj); obj = ToObjectFromStack(cx, receiver);
if (!obj)
goto error;
ReservedRooted<jsid> id(&rootId0); ReservedRooted<jsid> id(&rootId0);
FETCH_ELEMENT_ID(-2, id); FETCH_ELEMENT_ID(-2, id);
Value& value = REGS.sp[-1]; HandleValue value = REGS.stackHandleAt(-1);
ReservedRooted<Value> receiver(&rootValue0, ObjectValue(*obj)); if (!SetObjectElementOperation(cx, obj, id, value, receiver, *REGS.pc == JSOP_STRICTSETELEM))
if (!SetObjectElementOperation(cx, obj, receiver, id, value, *REGS.pc == JSOP_STRICTSETELEM))
goto error; goto error;
REGS.sp[-3] = value; REGS.sp[-3] = value;
REGS.sp -= 2; REGS.sp -= 2;
@ -2700,10 +2698,10 @@ CASE(JSOP_STRICTSETELEM_SUPER)
FETCH_ELEMENT_ID(-4, id); FETCH_ELEMENT_ID(-4, id);
ReservedRooted<Value> receiver(&rootValue0, REGS.sp[-3]); ReservedRooted<Value> receiver(&rootValue0, REGS.sp[-3]);
ReservedRooted<JSObject*> obj(&rootObject1, &REGS.sp[-2].toObject()); ReservedRooted<JSObject*> obj(&rootObject1, &REGS.sp[-2].toObject());
Value& value = REGS.sp[-1]; HandleValue value = REGS.stackHandleAt(-1);
bool strict = JSOp(*REGS.pc) == JSOP_STRICTSETELEM_SUPER; bool strict = JSOp(*REGS.pc) == JSOP_STRICTSETELEM_SUPER;
if (!SetObjectElementOperation(cx, obj, receiver, id, value, strict)) if (!SetObjectElementOperation(cx, obj, id, value, receiver, strict))
goto error; goto error;
REGS.sp[-4] = value; REGS.sp[-4] = value;
REGS.sp -= 3; REGS.sp -= 3;
@ -4333,7 +4331,7 @@ js::SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index, HandleV
if (!ToPropertyKey(cx, index, &id)) if (!ToPropertyKey(cx, index, &id))
return false; return false;
RootedValue receiver(cx, ObjectValue(*obj)); RootedValue receiver(cx, ObjectValue(*obj));
return SetObjectElementOperation(cx, obj, receiver, id, value, strict); return SetObjectElementOperation(cx, obj, id, value, receiver, strict);
} }
bool bool
@ -4345,7 +4343,18 @@ js::SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index, HandleV
if (!ToPropertyKey(cx, index, &id)) if (!ToPropertyKey(cx, index, &id))
return false; return false;
RootedValue receiver(cx, ObjectValue(*obj)); RootedValue receiver(cx, ObjectValue(*obj));
return SetObjectElementOperation(cx, obj, receiver, id, value, strict, script, pc); return SetObjectElementOperation(cx, obj, id, value, receiver, strict, script, pc);
}
bool
js::SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index, HandleValue value,
HandleValue receiver, bool strict, HandleScript script, jsbytecode* pc)
{
MOZ_ASSERT(pc);
RootedId id(cx);
if (!ToPropertyKey(cx, index, &id))
return false;
return SetObjectElementOperation(cx, obj, id, value, receiver, strict, script, pc);
} }
bool bool

View File

@ -369,6 +369,10 @@ bool
SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index, HandleValue value, SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index, HandleValue value,
bool strict, HandleScript script, jsbytecode* pc); bool strict, HandleScript script, jsbytecode* pc);
bool
SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index, HandleValue value,
HandleValue receiver, bool strict, HandleScript script, jsbytecode* pc);
bool bool
InitElementArray(JSContext* cx, jsbytecode* pc, InitElementArray(JSContext* cx, jsbytecode* pc,
HandleObject obj, uint32_t index, HandleValue value); HandleObject obj, uint32_t index, HandleValue value);