Bug 1141865 - Part 2: Plumb new.target on the stack and make it accessible to JSNatives. (r=jorendorff, r=jandem, r=shu)

This commit is contained in:
Eric Faust 2015-06-03 02:01:14 -07:00
parent da39aee9fd
commit 1fa16671fa
45 changed files with 588 additions and 171 deletions

View File

@ -285,6 +285,7 @@ class MOZ_STACK_CLASS CallArgsBase :
{ {
protected: protected:
unsigned argc_; unsigned argc_;
bool constructing_;
public: public:
/* Returns the number of arguments. */ /* Returns the number of arguments. */
@ -314,13 +315,18 @@ class MOZ_STACK_CLASS CallArgsBase :
return i < argc_ && !this->argv_[i].isUndefined(); return i < argc_ && !this->argv_[i].isUndefined();
} }
MutableHandleValue newTarget() const {
MOZ_ASSERT(constructing_);
return MutableHandleValue::fromMarkedLocation(&this->argv_[argc_]);
}
public: public:
// These methods are publicly exposed, but we're less sure of the interface // These methods are publicly exposed, but we're less sure of the interface
// here than we'd like (because they're hackish and drop assertions). Try // here than we'd like (because they're hackish and drop assertions). Try
// to avoid using these if you can. // to avoid using these if you can.
Value* array() const { return this->argv_; } Value* array() const { return this->argv_; }
Value* end() const { return this->argv_ + argc_; } Value* end() const { return this->argv_ + argc_ + constructing_; }
}; };
} // namespace detail } // namespace detail
@ -329,13 +335,14 @@ class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsed
{ {
private: private:
friend CallArgs CallArgsFromVp(unsigned argc, Value* vp); friend CallArgs CallArgsFromVp(unsigned argc, Value* vp);
friend CallArgs CallArgsFromSp(unsigned argc, Value* sp); friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing);
static CallArgs create(unsigned argc, Value* argv) { static CallArgs create(unsigned argc, Value* argv, bool constructing) {
CallArgs args; CallArgs args;
args.clearUsedRval(); args.clearUsedRval();
args.argv_ = argv; args.argv_ = argv;
args.argc_ = argc; args.argc_ = argc;
args.constructing_ = constructing;
return args; return args;
} }
@ -351,16 +358,16 @@ class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsed
MOZ_ALWAYS_INLINE CallArgs MOZ_ALWAYS_INLINE CallArgs
CallArgsFromVp(unsigned argc, Value* vp) CallArgsFromVp(unsigned argc, Value* vp)
{ {
return CallArgs::create(argc, vp + 2); return CallArgs::create(argc, vp + 2, vp[1].isMagic(JS_IS_CONSTRUCTING));
} }
// This method is only intended for internal use in SpiderMonkey. We may // This method is only intended for internal use in SpiderMonkey. We may
// eventually move it to an internal header. Embedders should use // eventually move it to an internal header. Embedders should use
// JS::CallArgsFromVp! // JS::CallArgsFromVp!
MOZ_ALWAYS_INLINE CallArgs MOZ_ALWAYS_INLINE CallArgs
CallArgsFromSp(unsigned argc, Value* sp) CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing = false)
{ {
return CallArgs::create(argc, sp - argc); return CallArgs::create(stackSlots - constructing, sp - stackSlots, constructing);
} }
} // namespace JS } // namespace JS

View File

@ -6447,6 +6447,8 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
return false; return false;
} }
bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW;
/* /*
* Emit code for each argument in order, then emit the JSOP_*CALL or * Emit code for each argument in order, then emit the JSOP_*CALL or
* JSOP_NEW bytecode with a two-byte immediate telling how many args * JSOP_NEW bytecode with a two-byte immediate telling how many args
@ -6459,9 +6461,20 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
if (!emitTree(pn3)) if (!emitTree(pn3))
return false; return false;
} }
if (isNewOp) {
// Repush the callee as new.target
if (!emitDupAt(this->stackDepth - 1 - (argc + 1)))
return false;
}
} else { } else {
if (!emitArray(pn2->pn_next, argc, JSOP_SPREADCALLARRAY)) if (!emitArray(pn2->pn_next, argc, JSOP_SPREADCALLARRAY))
return false; return false;
if (isNewOp) {
if (!emitDupAt(this->stackDepth - 1 - 2))
return false;
}
} }
emittingForInit = oldEmittingForInit; emittingForInit = oldEmittingForInit;

View File

@ -0,0 +1,12 @@
function testBailoutNewTarget() {
function Inner(ex, forceRectifier) {
bailout();
assertEq(new.target, ex);
}
for (let i = 0; i < 1100; i++)
new Inner(Inner);
}
for (let i = 0; i < 15; i++)
testBailoutNewTarget();

View File

@ -0,0 +1,37 @@
// Test that Ion frames are invalidated by turning on Debugger. Invalidation
// is unobservable, but we know that Ion scripts cannot handle Debugger
// handlers, so we test for the handlers being called.
load(libdir + "jitopts.js");
if (!jitTogglesMatch(Opts_Ion2NoOffthreadCompilation))
quit();
withJitOptions(Opts_Ion2NoOffthreadCompilation, function () {
var g = newGlobal();
var dbg = new Debugger;
g.toggle = function toggle(d) {
if (d) {
dbg.addDebuggee(g);
var frame = dbg.getNewestFrame();
assertEq(frame.implementation, "ion");
assertEq(frame.constructing, true);
// overflow args are read from the parent's frame
// ensure we have the right offset to read from those.
assertEq(frame.arguments[1], 15);
}
};
g.eval("" + function f(d) { new g(d, 15); });
g.eval("" + function g(d) { toggle(d); });
g.eval("(" + function test() {
for (var i = 0; i < 5; i++)
f(false);
f(true);
} + ")();");
});

View File

@ -1,9 +1,11 @@
// Forward to the target if the trap is undefined // Forward to the target if the trap is undefined
var p;
var target = function (x, y) { var target = function (x, y) {
assertEq(new.target, p);
this.foo = x + y; this.foo = x + y;
} }
for (let p of [new Proxy(target, {}), Proxy.revocable(target, {}).proxy]) { for (p of [new Proxy(target, {}), Proxy.revocable(target, {}).proxy]) {
var obj = new p(2, 3); var obj = new p(2, 3);
assertEq(obj.foo, 5); assertEq(obj.foo, 5);
assertEq(Object.getPrototypeOf(obj), target.prototype); assertEq(Object.getPrototypeOf(obj), target.prototype);

View File

@ -5,15 +5,17 @@ load(libdir + "asserts.js");
* *
* Hooks that don't return an object must throw. * Hooks that don't return an object must throw.
*/ */
var p;
var target = function () {}; var target = function () {};
var handler = { var handler = {
construct: function (target1, args) { construct: function (target1, args, newTarget) {
assertEq(this, handler); assertEq(this, handler);
assertEq(target1, target); assertEq(target1, target);
assertEq(args.length, 2); assertEq(args.length, 2);
assertEq(args[0], 2); assertEq(args[0], 2);
assertEq(args[1], 3); assertEq(args[1], 3);
assertEq(newTarget, p);
} }
} }
for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) for (p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy])
assertThrowsInstanceOf(function () {new p(2, 3)}, TypeError); assertThrowsInstanceOf(function () {new p(2, 3)}, TypeError);

View File

