Bug 1377051 - Support JSOP_SETPROP_SUPER in Baseline
MozReview-Commit-ID: LNOVCntYDsM
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/class/superSetPropThrow.js
@@ -0,0 +1,64 @@
+var g_foo = "foo";
+var g_bar = "bar";
+
+// Define base class with a read-only and a writable data property
+class Base
+{
+}
+Object.defineProperty(Base.prototype, "foo", { value: "Base", writable: true });
+Object.defineProperty(Base.prototype, "bar", { value: "Base", writable: false });
+
+// Test various cases that should throw during SETPROP_SUPER
+class Derived extends Base
+{
+ // ECMA-2018 9.1.9.1, step 4.a
+ testReadonly() {
+ super.bar = "Derived";
+ }
+ testReadonlyElem() {
+ super[g_bar] = "Derived";
+ }
+
+ // ECMA-2018 9.1.9.1, step 4.b
+ testPrimitiveReceiver() {
+ super.foo = "Derived";
+ }
+ testPrimitiveReceiverElem() {
+ super[g_foo] = "Derived";
+ }
+
+ // ECMA-2018 9.1.9.1, step 4.d.i
+ testAccessorShadow() {
+ Object.defineProperty(this, "foo", { get: function() { } });
+ super.foo = "Derived";
+ }
+ testAccessorShadowElem() {
+ Object.defineProperty(this, "foo", { get: function() { } });
+ super[g_foo] = "Derived";
+ }
+
+ // ECMA-2018 9.1.9.1, step 4.d.ii
+ testReadonlyShadow() {
+ Object.defineProperty(this, "foo", { writable: false });
+ super.foo = "Derived";
+ }
+ testReadonlyShadowElem() {
+ Object.defineProperty(this, "foo", { writable: false });
+ super[g_foo] = "Derived";
+ }
+}
+
+for (let i = 0; i < 10; ++i) {
+ var cnt = 0;
+
+ try { new Derived().testReadonly(); } catch(e) { cnt++; }
+ try { new Derived().testReadonlyElem(); } catch(e) { cnt++; }
+ try { Derived.prototype.testPrimitiveReceiver.call(null); } catch(e) { cnt++; }
+ try { Derived.prototype.testPrimitiveReceiverElem.call(null); } catch(e) { cnt++; }
+ try { new Derived().testAccessorShadow(); } catch(e) { cnt++; }
+ try { new Derived().testAccessorShadowElem(); } catch(e) { cnt++; }
+ try { new Derived().testReadonlyShadow(); } catch(e) { cnt++; }
+ try { new Derived().testReadonlyShadowElem(); } catch(e) { cnt++; }
+
+ assertEq(cnt, 8);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/class/superSetProperty.js
@@ -0,0 +1,67 @@
+
+class Base
+{
+ set setter(val) {
+ this.set_val = val;
+ this.set_this = this;
+ }
+}
+Base.prototype.prop = "Base";
+
+class Derived extends Base
+{
+ set setter(val) { super.setter = val; }
+ setelem(pname, val) { super[pname] = val; }
+}
+
+// Test SETPROP_SUPER invoke setters correctly
+function testSetterChain() {
+ let d = new Derived();
+
+ for (let i = 0; i < 10; ++i)
+ {
+ d.setter = i;
+ assertEq(d.set_val, i);
+ assertEq(d.set_this, d);
+ }
+}
+function testSetterChainElem() {
+ let d = new Derived();
+
+ for (let i = 0; i < 10; ++i)
+ {
+ d.setelem("setter", i);
+ assertEq(d.set_val, i);
+ assertEq(d.set_this, d);
+ }
+}
+
+// Test that SETPROP_SUPER modifies |this| and not home object
+function testSuperSetProp() {
+ let d = new Derived();
+
+ for (let i = 0; i < 10; ++i)
+ {
+ d.prop = i;
+ assertEq(d.prop, i);
+ assertEq(d.hasOwnProperty("prop"), true);
+ assertEq(Derived.prototype.prop, "Base");
+ }
+}
+function testSuperSetPropElem() {
+ let d = new Derived();
+
+ for (let i = 0; i < 10; ++i)
+ {
+ d.setelem("prop", i);
+ assertEq(d.prop, i);
+ assertEq(d.hasOwnProperty("prop"), true);
+ assertEq(Derived.prototype.prop, "Base");
+ }
+}
+
+testSetterChain();
+testSetterChainElem();
+
+testSuperSetProp();
+testSuperSetPropElem();
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2541,16 +2541,56 @@ BaselineCompiler::emit_JSOP_SETGNAME()
}
bool
BaselineCompiler::emit_JSOP_STRICTSETGNAME()
{
return emit_JSOP_SETPROP();
}
+typedef bool (*SetPropertySuperFn)(JSContext*, HandleObject, HandleValue,
+ HandlePropertyName, HandleValue, bool);
+static const VMFunction SetPropertySuperInfo =
+ FunctionInfo<SetPropertySuperFn>(js::SetPropertySuper, "SetPropertySuper");
+
+bool
+BaselineCompiler::emit_JSOP_SETPROP_SUPER()
+{
+ bool strict = IsCheckStrictOp(JSOp(*pc));
+
+ // Incoming stack is |receiver, obj, rval|. We need to shuffle stack to
+ // leave rval when operation is complete.
+
+ // Pop rval into R0, then load receiver into R1 and replace with rval.
+ frame.popRegsAndSync(1);
+ masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R1);
+ masm.storeValue(R0, frame.addressOfStackValue(frame.peek(-2)));
+
+ prepareVMCall();
+
+ pushArg(Imm32(strict));
+ pushArg(R0); // rval
+ pushArg(ImmGCPtr(script->getName(pc)));
+ pushArg(R1); // receiver
+ masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg());
+ pushArg(R0.scratchReg()); // obj
+
+ if (!callVM(SetPropertySuperInfo))
+ return false;
+
+ frame.pop();
+ return true;
+}
+
+bool
+BaselineCompiler::emit_JSOP_STRICTSETPROP_SUPER()
+{
+ return emit_JSOP_SETPROP_SUPER();
+}
+
bool
BaselineCompiler::emit_JSOP_GETPROP()
{
// Keep object in R0.
frame.popRegsAndSync(1);
// Call IC.
ICGetProp_Fallback::Compiler compiler(cx, ICStubCompiler::Engine::Baseline);
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -133,16 +133,18 @@ namespace jit {
_(JSOP_STRICTSETNAME) \
_(JSOP_GETPROP) \
_(JSOP_SETPROP) \
_(JSOP_STRICTSETPROP) \
_(JSOP_CALLPROP) \
_(JSOP_DELPROP) \
_(JSOP_STRICTDELPROP) \
_(JSOP_GETPROP_SUPER) \
+ _(JSOP_SETPROP_SUPER) \
+ _(JSOP_STRICTSETPROP_SUPER) \
_(JSOP_LENGTH) \
_(JSOP_GETBOUNDNAME) \
_(JSOP_GETALIASEDVAR) \
_(JSOP_SETALIASEDVAR) \
_(JSOP_GETNAME) \
_(JSOP_BINDNAME) \
_(JSOP_DELNAME) \
_(JSOP_GETIMPORT) \
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -2863,28 +2863,24 @@ CASE(JSOP_STRICTSETPROP)
END_CASE(JSOP_SETPROP)
CASE(JSOP_SETPROP_SUPER)
CASE(JSOP_STRICTSETPROP_SUPER)
{
static_assert(JSOP_SETPROP_SUPER_LENGTH == JSOP_STRICTSETPROP_SUPER_LENGTH,
"setprop-super and strictsetprop-super must be the same size");
-
ReservedRooted<Value> receiver(&rootValue0, REGS.sp[-3]);
ReservedRooted<JSObject*> obj(&rootObject0, ®S.sp[-2].toObject());
ReservedRooted<Value> rval(&rootValue1, REGS.sp[-1]);
- ReservedRooted<jsid> id(&rootId0, NameToId(script->getName(REGS.pc)));
-
- ObjectOpResult result;
- if (!SetProperty(cx, obj, id, rval, receiver, result))
- goto error;
+ ReservedRooted<PropertyName*> name(&rootName0, script->getName(REGS.pc));
bool strict = JSOp(*REGS.pc) == JSOP_STRICTSETPROP_SUPER;
- if (!result.checkStrictErrorOrWarning(cx, obj, id, strict))
+
+ if (!SetPropertySuper(cx, obj, receiver, name, rval, strict))
goto error;
REGS.sp[-3] = REGS.sp[-1];
REGS.sp -= 2;
}
END_CASE(JSOP_SETPROP_SUPER)
CASE(JSOP_GETELEM)
@@ -5252,8 +5248,20 @@ js::SuperFunOperation(JSContext* cx, Han
}
bool
js::ThrowInitializedThis(JSContext* cx, AbstractFramePtr frame)
{
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_REINIT_THIS);
return false;
}
+
+bool
+js::SetPropertySuper(JSContext* cx, HandleObject obj, HandleValue receiver,
+ HandlePropertyName name, HandleValue rval, bool strict)
+{
+ RootedId id(cx, NameToId(name));
+ ObjectOpResult result;
+ if (!SetProperty(cx, obj, id, rval, receiver, result))
+ return false;
+
+ return result.checkStrictErrorOrWarning(cx, obj, id, strict);
+}
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -593,11 +593,15 @@ JSFunction*
MakeDefaultConstructor(JSContext* cx, HandleScript script, jsbytecode* pc, HandleObject proto);
JSObject*
HomeObjectSuperBase(JSContext* cx, HandleObject homeObj);
JSObject*
SuperFunOperation(JSContext* cx, HandleObject callee);
+bool
+SetPropertySuper(JSContext* cx, HandleObject obj, HandleValue receiver,
+ HandlePropertyName id, HandleValue rval, bool strict);
+
} /* namespace js */
#endif /* vm_Interpreter_h */