mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
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:
parent
da39aee9fd
commit
1fa16671fa
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
12
js/src/jit-test/tests/basic/newTargetRectifier.js
Normal file
12
js/src/jit-test/tests/basic/newTargetRectifier.js
Normal 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();
|
37
js/src/jit-test/tests/debug/Frame-newTargetOverflow-01.js
Normal file
37
js/src/jit-test/tests/debug/Frame-newTargetOverflow-01.js
Normal 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);
|
||||||
|
} + ")();");
|
||||||
|
});
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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"))
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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_) {
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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,
|
||||||
|
@ -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),
|
||||||
|
¬Constructing);
|
||||||
|
|
||||||
|
// 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(¬Constructing);
|
||||||
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
|
@ -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, ©LoopTop);
|
masm.j(Assembler::NonZero, ©LoopTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if constructing, copy newTarget
|
||||||
|
{
|
||||||
|
Label notConstructing;
|
||||||
|
|
||||||
|
masm.branchTest32(Assembler::Zero, rax, Imm32(CalleeToken_FunctionConstructing),
|
||||||
|
¬Constructing);
|
||||||
|
|
||||||
|
// 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(¬Constructing);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Caller:
|
// Caller:
|
||||||
// [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- r9
|
// [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- r9
|
||||||
//
|
//
|
||||||
|
@ -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);
|
||||||
|
@ -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, ©LoopTop);
|
masm.j(Assembler::NonZero, ©LoopTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Label notConstructing;
|
||||||
|
|
||||||
|
masm.mov(eax, ebx);
|
||||||
|
masm.branchTest32(Assembler::Zero, ebx, Imm32(CalleeToken_FunctionConstructing),
|
||||||
|
¬Constructing);
|
||||||
|
|
||||||
|
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(¬Constructing);
|
||||||
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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());
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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()))
|
||||||
|
@ -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;
|
||||||
|
10
js/src/tests/ecma_6/Class/newTargetCCW.js
Normal file
10
js/src/tests/ecma_6/Class/newTargetCCW.js
Normal 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");
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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))
|
||||||
|
@ -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,
|
||||||
|
@ -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) \
|
||||||
/*
|
/*
|
||||||
|
@ -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
|
||||||
|
@ -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");
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user