@ -976,6 +976,8 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
BailoutKindString(bailoutKind)); BailoutKindString(bailoutKind));
#endif #endif
bool pushedNewTarget = op == JSOP_NEW;
// If this was the last inline frame, or we are bailing out to a catch or // If this was the last inline frame, or we are bailing out to a catch or
// finally block in this frame, then unpacking is almost done. // finally block in this frame, then unpacking is almost done.
if (!iter.moreFrames() || catchingException) { if (!iter.moreFrames() || catchingException) {
@ -1032,11 +1034,14 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
builder.writeValue(UndefinedValue(), "CallOp FillerThis"); builder.writeValue(UndefinedValue(), "CallOp FillerThis");
for (uint32_t i = 0; i < numCallArgs; i++) for (uint32_t i = 0; i < numCallArgs; i++)
builder.writeValue(UndefinedValue(), "CallOp FillerArg"); builder.writeValue(UndefinedValue(), "CallOp FillerArg");
if (pushedNewTarget)
builder.writeValue(UndefinedValue(), "CallOp FillerNewTarget");
frameSize += (numCallArgs + 2) * sizeof(Value); frameSize += (numCallArgs + 2 + pushedNewTarget) * sizeof(Value);
blFrame->setFrameSize(frameSize); blFrame->setFrameSize(frameSize);
JitSpew(JitSpew_BaselineBailouts, " Adjusted framesize += %d: %d", JitSpew(JitSpew_BaselineBailouts, " Adjusted framesize += %d: %d",
(int) ((numCallArgs + 2) * sizeof(Value)), (int) frameSize); (int) ((numCallArgs + 2 + pushedNewTarget) * sizeof(Value)),
(int) frameSize);
} }
// Set the resume address to the return point from the IC, and set // Set the resume address to the return point from the IC, and set
@ -1228,12 +1233,13 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
} }
// Align the stack based on the number of arguments. // Align the stack based on the number of arguments.
size_t afterFrameSize = (actualArgc + 1) * sizeof(Value) + JitFrameLayout::Size(); size_t afterFrameSize = (actualArgc + 1 + pushedNewTarget) * sizeof(Value) +
JitFrameLayout::Size();
if (!builder.maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding")) if (!builder.maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding"))
return false; return false;
MOZ_ASSERT(actualArgc + 2 <= exprStackSlots); MOZ_ASSERT(actualArgc + 2 + pushedNewTarget <= exprStackSlots);
for (unsigned i = 0; i < actualArgc + 1; i++) { for (unsigned i = 0; i < actualArgc + 1 + pushedNewTarget; i++) {
size_t argSlot = (script->nfixed() + exprStackSlots) - (i + 1); size_t argSlot = (script->nfixed() + exprStackSlots) - (i + 1);
if (!builder.writeValue(*blFrame->valueSlot(argSlot), "ArgVal")) if (!builder.writeValue(*blFrame->valueSlot(argSlot), "ArgVal"))
return false; return false;
@ -1260,7 +1266,7 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
// So get the callee from the specially saved vector. // So get the callee from the specially saved vector.
callee = savedCallerArgs[0]; callee = savedCallerArgs[0];
} else { } else {
uint32_t calleeStackSlot = exprStackSlots - uint32_t(actualArgc + 2); uint32_t calleeStackSlot = exprStackSlots - uint32_t(actualArgc + 2 + pushedNewTarget);
size_t calleeOffset = (builder.framePushed() - endOfBaselineJSFrameStack) size_t calleeOffset = (builder.framePushed() - endOfBaselineJSFrameStack)
+ ((exprStackSlots - (calleeStackSlot + 1)) * sizeof(Value)); + ((exprStackSlots - (calleeStackSlot + 1)) * sizeof(Value));
callee = *builder.valuePointerAtStackOffset(calleeOffset); callee = *builder.valuePointerAtStackOffset(calleeOffset);
@ -1331,10 +1337,18 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
#endif #endif
// Align the stack based on the number of arguments. // Align the stack based on the number of arguments.
size_t afterFrameSize = (calleeFun->nargs() + 1) * sizeof(Value) + RectifierFrameLayout::Size(); size_t afterFrameSize = (calleeFun->nargs() + 1 + pushedNewTarget) * sizeof(Value) +
RectifierFrameLayout::Size();
if (!builder.maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding")) if (!builder.maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding"))
return false; return false;
// Copy new.target, if necessary.
if (pushedNewTarget) {
size_t newTargetOffset = (builder.framePushed() - endOfBaselineStubArgs) +
(actualArgc + 1) * sizeof(Value);
builder.writeValue(*builder.valuePointerAtStackOffset(newTargetOffset), "CopiedNewTarget");
}
// Push undefined for missing arguments. // Push undefined for missing arguments.
for (unsigned i = 0; i < (calleeFun->nargs() - actualArgc); i++) { for (unsigned i = 0; i < (calleeFun->nargs() - actualArgc); i++) {
if (!builder.writeValue(UndefinedValue(), "FillerVal")) if (!builder.writeValue(UndefinedValue(), "FillerVal"))

View File

@ -2737,19 +2737,20 @@ BaselineCompiler::emitCall()
{ {
MOZ_ASSERT(IsCallPC(pc)); MOZ_ASSERT(IsCallPC(pc));
bool construct = JSOp(*pc) == JSOP_NEW;
uint32_t argc = GET_ARGC(pc); uint32_t argc = GET_ARGC(pc);
frame.syncStack(0); frame.syncStack(0);
masm.move32(Imm32(argc), R0.scratchReg()); masm.move32(Imm32(argc), R0.scratchReg());
// Call IC // Call IC
ICCall_Fallback::Compiler stubCompiler(cx, /* isConstructing = */ JSOp(*pc) == JSOP_NEW, ICCall_Fallback::Compiler stubCompiler(cx, /* isConstructing = */ construct,
/* isSpread = */ false); /* isSpread = */ false);
if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
return false; return false;
// Update FrameInfo. // Update FrameInfo.
frame.popn(argc + 2); frame.popn(2 + argc + construct);
frame.push(R0); frame.push(R0);
return true; return true;
} }
@ -2769,7 +2770,8 @@ BaselineCompiler::emitSpreadCall()
return false; return false;
// Update FrameInfo. // Update FrameInfo.
frame.popn(3); bool construct = JSOp(*pc) == JSOP_SPREADNEW;
frame.popn(3 + construct);
frame.push(R0); frame.push(R0);
return true; return true;
} }

View File

@ -37,7 +37,7 @@ BaselineFrame::trace(JSTracer* trc, JitFrameIterator& frameIterator)
// Mark actual and formal args. // Mark actual and formal args.
if (isNonEvalFunctionFrame()) { if (isNonEvalFunctionFrame()) {
unsigned numArgs = js::Max(numActualArgs(), numFormalArgs()); unsigned numArgs = js::Max(numActualArgs(), numFormalArgs());
TraceRootRange(trc, numArgs, argv(), "baseline-args"); TraceRootRange(trc, numArgs + isConstructing(), argv(), "baseline-args");
} }
// Mark scope chain, if it exists. // Mark scope chain, if it exists.

View File

@ -10248,6 +10248,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
if (fun->native() == intrinsic_IsSuspendedStarGenerator) { if (fun->native() == intrinsic_IsSuspendedStarGenerator) {
// This intrinsic only appears in self-hosted code. // This intrinsic only appears in self-hosted code.
MOZ_ASSERT(op != JSOP_NEW);
MOZ_ASSERT(argc == 1); MOZ_ASSERT(argc == 1);
JitSpew(JitSpew_BaselineIC, " Generating Call_IsSuspendedStarGenerator stub"); JitSpew(JitSpew_BaselineIC, " Generating Call_IsSuspendedStarGenerator stub");
@ -10319,6 +10320,10 @@ TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
RootedValue thisv(cx, vp[1]); RootedValue thisv(cx, vp[1]);
Value* args = vp + 2; Value* args = vp + 2;
// String.prototype.split will not yield a constructable.
if (JSOp(*pc) == JSOP_NEW)
return true;
if (!IsOptimizableCallStringSplit(callee, thisv, argc, args)) if (!IsOptimizableCallStringSplit(callee, thisv, argc, args))
return true; return true;
@ -10364,15 +10369,16 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint
// This fallback stub may trigger debug mode toggling. // This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICCall_Fallback*> stub(frame, stub_); DebugModeOSRVolatileStub<ICCall_Fallback*> stub(frame, stub_);
// Ensure vp array is rooted - we may GC in here.
AutoArrayRooter vpRoot(cx, argc + 2, vp);
RootedScript script(cx, frame->script()); RootedScript script(cx, frame->script());
jsbytecode* pc = stub->icEntry()->pc(script); jsbytecode* pc = stub->icEntry()->pc(script);
JSOp op = JSOp(*pc); JSOp op = JSOp(*pc);
FallbackICSpew(cx, stub, "Call(%s)", js_CodeName[op]); FallbackICSpew(cx, stub, "Call(%s)", js_CodeName[op]);
MOZ_ASSERT(argc == GET_ARGC(pc)); MOZ_ASSERT(argc == GET_ARGC(pc));
bool constructing = (op == JSOP_NEW);
// Ensure vp array is rooted - we may GC in here.
AutoArrayRooter vpRoot(cx, argc + 2 + constructing, vp);
RootedValue callee(cx, vp[0]); RootedValue callee(cx, vp[0]);
RootedValue thisv(cx, vp[1]); RootedValue thisv(cx, vp[1]);
@ -10386,8 +10392,6 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint
return false; return false;
} }
// Compute construcing and useNewGroup flags.
bool constructing = (op == JSOP_NEW);
bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, pc); bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, pc);
// Try attaching a call stub. // Try attaching a call stub.
@ -10399,7 +10403,7 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint
} }
if (op == JSOP_NEW) { if (op == JSOP_NEW) {
if (!InvokeConstructor(cx, callee, argc, args, res)) if (!InvokeConstructor(cx, callee, argc, args, true, res))
return false; return false;
} else if ((op == JSOP_EVAL || op == JSOP_STRICTEVAL) && } else if ((op == JSOP_EVAL || op == JSOP_STRICTEVAL) &&
frame->scopeChain()->global().valueIsEval(callee)) frame->scopeChain()->global().valueIsEval(callee))
@ -10448,19 +10452,19 @@ DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_
// This fallback stub may trigger debug mode toggling. // This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICCall_Fallback*> stub(frame, stub_); DebugModeOSRVolatileStub<ICCall_Fallback*> stub(frame, stub_);
// Ensure vp array is rooted - we may GC in here.
AutoArrayRooter vpRoot(cx, 3, vp);
RootedScript script(cx, frame->script()); RootedScript script(cx, frame->script());
jsbytecode* pc = stub->icEntry()->pc(script); jsbytecode* pc = stub->icEntry()->pc(script);
JSOp op = JSOp(*pc); JSOp op = JSOp(*pc);
bool constructing = (op == JSOP_SPREADNEW);
FallbackICSpew(cx, stub, "SpreadCall(%s)", js_CodeName[op]); FallbackICSpew(cx, stub, "SpreadCall(%s)", js_CodeName[op]);
// Ensure vp array is rooted - we may GC in here.
AutoArrayRooter vpRoot(cx, 3 + constructing, vp);
RootedValue callee(cx, vp[0]); RootedValue callee(cx, vp[0]);
RootedValue thisv(cx, vp[1]); RootedValue thisv(cx, vp[1]);
RootedValue arr(cx, vp[2]); RootedValue arr(cx, vp[2]);
RootedValue newTarget(cx, constructing ? vp[3] : NullValue());
bool constructing = (op == JSOP_SPREADNEW);
// Try attaching a call stub. // Try attaching a call stub.
bool handled = false; bool handled = false;
@ -10471,7 +10475,7 @@ DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_
return false; return false;
} }
if (!SpreadCallOperation(cx, script, pc, thisv, callee, arr, res)) if (!SpreadCallOperation(cx, script, pc, thisv, callee, arr, newTarget, res))
return false; return false;
// Check if debug mode toggling made the stub invalid. // Check if debug mode toggling made the stub invalid.
@ -10493,14 +10497,26 @@ DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_
void void
ICCallStubCompiler::pushCallArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs, ICCallStubCompiler::pushCallArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs,
Register argcReg, bool isJitCall) Register argcReg, bool isJitCall, bool isConstructing)
{ {
MOZ_ASSERT(!regs.has(argcReg)); MOZ_ASSERT(!regs.has(argcReg));
// Push the callee and |this| too. // Account for new.target
Register count = regs.takeAny(); Register count = regs.takeAny();
masm.mov(argcReg, count); masm.mov(argcReg, count);
masm.add32(Imm32(2), count);
// If we are setting up for a jitcall, we have to align the stack taking
// into account the args and newTarget. We could also count callee and |this|,
// but it's a waste of stack space. Because we want to keep argcReg unchanged,
// just account for newTarget initially, and add the other 2 after assuring
// allignment.
if (isJitCall) {
if (isConstructing)
masm.add32(Imm32(1), count);
} else {
masm.add32(Imm32(2 + isConstructing), count);
}
// argPtr initially points to the last argument. // argPtr initially points to the last argument.
Register argPtr = regs.takeAny(); Register argPtr = regs.takeAny();
@ -10512,8 +10528,12 @@ ICCallStubCompiler::pushCallArguments(MacroAssembler& masm, AllocatableGeneralRe
// Align the stack such that the JitFrameLayout is aligned on the // Align the stack such that the JitFrameLayout is aligned on the
// JitStackAlignment. // JitStackAlignment.
if (isJitCall) if (isJitCall) {
masm.alignJitStackBasedOnNArgs(argcReg); masm.alignJitStackBasedOnNArgs(count);
// Account for callee and |this|, skipped earlier
masm.add32(Imm32(2), count);
}
// Push all values, starting at the last one. // Push all values, starting at the last one.
Label loop, done; Label loop, done;
@ -10530,9 +10550,10 @@ ICCallStubCompiler::pushCallArguments(MacroAssembler& masm, AllocatableGeneralRe
} }
void void
ICCallStubCompiler::guardSpreadCall(MacroAssembler& masm, Register argcReg, Label* failure) ICCallStubCompiler::guardSpreadCall(MacroAssembler& masm, Register argcReg, Label* failure,
bool isConstructing)
{ {
masm.unboxObject(Address(BaselineStackReg, ICStackValueOffset), argcReg); masm.unboxObject(Address(BaselineStackReg, isConstructing * sizeof(Value) + ICStackValueOffset), argcReg);
masm.loadPtr(Address(argcReg, NativeObject::offsetOfElements()), argcReg); masm.loadPtr(Address(argcReg, NativeObject::offsetOfElements()), argcReg);
masm.load32(Address(argcReg, ObjectElements::offsetOfLength()), argcReg); masm.load32(Address(argcReg, ObjectElements::offsetOfLength()), argcReg);
@ -10547,23 +10568,41 @@ ICCallStubCompiler::guardSpreadCall(MacroAssembler& masm, Register argcReg, Labe
void void
ICCallStubCompiler::pushSpreadCallArguments(MacroAssembler& masm, ICCallStubCompiler::pushSpreadCallArguments(MacroAssembler& masm,
AllocatableGeneralRegisterSet regs, AllocatableGeneralRegisterSet regs,
Register argcReg, bool isJitCall) Register argcReg, bool isJitCall,
bool isConstructing)
{ {
// Push arguments // Pull the array off the stack before aligning.
Register startReg = regs.takeAny(); Register startReg = regs.takeAny();
Register endReg = regs.takeAny(); masm.unboxObject(Address(BaselineStackReg, (isConstructing * sizeof(Value)) + STUB_FRAME_SIZE), startReg);
masm.unboxObject(Address(BaselineStackReg, STUB_FRAME_SIZE), startReg);
masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg); masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg);
// Align the stack such that the JitFrameLayout is aligned on the
// JitStackAlignment.
if (isJitCall) {
Register alignReg = argcReg;
if (isConstructing) {
alignReg = regs.takeAny();
masm.mov(argcReg, alignReg);
masm.addPtr(Imm32(1), alignReg);
}
masm.alignJitStackBasedOnNArgs(alignReg);
if (isConstructing) {
MOZ_ASSERT(alignReg != argcReg);
regs.add(alignReg);
}
}
// Push newTarget, if necessary
if (isConstructing)
masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE));
// Push arguments: set up endReg to point to &array[argc]
Register endReg = regs.takeAny();
masm.mov(argcReg, endReg); masm.mov(argcReg, endReg);
static_assert(sizeof(Value) == 8, "Value must be 8 bytes"); static_assert(sizeof(Value) == 8, "Value must be 8 bytes");
masm.lshiftPtr(Imm32(3), endReg); masm.lshiftPtr(Imm32(3), endReg);
masm.addPtr(startReg, endReg); masm.addPtr(startReg, endReg);
// Align the stack such that the JitFrameLayout is aligned on the
// JitStackAlignment.
if (isJitCall)
masm.alignJitStackBasedOnNArgs(argcReg);
// Copying pre-decrements endReg by 8 until startReg is reached // Copying pre-decrements endReg by 8 until startReg is reached
Label copyDone; Label copyDone;
Label copyStart; Label copyStart;
@ -10578,8 +10617,8 @@ ICCallStubCompiler::pushSpreadCallArguments(MacroAssembler& masm,
regs.add(endReg); regs.add(endReg);
// Push the callee and |this|. // Push the callee and |this|.
masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE + 1 * sizeof(Value))); masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE + (1 + isConstructing) * sizeof(Value)));
masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE + 2 * sizeof(Value))); masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE + (2 + isConstructing) * sizeof(Value)));
} }
// (see Bug 1149377 comment 31) MSVC 2013 PGO miss-compiles branchTestObjClass // (see Bug 1149377 comment 31) MSVC 2013 PGO miss-compiles branchTestObjClass
@ -10784,9 +10823,20 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
// Use BaselineFrameReg instead of BaselineStackReg, because // Use BaselineFrameReg instead of BaselineStackReg, because
// BaselineFrameReg and BaselineStackReg hold the same value just after // BaselineFrameReg and BaselineStackReg hold the same value just after
// calling enterStubFrame. // calling enterStubFrame.
masm.pushValue(Address(BaselineFrameReg, 0 * sizeof(Value) + STUB_FRAME_SIZE)); // array
masm.pushValue(Address(BaselineFrameReg, 1 * sizeof(Value) + STUB_FRAME_SIZE)); // this // newTarget
masm.pushValue(Address(BaselineFrameReg, 2 * sizeof(Value) + STUB_FRAME_SIZE)); // callee if (isConstructing_)
masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE));
// array
uint32_t valueOffset = isConstructing_;
masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
// this
masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
// callee
masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
masm.push(BaselineStackReg); masm.push(BaselineStackReg);
masm.push(BaselineStubReg); masm.push(BaselineStubReg);
@ -10807,7 +10857,7 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
regs.take(R0.scratchReg()); // argc. regs.take(R0.scratchReg()); // argc.
pushCallArguments(masm, regs, R0.scratchReg(), /* isJitCall = */ false); pushCallArguments(masm, regs, R0.scratchReg(), /* isJitCall = */ false, isConstructing_);
masm.push(BaselineStackReg); masm.push(BaselineStackReg);
masm.push(R0.scratchReg()); masm.push(R0.scratchReg());
@ -10892,14 +10942,17 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
regs.takeUnchecked(BaselineTailCallReg); regs.takeUnchecked(BaselineTailCallReg);
if (isSpread_) if (isSpread_)
guardSpreadCall(masm, argcReg, &failure); guardSpreadCall(masm, argcReg, &failure, isConstructing_);
// Load the callee in R1. // Load the callee in R1, accounting for newTarget, if necessary
// Stack Layout: [ ..., CalleeVal, ThisVal, Arg0Val, ..., ArgNVal, +ICStackValueOffset+ ] // Stack Layout: [ ..., CalleeVal, ThisVal, Arg0Val, ..., ArgNVal, [newTarget] +ICStackValueOffset+ ]
if (isSpread_) { if (isSpread_) {
masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + ICStackValueOffset), R1); unsigned skipToCallee = (2 + isConstructing_) * sizeof(Value);
masm.loadValue(Address(BaselineStackReg, skipToCallee + ICStackValueOffset), R1);
} else { } else {
BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + sizeof(Value)); // Account for newTarget, if necessary
unsigned nonArgsSkip = (1 + isConstructing_) * sizeof(Value);
BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + nonArgsSkip);
masm.loadValue(calleeSlot, R1); masm.loadValue(calleeSlot, R1);
} }
regs.take(R1); regs.take(R1);
@ -10959,13 +11012,13 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
masm.push(argcReg); masm.push(argcReg);
// Stack now looks like: // Stack now looks like:
// [..., Callee, ThisV, Arg0V, ..., ArgNV, StubFrameHeader, ArgC ] // [..., Callee, ThisV, Arg0V, ..., ArgNV, NewTarget, StubFrameHeader, ArgC ]
if (isSpread_) { if (isSpread_) {
masm.loadValue(Address(BaselineStackReg, masm.loadValue(Address(BaselineStackReg,
2 * sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t)), R1); 3 * sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t)), R1);
} else { } else {
BaseValueIndex calleeSlot2(BaselineStackReg, argcReg, BaseValueIndex calleeSlot2(BaselineStackReg, argcReg,
sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t)); 2 * sizeof(Value) + STUB_FRAME_SIZE + sizeof(size_t));
masm.loadValue(calleeSlot2, R1); masm.loadValue(calleeSlot2, R1);
} }
masm.push(masm.extractObject(R1, ExtractTemp0)); masm.push(masm.extractObject(R1, ExtractTemp0));
@ -10993,11 +11046,11 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
// Save "this" value back into pushed arguments on stack. R0 can be clobbered after that. // Save "this" value back into pushed arguments on stack. R0 can be clobbered after that.
// Stack now looks like: // Stack now looks like:
// [..., Callee, ThisV, Arg0V, ..., ArgNV, StubFrameHeader ] // [..., Callee, ThisV, Arg0V, ..., ArgNV, [NewTarget], StubFrameHeader ]
if (isSpread_) { if (isSpread_) {
masm.storeValue(R0, Address(BaselineStackReg, sizeof(Value) + STUB_FRAME_SIZE)); masm.storeValue(R0, Address(BaselineStackReg, (1 + isConstructing_) * sizeof(Value) + STUB_FRAME_SIZE));
} else { } else {
BaseValueIndex thisSlot(BaselineStackReg, argcReg, STUB_FRAME_SIZE); BaseValueIndex thisSlot(BaselineStackReg, argcReg, STUB_FRAME_SIZE + isConstructing_ * sizeof(Value));
masm.storeValue(R0, thisSlot); masm.storeValue(R0, thisSlot);
} }
@ -11011,9 +11064,12 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
// Just need to load the script now. // Just need to load the script now.
if (isSpread_) { if (isSpread_) {
masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + STUB_FRAME_SIZE), R0); unsigned skipForCallee = (2 + isConstructing_) * sizeof(Value);
masm.loadValue(Address(BaselineStackReg, skipForCallee + STUB_FRAME_SIZE), R0);
} else { } else {
BaseValueIndex calleeSlot3(BaselineStackReg, argcReg, sizeof(Value) + STUB_FRAME_SIZE); // Account for newTarget, if necessary
unsigned nonArgsSkip = (1 + isConstructing_) * sizeof(Value);
BaseValueIndex calleeSlot3(BaselineStackReg, argcReg, nonArgsSkip + STUB_FRAME_SIZE);
masm.loadValue(calleeSlot3, R0); masm.loadValue(calleeSlot3, R0);
} }
callee = masm.extractObject(R0, ExtractTemp0); callee = masm.extractObject(R0, ExtractTemp0);
@ -11039,9 +11095,9 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
// right-to-left so duplicate them on the stack in reverse order. // right-to-left so duplicate them on the stack in reverse order.
// |this| and callee are pushed last. // |this| and callee are pushed last.
if (isSpread_) if (isSpread_)
pushSpreadCallArguments(masm, regs, argcReg, /* isJitCall = */ true); pushSpreadCallArguments(masm, regs, argcReg, /* isJitCall = */ true, isConstructing_);
else else
pushCallArguments(masm, regs, argcReg, /* isJitCall = */ true); pushCallArguments(masm, regs, argcReg, /* isJitCall = */ true, isConstructing_);
// The callee is on top of the stack. Pop and unbox it. // The callee is on top of the stack. Pop and unbox it.
ValueOperand val = regs.takeAnyValue(); ValueOperand val = regs.takeAnyValue();
@ -11113,8 +11169,10 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
// Current stack: [ ThisVal, ARGVALS..., ...STUB FRAME..., <-- BaselineFrameReg // Current stack: [ ThisVal, ARGVALS..., ...STUB FRAME..., <-- BaselineFrameReg
// Padding?, ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ] // Padding?, ARGVALS..., ThisVal, ActualArgc, Callee, Descriptor ]
// //
// &ThisVal = BaselineFrameReg + argc * sizeof(Value) + STUB_FRAME_SIZE // &ThisVal = BaselineFrameReg + argc * sizeof(Value) + STUB_FRAME_SIZE + sizeof(Value)
BaseValueIndex thisSlotAddr(BaselineFrameReg, argcReg, STUB_FRAME_SIZE); // This last sizeof(Value) accounts for the newTarget on the end of the arguments vector
// which is not reflected in actualArgc
BaseValueIndex thisSlotAddr(BaselineFrameReg, argcReg, STUB_FRAME_SIZE + sizeof(Value));
masm.loadValue(thisSlotAddr, JSReturnOperand); masm.loadValue(thisSlotAddr, JSReturnOperand);
#ifdef DEBUG #ifdef DEBUG
masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace); masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
@ -11286,13 +11344,14 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm)
regs.takeUnchecked(BaselineTailCallReg); regs.takeUnchecked(BaselineTailCallReg);
if (isSpread_) if (isSpread_)
guardSpreadCall(masm, argcReg, &failure); guardSpreadCall(masm, argcReg, &failure, isConstructing_);
// Load the callee in R1. // Load the callee in R1.
if (isSpread_) { if (isSpread_) {
masm.loadValue(Address(BaselineStackReg, ICStackValueOffset + 2 * sizeof(Value)), R1); masm.loadValue(Address(BaselineStackReg, ICStackValueOffset + 2 * sizeof(Value)), R1);
} else { } else {
BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + sizeof(Value)); unsigned nonArgsSlots = (1 + isConstructing_) * sizeof(Value);
BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + nonArgsSlots);
masm.loadValue(calleeSlot, R1); masm.loadValue(calleeSlot, R1);
} }
regs.take(R1); regs.take(R1);
@ -11315,9 +11374,9 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm)
// right-to-left so duplicate them on the stack in reverse order. // right-to-left so duplicate them on the stack in reverse order.
// |this| and callee are pushed last. // |this| and callee are pushed last.
if (isSpread_) if (isSpread_)
pushSpreadCallArguments(masm, regs, argcReg, /* isJitCall = */ false); pushSpreadCallArguments(masm, regs, argcReg, /* isJitCall = */ false, isConstructing_);
else else
pushCallArguments(masm, regs, argcReg, /* isJitCall = */ false); pushCallArguments(masm, regs, argcReg, /* isJitCall = */ false, isConstructing_);
if (isConstructing_) { if (isConstructing_) {
// Stack looks like: [ ..., Arg0Val, ThisVal, CalleeVal ] // Stack looks like: [ ..., Arg0Val, ThisVal, CalleeVal ]
@ -11390,7 +11449,8 @@ ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler& masm)
regs.takeUnchecked(BaselineTailCallReg); regs.takeUnchecked(BaselineTailCallReg);
// Load the callee in R1. // Load the callee in R1.
BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + sizeof(Value)); unsigned nonArgSlots = (1 + isConstructing_) * sizeof(Value);
BaseValueIndex calleeSlot(BaselineStackReg, argcReg, ICStackValueOffset + nonArgSlots);
masm.loadValue(calleeSlot, R1); masm.loadValue(calleeSlot, R1);
regs.take(R1); regs.take(R1);
@ -11412,7 +11472,7 @@ ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler& masm)
enterStubFrame(masm, regs.getAny()); enterStubFrame(masm, regs.getAny());
regs.add(scratch); regs.add(scratch);
pushCallArguments(masm, regs, argcReg, /* isJitCall = */ false); pushCallArguments(masm, regs, argcReg, /* isJitCall = */ false, isConstructing_);
regs.take(scratch); regs.take(scratch);
if (isConstructing_) { if (isConstructing_) {

View File

@ -5522,10 +5522,11 @@ class ICCallStubCompiler : public ICStubCompiler
}; };
void pushCallArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs, void pushCallArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs,
Register argcReg, bool isJitCall); Register argcReg, bool isJitCall, bool isConstructing = false);
void pushSpreadCallArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs, void pushSpreadCallArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs,
Register argcReg, bool isJitCall); Register argcReg, bool isJitCall, bool isConstructing);
void guardSpreadCall(MacroAssembler& masm, Register argcReg, Label* failure); void guardSpreadCall(MacroAssembler& masm, Register argcReg, Label* failure,
bool isConstructing);
Register guardFunApply(MacroAssembler& masm, AllocatableGeneralRegisterSet regs, Register guardFunApply(MacroAssembler& masm, AllocatableGeneralRegisterSet regs,
Register argcReg, bool checkNative, FunApplyThing applyThing, Register argcReg, bool checkNative, FunApplyThing applyThing,
Label* failure); Label* failure);

