author | Eric Faust <efaustbmo@mozilla.com> |
Wed, 03 Jun 2015 02:01:15 -0700 | |
changeset 247084 | d1d159eb02b1a1422d372fdc3b826ff3d9759c75 |
parent 247083 | 4d30f8210cd6661bafc9482bfad857452c252ee6 |
child 247085 | 5f7b5bd0505dacc083aa6d195b2491e1c8fa438e |
push id | 28852 |
push user | ryanvm@gmail.com |
push date | Thu, 04 Jun 2015 12:39:12 +0000 |
treeherder | mozilla-central@b2ad2f9f8e53 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jorendorff, jandem |
bugs | 1141865 |
milestone | 41.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/builtin/Eval.cpp +++ b/js/src/builtin/Eval.cpp @@ -356,25 +356,27 @@ EvalKernel(JSContext* cx, const CallArgs return false; if (compiled->strict()) staticScope->setStrict(); esg.setNewScript(compiled); } - return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType), + // Look up the newTarget from the frame iterator. + Value newTargetVal = NullValue(); + return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, newTargetVal, ExecuteType(evalType), NullFramePtr() /* evalInFrame */, args.rval().address()); } bool js::DirectEvalStringFromIon(JSContext* cx, HandleObject scopeobj, HandleScript callerScript, - HandleValue thisValue, HandleString str, - jsbytecode* pc, MutableHandleValue vp) + HandleValue thisValue, HandleValue newTargetValue, + HandleString str, jsbytecode* pc, MutableHandleValue vp) { AssertInnerizedScopeChain(cx, *scopeobj); Rooted<GlobalObject*> scopeObjGlobal(cx, &scopeobj->global()); if (!GlobalObject::isRuntimeCodeGenEnabled(cx, scopeObjGlobal)) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_EVAL); return false; } @@ -456,18 +458,18 @@ js::DirectEvalStringFromIon(JSContext* c RootedValue nthisValue(cx, thisValue); if (!callerScript->strict() && esg.script()->strict() && !thisValue.isObject()) { JSObject* obj = BoxNonStrictThis(cx, thisValue); if (!obj) return false; nthisValue = ObjectValue(*obj); } - return ExecuteKernel(cx, esg.script(), *scopeobj, nthisValue, ExecuteType(DIRECT_EVAL), - NullFramePtr() /* evalInFrame */, vp.address()); + return ExecuteKernel(cx, esg.script(), *scopeobj, nthisValue, newTargetValue, + ExecuteType(DIRECT_EVAL), NullFramePtr() /* evalInFrame */, vp.address()); } bool js::IndirectEval(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); Rooted<GlobalObject*> global(cx, &args.callee().global()); return EvalKernel(cx, args, INDIRECT_EVAL, NullFramePtr(), global, nullptr); @@ -529,17 +531,17 @@ js::ExecuteInGlobalAndReturnScope(JSCont JSObject* thisobj = GetThisObject(cx, global); if (!thisobj) return false; RootedValue thisv(cx, ObjectValue(*thisobj)); RootedValue rval(cx); // XXXbz when this is fixed to pass in an actual ScopeObject, fix // up the assert in js::CloneFunctionObject accordingly. - if (!ExecuteKernel(cx, script, *scope, thisv, EXECUTE_GLOBAL, + if (!ExecuteKernel(cx, script, *scope, thisv, UndefinedValue(), EXECUTE_GLOBAL, NullFramePtr() /* evalInFrame */, rval.address())) { return false; } scopeArg.set(scope); return true; }
--- a/js/src/builtin/Eval.h +++ b/js/src/builtin/Eval.h @@ -25,17 +25,17 @@ IndirectEval(JSContext* cx, unsigned arg // the result is returned in args.rval. extern bool DirectEval(JSContext* cx, const CallArgs& args); // Performs a direct eval called from Ion code. extern bool DirectEvalStringFromIon(JSContext* cx, HandleObject scopeObj, HandleScript callerScript, - HandleValue thisValue, HandleString str, - jsbytecode * pc, MutableHandleValue vp); + HandleValue thisValue, HandleValue newTargetValue, + HandleString str, jsbytecode * pc, MutableHandleValue vp); // True iff fun is a built-in eval function. extern bool IsAnyBuiltinEval(JSFunction* fun); } // namespace js #endif /* builtin_Eval_h */
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -7831,28 +7831,50 @@ Parser<ParseHandler>::argumentList(Yield return false; } handler.setEndPosition(listNode, pos().end); return true; } template <typename ParseHandler> bool -Parser<ParseHandler>::checkAndMarkSuperScope() +Parser<ParseHandler>::checkAllowedNestedSyntax(SharedContext::AllowedSyntax allowed, + SharedContext** allowingContext) { for (GenericParseContext* gpc = pc; gpc; gpc = gpc->parent) { SharedContext* sc = gpc->sc; - if (sc->allowSuperProperty()) { - if (sc->isFunctionBox()) - sc->asFunctionBox()->setNeedsHomeObject(); - return true; - } else if (sc->isFunctionBox() && !sc->asFunctionBox()->function()->isArrow()) { - // super is not legal in normal functions. - break; - } + + // Arrow functions don't help decide whether we should allow nested + // syntax, as they don't store any of the necessary state for themselves. + if (sc->isFunctionBox() && sc->asFunctionBox()->function()->isArrow()) { + // For now (!), disallow new.target in arrow functions. This will + // change later in this bug! + if (allowed == SharedContext::AllowedSyntax::NewTarget) + return false; + continue; + } + + if (!sc->allowSyntax(allowed)) + return false; + if (allowingContext) + *allowingContext = sc; + return true; + } + return false; +} + +template <typename ParseHandler> +bool +Parser<ParseHandler>::checkAndMarkSuperScope() +{ + SharedContext* foundContext = nullptr; + if (checkAllowedNestedSyntax(SharedContext::AllowedSyntax::SuperProperty, &foundContext)) { + if (foundContext->isFunctionBox()) + foundContext->asFunctionBox()->setNeedsHomeObject(); + return true; } return false; } template <typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TokenKind tt, bool allowCallSyntax, InvokedPrediction invoked) @@ -8639,19 +8661,17 @@ Parser<ParseHandler>::tryNewTarget(Node if (!tokenStream.getToken(&next)) return false; if (next != TOK_NAME || tokenStream.currentName() != context->names().target) { report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN, "target", TokenKindToDesc(next)); return false; } - if (!pc->sc->isFunctionBox() || pc->sc->asFunctionBox()->isGenerator() || - pc->sc->asFunctionBox()->function()->isArrow()) - { + if (!checkAllowedNestedSyntax(SharedContext::AllowedSyntax::NewTarget)) { reportWithOffset(ParseError, false, begin, JSMSG_BAD_NEWTARGET); return false; } newTarget = handler.newNewTarget(TokenPos(begin, pos().end)); return !!newTarget; }
--- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -584,16 +584,18 @@ class Parser : private JS::AutoGCRooter, Node unaryExpr(YieldHandling yieldHandling, InvokedPrediction invoked = PredictUninvoked); Node memberExpr(YieldHandling yieldHandling, TokenKind tt, bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked); Node primaryExpr(YieldHandling yieldHandling, TokenKind tt, InvokedPrediction invoked = PredictUninvoked); Node parenExprOrGeneratorComprehension(YieldHandling yieldHandling); Node exprInParens(InHandling inHandling, YieldHandling yieldHandling); + bool checkAllowedNestedSyntax(SharedContext::AllowedSyntax allowed, + SharedContext** allowingContext = nullptr); bool tryNewTarget(Node& newTarget); bool checkAndMarkSuperScope(); bool methodDefinition(YieldHandling yieldHandling, PropListType listType, Node propList, Node propname, FunctionSyntaxKind kind, GeneratorKind generatorKind, bool isStatic, JSOp Op); /*
--- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -225,17 +225,37 @@ class SharedContext bool needStrictChecks() { return strict() || extraWarnings; } bool isDotVariable(JSAtom* atom) const { return atom == context->names().dotGenerator || atom == context->names().dotGenRVal; } - virtual bool allowSuperProperty() const = 0; + enum class AllowedSyntax { + NewTarget, + SuperProperty + }; + virtual bool allowSyntax(AllowedSyntax allowed) const = 0; + + protected: + static bool FunctionAllowsSyntax(JSFunction* func, AllowedSyntax allowed) + { + MOZ_ASSERT(!func->isArrow()); + + switch (allowed) { + case AllowedSyntax::NewTarget: + // For now, disallow new.target inside generators + return !func->isGenerator(); + case AllowedSyntax::SuperProperty: + return func->allowSuperProperty(); + default:; + } + MOZ_CRASH("Unknown AllowedSyntax query"); + } }; class GlobalSharedContext : public SharedContext { private: Handle<StaticEvalObject*> staticEvalScope_; public: @@ -244,23 +264,27 @@ class GlobalSharedContext : public Share bool extraWarnings) : SharedContext(cx, directives, extraWarnings), staticEvalScope_(staticEvalScope) {} ObjectBox* toObjectBox() { return nullptr; } HandleObject evalStaticScope() const { return staticEvalScope_; } - bool allowSuperProperty() const { + bool allowSyntax(AllowedSyntax allowed) const { StaticScopeIter<CanGC> it(context, staticEvalScope_); for (; !it.done(); it++) { - if (it.type() == StaticScopeIter<CanGC>::Function && - !it.fun().isArrow()) - { - return it.fun().allowSuperProperty(); + if (it.type() == StaticScopeIter<CanGC>::Function) { + if (it.fun().isArrow()) { + // For the moment, disallow new.target inside arrow functions + if (allowed == AllowedSyntax::NewTarget) + return false; + continue; + } + return FunctionAllowsSyntax(&it.fun(), allowed); } } return false; } }; class FunctionBox : public ObjectBox, public SharedContext { @@ -315,17 +339,17 @@ class FunctionBox : public ObjectBox, pu bool needsHomeObject() const { return funCxFlags.needsHomeObject; } void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; } void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; } void setNeedsDeclEnvObject() { funCxFlags.needsDeclEnvObject = true; } void setArgumentsHasLocalBinding() { funCxFlags.argumentsHasLocalBinding = true; } void setDefinitelyNeedsArgsObj() { MOZ_ASSERT(funCxFlags.argumentsHasLocalBinding); funCxFlags.definitelyNeedsArgsObj = true; } - void setNeedsHomeObject() { MOZ_ASSERT(allowSuperProperty()); + void setNeedsHomeObject() { MOZ_ASSERT(function()->allowSuperProperty()); funCxFlags.needsHomeObject = true; } bool hasDefaults() const { return length != function()->nargs() - function()->hasRest(); } // Return whether this or an enclosing function is being parsed and // validated as asm.js. Note: if asm.js validation fails, this will be false @@ -347,18 +371,23 @@ class FunctionBox : public ObjectBox, pu // Note: this should be kept in sync with JSFunction::isHeavyweight(). return bindings.hasAnyAliasedBindings() || hasExtensibleScope() || needsDeclEnvObject() || needsHomeObject() || isGenerator(); } - bool allowSuperProperty() const { - return function()->allowSuperProperty(); + bool allowSyntax(AllowedSyntax allowed) const { + // For now (!) we don't allow new.target in generators, and can't + // check that for functions we haven't finished parsing, as they + // don't have initialized scripts. Check from our stashed bits instead. + if (allowed == AllowedSyntax::NewTarget) + return !isGenerator(); + return FunctionAllowsSyntax(function(), allowed); } }; inline FunctionBox* SharedContext::asFunctionBox() { MOZ_ASSERT(isFunctionBox()); return static_cast<FunctionBox*>(this); @@ -366,16 +395,17 @@ SharedContext::asFunctionBox() inline GlobalSharedContext* SharedContext::asGlobalSharedContext() { MOZ_ASSERT(!isFunctionBox()); return static_cast<GlobalSharedContext*>(this); } + // In generators, we treat all locals as aliased so that they get stored on the // heap. This way there is less information to copy off the stack when // suspending, and back on when resuming. It also avoids the need to create and // invalidate DebugScope proxies for unaliased locals in a generator frame, as // the generator frame will be copied out to the heap and released only by GC. inline bool SharedContext::allLocalsAliased() {
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/debug/Frame-newTargetEval-01.js @@ -0,0 +1,38 @@ +// Test that new.target is acceptably usable in RematerializedFrames. + +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, expected) { + if (d) { + dbg.addDebuggee(g); + + var frame = dbg.getNewestFrame(); + assertEq(frame.implementation, "ion"); + assertEq(frame.constructing, true); + + // CONGRATS IF THIS FAILS! You, proud saviour, have made new.target parse + // in debug frame evals (presumably by hooking up static scope walks). + // Uncomment the assert below for efaust's undying gratitude. + // Note that we use .name here because of CCW nonsense. + assertEq(frame.eval('new.target').throw.unsafeDereference().name, "SyntaxError"); + // assertEq(frame.eval('new.target').value.unsafeDereference(), expected); + } + }; + + g.eval("" + function f(d) { new g(d, g, 15); }); + + g.eval("" + function g(d, expected) { toggle(d, expected); }); + + g.eval("(" + function test() { + for (var i = 0; i < 5; i++) + f(false); + f(true); + } + ")();"); +});
--- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -1508,16 +1508,21 @@ BaselineCompiler::storeValue(const Stack case StackValue::ArgSlot: masm.loadValue(frame.addressOfArg(source->argSlot()), scratch); masm.storeValue(scratch, dest); break; case StackValue::ThisSlot: masm.loadValue(frame.addressOfThis(), scratch); masm.storeValue(scratch, dest); break; + case StackValue::EvalNewTargetSlot: + MOZ_ASSERT(script->isForEval()); + masm.loadValue(frame.addressOfEvalNewTarget(), scratch); + masm.storeValue(scratch, dest); + break; case StackValue::Stack: masm.loadValue(frame.addressOfStackValue(source), scratch); masm.storeValue(scratch, dest); break; default: MOZ_CRASH("Invalid kind"); } } @@ -2676,16 +2681,21 @@ BaselineCompiler::emit_JSOP_SETARG() uint32_t arg = GET_ARGNO(pc); return emitFormalArgAccess(arg, /* get = */ false); } bool BaselineCompiler::emit_JSOP_NEWTARGET() { + if (script->isForEval()) { + frame.pushEvalNewTarget(); + return true; + } + MOZ_ASSERT(function()); frame.syncStack(0); // if (!isConstructing()) push(undefined) Label constructing, done; masm.branchTestPtr(Assembler::NonZero, frame.addressOfCalleeToken(), Imm32(CalleeToken_FunctionConstructing), &constructing); masm.moveValue(UndefinedValue(), R0);
--- a/js/src/jit/BaselineFrame.cpp +++ b/js/src/jit/BaselineFrame.cpp @@ -43,18 +43,20 @@ BaselineFrame::trace(JSTracer* trc, JitF // Mark scope chain, if it exists. if (scopeChain_) TraceRoot(trc, &scopeChain_, "baseline-scopechain"); // Mark return value. if (hasReturnValue()) TraceRoot(trc, returnValue().address(), "baseline-rval"); - if (isEvalFrame()) + if (isEvalFrame()) { TraceRoot(trc, &evalScript_, "baseline-evalscript"); + TraceRoot(trc, evalNewTargetAddress(), "baseline-evalNewTarget"); + } if (hasArgsObj()) TraceRoot(trc, &argsObj_, "baseline-args-obj"); // Mark locals and stack values. JSScript* script = this->script(); size_t nfixed = script->nfixed(); size_t nlivefixed = script->nbodyfixed();
--- a/js/src/jit/BaselineFrame.h +++ b/js/src/jit/BaselineFrame.h @@ -212,16 +212,35 @@ class BaselineFrame offsetOfThis()); } Value* argv() const { return (Value*)(reinterpret_cast<const uint8_t*>(this) + BaselineFrame::Size() + offsetOfArg(0)); } + private: + Value* evalNewTargetAddress() const { + MOZ_ASSERT(isEvalFrame()); + return (Value*)(reinterpret_cast<const uint8_t*>(this) + + BaselineFrame::Size() + + offsetOfEvalNewTarget()); + } + + public: + Value newTarget() const { + if (isEvalFrame()) + return *evalNewTargetAddress(); + if (isConstructing()) + return *(Value*)(reinterpret_cast<const uint8_t*>(this) + + BaselineFrame::Size() + + offsetOfArg(Max(numFormalArgs(), numActualArgs()))); + return UndefinedValue(); + } + bool copyRawFrameSlots(AutoValueVector* vec) const; bool hasReturnValue() const { return flags_ & HAS_RVAL; } MutableHandleValue returnValue() { if (!hasReturnValue()) addressOfReturnValue()->setUndefined(); @@ -394,16 +413,19 @@ class BaselineFrame // Methods below are used by the compiler. static size_t offsetOfCalleeToken() { return FramePointerOffset + js::jit::JitFrameLayout::offsetOfCalleeToken(); } static size_t offsetOfThis() { return FramePointerOffset + js::jit::JitFrameLayout::offsetOfThis(); } + static size_t offsetOfEvalNewTarget() { + return offsetOfArg(0); + } static size_t offsetOfArg(size_t index) { return FramePointerOffset + js::jit::JitFrameLayout::offsetOfActualArg(index); } static size_t offsetOfNumActualArgs() { return FramePointerOffset + js::jit::JitFrameLayout::offsetOfNumActualArgs(); } static size_t Size() { return sizeof(BaselineFrame);
--- a/js/src/jit/BaselineFrameInfo.cpp +++ b/js/src/jit/BaselineFrameInfo.cpp @@ -34,16 +34,20 @@ FrameInfo::sync(StackValue* val) masm.pushValue(addressOfLocal(val->localSlot())); break; case StackValue::ArgSlot: masm.pushValue(addressOfArg(val->argSlot())); break; case StackValue::ThisSlot: masm.pushValue(addressOfThis()); break; + case StackValue::EvalNewTargetSlot: + MOZ_ASSERT(script->isForEval()); + masm.pushValue(addressOfEvalNewTarget()); + break; case StackValue::Register: masm.pushValue(val->reg()); break; case StackValue::Constant: masm.pushValue(val->constant()); break; default: MOZ_CRASH("Invalid kind"); @@ -90,16 +94,19 @@ FrameInfo::popValue(ValueOperand dest) masm.loadValue(addressOfLocal(val->localSlot()), dest); break; case StackValue::ArgSlot: masm.loadValue(addressOfArg(val->argSlot()), dest); break; case StackValue::ThisSlot: masm.loadValue(addressOfThis(), dest); break; + case StackValue::EvalNewTargetSlot: + masm.loadValue(addressOfEvalNewTarget(), dest); + break; case StackValue::Stack: masm.popValue(dest); break; case StackValue::Register: masm.moveValue(val->reg(), dest); break; default: MOZ_CRASH("Invalid kind");
--- a/js/src/jit/BaselineFrameInfo.h +++ b/js/src/jit/BaselineFrameInfo.h @@ -49,17 +49,18 @@ class StackValue { public: enum Kind { Constant, Register, Stack, LocalSlot, ArgSlot, - ThisSlot + ThisSlot, + EvalNewTargetSlot #ifdef DEBUG // In debug builds, assert Kind is initialized. , Uninitialized #endif }; private: Kind kind_; @@ -145,16 +146,20 @@ class StackValue kind_ = ArgSlot; data.arg.slot = slot; knownType_ = JSVAL_TYPE_UNKNOWN; } void setThis() { kind_ = ThisSlot; knownType_ = JSVAL_TYPE_UNKNOWN; } + void setEvalNewTarget() { + kind_ = EvalNewTargetSlot; + knownType_ = JSVAL_TYPE_UNKNOWN; + } void setStack() { kind_ = Stack; knownType_ = JSVAL_TYPE_UNKNOWN; } }; enum StackAdjustment { AdjustStack, DontAdjustStack }; @@ -254,32 +259,41 @@ class FrameInfo inline void pushArg(uint32_t arg) { StackValue* sv = rawPush(); sv->setArgSlot(arg); } inline void pushThis() { StackValue* sv = rawPush(); sv->setThis(); } + inline void pushEvalNewTarget() { + MOZ_ASSERT(script->isForEval()); + StackValue* sv = rawPush(); + sv->setEvalNewTarget(); + } + inline void pushScratchValue() { masm.pushValue(addressOfScratchValue()); StackValue* sv = rawPush(); sv->setStack(); } inline Address addressOfLocal(size_t local) const { MOZ_ASSERT(local < nlocals()); return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfLocal(local)); } Address addressOfArg(size_t arg) const { MOZ_ASSERT(arg < nargs()); return Address(BaselineFrameReg, BaselineFrame::offsetOfArg(arg)); } Address addressOfThis() const { return Address(BaselineFrameReg, BaselineFrame::offsetOfThis()); } + Address addressOfEvalNewTarget() const { + return Address(BaselineFrameReg, BaselineFrame::offsetOfEvalNewTarget()); + } Address addressOfCalleeToken() const { return Address(BaselineFrameReg, BaselineFrame::offsetOfCalleeToken()); } Address addressOfScopeChain() const { return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScopeChain()); } Address addressOfFlags() const { return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags());
--- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -176,16 +176,17 @@ jit::EnterBaselineAtBranch(JSContext* cx if (fp->isDebuggee()) { MOZ_RELEASE_ASSERT(baseline->hasDebugInstrumentation()); data.jitcode += MacroAssembler::ToggledCallSize(data.jitcode); } data.osrFrame = fp; data.osrNumStackValues = fp->script()->nfixed() + cx->interpreterRegs().stackDepth(); + AutoValueVector vals(cx); RootedValue thisv(cx); if (fp->isNonEvalFunctionFrame()) { data.constructing = fp->isConstructing(); data.numActualArgs = fp->numActualArgs(); data.maxArgc = Max(fp->numActualArgs(), fp->numFormalArgs()) + 1; // +1 = include |this| data.maxArgv = fp->argv() - 1; // -1 = include |this| data.scopeChain = nullptr; @@ -198,16 +199,31 @@ jit::EnterBaselineAtBranch(JSContext* cx data.maxArgv = thisv.address(); data.scopeChain = fp->scopeChain(); // For eval function frames, set the callee token to the enclosing function. if (fp->isFunctionFrame()) data.calleeToken = CalleeToToken(&fp->callee(), /* constructing = */ false); else data.calleeToken = CalleeToToken(fp->script()); + + if (fp->isEvalFrame()) { + if (!vals.reserve(2)) + return JitExec_Aborted; + + vals.infallibleAppend(thisv); + + if (fp->isFunctionFrame()) + vals.infallibleAppend(fp->newTarget()); + else + vals.infallibleAppend(NullValue()); + + data.maxArgc = 2; + data.maxArgv = vals.begin(); + } } TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime()); TraceLogStopEvent(logger, TraceLogger_Interpreter); TraceLogStartEvent(logger, TraceLogger_Baseline); JitExecStatus status = EnterBaseline(cx, data); if (status != JitExec_Ok)
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3480,28 +3480,29 @@ CodeGenerator::visitFilterArgumentsOrEva masm.branchTestString(Assembler::NotEqual, input, &done); emitFilterArgumentsOrEval(lir, masm.extractString(input, ToRegister(lir->temp3())), ToRegister(lir->temp1()), ToRegister(lir->temp2())); masm.bind(&done); } -typedef bool (*DirectEvalSFn)(JSContext*, HandleObject, HandleScript, HandleValue, HandleString, - jsbytecode*, MutableHandleValue); +typedef bool (*DirectEvalSFn)(JSContext*, HandleObject, HandleScript, HandleValue, HandleValue, + HandleString, jsbytecode*, MutableHandleValue); static const VMFunction DirectEvalStringInfo = FunctionInfo<DirectEvalSFn>(DirectEvalStringFromIon); void CodeGenerator::visitCallDirectEval(LCallDirectEval* lir) { Register scopeChain = ToRegister(lir->getScopeChain()); Register string = ToRegister(lir->getString()); pushArg(ImmPtr(lir->mir()->pc())); pushArg(string); + pushArg(ToValue(lir, LCallDirectEval::NewTarget)); pushArg(ToValue(lir, LCallDirectEval::ThisValue)); pushArg(ImmGCPtr(gen->info().script())); pushArg(scopeChain); callVM(DirectEvalStringInfo, lir); } void
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2590,16 +2590,32 @@ jit::SetEnterJitData(JSContext* cx, Ente data.calleeToken = CalleeToToken(state.script()); if (state.script()->isForEval() && !(state.asExecute()->type() & InterpreterFrame::GLOBAL)) { ScriptFrameIter iter(cx); if (iter.isFunctionFrame()) data.calleeToken = CalleeToToken(iter.callee(cx), /* constructing = */ false); + + // Push newTarget onto the stack, as well as Argv. + if (!vals.reserve(2)) + return false; + + data.maxArgc = 2; + data.maxArgv = vals.begin(); + vals.infallibleAppend(state.asExecute()->thisv()); + if (iter.isFunctionFrame()) { + if (state.asExecute()->newTarget().isNull()) + vals.infallibleAppend(iter.newTarget()); + else + vals.infallibleAppend(state.asExecute()->newTarget()); + } else { + vals.infallibleAppend(NullValue()); + } } } return true; } JitExecStatus jit::IonCannon(JSContext* cx, RunState& state)
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -6422,16 +6422,20 @@ IonBuilder::jsop_eval(uint32_t argc) current->push(string); TemporaryTypeSet* types = bytecodeTypes(pc); return pushTypeBarrier(string, types, BarrierKind::TypeSet); } current->pushSlot(info().thisSlot()); MDefinition* thisValue = current->pop(); + if (!jsop_newtarget()) + return false; + MDefinition* newTargetValue = current->pop(); + // Try to pattern match 'eval(v + "()")'. In this case v is likely a // name on the scope chain and the eval is performing a call on that // value. Use a dynamic scope chain lookup rather than a full eval. if (string->isConcat() && string->getOperand(1)->isConstantValue() && string->getOperand(1)->constantValue().isString()) { JSAtom* atom = &string->getOperand(1)->constantValue().toString()->asAtom(); @@ -6450,17 +6454,18 @@ IonBuilder::jsop_eval(uint32_t argc) return makeCall(nullptr, evalCallInfo); } } MInstruction* filterArguments = MFilterArgumentsOrEval::New(alloc(), string); current->add(filterArguments); - MInstruction* ins = MCallDirectEval::New(alloc(), scopeChain, string, thisValue, pc); + MInstruction* ins = MCallDirectEval::New(alloc(), scopeChain, string, + thisValue, newTargetValue, pc); current->add(ins); current->push(ins); TemporaryTypeSet* types = bytecodeTypes(pc); return resumeAfter(ins) && pushTypeBarrier(ins, types, BarrierKind::TypeSet); } return jsop_call(argc, /* constructing = */ false); @@ -9426,16 +9431,22 @@ IonBuilder::jsop_arguments() MOZ_ASSERT(lazyArguments_); current->push(lazyArguments_); return true; } bool IonBuilder::jsop_newtarget() { + if (!info().funMaybeLazy()) { + MOZ_ASSERT(!info().script()->isForEval()); + pushConstant(NullValue()); + return true; + } + MOZ_ASSERT(info().funMaybeLazy()); if (inliningDepth_ == 0) { MNewTarget* newTarget = MNewTarget::New(alloc()); current->add(newTarget); current->push(newTarget); return true; }
--- a/js/src/jit/JitFrameIterator.h +++ b/js/src/jit/JitFrameIterator.h @@ -696,18 +696,18 @@ class InlineFrameIterator if (more()) return numActualArgs_; return frame_->numActualArgs(); } template <class ArgOp, class LocalOp> void readFrameArgsAndLocals(JSContext* cx, ArgOp& argOp, LocalOp& localOp, - JSObject** scopeChain, bool* hasCallObj, Value* rval, - ArgumentsObject** argsObj, Value* thisv, + JSObject** scopeChain, bool* hasCallObj, + Value* rval, ArgumentsObject** argsObj, Value* thisv, ReadFrameArgsBehavior behavior, MaybeReadFallback& fallback) const { SnapshotIterator s(si_); // Read the scope chain. if (scopeChain) { Value scopeChainValue = s.maybeRead(fallback); @@ -757,23 +757,23 @@ class InlineFrameIterator for (unsigned j = 0; j < skip; j++) parent_s.skip(); // Get the overflown arguments MaybeReadFallback unusedFallback; parent_s.skip(); // scope chain parent_s.skip(); // return value parent_s.readFunctionFrameArgs(argOp, nullptr, nullptr, - nformal, nactual, it.script(), + nformal, nactual + isConstructing(), it.script(), fallback); } else { // There is no parent frame to this inlined frame, we can read // from the frame's Value vector directly. Value* argv = frame_->actualArgs(); - for (unsigned i = nformal; i < nactual; i++) + for (unsigned i = nformal; i < nactual + isConstructing(); i++) argOp(argv[i]); } } } // At this point we've read all the formals in s, and can read the // locals. for (unsigned i = 0; i < script()->nfixed(); i++)
--- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -1922,28 +1922,29 @@ class LFilterArgumentsOrEvalV : public L const LDefinition* temp2() { return getTemp(1); } const LDefinition* temp3() { return getTemp(2); } }; -class LCallDirectEval : public LCallInstructionHelper<BOX_PIECES, 2 + BOX_PIECES, 0> +class LCallDirectEval : public LCallInstructionHelper<BOX_PIECES, 2 + (2 * BOX_PIECES), 0> { public: LIR_HEADER(CallDirectEval) LCallDirectEval(const LAllocation& scopeChain, const LAllocation& string) { setOperand(0, scopeChain); setOperand(1, string); } static const size_t ThisValue = 2; + static const size_t NewTarget = 2 + BOX_PIECES; MCallDirectEval* mir() const { return mir_->toCallDirectEval(); } const LAllocation* getScopeChain() { return getOperand(0); }
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -604,20 +604,22 @@ LIRGenerator::visitCallDirectEval(MCallD { MDefinition* scopeChain = ins->getScopeChain(); MOZ_ASSERT(scopeChain->type() == MIRType_Object); MDefinition* string = ins->getString(); MOZ_ASSERT(string->type() == MIRType_String); MDefinition* thisValue = ins->getThisValue(); + MDefinition* newTargetValue = ins->getNewTargetValue(); LInstruction* lir = new(alloc()) LCallDirectEval(useRegisterAtStart(scopeChain), useRegisterAtStart(string)); useBoxAtStart(lir, LCallDirectEval::ThisValue, thisValue); + useBoxAtStart(lir, LCallDirectEval::NewTarget, newTargetValue); defineReturn(lir, ins); assignSafepoint(lir, ins); } static JSOp ReorderComparison(JSOp op, MDefinition** lhsp, MDefinition** rhsp) {
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -4064,51 +4064,56 @@ class MFilterArgumentsOrEval return getOperand(0); } bool possiblyCalls() const override { return true; } }; class MCallDirectEval - : public MAryInstruction<3>, - public Mix3Policy<ObjectPolicy<0>, + : public MAryInstruction<4>, + public Mix4Policy<ObjectPolicy<0>, StringPolicy<1>, - BoxPolicy<2> >::Data + BoxPolicy<2>, + BoxPolicy<3> >::Data { protected: MCallDirectEval(MDefinition* scopeChain, MDefinition* string, MDefinition* thisValue, - jsbytecode* pc) + MDefinition* newTargetValue, jsbytecode* pc) : pc_(pc) { initOperand(0, scopeChain); initOperand(1, string); initOperand(2, thisValue); + initOperand(3, newTargetValue); setResultType(MIRType_Value); } public: INSTRUCTION_HEADER(CallDirectEval) static MCallDirectEval* New(TempAllocator& alloc, MDefinition* scopeChain, MDefinition* string, MDefinition* thisValue, - jsbytecode* pc) - { - return new(alloc) MCallDirectEval(scopeChain, string, thisValue, pc); + MDefinition* newTargetValue, jsbytecode* pc) + { + return new(alloc) MCallDirectEval(scopeChain, string, thisValue, newTargetValue, pc); } MDefinition* getScopeChain() const { return getOperand(0); } MDefinition* getString() const { return getOperand(1); } MDefinition* getThisValue() const { return getOperand(2); } + MDefinition* getNewTargetValue() const { + return getOperand(3); + } jsbytecode* pc() const { return pc_; } bool possiblyCalls() const override { return true; }
--- a/js/src/jit/RematerializedFrame.cpp +++ b/js/src/jit/RematerializedFrame.cpp @@ -30,16 +30,17 @@ struct CopyValueToRematerializedFrame *slots++ = v; } }; RematerializedFrame::RematerializedFrame(JSContext* cx, uint8_t* top, unsigned numActualArgs, InlineFrameIterator& iter, MaybeReadFallback& fallback) : prevUpToDate_(false), isDebuggee_(iter.script()->isDebuggee()), + isConstructing_(iter.isConstructing()), top_(top), pc_(iter.pc()), frameNo_(iter.frameNo()), numActualArgs_(numActualArgs), script_(iter.script()) { if (iter.isFunctionFrame()) callee_ = iter.callee(fallback); @@ -52,17 +53,17 @@ RematerializedFrame::RematerializedFrame fallback); } /* static */ RematerializedFrame* RematerializedFrame::New(JSContext* cx, uint8_t* top, InlineFrameIterator& iter, MaybeReadFallback& fallback) { unsigned numFormals = iter.isFunctionFrame() ? iter.calleeTemplate()->nargs() : 0; - unsigned argSlots = Max(numFormals, iter.numActualArgs()); + unsigned argSlots = Max(numFormals, iter.numActualArgs()) + iter.isConstructing(); size_t numBytes = sizeof(RematerializedFrame) + (argSlots + iter.script()->nfixed()) * sizeof(Value) - sizeof(Value); // 1 Value included in sizeof(RematerializedFrame) void* buf = cx->pod_calloc<uint8_t>(numBytes); if (!buf) return nullptr; @@ -153,17 +154,18 @@ void RematerializedFrame::mark(JSTracer* trc) { TraceRoot(trc, &script_, "remat ion frame script"); TraceRoot(trc, &scopeChain_, "remat ion frame scope chain"); if (callee_) TraceRoot(trc, &callee_, "remat ion frame callee"); TraceRoot(trc, &returnValue_, "remat ion frame return value"); TraceRoot(trc, &thisValue_, "remat ion frame this"); - TraceRootRange(trc, numActualArgs_ + script_->nfixed(), slots_, "remat ion frame stack"); + TraceRootRange(trc, numActualArgs_ + isConstructing_ + script_->nfixed(), + slots_, "remat ion frame stack"); } void RematerializedFrame::dump() { fprintf(stderr, " Rematerialized Ion Frame%s\n", inlined() ? " (inlined)" : ""); if (isFunctionFrame()) { fprintf(stderr, " callee fun: ");
--- a/js/src/jit/RematerializedFrame.h +++ b/js/src/jit/RematerializedFrame.h @@ -27,16 +27,19 @@ class RematerializedFrame bool prevUpToDate_; // Propagated to the Baseline frame once this is popped. bool isDebuggee_; // Has a call object been pushed? bool hasCallObj_; + // Is this frame constructing? + bool isConstructing_; + // The fp of the top frame associated with this possibly inlined frame. uint8_t* top_; // The bytecode at the time of rematerialization. jsbytecode* pc_; size_t frameNo_; unsigned numActualArgs_; @@ -157,28 +160,32 @@ class RematerializedFrame } Value calleev() const { return ObjectValue(*callee()); } Value& thisValue() { return thisValue_; } + bool isConstructing() const { + return isConstructing_; + } + unsigned numFormalArgs() const { return maybeFun() ? fun()->nargs() : 0; } unsigned numActualArgs() const { return numActualArgs_; } Value* argv() { return slots_; } Value* locals() { - return slots_ + numActualArgs_; + return slots_ + numActualArgs_ + isConstructing_; } Value& unaliasedLocal(unsigned i) { MOZ_ASSERT(i < script()->nfixed()); return locals()[i]; } Value& unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) { MOZ_ASSERT(i < numFormalArgs()); @@ -188,16 +195,23 @@ class RematerializedFrame } Value& unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) { MOZ_ASSERT(i < numActualArgs()); MOZ_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals()); MOZ_ASSERT_IF(checkAliasing && i < numFormalArgs(), !script()->formalIsAliased(i)); return argv()[i]; } + Value newTarget() { + MOZ_ASSERT(isFunctionFrame()); + if (isConstructing()) + return argv()[numActualArgs()]; + return UndefinedValue(); + } + Value returnValue() const { return returnValue_; } void mark(JSTracer* trc); void dump(); };
--- a/js/src/jit/TypePolicy.cpp +++ b/js/src/jit/TypePolicy.cpp @@ -1147,16 +1147,17 @@ FilterTypeSetPolicy::adjustInputs(TempAl _(Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >) \ _(Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2> >) \ _(Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, BoxPolicy<2> >) \ _(Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >) \ _(Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, IntPolicy<2> >) \ _(Mix3Policy<StringPolicy<0>, IntPolicy<1>, IntPolicy<2>>) \ _(Mix3Policy<StringPolicy<0>, ObjectPolicy<1>, StringPolicy<2> >) \ _(Mix3Policy<StringPolicy<0>, StringPolicy<1>, StringPolicy<2> >) \ + _(Mix4Policy<ObjectPolicy<0>, StringPolicy<1>, BoxPolicy<2>, BoxPolicy<3>>) \ _(Mix4Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2>, IntPolicy<3>>) \ _(Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>, SimdScalarPolicy<2>, SimdScalarPolicy<3> >) \ _(MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >) \ _(MixPolicy<ConvertToStringPolicy<0>, ConvertToStringPolicy<1> >) \ _(MixPolicy<ConvertToStringPolicy<0>, ObjectPolicy<1> >) \ _(MixPolicy<DoublePolicy<0>, DoublePolicy<1> >) \ _(MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >) \ _(MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> >) \
--- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -441,16 +441,17 @@ template <> struct MatchContext<Exclusiv }; #define FOR_EACH_ARGS_1(Macro, Sep, Last) Macro(1) Last(1) #define FOR_EACH_ARGS_2(Macro, Sep, Last) FOR_EACH_ARGS_1(Macro, Sep, Sep) Macro(2) Last(2) #define FOR_EACH_ARGS_3(Macro, Sep, Last) FOR_EACH_ARGS_2(Macro, Sep, Sep) Macro(3) Last(3) #define FOR_EACH_ARGS_4(Macro, Sep, Last) FOR_EACH_ARGS_3(Macro, Sep, Sep) Macro(4) Last(4) #define FOR_EACH_ARGS_5(Macro, Sep, Last) FOR_EACH_ARGS_4(Macro, Sep, Sep) Macro(5) Last(5) #define FOR_EACH_ARGS_6(Macro, Sep, Last) FOR_EACH_ARGS_5(Macro, Sep, Sep) Macro(6) Last(6) +#define FOR_EACH_ARGS_7(Macro, Sep, Last) FOR_EACH_ARGS_6(Macro, Sep, Sep) Macro(7) Last(7) #define COMPUTE_INDEX(NbArg) NbArg #define COMPUTE_OUTPARAM_RESULT(NbArg) OutParamToDataType<A ## NbArg>::result #define COMPUTE_OUTPARAM_ROOT(NbArg) OutParamToRootType<A ## NbArg>::result #define COMPUTE_ARG_PROP(NbArg) (TypeToArgProperties<A ## NbArg>::result << (2 * (NbArg - 1))) #define COMPUTE_ARG_ROOT(NbArg) (uint64_t(TypeToRootType<A ## NbArg>::result) << (3 * (NbArg - 1))) #define COMPUTE_ARG_FLOAT(NbArg) (TypeToPassInFloatReg<A ## NbArg>::result) << (NbArg - 1) #define SEP_OR(_) | @@ -580,18 +581,25 @@ template <class R, class Context, class }; template <class R, class Context, class A1, class A2, class A3, class A4, class A5, class A6> struct FunctionInfo<R (*)(Context, A1, A2, A3, A4, A5, A6)> : public VMFunction { typedef R (*pf)(Context, A1, A2, A3, A4, A5, A6); FUNCTION_INFO_STRUCT_BODY(FOR_EACH_ARGS_6) }; +template <class R, class Context, class A1, class A2, class A3, class A4, class A5, class A6, class A7> + struct FunctionInfo<R (*)(Context, A1, A2, A3, A4, A5, A6, A7)> : public VMFunction { + typedef R (*pf)(Context, A1, A2, A3, A4, A5, A6, A7); + FUNCTION_INFO_STRUCT_BODY(FOR_EACH_ARGS_7) +}; + #undef FUNCTION_INFO_STRUCT_BODY +#undef FOR_EACH_ARGS_7 #undef FOR_EACH_ARGS_6 #undef FOR_EACH_ARGS_5 #undef FOR_EACH_ARGS_4 #undef FOR_EACH_ARGS_3 #undef FOR_EACH_ARGS_2 #undef FOR_EACH_ARGS_1 #undef COMPUTE_INDEX
deleted file mode 100644 --- a/js/src/tests/ecma_6/Class/newTargetContext.js +++ /dev/null @@ -1,11 +0,0 @@ -// new.target is valid inside Function() invocations -var func = new Function("new.target"); - -// new.target is invalid inside eval, even (for now!) eval inside a function. -assertThrowsInstanceOf(() => eval('new.target'), SyntaxError); - -function evalInFunction() { eval('new.target'); } -assertThrowsInstanceOf(() => evalInFunction(), SyntaxError); - -if (typeof reportCompare === "function") - reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/newTargetDirectInvoke.js +++ b/js/src/tests/ecma_6/Class/newTargetDirectInvoke.js @@ -1,8 +1,11 @@ +// new.target is valid inside Function() invocations +var func = new Function("new.target"); + // Note that this will also test new.target in ion inlines. When the toplevel // script is compiled, assertNewTarget will be inlined. function assertNewTarget(expected, unused) { assertEq(new.target, expected); } // Test non-constructing invocations, with arg underflow, overflow, and correct // numbers for (let i = 0; i < 100; i++) assertNewTarget(undefined, null);
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/Class/newTargetEval.js @@ -0,0 +1,34 @@ +// Eval of new.target is invalid outside functions. +try { + eval('new.target'); + assertEq(false, true); +} catch (e if e instanceof SyntaxError) { } + +// new.target is (for now!) invalid inside arrow functions, or eval inside arrow +// functions. +assertThrowsInstanceOf(() => eval('new.target'), SyntaxError); + +// new.target is invalid inside indirect eval. +let ieval = eval; +try { + (function () ieval('new.target'))(); + assertEq(false, true); +} catch (e if e instanceof SyntaxError) { } + +function assertNewTarget(expected) { + assertEq(eval('new.target'), expected); + + // Also test nestings "by induction" + assertEq(eval('eval("new.target")'), expected); + assertEq(eval("eval('eval(`new.target`)')"), expected); +} + +const ITERATIONS = 550; +for (let i = 0; i < ITERATIONS; i++) + assertNewTarget(undefined); + +for (let i = 0; i < ITERATIONS; i++) + new assertNewTarget(assertNewTarget); + +if (typeof reportCompare === "function") + reportCompare(0,0,"OK");
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -6282,17 +6282,17 @@ EvaluateInEnv(JSContext* cx, Handle<Env* if (!script) return false; if (script->strict()) staticScope->setStrict(); script->setActiveEval(); ExecuteType type = !frame ? EXECUTE_DEBUG_GLOBAL : EXECUTE_DEBUG; - return ExecuteKernel(cx, script, *env, thisv, type, frame, rval.address()); + return ExecuteKernel(cx, script, *env, thisv, NullValue(), type, frame, rval.address()); } enum EvalBindings { EvalHasExtraBindings = true, EvalWithDefaultBindings = false }; static bool DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code, EvalBindings evalWithBindings, HandleValue bindings, HandleValue options, MutableHandleValue vp, Debugger* dbg, HandleObject scope,
--- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -361,18 +361,18 @@ InterpreterFrame* InvokeState::pushInterpreterFrame(JSContext* cx) { return cx->runtime()->interpreterStack().pushInvokeFrame(cx, args_, initial_); } InterpreterFrame* ExecuteState::pushInterpreterFrame(JSContext* cx) { - return cx->runtime()->interpreterStack().pushExecuteFrame(cx, script_, thisv_, scopeChain_, - type_, evalInFrame_); + return cx->runtime()->interpreterStack().pushExecuteFrame(cx, script_, thisv_, newTargetValue_, + scopeChain_, type_, evalInFrame_); } namespace js { // Implementation of per-performance group performance measurement. // // // All mutable state is stored in `Runtime::stopwatch` (per-process // performance stats and logistics) and in `PerformanceGroup` (per @@ -842,17 +842,18 @@ js::InvokeSetter(JSContext* cx, const Va JS_CHECK_RECURSION(cx, return false); RootedValue ignored(cx); return Invoke(cx, thisv, fval, 1, v.address(), &ignored); } bool js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg, const Value& thisv, - ExecuteType type, AbstractFramePtr evalInFrame, Value* result) + const Value& newTargetValue, ExecuteType type, AbstractFramePtr evalInFrame, + Value* result) { MOZ_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG); MOZ_ASSERT_IF(type == EXECUTE_GLOBAL, !IsSyntacticScope(&scopeChainArg)); #ifdef DEBUG if (thisv.isObject()) { RootedObject thisObj(cx, &thisv.toObject()); AutoSuppressGC nogc(cx); MOZ_ASSERT(GetOuterObject(cx, thisObj) == thisObj); @@ -877,17 +878,17 @@ js::ExecuteKernel(JSContext* cx, HandleS if (result) result->setUndefined(); return true; } TypeScript::SetThis(cx, script, thisv); probes::StartExecution(script); - ExecuteState state(cx, script, thisv, scopeChainArg, type, evalInFrame, result); + ExecuteState state(cx, script, thisv, newTargetValue, scopeChainArg, type, evalInFrame, result); bool ok = RunScript(cx, state); probes::StopExecution(script); return ok; } bool js::Execute(JSContext* cx, HandleScript script, JSObject& scopeChainArg, Value* rval) @@ -917,17 +918,17 @@ js::Execute(JSContext* cx, HandleScript } /* Use the scope chain as 'this', modulo outerization. */ JSObject* thisObj = GetThisObject(cx, scopeChain); if (!thisObj) return false; Value thisv = ObjectValue(*thisObj); - return ExecuteKernel(cx, script, *scopeChain, thisv, EXECUTE_GLOBAL, + return ExecuteKernel(cx, script, *scopeChain, thisv, NullValue(), EXECUTE_GLOBAL, NullFramePtr() /* evalInFrame */, rval); } bool js::HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) { const Class* clasp = obj->getClass(); RootedValue local(cx, v);
--- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -100,17 +100,18 @@ InvokeConstructor(JSContext* cx, Value f /* * Executes a script with the given scopeChain/this. The 'type' indicates * whether this is eval code or global code. To support debugging, the * evalFrame parameter can point to an arbitrary frame in the context's call * stack to simulate executing an eval in that frame. */ extern bool ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChain, const Value& thisv, - ExecuteType type, AbstractFramePtr evalInFrame, Value* result); + const Value& newTargetVal, ExecuteType type, AbstractFramePtr evalInFrame, + Value* result); /* Execute a script with the given scopeChain as global code. */ extern bool Execute(JSContext* cx, HandleScript script, JSObject& scopeChain, Value* rval); class ExecuteState; class InvokeState; @@ -158,33 +159,38 @@ class RunState }; // Eval or global script. class ExecuteState : public RunState { ExecuteType type_; RootedValue thisv_; + RootedValue newTargetValue_; RootedObject scopeChain_; AbstractFramePtr evalInFrame_; Value* result_; public: - ExecuteState(JSContext* cx, JSScript* script, const Value& thisv, JSObject& scopeChain, - ExecuteType type, AbstractFramePtr evalInFrame, Value* result) + ExecuteState(JSContext* cx, JSScript* script, const Value& thisv, const Value& newTargetValue, + JSObject& scopeChain, ExecuteType type, AbstractFramePtr evalInFrame, + Value* result) : RunState(cx, Execute, script), type_(type), thisv_(cx, thisv), + newTargetValue_(cx, newTargetValue), scopeChain_(cx, &scopeChain), evalInFrame_(evalInFrame), result_(result) { } Value* addressOfThisv() { return thisv_.address(); } + Value thisv() { return thisv_; } + Value newTarget() { return newTargetValue_; } JSObject* scopeChain() const { return scopeChain_; } ExecuteType type() const { return type_; } virtual InterpreterFrame* pushInterpreterFrame(JSContext* cx); virtual void setReturnValue(Value v) { if (result_) *result_ = v;
--- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -811,16 +811,26 @@ AbstractFramePtr::thisValue() const { if (isInterpreterFrame()) return asInterpreterFrame()->thisValue(); if (isBaselineFrame()) return asBaselineFrame()->thisValue(); return asRematerializedFrame()->thisValue(); } +inline Value +AbstractFramePtr::newTarget() const +{ + if (isInterpreterFrame()) + return asInterpreterFrame()->newTarget(); + if (isBaselineFrame()) + return asBaselineFrame()->newTarget(); + return asRematerializedFrame()->newTarget(); +} + inline bool AbstractFramePtr::freshenBlock(JSContext* cx) const { if (isInterpreterFrame()) return asInterpreterFrame()->freshenBlock(cx); return asBaselineFrame()->freshenBlock(cx); }
--- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -28,63 +28,73 @@ using namespace js; using mozilla::Maybe; using mozilla::PodCopy; /*****************************************************************************/ void InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script, AbstractFramePtr evalInFramePrev, - const Value& thisv, HandleObject scopeChain, ExecuteType type) + const Value& thisv, const Value& newTargetValue, HandleObject scopeChain, + ExecuteType type) { /* * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a * script in the context of another frame and the frame type is determined * by the context. */ flags_ = type | HAS_SCOPECHAIN; JSObject* callee = nullptr; + RootedValue newTarget(cx, newTargetValue); if (!(flags_ & (GLOBAL))) { if (evalInFramePrev) { MOZ_ASSERT(evalInFramePrev.isFunctionFrame() || evalInFramePrev.isGlobalFrame()); if (evalInFramePrev.isFunctionFrame()) { callee = evalInFramePrev.callee(); + if (newTarget.isNull()) + newTarget = evalInFramePrev.newTarget(); flags_ |= FUNCTION; } else { flags_ |= GLOBAL; } } else { FrameIter iter(cx); MOZ_ASSERT(iter.isFunctionFrame() || iter.isGlobalFrame()); MOZ_ASSERT(!iter.isAsmJS()); if (iter.isFunctionFrame()) { + if (newTarget.isNull()) + newTarget = iter.newTarget(); callee = iter.callee(cx); flags_ |= FUNCTION; } else { flags_ |= GLOBAL; } } } + + // Null is just a sentinel value. We should have figured it out by now. + MOZ_ASSERT_IF(isFunctionFrame(), newTarget.isObject() || newTarget.isUndefined()); - Value* dstvp = (Value*)this - 2; - dstvp[1] = thisv; + Value* dstvp = (Value*)this - 3; + dstvp[2] = thisv; if (isFunctionFrame()) { - dstvp[0] = ObjectValue(*callee); + dstvp[1] = ObjectValue(*callee); exec.fun = &callee->as<JSFunction>(); u.evalScript = script; } else { MOZ_ASSERT(isGlobalFrame()); - dstvp[0] = NullValue(); + dstvp[1] = NullValue(); exec.script = script; #ifdef DEBUG u.evalScript = (JSScript*)0xbad; #endif } + dstvp[0] = newTarget; scopeChain_ = scopeChain.get(); prev_ = nullptr; prevpc_ = nullptr; prevsp_ = nullptr; MOZ_ASSERT_IF(evalInFramePrev, isDebuggerEvalFrame()); evalInFramePrev_ = evalInFramePrev; @@ -394,18 +404,18 @@ InterpreterFrame::markValues(JSTracer* t markValues(trc, 0, nlivefixed); } if (hasArgs()) { // Mark callee, |this| and arguments. unsigned argc = Max(numActualArgs(), numFormalArgs()); TraceRootRange(trc, argc + 2 + isConstructing(), argv_ - 2, "fp argv"); } else { - // Mark callee and |this| - TraceRootRange(trc, 2, ((Value*)this) - 2, "stack callee and this"); + // Mark callee, |this|, and newTarget + TraceRootRange(trc, 3, ((Value*)this) - 3, "stack callee, this, newTarget"); } } static void MarkInterpreterActivation(JSTracer* trc, InterpreterActivation* act) { for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) { InterpreterFrame* fp = frames.frame(); @@ -453,29 +463,29 @@ InterpreterStack::pushInvokeFrame(JSCont fp->mark_ = mark; fp->initCallFrame(cx, nullptr, nullptr, nullptr, *fun, script, argv, args.length(), flags); return fp; } InterpreterFrame* InterpreterStack::pushExecuteFrame(JSContext* cx, HandleScript script, const Value& thisv, - HandleObject scopeChain, ExecuteType type, - AbstractFramePtr evalInFrame) + const Value& newTargetValue, HandleObject scopeChain, + ExecuteType type, AbstractFramePtr evalInFrame) { LifoAlloc::Mark mark = allocator_.mark(); - unsigned nvars = 2 /* callee, this */ + script->nslots(); + unsigned nvars = 3 /* callee, this, newTarget */ + script->nslots(); uint8_t* buffer = allocateFrame(cx, sizeof(InterpreterFrame) + nvars * sizeof(Value)); if (!buffer) return nullptr; - InterpreterFrame* fp = reinterpret_cast<InterpreterFrame*>(buffer + 2 * sizeof(Value)); + InterpreterFrame* fp = reinterpret_cast<InterpreterFrame*>(buffer + 3 * sizeof(Value)); fp->mark_ = mark; - fp->initExecuteFrame(cx, script, evalInFrame, thisv, scopeChain, type); + fp->initExecuteFrame(cx, script, evalInFrame, thisv, newTargetValue, scopeChain, type); fp->initLocals(); return fp; } /*****************************************************************************/ void @@ -1248,16 +1258,32 @@ FrameIter::thisv(JSContext* cx) const return data_.jitFrames_.baselineFrame()->thisValue(); case INTERP: return interpFrame()->thisValue(); } MOZ_CRASH("Unexpected state"); } Value +FrameIter::newTarget() const +{ + switch (data_.state_) { + case DONE: + case ASMJS: + break; + case INTERP: + return interpFrame()->newTarget(); + case JIT: + MOZ_ASSERT(data_.jitFrames_.isBaselineJS()); + return data_.jitFrames_.baselineFrame()->newTarget(); + } + MOZ_CRASH("Unexpected state"); +} + +Value FrameIter::returnValue() const { switch (data_.state_) { case DONE: case ASMJS: break; case JIT: if (data_.jitFrames_.isBaselineJS())
--- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -199,16 +199,18 @@ class AbstractFramePtr inline JSScript* script() const; inline JSFunction* fun() const; inline JSFunction* maybeFun() const; inline JSFunction* callee() const; inline Value calleev() const; inline Value& thisValue() const; + inline Value newTarget() const; + inline bool isNonEvalFunctionFrame() const; inline bool isNonStrictDirectEvalFrame() const; inline bool isStrictEvalFrame() const; inline unsigned numActualArgs() const; inline unsigned numFormalArgs() const; inline Value* argv() const; @@ -400,17 +402,18 @@ class InterpreterFrame /* Used for Invoke and Interpret. */ void initCallFrame(JSContext* cx, InterpreterFrame* prev, jsbytecode* prevpc, Value* prevsp, JSFunction& callee, JSScript* script, Value* argv, uint32_t nactual, InterpreterFrame::Flags flags); /* Used for global and eval frames. */ void initExecuteFrame(JSContext* cx, HandleScript script, AbstractFramePtr prev, - const Value& thisv, HandleObject scopeChain, ExecuteType type); + const Value& thisv, const Value& newTargetValue, + HandleObject scopeChain, ExecuteType type); public: /* * Frame prologue/epilogue * * Every stack frame must have 'prologue' called before executing the * first op and 'epilogue' called after executing the last op and before * popping the frame (whether the exit is exceptional or not). @@ -743,18 +746,20 @@ class InterpreterFrame /* * New Target * * Only function frames have a meaningful newTarget. An eval frame in a * function will have a copy of the newTarget of the enclosing function * frame. */ Value newTarget() const { - // new.target in eval() NYI. - MOZ_ASSERT(isNonEvalFunctionFrame()); + MOZ_ASSERT(isFunctionFrame()); + if (isEvalFrame()) + return ((Value*)this)[-3]; + if (isConstructing()) { unsigned pushedArgs = Max(numFormalArgs(), numActualArgs()); return argv()[pushedArgs]; } return UndefinedValue(); } /* @@ -1012,18 +1017,18 @@ class InterpreterStack { } ~InterpreterStack() { MOZ_ASSERT(frameCount_ == 0); } // For execution of eval or global code. InterpreterFrame* pushExecuteFrame(JSContext* cx, HandleScript script, const Value& thisv, - HandleObject scopeChain, ExecuteType type, - AbstractFramePtr evalInFrame); + const Value& newTargetValue, HandleObject scopeChain, + ExecuteType type, AbstractFramePtr evalInFrame); // Called to invoke a function. InterpreterFrame* pushInvokeFrame(JSContext* cx, const CallArgs& args, InitialFrameFlags initial); // The interpreter can push light-weight, "inline" frames without entering a // new InterpreterActivation or recursively calling Interpret. bool pushInlineFrame(JSContext* cx, InterpreterRegs& regs, const CallArgs& args, @@ -1751,16 +1756,18 @@ class FrameIter // the Ion frame but is instead saved in the RematerializedFrame for use // by Debugger. // // Both methods exist because of speed. thisv() will never rematerialize // an Ion frame, whereas computedThisValue() will. Value computedThisValue() const; Value thisv(JSContext* cx) const; + Value newTarget() const; + Value returnValue() const; void setReturnValue(const Value& v); // These are only valid for the top frame. size_t numFrameSlots() const; Value frameSlotValue(size_t index) const; // Ensures that we have rematerialized the top frame and its associated