View File

@ -2932,12 +2932,12 @@ CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir)
callVM(GetIntrinsicValueInfo, lir); callVM(GetIntrinsicValueInfo, lir);
} }
typedef bool (*InvokeFunctionFn)(JSContext*, HandleObject, uint32_t, Value*, MutableHandleValue); typedef bool (*InvokeFunctionFn)(JSContext*, HandleObject, bool, uint32_t, Value*, MutableHandleValue);
static const VMFunction InvokeFunctionInfo = FunctionInfo<InvokeFunctionFn>(InvokeFunction); static const VMFunction InvokeFunctionInfo = FunctionInfo<InvokeFunctionFn>(InvokeFunction);
void void
CodeGenerator::emitCallInvokeFunction(LInstruction* call, Register calleereg, CodeGenerator::emitCallInvokeFunction(LInstruction* call, Register calleereg,
uint32_t argc, uint32_t unusedStack) bool constructing, uint32_t argc, uint32_t unusedStack)
{ {
// Nestle %esp up to the argument vector. // Nestle %esp up to the argument vector.
// Each path must account for framePushed_ separately, for callVM to be valid. // Each path must account for framePushed_ separately, for callVM to be valid.
@ -2945,6 +2945,7 @@ CodeGenerator::emitCallInvokeFunction(LInstruction* call, Register calleereg,
pushArg(masm.getStackPointer()); // argv. pushArg(masm.getStackPointer()); // argv.
pushArg(Imm32(argc)); // argc. pushArg(Imm32(argc)); // argc.
pushArg(Imm32(constructing)); // constructing.
pushArg(calleereg); // JSFunction*. pushArg(calleereg); // JSFunction*.
callVM(InvokeFunctionInfo, call); callVM(InvokeFunctionInfo, call);
@ -2999,7 +3000,8 @@ CodeGenerator::visitCallGeneric(LCallGeneric* call)
// Check whether the provided arguments satisfy target argc. // Check whether the provided arguments satisfy target argc.
// We cannot have lowered to LCallGeneric with a known target. Assert that we didn't // We cannot have lowered to LCallGeneric with a known target. Assert that we didn't
// add any undefineds in IonBuilder. NB: MCall::numStackArgs includes |this|. // add any undefineds in IonBuilder. NB: MCall::numStackArgs includes |this|.
MOZ_ASSERT(call->numActualArgs() == call->mir()->numStackArgs() - 1); DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing();
MOZ_ASSERT(call->numActualArgs() == call->mir()->numStackArgs() - numNonArgsOnStack);
masm.load16ZeroExtend(Address(calleereg, JSFunction::offsetOfNargs()), nargsreg); masm.load16ZeroExtend(Address(calleereg, JSFunction::offsetOfNargs()), nargsreg);
masm.branch32(Assembler::Above, nargsreg, Imm32(call->numActualArgs()), &thunk); masm.branch32(Assembler::Above, nargsreg, Imm32(call->numActualArgs()), &thunk);
masm.jump(&makeCall); masm.jump(&makeCall);
@ -3026,7 +3028,8 @@ CodeGenerator::visitCallGeneric(LCallGeneric* call)
// Handle uncompiled or native functions. // Handle uncompiled or native functions.
masm.bind(&invoke); masm.bind(&invoke);
emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack); emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->numActualArgs(),
unusedStack);
masm.bind(&end); masm.bind(&end);
@ -3040,19 +3043,40 @@ CodeGenerator::visitCallGeneric(LCallGeneric* call)
} }
} }
typedef bool (*InvokeFunctionShuffleFn)(JSContext*, HandleObject, uint32_t, uint32_t, Value*,
MutableHandleValue);
static const VMFunction InvokeFunctionShuffleInfo =
FunctionInfo<InvokeFunctionShuffleFn>(InvokeFunctionShuffleNewTarget);
void
CodeGenerator::emitCallInvokeFunctionShuffleNewTarget(LCallKnown* call, Register calleeReg,
uint32_t numFormals, uint32_t unusedStack)
{
masm.freeStack(unusedStack);
pushArg(masm.getStackPointer());
pushArg(Imm32(numFormals));
pushArg(Imm32(call->numActualArgs()));
pushArg(calleeReg);
callVM(InvokeFunctionShuffleInfo, call);
masm.reserveStack(unusedStack);
}
void void
CodeGenerator::visitCallKnown(LCallKnown* call) CodeGenerator::visitCallKnown(LCallKnown* call)
{ {
Register calleereg = ToRegister(call->getFunction()); Register calleereg = ToRegister(call->getFunction());
Register objreg = ToRegister(call->getTempObject()); Register objreg = ToRegister(call->getTempObject());
uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot()); uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot());
DebugOnly<JSFunction*> target = call->getSingleTarget(); JSFunction* target = call->getSingleTarget();
Label end, uncompiled; Label end, uncompiled;
// Native single targets are handled by LCallNative. // Native single targets are handled by LCallNative.
MOZ_ASSERT(!target->isNative()); MOZ_ASSERT(!target->isNative());
// Missing arguments must have been explicitly appended by the IonBuilder. // Missing arguments must have been explicitly appended by the IonBuilder.
MOZ_ASSERT(target->nargs() <= call->mir()->numStackArgs() - 1); DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing();
MOZ_ASSERT(target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack);
MOZ_ASSERT_IF(call->mir()->isConstructing(), target->isConstructor()); MOZ_ASSERT_IF(call->mir()->isConstructing(), target->isConstructor());
@ -3092,7 +3116,10 @@ CodeGenerator::visitCallKnown(LCallKnown* call)
// Handle uncompiled functions. // Handle uncompiled functions.
masm.bind(&uncompiled); masm.bind(&uncompiled);
emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack); if (call->isConstructing() && target->nargs() > call->numActualArgs())
emitCallInvokeFunctionShuffleNewTarget(call, calleereg, target->nargs(), unusedStack);
else
emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->numActualArgs(), unusedStack);
masm.bind(&end); masm.bind(&end);
@ -3118,6 +3145,7 @@ CodeGenerator::emitCallInvokeFunction(LApplyArgsGeneric* apply, Register extraSt
pushArg(objreg); // argv. pushArg(objreg); // argv.
pushArg(ToRegister(apply->getArgc())); // argc. pushArg(ToRegister(apply->getArgc())); // argc.
pushArg(Imm32(false)); // isConstrucing.
pushArg(ToRegister(apply->getFunction())); // JSFunction*. pushArg(ToRegister(apply->getFunction())); // JSFunction*.
// This specialization og callVM restore the extraStackSize after the call. // This specialization og callVM restore the extraStackSize after the call.

View File

@ -130,8 +130,13 @@ class CodeGenerator : public CodeGeneratorSpecific
void visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier* ool); void visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier* ool);
void visitCallNative(LCallNative* call); void visitCallNative(LCallNative* call);
void emitCallInvokeFunction(LInstruction* call, Register callereg, void emitCallInvokeFunction(LInstruction* call, Register callereg,
uint32_t argc, uint32_t unusedStack); bool isConstructing, uint32_t argc,
uint32_t unusedStack);
void visitCallGeneric(LCallGeneric* call); void visitCallGeneric(LCallGeneric* call);
void emitCallInvokeFunctionShuffleNewTarget(LCallKnown *call,
Register calleeReg,
uint32_t numFormals,
uint32_t unusedStack);
void visitCallKnown(LCallKnown* call); void visitCallKnown(LCallKnown* call);
void emitCallInvokeFunction(LApplyArgsGeneric* apply, Register extraStackSize); void emitCallInvokeFunction(LApplyArgsGeneric* apply, Register extraStackSize);
void emitPushArguments(LApplyArgsGeneric* apply, Register extraStackSpace); void emitPushArguments(LApplyArgsGeneric* apply, Register extraStackSpace);

View File

@ -2562,7 +2562,8 @@ jit::SetEnterJitData(JSContext* cx, EnterJitData& data, RunState& state, AutoVal
data.maxArgv = args.base() + 1; data.maxArgv = args.base() + 1;
} else { } else {
MOZ_ASSERT(vals.empty()); MOZ_ASSERT(vals.empty());
if (!vals.reserve(Max(args.length() + 1, numFormals + 1))) unsigned numPushedArgs = Max(args.length(), numFormals);
if (!vals.reserve(numPushedArgs + 1 + data.constructing))
return false; return false;
// Append |this| and any provided arguments. // Append |this| and any provided arguments.
@ -2573,7 +2574,10 @@ jit::SetEnterJitData(JSContext* cx, EnterJitData& data, RunState& state, AutoVal
while (vals.length() < numFormals + 1) while (vals.length() < numFormals + 1)
vals.infallibleAppend(UndefinedValue()); vals.infallibleAppend(UndefinedValue());
MOZ_ASSERT(vals.length() >= numFormals + 1); if (data.constructing)
vals.infallibleAppend(args.newTarget());
MOZ_ASSERT(vals.length() >= numFormals + 1 + data.constructing);
data.maxArgv = vals.begin(); data.maxArgv = vals.begin();
} }
} else { } else {

View File

@ -5268,6 +5268,8 @@ IonBuilder::inlineCallsite(const ObjectVector& targets, CallInfo& callInfo)
if (target->isSingleton()) { if (target->isSingleton()) {
// Replace the function with an MConstant. // Replace the function with an MConstant.
MConstant* constFun = constant(ObjectValue(*target)); MConstant* constFun = constant(ObjectValue(*target));
if (callInfo.constructing() && callInfo.getNewTarget() == callInfo.fun())
callInfo.setNewTarget(constFun);
callInfo.setFun(constFun); callInfo.setFun(constFun);
} }
@ -6136,7 +6138,7 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
} }
} }
int calleeDepth = -((int)argc + 2); int calleeDepth = -((int)argc + 2 + constructing);
// Acquire known call target if existent. // Acquire known call target if existent.
ObjectVector targets(alloc()); ObjectVector targets(alloc());
@ -6279,11 +6281,14 @@ IonBuilder::makeCallHelper(JSFunction* target, CallInfo& callInfo)
} }
} }
MCall* call = MCall::New(alloc(), target, targetArgs + 1, callInfo.argc(), MCall* call = MCall::New(alloc(), target, targetArgs + 1 + callInfo.constructing(),
callInfo.constructing(), isDOMCall); callInfo.argc(), callInfo.constructing(), isDOMCall);
if (!call) if (!call)
return nullptr; return nullptr;
if (callInfo.constructing())
call->addArg(targetArgs + 1, callInfo.getNewTarget());
// Explicitly pad any missing arguments with |undefined|. // Explicitly pad any missing arguments with |undefined|.
// This permits skipping the argumentsRectifier. // This permits skipping the argumentsRectifier.
for (int i = targetArgs; i > (int)callInfo.argc(); i--) { for (int i = targetArgs; i > (int)callInfo.argc(); i--) {
@ -9440,7 +9445,7 @@ IonBuilder::jsop_rest()
} }
// We know the exact number of arguments the callee pushed. // We know the exact number of arguments the callee pushed.
unsigned numActuals = inlineCallInfo_->argv().length(); unsigned numActuals = inlineCallInfo_->argc();
unsigned numFormals = info().nargs() - 1; unsigned numFormals = info().nargs() - 1;
unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0; unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;

View File

@ -1244,6 +1244,7 @@ class CallInfo
{ {
MDefinition* fun_; MDefinition* fun_;
MDefinition* thisArg_; MDefinition* thisArg_;
MDefinition* newTargetArg_;
MDefinitionVector args_; MDefinitionVector args_;
bool constructing_; bool constructing_;
@ -1253,6 +1254,7 @@ class CallInfo
CallInfo(TempAllocator& alloc, bool constructing) CallInfo(TempAllocator& alloc, bool constructing)
: fun_(nullptr), : fun_(nullptr),
thisArg_(nullptr), thisArg_(nullptr),
newTargetArg_(nullptr),
args_(alloc), args_(alloc),
constructing_(constructing), constructing_(constructing),
setter_(false) setter_(false)
@ -1264,6 +1266,9 @@ class CallInfo
fun_ = callInfo.fun(); fun_ = callInfo.fun();
thisArg_ = callInfo.thisArg(); thisArg_ = callInfo.thisArg();
if (constructing())
newTargetArg_ = callInfo.getNewTarget();
if (!args_.appendAll(callInfo.argv())) if (!args_.appendAll(callInfo.argv()))
return false; return false;
@ -1276,6 +1281,10 @@ class CallInfo
// Get the arguments in the right order // Get the arguments in the right order
if (!args_.reserve(argc)) if (!args_.reserve(argc))
return false; return false;
if (constructing())
setNewTarget(current->pop());
for (int32_t i = argc; i > 0; i--) for (int32_t i = argc; i > 0; i--)
args_.infallibleAppend(current->peek(-i)); args_.infallibleAppend(current->peek(-i));
current->popn(argc); current->popn(argc);
@ -1297,13 +1306,16 @@ class CallInfo
for (uint32_t i = 0; i < argc(); i++) for (uint32_t i = 0; i < argc(); i++)
current->push(getArg(i)); current->push(getArg(i));
if (constructing())
current->push(getNewTarget());
} }
uint32_t argc() const { uint32_t argc() const {
return args_.length(); return args_.length();
} }
uint32_t numFormals() const { uint32_t numFormals() const {
return argc() + 2; return argc() + 2 + constructing();
} }
bool setArgs(const MDefinitionVector& args) { bool setArgs(const MDefinitionVector& args) {
@ -1349,6 +1361,15 @@ class CallInfo
return constructing_; return constructing_;
} }
void setNewTarget(MDefinition* newTarget) {
MOZ_ASSERT(constructing());
newTargetArg_ = newTarget;
}
MDefinition* getNewTarget() const {
MOZ_ASSERT(newTargetArg_);
return newTargetArg_;
}
bool isSetter() const { bool isSetter() const {
return setter_; return setter_;
} }
@ -1368,6 +1389,8 @@ class CallInfo
void setImplicitlyUsedUnchecked() { void setImplicitlyUsedUnchecked() {
fun_->setImplicitlyUsedUnchecked(); fun_->setImplicitlyUsedUnchecked();
thisArg_->setImplicitlyUsedUnchecked(); thisArg_->setImplicitlyUsedUnchecked();
if (newTargetArg_)
newTargetArg_->setImplicitlyUsedUnchecked();
for (uint32_t i = 0; i < argc(); i++) for (uint32_t i = 0; i < argc(); i++)
getArg(i)->setImplicitlyUsedUnchecked(); getArg(i)->setImplicitlyUsedUnchecked();
} }

View File

@ -746,13 +746,14 @@ class InlineFrameIterator
InlineFrameIterator it(cx, this); InlineFrameIterator it(cx, this);
++it; ++it;
unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0; unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0;
bool hasNewTarget = isConstructing();
SnapshotIterator parent_s(it.snapshotIterator()); SnapshotIterator parent_s(it.snapshotIterator());
// Skip over all slots until we get to the last slots // Skip over all slots until we get to the last slots
// (= arguments slots of callee) the +3 is for [this], [returnvalue], // (= arguments slots of callee) the +3 is for [this], [returnvalue],
// [scopechain], and maybe +1 for [argsObj] // [scopechain], and maybe +1 for [argsObj]
MOZ_ASSERT(parent_s.numAllocations() >= nactual + 3 + argsObjAdj); MOZ_ASSERT(parent_s.numAllocations() >= nactual + 3 + argsObjAdj + hasNewTarget);
unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj; unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj - hasNewTarget;
for (unsigned j = 0; j < skip; j++) for (unsigned j = 0; j < skip; j++)
parent_s.skip(); parent_s.skip();

View File

@ -1047,8 +1047,9 @@ MarkThisAndArguments(JSTracer* trc, JitFrameLayout* layout)
// Trace |this|. // Trace |this|.
TraceRoot(trc, argv, "ion-thisv"); TraceRoot(trc, argv, "ion-thisv");
// Trace actual arguments beyond the formals. Note + 1 for thisv. // Trace actual arguments and newTarget beyond the formals. Note + 1 for thisv.
for (size_t i = nformals + 1; i < nargs + 1; i++) bool constructing = CalleeTokenIsConstructing(layout->calleeToken());
for (size_t i = nformals + 1; i < nargs + 1 + constructing; i++)
TraceRoot(trc, &argv[i], "ion-argv"); TraceRoot(trc, &argv[i], "ion-argv");
} }
@ -2458,7 +2459,8 @@ InlineFrameIterator::findNextFrame()
MOZ_CRASH("Couldn't deduce the number of arguments of an ionmonkey frame"); MOZ_CRASH("Couldn't deduce the number of arguments of an ionmonkey frame");
// Skip over non-argument slots, as well as |this|. // Skip over non-argument slots, as well as |this|.
unsigned skipCount = (si_.numAllocations() - 1) - numActualArgs_ - 1; bool skipNewTarget = JSOp(*pc_) == JSOP_NEW;
unsigned skipCount = (si_.numAllocations() - 1) - numActualArgs_ - 1 - skipNewTarget;
for (unsigned j = 0; j < skipCount; j++) for (unsigned j = 0; j < skipCount; j++)
si_.skip(); si_.skip();

View File

@ -1533,6 +1533,10 @@ class LJSCallInstructionHelper : public LCallInstructionHelper<Defs, Operands, T
uint32_t numActualArgs() const { uint32_t numActualArgs() const {
return mir()->numActualArgs(); return mir()->numActualArgs();
} }
bool isConstructing() const {
return mir()->isConstructing();
}
}; };
// Generates a polymorphic callsite, wherein the function being called is // Generates a polymorphic callsite, wherein the function being called is

View File

@ -2882,9 +2882,11 @@ IonBuilder::inlineBoundFunction(CallInfo& nativeCallInfo, JSFunction* target)
// Don't optimize if we're constructing and the callee is not a // Don't optimize if we're constructing and the callee is not a
// constructor, so that CallKnown does not have to handle this case // constructor, so that CallKnown does not have to handle this case
// (it should always throw). // (it should always throw).
if (nativeCallInfo.constructing() && !scriptedTarget->isConstructor()) { if (nativeCallInfo.constructing() && !scriptedTarget->isConstructor())
return InliningStatus_NotInlined;
if (nativeCallInfo.constructing() && nativeCallInfo.getNewTarget() != nativeCallInfo.fun())
return InliningStatus_NotInlined; return InliningStatus_NotInlined;
}
if (gc::IsInsideNursery(scriptedTarget)) if (gc::IsInsideNursery(scriptedTarget))
return InliningStatus_NotInlined; return InliningStatus_NotInlined;
@ -2923,6 +2925,11 @@ IonBuilder::inlineBoundFunction(CallInfo& nativeCallInfo, JSFunction* target)
for (size_t i = 0; i < nativeCallInfo.argc(); i++) for (size_t i = 0; i < nativeCallInfo.argc(); i++)
callInfo.argv().infallibleAppend(nativeCallInfo.getArg(i)); callInfo.argv().infallibleAppend(nativeCallInfo.getArg(i));
// We only inline when it was not a super-call, so just set the newTarget
// to be the target function, per spec.
if (nativeCallInfo.constructing())
callInfo.setNewTarget(callInfo.fun());
if (!makeCall(scriptedTarget, callInfo)) if (!makeCall(scriptedTarget, callInfo))
return InliningStatus_Error; return InliningStatus_Error;

View File

@ -57,10 +57,10 @@ VMFunction::addToFunctions()
} }
bool bool
InvokeFunction(JSContext* cx, HandleObject obj, uint32_t argc, Value* argv, InvokeFunction(JSContext* cx, HandleObject obj, bool constructing, uint32_t argc, Value* argv,
MutableHandleValue rval) MutableHandleValue rval)
{ {
AutoArrayRooter argvRoot(cx, argc + 1, argv); AutoArrayRooter argvRoot(cx, argc + 1 + constructing, argv);
// Data in the argument vector is arranged for a JIT -> JIT call. // Data in the argument vector is arranged for a JIT -> JIT call.
Value thisv = argv[0]; Value thisv = argv[0];
@ -70,11 +70,20 @@ InvokeFunction(JSContext* cx, HandleObject obj, uint32_t argc, Value* argv,
// When creating this failed / is impossible at caller site, i.e. MagicValue(JS_IS_CONSTRUCTING), // When creating this failed / is impossible at caller site, i.e. MagicValue(JS_IS_CONSTRUCTING),
// we use InvokeConstructor that creates it at the callee side. // we use InvokeConstructor that creates it at the callee side.
if (thisv.isMagic(JS_IS_CONSTRUCTING)) if (thisv.isMagic(JS_IS_CONSTRUCTING))
return InvokeConstructor(cx, ObjectValue(*obj), argc, argvWithoutThis, rval); return InvokeConstructor(cx, ObjectValue(*obj), argc, argvWithoutThis, true, rval);
return Invoke(cx, thisv, ObjectValue(*obj), argc, argvWithoutThis, rval); return Invoke(cx, thisv, ObjectValue(*obj), argc, argvWithoutThis, rval);
} }
bool
InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj, uint32_t numActualArgs,
uint32_t numFormalArgs, Value* argv, MutableHandleValue rval)
{
MOZ_ASSERT(numFormalArgs > numActualArgs);
argv[1 + numActualArgs] = argv[1 + numFormalArgs];
return InvokeFunction(cx, obj, true, numActualArgs, argv, rval);
}
bool bool
CheckOverRecursed(JSContext* cx) CheckOverRecursed(JSContext* cx)
{ {

View File

@ -631,8 +631,10 @@ class AutoDetectInvalidation
} }
}; };
bool InvokeFunction(JSContext* cx, HandleObject obj0, uint32_t argc, Value* argv, bool InvokeFunction(JSContext* cx, HandleObject obj0, bool constructing, uint32_t argc,
MutableHandleValue rval); Value* argv, MutableHandleValue rval);
bool InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj, uint32_t numActualArgs,
uint32_t numFormalArgs, Value* argv, MutableHandleValue rval);
bool CheckOverRecursed(JSContext* cx); bool CheckOverRecursed(JSContext* cx);
bool CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame, bool CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame,

View File

@ -152,6 +152,16 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
masm.loadPtr(slot_vp, r10); masm.loadPtr(slot_vp, r10);
masm.unboxInt32(Address(r10, 0), r10); masm.unboxInt32(Address(r10, 0), r10);
{
Label noNewTarget;
masm.branchTest32(Assembler::Zero, r9, Imm32(CalleeToken_FunctionConstructing),
&noNewTarget);
masm.add32(Imm32(1), r1);
masm.bind(&noNewTarget);
}
// Guarantee stack alignment of Jit frames. // Guarantee stack alignment of Jit frames.
// //
// This code moves the stack pointer to the location where it should be when // This code moves the stack pointer to the location where it should be when
@ -465,12 +475,29 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
masm.ma_sub(r6, r8, r2); masm.ma_sub(r6, r8, r2);
masm.moveValue(UndefinedValue(), r5, r4); // Get the topmost argument.
masm.ma_alu(sp, lsl(r8, 3), r3, OpAdd); // r3 <- r3 + nargs * 8
masm.ma_add(r3, Imm32(sizeof(RectifierFrameLayout)), r3);
masm.ma_mov(sp, r3); // Save %sp. {
masm.ma_mov(sp, r7); // Save %sp again. Label notConstructing;
masm.branchTest32(Assembler::Zero, r1, Imm32(CalleeToken_FunctionConstructing),
&notConstructing);
// Add sizeof(Value) to overcome |this|
masm.ma_dataTransferN(IsLoad, 64, true, r3, Imm32(8), r4, Offset);
masm.ma_dataTransferN(IsStore, 64, true, sp, Imm32(-8), r4, PreIndex);
// Include the newly pushed newTarget value in the frame size
// calculated below.
masm.add32(Imm32(1), r6);
masm.bind(&notConstructing);
}
// Push undefined. // Push undefined.
masm.moveValue(UndefinedValue(), r5, r4);
{ {
Label undefLoopTop; Label undefLoopTop;
masm.bind(&undefLoopTop); masm.bind(&undefLoopTop);
@ -480,11 +507,6 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
masm.ma_b(&undefLoopTop, Assembler::NonZero); masm.ma_b(&undefLoopTop, Assembler::NonZero);
} }
// Get the topmost argument.
masm.ma_alu(r3, lsl(r8, 3), r3, OpAdd); // r3 <- r3 + nargs * 8
masm.ma_add(r3, Imm32(sizeof(RectifierFrameLayout)), r3);
// Push arguments, |nargs| + 1 times (to include |this|). // Push arguments, |nargs| + 1 times (to include |this|).
{ {
Label copyLoopTop; Label copyLoopTop;

View File

@ -87,6 +87,18 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
// Remember number of bytes occupied by argument vector // Remember number of bytes occupied by argument vector
masm.mov(reg_argc, r13); masm.mov(reg_argc, r13);
// if we are constructing, that also needs to include newTarget
{
Label noNewTarget;
masm.branchTest32(Assembler::Zero, token, Imm32(CalleeToken_FunctionConstructing),
&noNewTarget);
masm.addq(Imm32(1), r13);
masm.bind(&noNewTarget);
}
masm.shll(Imm32(3), r13); // r13 = argc * sizeof(Value) masm.shll(Imm32(3), r13); // r13 = argc * sizeof(Value)
static_assert(sizeof(Value) == 1 << 3, "Constant is baked in assembly code"); static_assert(sizeof(Value) == 1 << 3, "Constant is baked in assembly code");
@ -111,7 +123,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
***************************************************************/ ***************************************************************/
// r13 still stores the number of bytes in the argument vector. // r13 still stores the number of bytes in the argument vector.
masm.addq(reg_argv, r13); // r13 points above last argument. masm.addq(reg_argv, r13); // r13 points above last argument or newTarget
// while r13 > rdx, push arguments. // while r13 > rdx, push arguments.
{ {
@ -397,9 +409,19 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
masm.andq(Imm32(uint32_t(CalleeTokenMask)), rcx); masm.andq(Imm32(uint32_t(CalleeTokenMask)), rcx);
masm.movzwl(Operand(rcx, JSFunction::offsetOfNargs()), rcx); masm.movzwl(Operand(rcx, JSFunction::offsetOfNargs()), rcx);
// Including |this|, there are (|nformals| + 1) arguments to push to the // Stash another copy in r11, since we are going to do destructive operations
// stack. Then we push a JitFrameLayout. We compute the padding expressed // on rcx
// in the number of extra |undefined| values to push on the stack. masm.mov(rcx, r11);
static_assert(CalleeToken_FunctionConstructing == 1,
"Ensure that we can use the constructing bit to count the value");
masm.mov(rax, rdx);
masm.andq(Imm32(uint32_t(CalleeToken_FunctionConstructing)), rdx);
// Including |this|, and |new.target|, there are (|nformals| + 1 + isConstructing)
// arguments to push to the stack. Then we push a JitFrameLayout. We
// compute the padding expressed in the number of extra |undefined| values
// to push on the stack.
static_assert(sizeof(JitFrameLayout) % JitStackAlignment == 0, static_assert(sizeof(JitFrameLayout) % JitStackAlignment == 0,
"No need to consider the JitFrameLayout for aligning the stack"); "No need to consider the JitFrameLayout for aligning the stack");
static_assert(JitStackAlignment % sizeof(Value) == 0, static_assert(JitStackAlignment % sizeof(Value) == 0,
@ -407,6 +429,7 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
MOZ_ASSERT(IsPowerOfTwo(JitStackValueAlignment)); MOZ_ASSERT(IsPowerOfTwo(JitStackValueAlignment));
masm.addl(Imm32(JitStackValueAlignment - 1 /* for padding */ + 1 /* for |this| */), rcx); masm.addl(Imm32(JitStackValueAlignment - 1 /* for padding */ + 1 /* for |this| */), rcx);
masm.addl(rdx, rcx);
masm.andl(Imm32(~(JitStackValueAlignment - 1)), rcx); masm.andl(Imm32(~(JitStackValueAlignment - 1)), rcx);
// Load the number of |undefined|s to push into %rcx. // Load the number of |undefined|s to push into %rcx.
@ -456,6 +479,28 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
masm.j(Assembler::NonZero, &copyLoopTop); masm.j(Assembler::NonZero, &copyLoopTop);
} }
// if constructing, copy newTarget
{
Label notConstructing;
masm.branchTest32(Assembler::Zero, rax, Imm32(CalleeToken_FunctionConstructing),
&notConstructing);
// thisFrame[numFormals] = prevFrame[argc]
ValueOperand newTarget(r10);
// +1 for |this|. We want vp[argc], so don't subtract 1
BaseIndex newTargetSrc(r9, rdx, TimesEight, sizeof(RectifierFrameLayout) + sizeof(Value));
masm.loadValue(newTargetSrc, newTarget);
// Again, 1 for |this|
BaseIndex newTargetDest(rsp, r11, TimesEight, sizeof(Value));
masm.storeValue(newTarget, newTargetDest);
masm.bind(&notConstructing);
}
// Caller: // Caller:
// [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- r9 // [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- r9
// //

View File

@ -624,6 +624,11 @@ class MacroAssemblerX86Shared : public Assembler
test32(Operand(address), imm); test32(Operand(address), imm);
j(cond, label); j(cond, label);
} }
void branchTest32(Condition cond, const Operand& lhs, Imm32 imm, Label* label) {
MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
test32(lhs, imm);
j(cond, label);
}
void jump(Label* label) { void jump(Label* label) {
jmp(label); jmp(label);

View File

@ -65,8 +65,22 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
// compiled function. // compiled function.
masm.movl(esp, esi); masm.movl(esp, esi);
// eax <- 8*argc, eax is now the offset betwen argv and the last // Load the number of values to be copied (argc) into eax
masm.loadPtr(Address(ebp, ARG_ARGC), eax); masm.loadPtr(Address(ebp, ARG_ARGC), eax);
// If we are constructing, that also needs to include newTarget
{
Label noNewTarget;
masm.loadPtr(Address(ebp, ARG_CALLEETOKEN), edx);
masm.branchTest32(Assembler::Zero, edx, Imm32(CalleeToken_FunctionConstructing),
&noNewTarget);
masm.addl(Imm32(1), eax);
masm.bind(&noNewTarget);
}
// eax <- 8*numValues, eax is now the offset betwen argv and the last value.
masm.shll(Imm32(3), eax); masm.shll(Imm32(3), eax);
// Guarantee stack alignment of Jit frames. // Guarantee stack alignment of Jit frames.
@ -397,6 +411,14 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
MOZ_ASSERT(IsPowerOfTwo(JitStackValueAlignment)); MOZ_ASSERT(IsPowerOfTwo(JitStackValueAlignment));
masm.addl(Imm32(JitStackValueAlignment - 1 /* for padding */), ecx); masm.addl(Imm32(JitStackValueAlignment - 1 /* for padding */), ecx);
// Account for newTarget, if necessary.
static_assert(CalleeToken_FunctionConstructing == 1,
"Ensure that we can use the constructing bit to count an extra push");
masm.mov(eax, edx);
masm.andl(Imm32(CalleeToken_FunctionConstructing), edx);
masm.addl(edx, ecx);
masm.andl(Imm32(~(JitStackValueAlignment - 1)), ecx); masm.andl(Imm32(~(JitStackValueAlignment - 1)), ecx);
masm.subl(esi, ecx); masm.subl(esi, ecx);
@ -453,6 +475,31 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
masm.j(Assembler::NonZero, &copyLoopTop); masm.j(Assembler::NonZero, &copyLoopTop);
} }
{
Label notConstructing;
masm.mov(eax, ebx);
masm.branchTest32(Assembler::Zero, ebx, Imm32(CalleeToken_FunctionConstructing),
&notConstructing);
BaseValueIndex src(FramePointer, edx,
sizeof(RectifierFrameLayout) +
sizeof(Value) +
sizeof(void*));
masm.andl(Imm32(CalleeTokenMask), ebx);
masm.movzwl(Operand(ebx, JSFunction::offsetOfNargs()), ebx);
BaseValueIndex dst(esp, ebx, sizeof(Value));
ValueOperand newTarget(ecx, edi);
masm.loadValue(src, newTarget);
masm.storeValue(newTarget, dst);
masm.bind(&notConstructing);
}
// Construct descriptor, accounting for pushed frame pointer above // Construct descriptor, accounting for pushed frame pointer above
masm.lea(Operand(FramePointer, sizeof(void*)), ebx); masm.lea(Operand(FramePointer, sizeof(void*)), ebx);
masm.subl(esp, ebx); masm.subl(esp, ebx);

View File

@ -4439,7 +4439,7 @@ JS::Construct(JSContext* cx, HandleValue fval, const JS::HandleValueArray& args,
assertSameCompartment(cx, fval, args); assertSameCompartment(cx, fval, args);
AutoLastFrameCheck lfc(cx); AutoLastFrameCheck lfc(cx);
return InvokeConstructor(cx, fval, args.length(), args.begin(), rval); return InvokeConstructor(cx, fval, args.length(), args.begin(), false, rval);
} }
static JSObject* static JSObject*
@ -4454,12 +4454,13 @@ JS_NewHelper(JSContext* cx, HandleObject ctor, const JS::HandleValueArray& input
// of object to create, create it, and clamp the return value to an object, // of object to create, create it, and clamp the return value to an object,
// among other details. InvokeConstructor does the hard work. // among other details. InvokeConstructor does the hard work.
InvokeArgs args(cx); InvokeArgs args(cx);
if (!args.init(inputArgs.length())) if (!args.init(inputArgs.length(), true))
return nullptr; return nullptr;
args.setCallee(ObjectValue(*ctor)); args.setCallee(ObjectValue(*ctor));
args.setThis(NullValue()); args.setThis(NullValue());
PodCopy(args.array(), inputArgs.begin(), inputArgs.length()); PodCopy(args.array(), inputArgs.begin(), inputArgs.length());
args.newTarget().setObject(*ctor);
if (!InvokeConstructor(cx, args)) if (!InvokeConstructor(cx, args))
return nullptr; return nullptr;

View File

@ -3191,7 +3191,7 @@ array_of(JSContext* cx, unsigned argc, Value* vp)
{ {
RootedValue v(cx); RootedValue v(cx);
Value argv[1] = {NumberValue(args.length())}; Value argv[1] = {NumberValue(args.length())};
if (!InvokeConstructor(cx, args.thisv(), 1, argv, &v)) if (!InvokeConstructor(cx, args.thisv(), 1, argv, false, &v))
return false; return false;
obj = ToObject(cx, v); obj = ToObject(cx, v);
if (!obj) if (!obj)
@ -3286,6 +3286,10 @@ bool
js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp) js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
if (args.isConstructing())
MOZ_ASSERT(args.newTarget().toObject().as<JSFunction>().native() == js::ArrayConstructor);
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
if (!group) if (!group)
return false; return false;

View File

@ -1552,7 +1552,7 @@ js::CallOrConstructBoundFunction(JSContext* cx, unsigned argc, Value* vp)
const Value& boundThis = fun->getBoundFunctionThis(); const Value& boundThis = fun->getBoundFunctionThis();
InvokeArgs invokeArgs(cx); InvokeArgs invokeArgs(cx);
if (!invokeArgs.init(args.length() + argslen)) if (!invokeArgs.init(args.length() + argslen, args.isConstructing()))
return false; return false;
/* 15.3.4.5.1, 15.3.4.5.2 step 4. */ /* 15.3.4.5.1, 15.3.4.5.2 step 4. */
@ -1567,6 +1567,14 @@ js::CallOrConstructBoundFunction(JSContext* cx, unsigned argc, Value* vp)
if (!constructing) if (!constructing)
invokeArgs.setThis(boundThis); invokeArgs.setThis(boundThis);
/* ES6 9.4.1.2 step 5 */
if (constructing) {
if (&args.newTarget().toObject() == fun)
invokeArgs.newTarget().setObject(*target);
else
invokeArgs.newTarget().set(args.newTarget());
}
if (constructing ? !InvokeConstructor(cx, invokeArgs) : !Invoke(cx, invokeArgs)) if (constructing ? !InvokeConstructor(cx, invokeArgs) : !Invoke(cx, invokeArgs))
return false; return false;

View File

@ -448,16 +448,14 @@ GetCustomIterator(JSContext* cx, HandleObject obj, unsigned flags, MutableHandle
if (!Invoke(cx, ObjectValue(*obj), rval, 1, &arg, &rval)) if (!Invoke(cx, ObjectValue(*obj), rval, 1, &arg, &rval))
return false; return false;
if (rval.isPrimitive()) { if (rval.isPrimitive()) {
/* // Ignore the stack when throwing. We can't tell whether we were
* We are always coming from js::ValueToIterator, and we are no longer on // supposed to skip over a new.target or not.
* trace, so the object we are iterating over is on top of the stack (-1).
*/
JSAutoByteString bytes; JSAutoByteString bytes;
if (!AtomToPrintableString(cx, name, &bytes)) if (!AtomToPrintableString(cx, name, &bytes))
return false; return false;
RootedValue val(cx, ObjectValue(*obj)); RootedValue val(cx, ObjectValue(*obj));
ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE, ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
-1, val, nullptr, bytes.ptr()); JSDVG_IGNORE_STACK, val, nullptr, bytes.ptr());
return false; return false;
} }
objp.set(&rval.toObject()); objp.set(&rval.toObject());

View File

@ -119,9 +119,11 @@ js::StackUses(JSScript* script, jsbytecode* pc)
switch (op) { switch (op) {
case JSOP_POPN: case JSOP_POPN:
return GET_UINT16(pc); return GET_UINT16(pc);
case JSOP_NEW:
return 2 + GET_ARGC(pc) + 1;
default: default:
/* stack: fun, this, [argc arguments] */ /* stack: fun, this, [argc arguments] */
MOZ_ASSERT(op == JSOP_NEW || op == JSOP_CALL || op == JSOP_EVAL || MOZ_ASSERT(op == JSOP_CALL || op == JSOP_EVAL ||
op == JSOP_STRICTEVAL || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY); op == JSOP_STRICTEVAL || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY);
return 2 + GET_ARGC(pc); return 2 + GET_ARGC(pc);
} }

View File

@ -304,6 +304,8 @@ CrossCompartmentWrapper::construct(JSContext* cx, HandleObject wrapper, const Ca
if (!cx->compartment()->wrap(cx, args[n])) if (!cx->compartment()->wrap(cx, args[n]))
return false; return false;
} }
if (!cx->compartment()->wrap(cx, args.newTarget()))
return false;
if (!Wrapper::construct(cx, wrapper, args)) if (!Wrapper::construct(cx, wrapper, args))
return false; return false;
} }

View File

@ -82,7 +82,7 @@ DirectProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs&
{ {
assertEnteredPolicy(cx, proxy, JSID_VOID, CALL); assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
RootedValue target(cx, proxy->as<ProxyObject>().private_()); RootedValue target(cx, proxy->as<ProxyObject>().private_());
return InvokeConstructor(cx, target, args.length(), args.array(), args.rval()); return InvokeConstructor(cx, target, args.length(), args.array(), true, args.rval());
} }
bool bool

View File

@ -1038,13 +1038,14 @@ ScriptedDirectProxyHandler::construct(JSContext* cx, HandleObject proxy, const C
// step 6 // step 6
if (trap.isUndefined()) { if (trap.isUndefined()) {
RootedValue targetv(cx, ObjectValue(*target)); RootedValue targetv(cx, ObjectValue(*target));
return InvokeConstructor(cx, targetv, args.length(), args.array(), args.rval()); return InvokeConstructor(cx, targetv, args.length(), args.array(), true, args.rval());
} }
// step 8-9 // step 8-9
Value constructArgv[] = { Value constructArgv[] = {
ObjectValue(*target), ObjectValue(*target),
ObjectValue(*argsArray) ObjectValue(*argsArray),
args.newTarget()
}; };
RootedValue thisValue(cx, ObjectValue(*handler)); RootedValue thisValue(cx, ObjectValue(*handler));
if (!Invoke(cx, thisValue, trap, ArrayLength(constructArgv), constructArgv, args.rval())) if (!Invoke(cx, thisValue, trap, ArrayLength(constructArgv), constructArgv, args.rval()))

View File

@ -467,7 +467,7 @@ CallableScriptedIndirectProxyHandler::construct(JSContext* cx, HandleObject prox
MOZ_ASSERT(ccHolder->getClass() == &CallConstructHolder); MOZ_ASSERT(ccHolder->getClass() == &CallConstructHolder);
RootedValue construct(cx, ccHolder->as<NativeObject>().getReservedSlot(1)); RootedValue construct(cx, ccHolder->as<NativeObject>().getReservedSlot(1));
MOZ_ASSERT(construct.isObject() && construct.toObject().isCallable()); MOZ_ASSERT(construct.isObject() && construct.toObject().isCallable());
return InvokeConstructor(cx, construct, args.length(), args.array(), args.rval()); return InvokeConstructor(cx, construct, args.length(), args.array(), true, args.rval());
} }
const CallableScriptedIndirectProxyHandler CallableScriptedIndirectProxyHandler::singleton; const CallableScriptedIndirectProxyHandler CallableScriptedIndirectProxyHandler::singleton;

View File

@ -0,0 +1,10 @@
// Make sure we wrap the new target on CCW construct calls.
var g = newGlobal();
let f = g.eval('(function (expected) { this.accept = new.target === expected; })');
for (let i = 0; i < 1100; i++)
assertEq(new f(f).accept, true);
if (typeof reportCompare === 'function')
reportCompare(0,0,"OK");

View File

@ -6,7 +6,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
var BUGNUMBER = 354945; var BUGNUMBER = 354945;
var summary = 'Do not crash with new Iterator'; var summary = 'Do not crash with new Iterator';
var expect = 'TypeError: trap __iterator__ for obj returned a primitive value'; var expect = 'TypeError: trap __iterator__ for ({__iterator__:(function (){ })}) returned a primitive value';
var actual; var actual;

View File

@ -20,7 +20,7 @@ function test()
printBugNumber(BUGNUMBER); printBugNumber(BUGNUMBER);
printStatus (summary); printStatus (summary);
expect = 'TypeError: trap __iterator__ for obj returned a primitive value'; expect = 'TypeError: trap __iterator__ for ({__iterator__:(function (){ })}) returned a primitive value';
var obj = {}; var obj = {};
obj.__iterator__ = function(){ }; obj.__iterator__ = function(){ };
try try

View File

@ -677,8 +677,9 @@ js::Invoke(JSContext* cx, CallArgs args, MaybeConstruct construct)
/* MaybeConstruct is a subset of InitialFrameFlags */ /* MaybeConstruct is a subset of InitialFrameFlags */
InitialFrameFlags initial = (InitialFrameFlags) construct; InitialFrameFlags initial = (InitialFrameFlags) construct;
unsigned skipForCallee = args.length() + 1 + (construct == CONSTRUCT);
if (args.calleev().isPrimitive()) if (args.calleev().isPrimitive())
return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, construct); return ReportIsNotFunction(cx, args.calleev(), skipForCallee, construct);
const Class* clasp = args.callee().getClass(); const Class* clasp = args.callee().getClass();
@ -691,7 +692,7 @@ js::Invoke(JSContext* cx, CallArgs args, MaybeConstruct construct)
MOZ_ASSERT_IF(construct, !args.callee().constructHook()); MOZ_ASSERT_IF(construct, !args.callee().constructHook());
JSNative call = args.callee().callHook(); JSNative call = args.callee().callHook();
if (!call) if (!call)
return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, construct); return ReportIsNotFunction(cx, args.calleev(), skipForCallee, construct);
return CallJSNative(cx, call, args); return CallJSNative(cx, call, args);
} }
@ -770,15 +771,18 @@ js::InvokeConstructor(JSContext* cx, CallArgs args)
args.setThis(MagicValue(JS_IS_CONSTRUCTING)); args.setThis(MagicValue(JS_IS_CONSTRUCTING));
// +2 here and below to pass over |this| and |new.target|
if (!args.calleev().isObject()) if (!args.calleev().isObject())
return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, CONSTRUCT); return ReportIsNotFunction(cx, args.calleev(), args.length() + 2, CONSTRUCT);
MOZ_ASSERT(args.newTarget().isObject());
JSObject& callee = args.callee(); JSObject& callee = args.callee();
if (callee.is<JSFunction>()) { if (callee.is<JSFunction>()) {
RootedFunction fun(cx, &callee.as<JSFunction>()); RootedFunction fun(cx, &callee.as<JSFunction>());
if (!fun->isConstructor()) if (!fun->isConstructor())
return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, CONSTRUCT); return ReportIsNotFunction(cx, args.calleev(), args.length() + 2, CONSTRUCT);
if (fun->isNative()) if (fun->isNative())
return CallJSNativeConstructor(cx, fun->native(), args); return CallJSNativeConstructor(cx, fun->native(), args);
@ -792,22 +796,26 @@ js::InvokeConstructor(JSContext* cx, CallArgs args)
JSNative construct = callee.constructHook(); JSNative construct = callee.constructHook();
if (!construct) if (!construct)
return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, CONSTRUCT); return ReportIsNotFunction(cx, args.calleev(), args.length() + 2, CONSTRUCT);
return CallJSNativeConstructor(cx, construct, args); return CallJSNativeConstructor(cx, construct, args);
} }
bool bool
js::InvokeConstructor(JSContext* cx, Value fval, unsigned argc, const Value* argv, js::InvokeConstructor(JSContext* cx, Value fval, unsigned argc, const Value* argv,
MutableHandleValue rval) bool newTargetInArgv, MutableHandleValue rval)
{ {
InvokeArgs args(cx); InvokeArgs args(cx);
if (!args.init(argc)) if (!args.init(argc, true))
return false; return false;
args.setCallee(fval); args.setCallee(fval);
args.setThis(MagicValue(JS_THIS_POISON)); args.setThis(MagicValue(JS_THIS_POISON));
PodCopy(args.array(), argv, argc); PodCopy(args.array(), argv, argc);
if (newTargetInArgv)
args.newTarget().set(argv[argc]);
else
args.newTarget().set(fval);
if (!InvokeConstructor(cx, args)) if (!InvokeConstructor(cx, args))
return false; return false;
@ -2891,16 +2899,25 @@ CASE(JSOP_STRICTSPREADEVAL)
{ {
static_assert(JSOP_SPREADEVAL_LENGTH == JSOP_STRICTSPREADEVAL_LENGTH, static_assert(JSOP_SPREADEVAL_LENGTH == JSOP_STRICTSPREADEVAL_LENGTH,
"spreadeval and strictspreadeval must be the same size"); "spreadeval and strictspreadeval must be the same size");
MOZ_ASSERT(REGS.stackDepth() >= 3); bool construct = JSOp(*REGS.pc) == JSOP_SPREADNEW;
HandleValue callee = REGS.stackHandleAt(-3); MOZ_ASSERT(REGS.stackDepth() >= 3u + construct);
HandleValue thisv = REGS.stackHandleAt(-2);
HandleValue arr = REGS.stackHandleAt(-1); HandleValue callee = REGS.stackHandleAt(-3 - construct);
MutableHandleValue ret = REGS.stackHandleAt(-3); HandleValue thisv = REGS.stackHandleAt(-2 - construct);
if (!SpreadCallOperation(cx, script, REGS.pc, thisv, callee, arr, ret)) HandleValue arr = REGS.stackHandleAt(-1 - construct);
MutableHandleValue ret = REGS.stackHandleAt(-3 - construct);
RootedValue& newTarget = rootValue0;
if (construct)
newTarget = REGS.sp[-1];
else
newTarget = NullValue();
if (!SpreadCallOperation(cx, script, REGS.pc, thisv, callee, arr, newTarget, ret))
goto error; goto error;
REGS.sp -= 2; REGS.sp -= 2 + construct;
} }
END_CASE(JSOP_SPREADCALL) END_CASE(JSOP_SPREADCALL)
@ -2919,10 +2936,11 @@ CASE(JSOP_FUNCALL)
if (REGS.fp()->hasPushedSPSFrame()) if (REGS.fp()->hasPushedSPSFrame())
cx->runtime()->spsProfiler.updatePC(script, REGS.pc); cx->runtime()->spsProfiler.updatePC(script, REGS.pc);
MOZ_ASSERT(REGS.stackDepth() >= 2u + GET_ARGC(REGS.pc));
CallArgs args = CallArgsFromSp(GET_ARGC(REGS.pc), REGS.sp);
bool construct = (*REGS.pc == JSOP_NEW); bool construct = (*REGS.pc == JSOP_NEW);
unsigned argStackSlots = GET_ARGC(REGS.pc) + construct;
MOZ_ASSERT(REGS.stackDepth() >= 2u + GET_ARGC(REGS.pc));
CallArgs args = CallArgsFromSp(argStackSlots, REGS.sp, construct);
JSFunction* maybeFun; JSFunction* maybeFun;
bool isFunction = IsFunctionObject(args.calleev(), &maybeFun); bool isFunction = IsFunctionObject(args.calleev(), &maybeFun);
@ -4526,15 +4544,16 @@ js::InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, H
bool bool
js::SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue thisv, js::SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue thisv,
HandleValue callee, HandleValue arr, MutableHandleValue res) HandleValue callee, HandleValue arr, HandleValue newTarget, MutableHandleValue res)
{ {
RootedArrayObject aobj(cx, &arr.toObject().as<ArrayObject>()); RootedArrayObject aobj(cx, &arr.toObject().as<ArrayObject>());
uint32_t length = aobj->length(); uint32_t length = aobj->length();
JSOp op = JSOp(*pc); JSOp op = JSOp(*pc);
bool constructing = op == JSOP_SPREADNEW;
if (length > ARGS_LENGTH_MAX) { if (length > ARGS_LENGTH_MAX) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
op == JSOP_SPREADNEW ? JSMSG_TOO_MANY_CON_SPREADARGS constructing ? JSMSG_TOO_MANY_CON_SPREADARGS
: JSMSG_TOO_MANY_FUN_SPREADARGS); : JSMSG_TOO_MANY_FUN_SPREADARGS);
return false; return false;
} }
@ -4550,7 +4569,7 @@ js::SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, Hand
InvokeArgs args(cx); InvokeArgs args(cx);
if (!args.init(length)) if (!args.init(length, constructing))
return false; return false;
args.setCallee(callee); args.setCallee(callee);
@ -4559,6 +4578,11 @@ js::SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, Hand
if (!GetElements(cx, aobj, length, args.array())) if (!GetElements(cx, aobj, length, args.array()))
return false; return false;
if (constructing) {
MOZ_ASSERT(newTarget.isObject());
args.newTarget().set(newTarget);
}
switch (op) { switch (op) {
case JSOP_SPREADNEW: case JSOP_SPREADNEW:
if (!InvokeConstructor(cx, args)) if (!InvokeConstructor(cx, args))

View File

@ -95,7 +95,7 @@ InvokeConstructor(JSContext* cx, CallArgs args);
/* See the fval overload of Invoke. */ /* See the fval overload of Invoke. */
extern bool extern bool
InvokeConstructor(JSContext* cx, Value fval, unsigned argc, const Value* argv, InvokeConstructor(JSContext* cx, Value fval, unsigned argc, const Value* argv,
MutableHandleValue rval); bool newTargetInArgv, MutableHandleValue rval);
/* /*
* Executes a script with the given scopeChain/this. The 'type' indicates * Executes a script with the given scopeChain/this. The 'type' indicates
@ -436,7 +436,7 @@ InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, Handl
bool bool
SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue thisv, SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue thisv,
HandleValue callee, HandleValue arr, MutableHandleValue res); HandleValue callee, HandleValue arr, HandleValue newTarget, MutableHandleValue res);
JSObject* JSObject*
NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc, NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc,

View File

@ -391,9 +391,9 @@
* Category: Statements * Category: Statements
* Type: Function * Type: Function
* Operands: * Operands:
* Stack: callee, this, args => rval * Stack: callee, this, args, newTarget => rval
*/ \ */ \
macro(JSOP_SPREADNEW, 42, "spreadnew", NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET) \ macro(JSOP_SPREADNEW, 42, "spreadnew", NULL, 1, 4, 1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET) \
/* /*
* spreadcall variant of JSOP_EVAL * spreadcall variant of JSOP_EVAL
* *
@ -766,8 +766,8 @@
* Category: Statements * Category: Statements
* Type: Function * Type: Function
* Operands: uint16_t argc * Operands: uint16_t argc
* Stack: callee, this, args[0], ..., args[argc-1] => rval * Stack: callee, this, args[0], ..., args[argc-1], newTarget => rval
* nuses: (argc+2) * nuses: (argc+3)
*/ \ */ \
macro(JSOP_NEW, 82, js_new_str, NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \ macro(JSOP_NEW, 82, js_new_str, NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \
/* /*

View File

@ -292,7 +292,10 @@ InterpreterStack::getCallFrame(JSContext* cx, const CallArgs& args, HandleScript
// Pad any missing arguments with |undefined|. // Pad any missing arguments with |undefined|.
MOZ_ASSERT(args.length() < nformal); MOZ_ASSERT(args.length() < nformal);
nvals += nformal + 2; // Include callee, |this|. bool isConstructing = *flags & InterpreterFrame::CONSTRUCTING;
unsigned nfunctionState = 2 + isConstructing; // callee, |this|, |new.target|
nvals += nformal + nfunctionState;
uint8_t* buffer = allocateFrame(cx, sizeof(InterpreterFrame) + nvals * sizeof(Value)); uint8_t* buffer = allocateFrame(cx, sizeof(InterpreterFrame) + nvals * sizeof(Value));
if (!buffer) if (!buffer)
return nullptr; return nullptr;
@ -303,8 +306,11 @@ InterpreterStack::getCallFrame(JSContext* cx, const CallArgs& args, HandleScript
mozilla::PodCopy(argv, args.base(), 2 + args.length()); mozilla::PodCopy(argv, args.base(), 2 + args.length());
SetValueRangeToUndefined(argv + 2 + args.length(), nmissing); SetValueRangeToUndefined(argv + 2 + args.length(), nmissing);
if (isConstructing)
argv[2 + nformal] = args.newTarget();
*pargv = argv + 2; *pargv = argv + 2;
return reinterpret_cast<InterpreterFrame*>(argv + 2 + nformal); return reinterpret_cast<InterpreterFrame*>(argv + nfunctionState + nformal);
} }
MOZ_ALWAYS_INLINE bool MOZ_ALWAYS_INLINE bool

View File

@ -397,7 +397,7 @@ InterpreterFrame::markValues(JSTracer* trc, Value* sp, jsbytecode* pc)
if (hasArgs()) { if (hasArgs()) {
// Mark callee, |this| and arguments. // Mark callee, |this| and arguments.
unsigned argc = Max(numActualArgs(), numFormalArgs()); unsigned argc = Max(numActualArgs(), numFormalArgs());
TraceRootRange(trc, argc + 2, argv_ - 2, "fp argv"); TraceRootRange(trc, argc + 2 + isConstructing(), argv_ - 2, "fp argv");
} else { } else {
// Mark callee and |this| // Mark callee and |this|
TraceRootRange(trc, 2, ((Value*)this) - 2, "stack callee and this"); TraceRootRange(trc, 2, ((Value*)this) - 2, "stack callee and this");

View File

@ -938,7 +938,7 @@ class InterpreterRegs
void popInlineFrame() { void popInlineFrame() {
pc = fp_->prevpc(); pc = fp_->prevpc();
sp = fp_->prevsp() - fp_->numActualArgs() - 1; sp = fp_->prevsp() - fp_->numActualArgs() - 1 - fp_->isConstructing();
fp_ = fp_->prev(); fp_ = fp_->prev();
MOZ_ASSERT(fp_); MOZ_ASSERT(fp_);
} }
@ -1033,12 +1033,14 @@ class InvokeArgs : public JS::CallArgs
AutoValueVector v_; AutoValueVector v_;
public: public:
explicit InvokeArgs(JSContext* cx) : v_(cx) {} explicit InvokeArgs(JSContext* cx, bool construct = false) : v_(cx) {}
bool init(unsigned argc) { bool init(unsigned argc, bool construct = false) {
if (!v_.resize(2 + argc)) if (!v_.resize(2 + argc + construct))
return false; return false;
ImplicitCast<CallArgs>(*this) = CallArgsFromVp(argc, v_.begin()); ImplicitCast<CallArgs>(*this) = CallArgsFromVp(argc, v_.begin());
// Set the internal flag, since we are not initializing from a made array
constructing_ = construct;
return true; return true;
} }
}; };

View File

@ -29,7 +29,7 @@ namespace js {
* *
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
*/ */
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 288; static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 289;
static const uint32_t XDR_BYTECODE_VERSION = static const uint32_t XDR_BYTECODE_VERSION =
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);