mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-18 15:55:36 +00:00
Bug 1132183
- Make |this| a real binding, remove lazy this computation. r=efaust,shu
This commit is contained in:
parent
4e3be28022
commit
8f55c26a1c
@ -260,22 +260,8 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame
|
||||
// Per ES5, indirect eval runs in the global scope. (eval is specified this
|
||||
// way so that the compiler can make assumptions about what bindings may or
|
||||
// may not exist in the current frame if it doesn't see 'eval'.)
|
||||
RootedValue thisv(cx);
|
||||
if (evalType == DIRECT_EVAL) {
|
||||
MOZ_ASSERT_IF(caller.isInterpreterFrame(), !caller.asInterpreterFrame()->runningInJit());
|
||||
|
||||
// Direct calls to eval are supposed to see the caller's |this|. If we
|
||||
// haven't wrapped that yet, do so now, before we make a copy of it for
|
||||
// the eval code to use.
|
||||
if (!ComputeThis(cx, caller))
|
||||
return false;
|
||||
thisv = caller.thisValue();
|
||||
} else {
|
||||
MOZ_ASSERT(args.callee().global() == scopeobj->as<ClonedBlockObject>().global());
|
||||
|
||||
// Use the global as 'this' (or the WindowProxy if it's a Window).
|
||||
thisv = GetThisValue(scopeobj);
|
||||
}
|
||||
MOZ_ASSERT_IF(evalType != DIRECT_EVAL,
|
||||
args.callee().global() == scopeobj->as<ClonedBlockObject>().global());
|
||||
|
||||
RootedLinearString linearStr(cx, str->ensureLinear(cx));
|
||||
if (!linearStr)
|
||||
@ -348,15 +334,15 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame
|
||||
|
||||
// Look up the newTarget from the frame iterator.
|
||||
Value newTargetVal = NullValue();
|
||||
return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, newTargetVal, ExecuteType(evalType),
|
||||
return ExecuteKernel(cx, esg.script(), *scopeobj, newTargetVal, ExecuteType(evalType),
|
||||
NullFramePtr() /* evalInFrame */, args.rval().address());
|
||||
}
|
||||
|
||||
bool
|
||||
js::DirectEvalStringFromIon(JSContext* cx,
|
||||
HandleObject scopeobj, HandleScript callerScript,
|
||||
HandleValue thisValue, HandleValue newTargetValue,
|
||||
HandleString str, jsbytecode* pc, MutableHandleValue vp)
|
||||
HandleValue newTargetValue, HandleString str,
|
||||
jsbytecode* pc, MutableHandleValue vp)
|
||||
{
|
||||
AssertInnerizedScopeChain(cx, *scopeobj);
|
||||
|
||||
@ -428,21 +414,7 @@ js::DirectEvalStringFromIon(JSContext* cx,
|
||||
esg.setNewScript(compiled);
|
||||
}
|
||||
|
||||
// Primitive 'this' values should have been filtered out by Ion. If boxed,
|
||||
// the calling frame cannot be updated to store the new object.
|
||||
MOZ_ASSERT(thisValue.isObject() || thisValue.isUndefined() || thisValue.isNull());
|
||||
|
||||
// When eval'ing strict code in a non-strict context, compute the 'this'
|
||||
// value to use from what the caller passed in. This isn't necessary if
|
||||
// the callee is not strict, as it will compute the non-strict 'this'
|
||||
// value as necessary while it executes.
|
||||
RootedValue nthisValue(cx, thisValue);
|
||||
if (!callerScript->strict() && esg.script()->strict() && !thisValue.isObject()) {
|
||||
if (!BoxNonStrictThis(cx, thisValue, &nthisValue))
|
||||
return false;
|
||||
}
|
||||
|
||||
return ExecuteKernel(cx, esg.script(), *scopeobj, nthisValue, newTargetValue,
|
||||
return ExecuteKernel(cx, esg.script(), *scopeobj, newTargetValue,
|
||||
ExecuteType(DIRECT_EVAL), NullFramePtr() /* evalInFrame */, vp.address());
|
||||
}
|
||||
|
||||
@ -515,10 +487,8 @@ js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScri
|
||||
if (!scope)
|
||||
return false;
|
||||
|
||||
RootedValue thisv(cx, GetThisValue(global));
|
||||
|
||||
RootedValue rval(cx);
|
||||
if (!ExecuteKernel(cx, script, *scope, thisv, UndefinedValue(), EXECUTE_GLOBAL,
|
||||
if (!ExecuteKernel(cx, script, *scope, UndefinedValue(), EXECUTE_GLOBAL,
|
||||
NullFramePtr() /* evalInFrame */, rval.address()))
|
||||
{
|
||||
return false;
|
||||
|
@ -30,8 +30,8 @@ DirectEval(JSContext* cx, const CallArgs& args);
|
||||
extern bool
|
||||
DirectEvalStringFromIon(JSContext* cx,
|
||||
HandleObject scopeObj, HandleScript callerScript,
|
||||
HandleValue thisValue, HandleValue newTargetValue,
|
||||
HandleString str, jsbytecode * pc, MutableHandleValue vp);
|
||||
HandleValue newTargetValue, HandleString str,
|
||||
jsbytecode* pc, MutableHandleValue vp);
|
||||
|
||||
// True iff fun is a built-in eval function.
|
||||
extern bool
|
||||
|
@ -2985,7 +2985,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
||||
|
||||
RootedValue callee(cx);
|
||||
if (pn->isKind(PNK_SUPERCALL)) {
|
||||
MOZ_ASSERT(next->isKind(PNK_POSHOLDER));
|
||||
MOZ_ASSERT(next->isKind(PNK_SUPERBASE));
|
||||
if (!builder.super(&next->pn_pos, &callee))
|
||||
return false;
|
||||
} else {
|
||||
@ -3216,6 +3216,12 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
||||
builder.metaProperty(newIdent, targetIdent, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
case PNK_SETTHIS:
|
||||
// SETTHIS is used to assign the result of a super() call to |this|.
|
||||
// It's not part of the original AST, so just forward to the call.
|
||||
MOZ_ASSERT(pn->pn_left->isKind(PNK_NAME));
|
||||
return expression(pn->pn_right, dst);
|
||||
|
||||
default:
|
||||
LOCAL_NOT_REACHED("unexpected expression type");
|
||||
}
|
||||
|
@ -496,11 +496,15 @@ BytecodeCompiler::compileScript(HandleObject scopeChain, HandleScript evalCaller
|
||||
if (!createSourceAndParser())
|
||||
return nullptr;
|
||||
|
||||
bool savedCallerFun = evalCaller && evalCaller->functionOrCallerFunction();
|
||||
RootedFunction savedCallerFun(cx);
|
||||
if (evalCaller)
|
||||
savedCallerFun = evalCaller->functionOrCallerFunction();
|
||||
|
||||
if (!createScript(enclosingStaticScope, savedCallerFun))
|
||||
return nullptr;
|
||||
|
||||
GlobalSharedContext globalsc(cx, enclosingStaticScope, directives, options.extraWarningsOption);
|
||||
GlobalSharedContext globalsc(cx, enclosingStaticScope, directives, options.extraWarningsOption,
|
||||
savedCallerFun);
|
||||
if (!createEmitter(&globalsc, evalCaller, isNonGlobalEvalCompilationUnit()))
|
||||
return nullptr;
|
||||
|
||||
|
@ -1039,7 +1039,10 @@ BytecodeEmitter::emitAtomOp(JSAtom* atom, JSOp op)
|
||||
|
||||
// .generator lookups should be emitted as JSOP_GETALIASEDVAR instead of
|
||||
// JSOP_GETNAME etc, to bypass |with| objects on the scope chain.
|
||||
MOZ_ASSERT_IF(op == JSOP_GETNAME || op == JSOP_GETGNAME, !sc->isDotVariable(atom));
|
||||
// It's safe to emit .this lookups though because |with| objects skip
|
||||
// those.
|
||||
MOZ_ASSERT_IF(op == JSOP_GETNAME || op == JSOP_GETGNAME,
|
||||
!sc->isDotVariable(atom) || atom == cx->names().dotThis);
|
||||
|
||||
if (op == JSOP_GETPROP && atom == cx->names().length) {
|
||||
/* Specialize length accesses for the interpreter. */
|
||||
@ -1968,10 +1971,11 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||
*answer = false;
|
||||
return true;
|
||||
|
||||
// |this| can throw in derived class constructors.
|
||||
// |this| can throw in derived class constructors, including nested arrow
|
||||
// functions or eval.
|
||||
case PNK_THIS:
|
||||
MOZ_ASSERT(pn->isArity(PN_NULLARY));
|
||||
*answer = sc->isFunctionBox() && sc->asFunctionBox()->isDerivedClassConstructor();
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
*answer = sc->needsThisTDZChecks();
|
||||
return true;
|
||||
|
||||
// Trivial binary nodes with more token pos holders.
|
||||
@ -2089,6 +2093,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||
case PNK_DIVASSIGN:
|
||||
case PNK_MODASSIGN:
|
||||
case PNK_POWASSIGN:
|
||||
case PNK_SETTHIS:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
@ -2355,6 +2360,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||
case PNK_EXPORT_SPEC: // by PNK_EXPORT
|
||||
case PNK_CALLSITEOBJ: // by PNK_TAGGED_TEMPLATE
|
||||
case PNK_POSHOLDER: // by PNK_NEWTARGET
|
||||
case PNK_SUPERBASE: // by PNK_ELEM and others
|
||||
MOZ_CRASH("handled by parent nodes");
|
||||
|
||||
case PNK_LIMIT: // invalid sentinel value
|
||||
@ -2631,9 +2637,9 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn)
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitSuperPropLHS(bool isCall)
|
||||
BytecodeEmitter::emitSuperPropLHS(ParseNode* superBase, bool isCall)
|
||||
{
|
||||
if (!emit1(JSOP_THIS))
|
||||
if (!emitGetThisForSuperBase(superBase))
|
||||
return false;
|
||||
if (isCall && !emit1(JSOP_DUP))
|
||||
return false;
|
||||
@ -2665,7 +2671,8 @@ BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op)
|
||||
bool
|
||||
BytecodeEmitter::emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall)
|
||||
{
|
||||
if (!emitSuperPropLHS(isCall))
|
||||
ParseNode* base = &pn->as<PropertyAccess>().expression();
|
||||
if (!emitSuperPropLHS(base, isCall))
|
||||
return false;
|
||||
|
||||
if (!emitAtomOp(pn, op))
|
||||
@ -2687,7 +2694,8 @@ BytecodeEmitter::emitPropIncDec(ParseNode* pn)
|
||||
JSOp binop = GetIncDecInfo(pn->getKind(), &post);
|
||||
|
||||
if (isSuper) {
|
||||
if (!emitSuperPropLHS()) // THIS OBJ
|
||||
ParseNode* base = &pn->pn_kid->as<PropertyAccess>().expression();
|
||||
if (!emitSuperPropLHS(base)) // THIS OBJ
|
||||
return false;
|
||||
if (!emit1(JSOP_DUP2)) // THIS OBJ THIS OBJ
|
||||
return false;
|
||||
@ -2807,7 +2815,7 @@ BytecodeEmitter::emitSuperElemOperands(ParseNode* pn, SuperElemOptions opts)
|
||||
if (opts == SuperElem_IncDec && !emit1(JSOP_TOID))
|
||||
return false;
|
||||
|
||||
if (!emit1(JSOP_THIS))
|
||||
if (!emitGetThisForSuperBase(pn->pn_left))
|
||||
return false;
|
||||
|
||||
if (opts == SuperElem_Call) {
|
||||
@ -3398,6 +3406,67 @@ BytecodeEmitter::emitYieldOp(JSOp op)
|
||||
return emit1(JSOP_DEBUGAFTERYIELD);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitCreateFunctionThis()
|
||||
{
|
||||
// Do nothing if the function doesn't have a this-binding (this happens for
|
||||
// instance if it doesn't use this/eval or if it's an arrow function).
|
||||
if (!sc->asFunctionBox()->hasThisBinding())
|
||||
return true;
|
||||
|
||||
switchToPrologue();
|
||||
|
||||
if (!emit1(JSOP_FUNCTIONTHIS))
|
||||
return false;
|
||||
|
||||
BindingIter bi = Bindings::thisBinding(cx, script);
|
||||
if (!emitStoreToTopScope(bi))
|
||||
return false;
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
|
||||
switchToMain();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitSetThis(ParseNode* pn)
|
||||
{
|
||||
// PNK_SETTHIS is used to update |this| after a super() call in a derived
|
||||
// class constructor.
|
||||
|
||||
MOZ_ASSERT(pn->isKind(PNK_SETTHIS));
|
||||
|
||||
ParseNode* name = pn->pn_left;
|
||||
MOZ_ASSERT(name->isKind(PNK_NAME));
|
||||
|
||||
if (!emitTree(pn->pn_right))
|
||||
return false;
|
||||
|
||||
if (!bindNameToSlot(name))
|
||||
return false;
|
||||
|
||||
JSOp setOp = name->getOp();
|
||||
|
||||
JSOp getOp;
|
||||
switch (setOp) {
|
||||
case JSOP_SETLOCAL: getOp = JSOP_GETLOCAL; break;
|
||||
case JSOP_SETALIASEDVAR: getOp = JSOP_GETALIASEDVAR; break;
|
||||
default: MOZ_CRASH("Unexpected op");
|
||||
}
|
||||
|
||||
// First, get the original |this| and throw if we already initialized it.
|
||||
if (!emitVarOp(name, getOp))
|
||||
return false;
|
||||
if (!emit1(JSOP_CHECKTHISREINIT))
|
||||
return false;
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
|
||||
// Emit the set.
|
||||
return emitVarOp(name, setOp);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsModuleOnScopeChain(JSObject* obj)
|
||||
{
|
||||
@ -3436,22 +3505,16 @@ BytecodeEmitter::emitFunctionScript(ParseNode* body)
|
||||
if (!emit1(JSOP_ARGUMENTS))
|
||||
return false;
|
||||
BindingIter bi = Bindings::argumentsBinding(cx, script);
|
||||
if (script->bindingIsAliased(bi)) {
|
||||
ScopeCoordinate sc;
|
||||
sc.setHops(0);
|
||||
sc.setSlot(0); // initialize to silence GCC warning
|
||||
MOZ_ALWAYS_TRUE(lookupAliasedNameSlot(cx->names().arguments, &sc));
|
||||
if (!emitAliasedVarOp(JSOP_SETALIASEDVAR, sc, DontCheckLexical))
|
||||
return false;
|
||||
} else {
|
||||
if (!emitUnaliasedVarOp(JSOP_SETLOCAL, bi.localIndex(), DontCheckLexical))
|
||||
return false;
|
||||
}
|
||||
if (!emitStoreToTopScope(bi))
|
||||
return false;
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
switchToMain();
|
||||
}
|
||||
|
||||
if (!emitCreateFunctionThis())
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Emit a prologue for run-once scripts which will deoptimize JIT code if
|
||||
* the script ends up running multiple times via foo.caller related
|
||||
@ -3498,16 +3561,24 @@ BytecodeEmitter::emitFunctionScript(ParseNode* body)
|
||||
// Non-generator functions just return |undefined|. The JSOP_RETRVAL
|
||||
// emitted below will do that, except if the script has a finally
|
||||
// block: there can be a non-undefined value in the return value
|
||||
// slot. We just emit an explicit return in this case.
|
||||
// slot. Make sure the return value is |undefined|.
|
||||
if (hasTryFinally) {
|
||||
if (!emit1(JSOP_UNDEFINED))
|
||||
return false;
|
||||
if (!emit1(JSOP_RETURN))
|
||||
if (!emit1(JSOP_SETRVAL))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sc->isFunctionBox() && sc->asFunctionBox()->isDerivedClassConstructor()) {
|
||||
BindingIter bi = Bindings::thisBinding(cx, script);
|
||||
if (!emitLoadFromTopScope(bi))
|
||||
return false;
|
||||
if (!emit1(JSOP_CHECKRETURN))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Always end the script with a JSOP_RETRVAL. Some other parts of the codebase
|
||||
// depend on this opcode, e.g. InterpreterRegs::setToEndOfScript.
|
||||
if (!emit1(JSOP_RETRVAL))
|
||||
@ -3790,7 +3861,7 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, VarEmitOption emitOptio
|
||||
// wrong order for JSOP_SETPROP, so we have to add a JSOP_SWAP.
|
||||
JSOp setOp;
|
||||
if (target->as<PropertyAccess>().isSuper()) {
|
||||
if (!emitSuperPropLHS())
|
||||
if (!emitSuperPropLHS(&target->as<PropertyAccess>().expression()))
|
||||
return false;
|
||||
if (!emit2(JSOP_PICK, 2))
|
||||
return false;
|
||||
@ -4433,7 +4504,7 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs)
|
||||
break;
|
||||
case PNK_DOT:
|
||||
if (lhs->as<PropertyAccess>().isSuper()) {
|
||||
if (!emitSuperPropLHS())
|
||||
if (!emitSuperPropLHS(&lhs->as<PropertyAccess>().expression()))
|
||||
return false;
|
||||
offset += 2;
|
||||
} else {
|
||||
@ -5906,12 +5977,9 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
|
||||
/* Non-hoisted functions simply emit their respective op. */
|
||||
if (!pn->functionIsHoisted()) {
|
||||
/* JSOP_LAMBDA_ARROW is always preceded by JSOP_THIS and a new.target */
|
||||
/* JSOP_LAMBDA_ARROW is always preceded by a new.target */
|
||||
MOZ_ASSERT(fun->isArrow() == (pn->getOp() == JSOP_LAMBDA_ARROW));
|
||||
if (fun->isArrow()) {
|
||||
if (!emit1(JSOP_THIS))
|
||||
return false;
|
||||
|
||||
if (sc->allowNewTarget()) {
|
||||
if (!emit1(JSOP_NEWTARGET))
|
||||
return false;
|
||||
@ -6148,6 +6216,72 @@ BytecodeEmitter::emitContinue(PropertyName* label)
|
||||
return emitGoto(stmt, &stmt->continues, SRC_CONTINUE);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitGetFunctionThis(ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(sc->thisBinding() == ThisBinding::Function);
|
||||
MOZ_ASSERT(pn->isKind(PNK_NAME));
|
||||
MOZ_ASSERT(pn->name() == cx->names().dotThis);
|
||||
|
||||
if (!emitTree(pn))
|
||||
return false;
|
||||
if (sc->needsThisTDZChecks() && !emit1(JSOP_CHECKTHIS))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitGetThisForSuperBase(ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(pn->isKind(PNK_SUPERBASE));
|
||||
return emitGetFunctionThis(pn->pn_kid);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitThisLiteral(ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(pn->isKind(PNK_THIS));
|
||||
|
||||
if (ParseNode* thisName = pn->pn_kid)
|
||||
return emitGetFunctionThis(thisName);
|
||||
|
||||
if (sc->thisBinding() == ThisBinding::Module)
|
||||
return emit1(JSOP_UNDEFINED);
|
||||
|
||||
MOZ_ASSERT(sc->thisBinding() == ThisBinding::Global);
|
||||
return emit1(JSOP_GLOBALTHIS);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitLoadFromTopScope(BindingIter& bi)
|
||||
{
|
||||
if (script->bindingIsAliased(bi)) {
|
||||
ScopeCoordinate sc;
|
||||
sc.setHops(0);
|
||||
sc.setSlot(0);
|
||||
MOZ_ALWAYS_TRUE(lookupAliasedNameSlot(bi->name(), &sc));
|
||||
return emitAliasedVarOp(JSOP_GETALIASEDVAR, sc, DontCheckLexical);
|
||||
}
|
||||
|
||||
return emitUnaliasedVarOp(JSOP_GETLOCAL, bi.localIndex(), DontCheckLexical);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitStoreToTopScope(BindingIter& bi)
|
||||
{
|
||||
if (script->bindingIsAliased(bi)) {
|
||||
ScopeCoordinate sc;
|
||||
sc.setHops(0);
|
||||
sc.setSlot(0); // initialize to silence GCC warning
|
||||
MOZ_ALWAYS_TRUE(lookupAliasedNameSlot(bi->name(), &sc));
|
||||
return emitAliasedVarOp(JSOP_SETALIASEDVAR, sc, DontCheckLexical);
|
||||
}
|
||||
|
||||
return emitUnaliasedVarOp(JSOP_SETLOCAL, bi.localIndex(), DontCheckLexical);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitReturn(ParseNode* pn)
|
||||
{
|
||||
@ -6188,7 +6322,10 @@ BytecodeEmitter::emitReturn(ParseNode* pn)
|
||||
ptrdiff_t top = offset();
|
||||
|
||||
bool isGenerator = sc->isFunctionBox() && sc->asFunctionBox()->isGenerator();
|
||||
if (!emit1(isGenerator ? JSOP_SETRVAL : JSOP_RETURN))
|
||||
bool isDerivedClassConstructor =
|
||||
sc->isFunctionBox() && sc->asFunctionBox()->isDerivedClassConstructor();
|
||||
|
||||
if (!emit1((isGenerator || isDerivedClassConstructor) ? JSOP_SETRVAL : JSOP_RETURN))
|
||||
return false;
|
||||
|
||||
NonLocalExitScope nle(this);
|
||||
@ -6206,6 +6343,15 @@ BytecodeEmitter::emitReturn(ParseNode* pn)
|
||||
return false;
|
||||
if (!emitYieldOp(JSOP_FINALYIELDRVAL))
|
||||
return false;
|
||||
} else if (isDerivedClassConstructor) {
|
||||
MOZ_ASSERT(code()[top] == JSOP_SETRVAL);
|
||||
BindingIter bi = Bindings::thisBinding(cx, script);
|
||||
if (!emitLoadFromTopScope(bi))
|
||||
return false;
|
||||
if (!emit1(JSOP_CHECKRETURN))
|
||||
return false;
|
||||
if (!emit1(JSOP_RETRVAL))
|
||||
return false;
|
||||
} else if (top + static_cast<ptrdiff_t>(JSOP_RETURN_LENGTH) != offset()) {
|
||||
code()[top] = JSOP_SETRVAL;
|
||||
if (!emit1(JSOP_RETRVAL))
|
||||
@ -6777,9 +6923,9 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
|
||||
}
|
||||
callop = false;
|
||||
break;
|
||||
case PNK_POSHOLDER:
|
||||
case PNK_SUPERBASE:
|
||||
MOZ_ASSERT(pn->isKind(PNK_SUPERCALL));
|
||||
MOZ_ASSERT(parser->handler.isSuperBase(pn2, cx));
|
||||
MOZ_ASSERT(parser->handler.isSuperBase(pn2));
|
||||
if (!emit1(JSOP_SUPERFUN))
|
||||
return false;
|
||||
break;
|
||||
@ -6790,8 +6936,7 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
|
||||
break;
|
||||
}
|
||||
if (!callop) {
|
||||
JSOp thisop = pn->isKind(PNK_GENEXP) ? JSOP_THIS : JSOP_UNDEFINED;
|
||||
if (!emit1(thisop))
|
||||
if (!emit1(JSOP_UNDEFINED))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -6859,8 +7004,6 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pn->isKind(PNK_SUPERCALL) && !emit1(JSOP_SETTHIS))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -8147,12 +8290,16 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
|
||||
|
||||
case PNK_TRUE:
|
||||
case PNK_FALSE:
|
||||
case PNK_THIS:
|
||||
case PNK_NULL:
|
||||
if (!emit1(pn->getOp()))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_THIS:
|
||||
if (!emitThisLiteral(pn))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_DEBUGGER:
|
||||
if (!updateSourceCoordNotes(pn->pn_pos.begin))
|
||||
return false;
|
||||
@ -8174,6 +8321,11 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_SETTHIS:
|
||||
if (!emitSetThis(pn))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_POSHOLDER:
|
||||
MOZ_ASSERT_UNREACHABLE("Should never try to emit PNK_POSHOLDER");
|
||||
|
||||
|
@ -420,6 +420,18 @@ struct BytecodeEmitter
|
||||
|
||||
bool emitNumberOp(double dval);
|
||||
|
||||
bool emitThisLiteral(ParseNode* pn);
|
||||
bool emitCreateFunctionThis();
|
||||
bool emitGetFunctionThis(ParseNode* pn);
|
||||
bool emitGetThisForSuperBase(ParseNode* pn);
|
||||
bool emitSetThis(ParseNode* pn);
|
||||
|
||||
// These functions are used to emit GETLOCAL/GETALIASEDVAR or
|
||||
// SETLOCAL/SETALIASEDVAR for a particular binding. The CallObject must be
|
||||
// on top of the scope chain.
|
||||
bool emitLoadFromTopScope(BindingIter& bi);
|
||||
bool emitStoreToTopScope(BindingIter& bi);
|
||||
|
||||
bool emitJump(JSOp op, ptrdiff_t off, ptrdiff_t* jumpOffset = nullptr);
|
||||
bool emitCall(JSOp op, uint16_t argc, ParseNode* pn = nullptr);
|
||||
|
||||
@ -625,7 +637,7 @@ struct BytecodeEmitter
|
||||
bool emitForOf(StmtType type, ParseNode* pn);
|
||||
|
||||
bool emitClass(ParseNode* pn);
|
||||
bool emitSuperPropLHS(bool isCall = false);
|
||||
bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false);
|
||||
bool emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall = false);
|
||||
enum SuperElemOptions { SuperElem_Get, SuperElem_Set, SuperElem_Call, SuperElem_IncDec };
|
||||
bool emitSuperElemOperands(ParseNode* pn, SuperElemOptions opts = SuperElem_Get);
|
||||
|
@ -409,6 +409,8 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
||||
case PNK_NEWTARGET:
|
||||
case PNK_POSHOLDER:
|
||||
case PNK_SUPERCALL:
|
||||
case PNK_SUPERBASE:
|
||||
case PNK_SETTHIS:
|
||||
MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on "
|
||||
"some parent node without recurring to test this node");
|
||||
|
||||
@ -1700,7 +1702,6 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
||||
case PNK_BREAK:
|
||||
case PNK_CONTINUE:
|
||||
case PNK_TEMPLATE_STRING:
|
||||
case PNK_THIS:
|
||||
case PNK_GENERATOR:
|
||||
case PNK_EXPORT_BATCH_SPEC:
|
||||
case PNK_OBJECT_PROPERTY_NAME:
|
||||
@ -1708,6 +1709,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
||||
MOZ_ASSERT(pn->isArity(PN_NULLARY));
|
||||
return true;
|
||||
|
||||
case PNK_SUPERBASE:
|
||||
case PNK_TYPEOFNAME:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
MOZ_ASSERT(pn->pn_kid->isKind(PNK_NAME));
|
||||
@ -1767,6 +1769,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
||||
return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
|
||||
|
||||
case PNK_SEMI:
|
||||
case PNK_THIS:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
if (ParseNode*& expr = pn->pn_kid)
|
||||
return Fold(cx, &expr, parser, inGenexpLambda);
|
||||
@ -1890,6 +1893,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
||||
case PNK_CLASSMETHOD:
|
||||
case PNK_IMPORT_SPEC:
|
||||
case PNK_EXPORT_SPEC:
|
||||
case PNK_SETTHIS:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
return Fold(cx, &pn->pn_left, parser, inGenexpLambda) &&
|
||||
Fold(cx, &pn->pn_right, parser, inGenexpLambda);
|
||||
|
@ -187,8 +187,8 @@ class FullParseHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
ParseNode* newThisLiteral(const TokenPos& pos) {
|
||||
return new_<ThisLiteral>(pos);
|
||||
ParseNode* newThisLiteral(const TokenPos& pos, ParseNode* thisName) {
|
||||
return new_<ThisLiteral>(pos, thisName);
|
||||
}
|
||||
|
||||
ParseNode* newNullLiteral(const TokenPos& pos) {
|
||||
@ -346,14 +346,8 @@ class FullParseHandler
|
||||
ParseNode* newPosHolder(const TokenPos& pos) {
|
||||
return new_<NullaryNode>(PNK_POSHOLDER, pos);
|
||||
}
|
||||
ParseNode* newSuperBase(const TokenPos& pos, ExclusiveContext* cx) {
|
||||
ParseNode* node = newPosHolder(pos);
|
||||
#ifdef DEBUG
|
||||
// Set the atom for assertion purposes
|
||||
if (node)
|
||||
node->pn_atom = cx->names().super;
|
||||
#endif
|
||||
return node;
|
||||
ParseNode* newSuperBase(ParseNode* thisName, const TokenPos& pos) {
|
||||
return new_<UnaryNode>(PNK_SUPERBASE, JSOP_NOP, pos, thisName);
|
||||
}
|
||||
|
||||
bool addPrototypeMutation(ParseNode* literal, uint32_t begin, ParseNode* expr) {
|
||||
@ -494,6 +488,13 @@ class FullParseHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
ParseNode* newSetThis(ParseNode* thisName, ParseNode* val) {
|
||||
MOZ_ASSERT(thisName->getOp() == JSOP_GETNAME);
|
||||
thisName->setOp(JSOP_SETNAME);
|
||||
thisName->markAsAssigned();
|
||||
return newBinary(PNK_SETTHIS, thisName, val);
|
||||
}
|
||||
|
||||
ParseNode* newEmptyStatement(const TokenPos& pos) {
|
||||
return new_<UnaryNode>(PNK_SEMI, JSOP_NOP, pos, (ParseNode*) nullptr);
|
||||
}
|
||||
@ -709,9 +710,8 @@ class FullParseHandler
|
||||
(kind == PNK_SEMI && !node->pn_kid);
|
||||
}
|
||||
|
||||
bool isSuperBase(ParseNode* node, ExclusiveContext* cx) {
|
||||
MOZ_ASSERT_IF(node->isKind(PNK_POSHOLDER), node->pn_atom == cx->names().super);
|
||||
return node->isKind(PNK_POSHOLDER);
|
||||
bool isSuperBase(ParseNode* node) {
|
||||
return node->isKind(PNK_SUPERBASE);
|
||||
}
|
||||
|
||||
inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op);
|
||||
|
@ -366,7 +366,6 @@ class NameResolver
|
||||
case PNK_TRUE:
|
||||
case PNK_FALSE:
|
||||
case PNK_NULL:
|
||||
case PNK_THIS:
|
||||
case PNK_ELISION:
|
||||
case PNK_GENERATOR:
|
||||
case PNK_NUMBER:
|
||||
@ -380,6 +379,7 @@ class NameResolver
|
||||
break;
|
||||
|
||||
case PNK_TYPEOFNAME:
|
||||
case PNK_SUPERBASE:
|
||||
MOZ_ASSERT(cur->isArity(PN_UNARY));
|
||||
MOZ_ASSERT(cur->pn_kid->isKind(PNK_NAME));
|
||||
MOZ_ASSERT(!cur->pn_kid->maybeExpr());
|
||||
@ -419,6 +419,7 @@ class NameResolver
|
||||
|
||||
// Nodes with a single nullable child.
|
||||
case PNK_SEMI:
|
||||
case PNK_THIS:
|
||||
MOZ_ASSERT(cur->isArity(PN_UNARY));
|
||||
if (ParseNode* expr = cur->pn_kid) {
|
||||
if (!resolve(expr, prefix))
|
||||
@ -448,6 +449,7 @@ class NameResolver
|
||||
case PNK_LETBLOCK:
|
||||
case PNK_FOR:
|
||||
case PNK_CLASSMETHOD:
|
||||
case PNK_SETTHIS:
|
||||
MOZ_ASSERT(cur->isArity(PN_BINARY));
|
||||
if (!resolve(cur->pn_left, prefix))
|
||||
return false;
|
||||
|
@ -205,7 +205,6 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
||||
case PNK_TRUE:
|
||||
case PNK_FALSE:
|
||||
case PNK_NULL:
|
||||
case PNK_THIS:
|
||||
case PNK_ELISION:
|
||||
case PNK_GENERATOR:
|
||||
case PNK_NUMBER:
|
||||
@ -242,9 +241,11 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
||||
case PNK_SPREAD:
|
||||
case PNK_MUTATEPROTO:
|
||||
case PNK_EXPORT:
|
||||
case PNK_SUPERBASE:
|
||||
return PushUnaryNodeChild(pn, stack);
|
||||
|
||||
// Nodes with a single nullable child.
|
||||
case PNK_THIS:
|
||||
case PNK_SEMI: {
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
if (pn->pn_kid)
|
||||
@ -280,6 +281,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
||||
case PNK_LETBLOCK:
|
||||
case PNK_CLASSMETHOD:
|
||||
case PNK_NEWTARGET:
|
||||
case PNK_SETTHIS:
|
||||
case PNK_FOR: {
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
stack->push(pn->pn_left);
|
||||
|
@ -173,7 +173,9 @@ class PackedScopeCoordinate
|
||||
F(CLASSNAMES) \
|
||||
F(NEWTARGET) \
|
||||
F(POSHOLDER) \
|
||||
F(SUPERBASE) \
|
||||
F(SUPERCALL) \
|
||||
F(SETTHIS) \
|
||||
\
|
||||
/* Unary operators. */ \
|
||||
F(TYPEOFNAME) \
|
||||
@ -468,8 +470,12 @@ IsDeleteKind(ParseNodeKind kind)
|
||||
* PNK_NUMBER dval pn_dval: double value of numeric literal
|
||||
* PNK_TRUE, nullary pn_op: JSOp bytecode
|
||||
* PNK_FALSE,
|
||||
* PNK_NULL,
|
||||
* PNK_THIS
|
||||
* PNK_NULL
|
||||
*
|
||||
* PNK_THIS, unary pn_kid: '.this' Name if function `this`, else nullptr
|
||||
* PNK_SUPERBASE unary pn_kid: '.this' Name
|
||||
*
|
||||
* PNK_SETTHIS binary pn_left: '.this' Name, pn_right: SuperCall
|
||||
*
|
||||
* PNK_LEXICALSCOPE name pn_objbox: block object in ObjectBox holder
|
||||
* pn_expr: block body
|
||||
@ -1273,10 +1279,12 @@ class ConditionalExpression : public ParseNode
|
||||
}
|
||||
};
|
||||
|
||||
class ThisLiteral : public ParseNode
|
||||
class ThisLiteral : public UnaryNode
|
||||
{
|
||||
public:
|
||||
explicit ThisLiteral(const TokenPos& pos) : ParseNode(PNK_THIS, JSOP_THIS, PN_NULLARY, pos) { }
|
||||
ThisLiteral(const TokenPos& pos, ParseNode* thisName)
|
||||
: UnaryNode(PNK_THIS, JSOP_NOP, pos, thisName)
|
||||
{ }
|
||||
};
|
||||
|
||||
class NullLiteral : public ParseNode
|
||||
@ -1339,8 +1347,8 @@ class PropertyAccess : public ParseNode
|
||||
}
|
||||
|
||||
bool isSuper() const {
|
||||
// PNK_POSHOLDER cannot result from any expression syntax.
|
||||
return expression().isKind(PNK_POSHOLDER);
|
||||
// PNK_SUPERBASE cannot result from any expression syntax.
|
||||
return expression().isKind(PNK_SUPERBASE);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1361,8 +1369,7 @@ class PropertyByValue : public ParseNode
|
||||
}
|
||||
|
||||
bool isSuper() const {
|
||||
// Like PropertyAccess above, PNK_POSHOLDER is "good enough".
|
||||
return pn_left->isKind(PNK_POSHOLDER);
|
||||
return pn_left->isKind(PNK_SUPERBASE);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -58,6 +58,7 @@ JSFunction::AutoParseUsingFunctionBox::AutoParseUsingFunctionBox(ExclusiveContex
|
||||
fun_->setFunctionBox(funbox);
|
||||
funbox->computeAllowSyntax(fun_);
|
||||
funbox->computeInWith(fun_);
|
||||
funbox->computeThisBinding(fun_);
|
||||
}
|
||||
|
||||
JSFunction::AutoParseUsingFunctionBox::~AutoParseUsingFunctionBox()
|
||||
@ -133,6 +134,44 @@ SharedContext::computeAllowSyntax(JSObject* staticScope)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SharedContext::computeThisBinding(JSObject* staticScope)
|
||||
{
|
||||
for (StaticScopeIter<CanGC> it(context, staticScope); !it.done(); it++) {
|
||||
if (it.type() == StaticScopeIter<CanGC>::Module) {
|
||||
thisBinding_ = ThisBinding::Module;
|
||||
return;
|
||||
}
|
||||
|
||||
if (it.type() == StaticScopeIter<CanGC>::Function) {
|
||||
// Arrow functions and generator expression lambdas don't have
|
||||
// their own `this` binding.
|
||||
if (it.fun().isArrow())
|
||||
continue;
|
||||
bool isDerived;
|
||||
if (it.maybeFunctionBox()) {
|
||||
if (it.maybeFunctionBox()->inGenexpLambda)
|
||||
continue;
|
||||
isDerived = it.maybeFunctionBox()->isDerivedClassConstructor();
|
||||
} else {
|
||||
if (it.fun().nonLazyScript()->isGeneratorExp())
|
||||
continue;
|
||||
isDerived = it.fun().isDerivedClassConstructor();
|
||||
}
|
||||
|
||||
// Derived class constructors (including nested arrow functions and
|
||||
// eval) need TDZ checks when accessing |this|.
|
||||
if (isDerived)
|
||||
needsThisTDZChecks_ = true;
|
||||
|
||||
thisBinding_ = ThisBinding::Function;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
thisBinding_ = ThisBinding::Global;
|
||||
}
|
||||
|
||||
void
|
||||
SharedContext::computeInWith(JSObject* staticScope)
|
||||
{
|
||||
@ -766,7 +805,9 @@ ModuleBox::ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObjec
|
||||
SharedContext(cx, Directives(true), false),
|
||||
bindings(),
|
||||
exportNames(cx)
|
||||
{}
|
||||
{
|
||||
computeThisBinding(staticScope());
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
ModuleBox*
|
||||
@ -1017,6 +1058,59 @@ Parser<FullParseHandler>::globalBody()
|
||||
return body;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::newThisName()
|
||||
{
|
||||
Node thisName = newName(context->names().dotThis);
|
||||
if (!thisName)
|
||||
return null();
|
||||
if (!noteNameUse(context->names().dotThis, thisName))
|
||||
return null();
|
||||
return thisName;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::defineFunctionThis()
|
||||
{
|
||||
HandlePropertyName dotThis = context->names().dotThis;
|
||||
|
||||
// Create a declaration for '.this' if there are any unbound uses in the
|
||||
// function body.
|
||||
for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
|
||||
if (r.front().key() == dotThis) {
|
||||
Definition* dn = r.front().value().get<FullParseHandler>();
|
||||
MOZ_ASSERT(dn->isPlaceholder());
|
||||
pc->sc->asFunctionBox()->setHasThisBinding();
|
||||
return pc->define(tokenStream, dotThis, dn, Definition::VAR);
|
||||
}
|
||||
}
|
||||
|
||||
// Also define a this-binding if direct eval is used, in derived class
|
||||
// constructors (JSOP_CHECKRETURN relies on it) or if there's a debugger
|
||||
// statement.
|
||||
if (pc->sc->hasDirectEval() ||
|
||||
pc->sc->asFunctionBox()->isDerivedClassConstructor() ||
|
||||
pc->sc->hasDebuggerStatement())
|
||||
{
|
||||
ParseNode* pn = newName(dotThis);
|
||||
if (!pn)
|
||||
return false;
|
||||
pc->sc->asFunctionBox()->setHasThisBinding();
|
||||
return pc->define(tokenStream, dotThis, pn, Definition::VAR);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<SyntaxParseHandler>::defineFunctionThis()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode*
|
||||
Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
|
||||
@ -1284,10 +1378,12 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
|
||||
}
|
||||
|
||||
if (kind != Arrow) {
|
||||
// Define the 'arguments' binding if necessary. Arrow functions
|
||||
// don't have 'arguments'.
|
||||
// Define the 'arguments' and 'this' bindings if necessary. Arrow
|
||||
// functions don't have these bindings.
|
||||
if (!checkFunctionArguments())
|
||||
return null();
|
||||
if (!defineFunctionThis())
|
||||
return null();
|
||||
}
|
||||
|
||||
return pn;
|
||||
@ -7648,6 +7744,7 @@ LegacyCompExprTransplanter::transplant(ParseNode* pn)
|
||||
if (pc->sc->isDotVariable(atom)) {
|
||||
if (dn->dn_uses == pn)
|
||||
adjustBlockId(dn);
|
||||
dn->pn_dflags |= PND_CLOSED;
|
||||
} else if (dn->pn_pos < root->pn_pos) {
|
||||
/*
|
||||
* The variable originally appeared to be a use of a
|
||||
@ -8071,6 +8168,8 @@ Parser<ParseHandler>::generatorComprehensionLambda(GeneratorKind comprehensionKi
|
||||
if (!genFunbox)
|
||||
return null();
|
||||
|
||||
genFunbox->inGenexpLambda = true;
|
||||
|
||||
ParseContext<ParseHandler> genpc(this, outerpc, genfn, genFunbox,
|
||||
/* newDirectives = */ nullptr);
|
||||
if (!genpc.init(*this))
|
||||
@ -8087,7 +8186,6 @@ Parser<ParseHandler>::generatorComprehensionLambda(GeneratorKind comprehensionKi
|
||||
genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags;
|
||||
|
||||
MOZ_ASSERT(genFunbox->generatorKind() == comprehensionKind);
|
||||
genFunbox->inGenexpLambda = true;
|
||||
handler.setBlockId(genfn, genpc.bodyid);
|
||||
|
||||
Node generator = newName(context->names().dotGenerator);
|
||||
@ -8561,7 +8659,10 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
|
||||
}
|
||||
}
|
||||
} else if (tt == TOK_SUPER) {
|
||||
lhs = handler.newSuperBase(pos(), context);
|
||||
Node thisName = newThisName();
|
||||
if (!thisName)
|
||||
return null();
|
||||
lhs = handler.newSuperBase(thisName, pos());
|
||||
if (!lhs)
|
||||
return null();
|
||||
} else {
|
||||
@ -8570,7 +8671,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
|
||||
return null();
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(handler.isSuperBase(lhs, context), tokenStream.isCurrentTokenType(TOK_SUPER));
|
||||
MOZ_ASSERT_IF(handler.isSuperBase(lhs), tokenStream.isCurrentTokenType(TOK_SUPER));
|
||||
|
||||
while (true) {
|
||||
if (!tokenStream.getToken(&tt))
|
||||
@ -8584,7 +8685,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
|
||||
return null();
|
||||
if (tt == TOK_NAME) {
|
||||
PropertyName* field = tokenStream.currentName();
|
||||
if (handler.isSuperBase(lhs, context) && !checkAndMarkSuperScope()) {
|
||||
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "property");
|
||||
return null();
|
||||
}
|
||||
@ -8602,7 +8703,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
|
||||
|
||||
if (handler.isSuperBase(lhs, context) && !checkAndMarkSuperScope()) {
|
||||
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "member");
|
||||
return null();
|
||||
}
|
||||
@ -8613,7 +8714,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
|
||||
tt == TOK_TEMPLATE_HEAD ||
|
||||
tt == TOK_NO_SUBS_TEMPLATE)
|
||||
{
|
||||
if (handler.isSuperBase(lhs, context)) {
|
||||
if (handler.isSuperBase(lhs)) {
|
||||
if (!pc->sc->isFunctionBox() || !pc->sc->asFunctionBox()->isDerivedClassConstructor()) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_SUPERCALL);
|
||||
return null();
|
||||
@ -8638,7 +8739,10 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
|
||||
if (isSpread)
|
||||
handler.setOp(nextMember, JSOP_SPREADSUPERCALL);
|
||||
|
||||
return nextMember;
|
||||
Node thisName = newThisName();
|
||||
if (!thisName)
|
||||
return null();
|
||||
return handler.newSetThis(thisName, nextMember);
|
||||
}
|
||||
|
||||
if (options().selfHostingMode && handler.isPropertyAccess(lhs)) {
|
||||
@ -8705,7 +8809,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
|
||||
handler.setOp(nextMember, op);
|
||||
} else {
|
||||
tokenStream.ungetToken();
|
||||
if (handler.isSuperBase(lhs, context))
|
||||
if (handler.isSuperBase(lhs))
|
||||
break;
|
||||
return lhs;
|
||||
}
|
||||
@ -8713,7 +8817,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
|
||||
lhs = nextMember;
|
||||
}
|
||||
|
||||
if (handler.isSuperBase(lhs, context)) {
|
||||
if (handler.isSuperBase(lhs)) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_SUPER);
|
||||
return null();
|
||||
}
|
||||
@ -9388,10 +9492,17 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
|
||||
return handler.newBooleanLiteral(true, pos());
|
||||
case TOK_FALSE:
|
||||
return handler.newBooleanLiteral(false, pos());
|
||||
case TOK_THIS:
|
||||
case TOK_THIS: {
|
||||
if (pc->sc->isFunctionBox())
|
||||
pc->sc->asFunctionBox()->usesThis = true;
|
||||
return handler.newThisLiteral(pos());
|
||||
Node thisName = null();
|
||||
if (pc->sc->thisBinding() == ThisBinding::Function) {
|
||||
thisName = newThisName();
|
||||
if (!thisName)
|
||||
return null();
|
||||
}
|
||||
return handler.newThisLiteral(pos(), thisName);
|
||||
}
|
||||
case TOK_NULL:
|
||||
return handler.newNullLiteral(pos());
|
||||
|
||||
|
@ -789,6 +789,10 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
bool matchInOrOf(bool* isForInp, bool* isForOfp);
|
||||
|
||||
bool checkFunctionArguments();
|
||||
|
||||
bool defineFunctionThis();
|
||||
Node newThisName();
|
||||
|
||||
bool makeDefIntoUse(Definition* dn, Node pn, HandleAtom atom);
|
||||
bool checkFunctionDefinition(HandlePropertyName funName, Node* pn, FunctionSyntaxKind kind,
|
||||
bool* pbodyProcessed);
|
||||
|
@ -130,6 +130,10 @@ class FunctionContextFlags
|
||||
bool needsHomeObject:1;
|
||||
bool isDerivedClassConstructor:1;
|
||||
|
||||
// Whether this function has a .this binding. If true, we need to emit
|
||||
// JSOP_FUNCTIONTHIS in the prologue to initialize it.
|
||||
bool hasThisBinding:1;
|
||||
|
||||
public:
|
||||
FunctionContextFlags()
|
||||
: mightAliasLocals(false),
|
||||
@ -138,7 +142,8 @@ class FunctionContextFlags
|
||||
argumentsHasLocalBinding(false),
|
||||
definitelyNeedsArgsObj(false),
|
||||
needsHomeObject(false),
|
||||
isDerivedClassConstructor(false)
|
||||
isDerivedClassConstructor(false),
|
||||
hasThisBinding(false)
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -171,6 +176,12 @@ class Directives
|
||||
}
|
||||
};
|
||||
|
||||
// The kind of this-binding for the current scope. Note that arrow functions
|
||||
// (and generator expression lambdas) have a lexical this-binding so their
|
||||
// ThisBinding is the same as the ThisBinding of their enclosing scope and can
|
||||
// be any value.
|
||||
enum class ThisBinding { Global, Function, Module };
|
||||
|
||||
/*
|
||||
* The struct SharedContext is part of the current parser context (see
|
||||
* ParseContext). It stores information that is reused between the parser and
|
||||
@ -187,9 +198,12 @@ class SharedContext
|
||||
bool extraWarnings;
|
||||
|
||||
private:
|
||||
ThisBinding thisBinding_;
|
||||
|
||||
bool allowNewTarget_;
|
||||
bool allowSuperProperty_;
|
||||
bool inWith_;
|
||||
bool needsThisTDZChecks_;
|
||||
bool superScopeAlreadyNeedsHomeObject_;
|
||||
|
||||
public:
|
||||
@ -200,9 +214,11 @@ class SharedContext
|
||||
strictScript(directives.strict()),
|
||||
localStrict(false),
|
||||
extraWarnings(extraWarnings),
|
||||
thisBinding_(ThisBinding::Global),
|
||||
allowNewTarget_(false),
|
||||
allowSuperProperty_(false),
|
||||
inWith_(false),
|
||||
needsThisTDZChecks_(false),
|
||||
superScopeAlreadyNeedsHomeObject_(false)
|
||||
{ }
|
||||
|
||||
@ -214,6 +230,7 @@ class SharedContext
|
||||
virtual JSObject* staticScope() const = 0;
|
||||
void computeAllowSyntax(JSObject* staticScope);
|
||||
void computeInWith(JSObject* staticScope);
|
||||
void computeThisBinding(JSObject* staticScope);
|
||||
|
||||
virtual ObjectBox* toObjectBox() { return nullptr; }
|
||||
bool isObjectBox() { return toObjectBox() != nullptr; }
|
||||
@ -223,9 +240,13 @@ class SharedContext
|
||||
inline ModuleBox* asModuleBox();
|
||||
bool isGlobalContext() { return !toObjectBox(); }
|
||||
|
||||
ThisBinding thisBinding() const { return thisBinding_; }
|
||||
|
||||
bool allowNewTarget() const { return allowNewTarget_; }
|
||||
bool allowSuperProperty() const { return allowSuperProperty_; }
|
||||
bool inWith() const { return inWith_; }
|
||||
bool needsThisTDZChecks() const { return needsThisTDZChecks_; }
|
||||
|
||||
void markSuperScopeNeedsHomeObject();
|
||||
|
||||
bool hasExplicitUseStrict() const { return anyCxFlags.hasExplicitUseStrict; }
|
||||
@ -255,7 +276,7 @@ class SharedContext
|
||||
}
|
||||
|
||||
bool isDotVariable(JSAtom* atom) const {
|
||||
return atom == context->names().dotGenerator;
|
||||
return atom == context->names().dotGenerator || atom == context->names().dotThis;
|
||||
}
|
||||
};
|
||||
|
||||
@ -265,12 +286,20 @@ class MOZ_STACK_CLASS GlobalSharedContext : public SharedContext
|
||||
|
||||
public:
|
||||
GlobalSharedContext(ExclusiveContext* cx, ScopeObject* staticScope, Directives directives,
|
||||
bool extraWarnings)
|
||||
bool extraWarnings, JSFunction* maybeEvalCaller = nullptr)
|
||||
: SharedContext(cx, directives, extraWarnings),
|
||||
staticScope_(cx, staticScope)
|
||||
{
|
||||
computeAllowSyntax(staticScope);
|
||||
computeInWith(staticScope);
|
||||
|
||||
// If we're executing a Debugger eval-in-frame, staticScope is always a
|
||||
// non-function scope, so we have to compute our ThisBinding based on
|
||||
// the actual callee.
|
||||
if (maybeEvalCaller)
|
||||
computeThisBinding(maybeEvalCaller);
|
||||
else
|
||||
computeThisBinding(staticScope);
|
||||
}
|
||||
|
||||
JSObject* staticScope() const override { return staticScope_; }
|
||||
@ -328,6 +357,7 @@ class FunctionBox : public ObjectBox, public SharedContext
|
||||
bool mightAliasLocals() const { return funCxFlags.mightAliasLocals; }
|
||||
bool hasExtensibleScope() const { return funCxFlags.hasExtensibleScope; }
|
||||
bool needsDeclEnvObject() const { return funCxFlags.needsDeclEnvObject; }
|
||||
bool hasThisBinding() const { return funCxFlags.hasThisBinding; }
|
||||
bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; }
|
||||
bool definitelyNeedsArgsObj() const { return funCxFlags.definitelyNeedsArgsObj; }
|
||||
bool needsHomeObject() const { return funCxFlags.needsHomeObject; }
|
||||
@ -336,6 +366,7 @@ class FunctionBox : public ObjectBox, public SharedContext
|
||||
void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; }
|
||||
void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; }
|
||||
void setNeedsDeclEnvObject() { funCxFlags.needsDeclEnvObject = true; }
|
||||
void setHasThisBinding() { funCxFlags.hasThisBinding = true; }
|
||||
void setArgumentsHasLocalBinding() { funCxFlags.argumentsHasLocalBinding = true; }
|
||||
void setDefinitelyNeedsArgsObj() { MOZ_ASSERT(funCxFlags.argumentsHasLocalBinding);
|
||||
funCxFlags.definitelyNeedsArgsObj = true; }
|
||||
|
@ -220,7 +220,7 @@ class SyntaxParseHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
Node newThisLiteral(const TokenPos& pos) { return NodeGeneric; }
|
||||
Node newThisLiteral(const TokenPos& pos, Node thisName) { return NodeGeneric; }
|
||||
Node newNullLiteral(const TokenPos& pos) { return NodeGeneric; }
|
||||
|
||||
template <class Boxer>
|
||||
@ -276,7 +276,7 @@ class SyntaxParseHandler
|
||||
|
||||
Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; }
|
||||
Node newPosHolder(const TokenPos& pos) { return NodeGeneric; }
|
||||
Node newSuperBase(const TokenPos& pos, ExclusiveContext* cx) { return NodeSuperBase; }
|
||||
Node newSuperBase(Node thisName, const TokenPos& pos) { return NodeSuperBase; }
|
||||
|
||||
bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
|
||||
bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
|
||||
@ -293,6 +293,8 @@ class SyntaxParseHandler
|
||||
bool prependInitialYield(Node stmtList, Node gen) { return true; }
|
||||
Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; }
|
||||
|
||||
Node newSetThis(Node thisName, Node value) { return value; }
|
||||
|
||||
Node newExprStatement(Node expr, uint32_t end) {
|
||||
return expr == NodeUnparenthesizedString ? NodeStringExprStatement : NodeGeneric;
|
||||
}
|
||||
@ -430,9 +432,7 @@ class SyntaxParseHandler
|
||||
pn == NodeEmptyStatement;
|
||||
}
|
||||
|
||||
bool isSuperBase(Node pn, ExclusiveContext* cx) {
|
||||
// While NodePosHolder is used in other places than just as super-base,
|
||||
// it is unique enough for our purposes.
|
||||
bool isSuperBase(Node pn) {
|
||||
return pn == NodeSuperBase;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ function f(x, y) {
|
||||
for (var i=0; i<40; i++) {
|
||||
var stack = getBacktrace({args: true, locals: true, thisprops: true});
|
||||
assertEq(stack.includes("f(x = "), true);
|
||||
assertEq(stack.includes("this = "), true);
|
||||
backtrace();
|
||||
}
|
||||
}
|
||||
|
51
js/src/jit-test/tests/basic/this-binding-with-eval.js
Normal file
51
js/src/jit-test/tests/basic/this-binding-with-eval.js
Normal file
@ -0,0 +1,51 @@
|
||||
function Test1() {
|
||||
this.x = 8;
|
||||
this.y = 5;
|
||||
this.z = 2;
|
||||
var o = {".this": {x: 1}};
|
||||
var res;
|
||||
with (o) {
|
||||
res = this.x + (() => this.z)() + eval("this.x + (() => this.y)()");
|
||||
}
|
||||
assertEq(res, 23);
|
||||
}
|
||||
new Test1();
|
||||
|
||||
function Test2() {
|
||||
this.x = 8;
|
||||
var o = {".this": {x: 1}};
|
||||
with (o) {
|
||||
return eval("() => this.x");
|
||||
}
|
||||
}
|
||||
var fun = new Test2();
|
||||
assertEq(fun(), 8);
|
||||
|
||||
function Test3() {
|
||||
this.x = 8;
|
||||
var o = {".this": {x: 1}};
|
||||
with (o) {
|
||||
assertEq(this.x, 8);
|
||||
}
|
||||
}
|
||||
new Test3();
|
||||
|
||||
function test4() {
|
||||
var o = {".this": {x: 1}};
|
||||
with (o) {
|
||||
return () => this;
|
||||
}
|
||||
}
|
||||
assertEq(test4()(), this);
|
||||
|
||||
function test5() {
|
||||
var o = {".this": {x: 1}};
|
||||
with (o) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
assertEq(test5(), this);
|
||||
|
||||
var global = this;
|
||||
evaluate("with({}) { assertEq(this, global); }");
|
||||
eval("with({}) { assertEq(this, global); }");
|
23
js/src/jit-test/tests/debug/Frame-this-05.js
Normal file
23
js/src/jit-test/tests/debug/Frame-this-05.js
Normal file
@ -0,0 +1,23 @@
|
||||
// Frame.this and evalInFrame in the global scope.
|
||||
var g = newGlobal();
|
||||
g.eval("x = 4; this['.this'] = 222;");
|
||||
var dbg = new Debugger(g);
|
||||
var res;
|
||||
dbg.onDebuggerStatement = function (frame) {
|
||||
res = frame.eval("this.x").return;
|
||||
res += frame.this.unsafeDereference().x;
|
||||
};
|
||||
g.eval("debugger;");
|
||||
assertEq(res, 8);
|
||||
|
||||
// And inside eval.
|
||||
g.eval("x = 3; eval('debugger')");
|
||||
assertEq(res, 6);
|
||||
g.eval("x = 2; eval('eval(\\'debugger\\')')");
|
||||
assertEq(res, 4);
|
||||
|
||||
// And inside arrow functions.
|
||||
g.eval("x = 1; (() => { debugger; })()");
|
||||
assertEq(res, 2);
|
||||
g.eval("x = 5; (() => { eval('debugger'); })()");
|
||||
assertEq(res, 10);
|
22
js/src/jit-test/tests/debug/Frame-this-06.js
Normal file
22
js/src/jit-test/tests/debug/Frame-this-06.js
Normal file
@ -0,0 +1,22 @@
|
||||
// Frame.this and evalInFrame with missing this, strict and non-strict.
|
||||
var g = newGlobal();
|
||||
var dbg = new Debugger(g);
|
||||
var evalThis, frameThis;
|
||||
dbg.onEnterFrame = function (frame) {
|
||||
if (frame.type === "eval")
|
||||
return;
|
||||
assertEq(frame.type, "call");
|
||||
evalThis = frame.eval("this");
|
||||
frameThis = frame.this;
|
||||
};
|
||||
|
||||
// Strict, this is primitive.
|
||||
g.eval("var foo = function() { 'use strict'; }; foo.call(33);");
|
||||
assertEq(evalThis.return, 33);
|
||||
assertEq(frameThis, 33);
|
||||
|
||||
// Non-strict, this has to be boxed.
|
||||
g.eval("var bar = function() { }; bar.call(22);");
|
||||
assertEq(typeof evalThis.return, "object");
|
||||
assertEq(evalThis.return.unsafeDereference().valueOf(), 22);
|
||||
assertEq(frameThis.optimizedOut, true); // Frame.this currently doesn't box missing primitive this.
|
19
js/src/jit-test/tests/debug/Frame-this-07.js
Normal file
19
js/src/jit-test/tests/debug/Frame-this-07.js
Normal file
@ -0,0 +1,19 @@
|
||||
// Frame.this can be marked as optimized-out in some cases. Here we call an
|
||||
// arrow function but its enclosing function is no longer live, so it's
|
||||
// impossible to recover its missing 'this' binding.
|
||||
var g = newGlobal();
|
||||
g.eval("x = 4");
|
||||
g.eval("var foo = function() { return () => 1; }; var arrow = foo.call(3);");
|
||||
var dbg = new Debugger(g);
|
||||
var log = "";
|
||||
dbg.onEnterFrame = function (frame) {
|
||||
if (frame.type === "eval")
|
||||
return;
|
||||
assertEq(frame.type, "call");
|
||||
assertEq(frame.this.optimizedOut, true);
|
||||
frame.eval("try { print(this.x); } catch(e) { exc = e; }");
|
||||
assertEq(typeof g.exc, "object");
|
||||
log += "d";
|
||||
};
|
||||
g.eval("arrow();");
|
||||
assertEq(log, "d");
|
16
js/src/jit-test/tests/debug/Frame-this-08.js
Normal file
16
js/src/jit-test/tests/debug/Frame-this-08.js
Normal file
@ -0,0 +1,16 @@
|
||||
// Frame.this and evalInFrame in arrow function that uses 'this'.
|
||||
var g = newGlobal();
|
||||
g.eval("x = 4");
|
||||
g.eval("var foo = function() { 'use strict'; return () => this; }; var arrow = foo.call(3);");
|
||||
var dbg = new Debugger(g);
|
||||
var hits = 0;
|
||||
dbg.onEnterFrame = function (frame) {
|
||||
if (frame.type === "eval")
|
||||
return;
|
||||
hits++;
|
||||
assertEq(frame.type, "call");
|
||||
assertEq(frame.this, 3);
|
||||
assertEq(frame.eval("this + 1").return, 4);
|
||||
};
|
||||
g.eval("arrow();");
|
||||
assertEq(hits, 1);
|
@ -1280,17 +1280,24 @@ static const VMFunction ThrowUninitializedThisInfo =
|
||||
FunctionInfo<ThrowUninitializedThisFn>(BaselineThrowUninitializedThis);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emitCheckThis()
|
||||
BaselineCompiler::emit_JSOP_CHECKTHIS()
|
||||
{
|
||||
frame.assertSyncedStack();
|
||||
frame.syncStack(0);
|
||||
masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
|
||||
|
||||
return emitCheckThis(R0);
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emitCheckThis(ValueOperand val)
|
||||
{
|
||||
Label thisOK;
|
||||
masm.branchTestMagic(Assembler::NotEqual, frame.addressOfThis(), &thisOK);
|
||||
masm.branchTestMagic(Assembler::NotEqual, val, &thisOK);
|
||||
|
||||
prepareVMCall();
|
||||
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
pushArg(R0.scratchReg());
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, val.scratchReg());
|
||||
pushArg(val.scratchReg());
|
||||
|
||||
if (!callVM(ThrowUninitializedThisInfo))
|
||||
return false;
|
||||
@ -1299,56 +1306,101 @@ BaselineCompiler::emitCheckThis()
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*ThrowBadDerivedReturnFn)(JSContext*, HandleValue);
|
||||
static const VMFunction ThrowBadDerivedReturnInfo =
|
||||
FunctionInfo<ThrowBadDerivedReturnFn>(jit::ThrowBadDerivedReturn);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_THIS()
|
||||
BaselineCompiler::emit_JSOP_CHECKRETURN()
|
||||
{
|
||||
// |this| is undefined in modules.
|
||||
if (module()) {
|
||||
frame.push(UndefinedValue());
|
||||
return true;
|
||||
}
|
||||
MOZ_ASSERT(script->isDerivedClassConstructor());
|
||||
|
||||
if (function() && function()->isArrow()) {
|
||||
// Arrow functions store their (lexical) |this| value in an
|
||||
// extended slot.
|
||||
frame.syncStack(0);
|
||||
Register scratch = R0.scratchReg();
|
||||
masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(), scratch);
|
||||
masm.loadValue(Address(scratch, FunctionExtended::offsetOfArrowThisSlot()), R0);
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
// Load |this| in R0, return value in R1.
|
||||
frame.popRegsAndSync(1);
|
||||
emitLoadReturnValue(R1);
|
||||
|
||||
if (script->isDerivedClassConstructor()) {
|
||||
frame.syncStack(0);
|
||||
if (!emitCheckThis())
|
||||
return false;
|
||||
}
|
||||
Label done, returnOK;
|
||||
masm.branchTestObject(Assembler::Equal, R1, &done);
|
||||
masm.branchTestUndefined(Assembler::Equal, R1, &returnOK);
|
||||
|
||||
prepareVMCall();
|
||||
pushArg(R1);
|
||||
if (!callVM(ThrowBadDerivedReturnInfo))
|
||||
return false;
|
||||
masm.assumeUnreachable("Should throw on bad derived constructor return");
|
||||
|
||||
masm.bind(&returnOK);
|
||||
|
||||
if (!emitCheckThis(R0))
|
||||
return false;
|
||||
|
||||
// Store |this| in the return value slot.
|
||||
masm.storeValue(R0, frame.addressOfReturnValue());
|
||||
masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags());
|
||||
|
||||
masm.bind(&done);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*GetFunctionThisFn)(JSContext*, BaselineFrame*, MutableHandleValue);
|
||||
static const VMFunction GetFunctionThisInfo =
|
||||
FunctionInfo<GetFunctionThisFn>(jit::BaselineGetFunctionThis);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_FUNCTIONTHIS()
|
||||
{
|
||||
MOZ_ASSERT(function());
|
||||
MOZ_ASSERT(!function()->isArrow());
|
||||
|
||||
// Keep this value in R0
|
||||
frame.pushThis();
|
||||
|
||||
// In strict mode code or self-hosted functions, |this| is left alone.
|
||||
if (script->strict() || (function() && function()->isSelfHostedBuiltin()))
|
||||
return true;
|
||||
|
||||
Label skipIC;
|
||||
// Keep |thisv| in R0
|
||||
// Load |thisv| in R0. Skip the call if it's already an object.
|
||||
Label skipCall;
|
||||
frame.popRegsAndSync(1);
|
||||
// If |this| is already an object, skip the IC.
|
||||
masm.branchTestObject(Assembler::Equal, R0, &skipIC);
|
||||
masm.branchTestObject(Assembler::Equal, R0, &skipCall);
|
||||
|
||||
// Call IC
|
||||
ICThis_Fallback::Compiler stubCompiler(cx);
|
||||
if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
|
||||
prepareVMCall();
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
|
||||
|
||||
pushArg(R1.scratchReg());
|
||||
|
||||
if (!callVM(GetFunctionThisInfo))
|
||||
return false;
|
||||
|
||||
masm.storeValue(R0, frame.addressOfThis());
|
||||
|
||||
// R0 is new pushed |this| value.
|
||||
masm.bind(&skipIC);
|
||||
masm.bind(&skipCall);
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*GetNonSyntacticGlobalThisFn)(JSContext*, HandleObject, MutableHandleValue);
|
||||
static const VMFunction GetNonSyntacticGlobalThisInfo =
|
||||
FunctionInfo<GetNonSyntacticGlobalThisFn>(js::GetNonSyntacticGlobalThis);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_GLOBALTHIS()
|
||||
{
|
||||
frame.syncStack(0);
|
||||
|
||||
if (!script->hasNonSyntacticScope()) {
|
||||
ClonedBlockObject* globalLexical = &script->global().lexicalScope();
|
||||
masm.moveValue(globalLexical->thisValue(), R0);
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
prepareVMCall();
|
||||
|
||||
masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg());
|
||||
pushArg(R0.scratchReg());
|
||||
|
||||
if (!callVM(GetNonSyntacticGlobalThisInfo))
|
||||
return false;
|
||||
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1522,22 +1574,20 @@ BaselineCompiler::emit_JSOP_LAMBDA()
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef JSObject* (*LambdaArrowFn)(JSContext*, HandleFunction, HandleObject,
|
||||
HandleValue, HandleValue);
|
||||
typedef JSObject* (*LambdaArrowFn)(JSContext*, HandleFunction, HandleObject, HandleValue);
|
||||
static const VMFunction LambdaArrowInfo = FunctionInfo<LambdaArrowFn>(js::LambdaArrow);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_LAMBDA_ARROW()
|
||||
{
|
||||
// Keep pushed |this| in R0, and newTarget in R1.
|
||||
frame.popRegsAndSync(2);
|
||||
// Keep pushed newTarget in R0.
|
||||
frame.popRegsAndSync(1);
|
||||
|
||||
RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc)));
|
||||
|
||||
prepareVMCall();
|
||||
masm.loadPtr(frame.addressOfScopeChain(), R2.scratchReg());
|
||||
|
||||
pushArg(R1);
|
||||
pushArg(R0);
|
||||
pushArg(R2.scratchReg());
|
||||
pushArg(ImmGCPtr(fun));
|
||||
@ -3430,10 +3480,6 @@ BaselineCompiler::emit_JSOP_DEBUGGER()
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*ThrowBadDerivedReturnFn)(JSContext*, HandleValue);
|
||||
static const VMFunction ThrowBadDerivedReturnInfo =
|
||||
FunctionInfo<ThrowBadDerivedReturnFn>(jit::ThrowBadDerivedReturn);
|
||||
|
||||
typedef bool (*DebugEpilogueFn)(JSContext*, BaselineFrame*, jsbytecode*);
|
||||
static const VMFunction DebugEpilogueInfo =
|
||||
FunctionInfo<DebugEpilogueFn>(jit::DebugEpilogueOnBaselineReturn);
|
||||
@ -3441,29 +3487,6 @@ static const VMFunction DebugEpilogueInfo =
|
||||
bool
|
||||
BaselineCompiler::emitReturn()
|
||||
{
|
||||
if (script->isDerivedClassConstructor()) {
|
||||
frame.syncStack(0);
|
||||
|
||||
Label derivedDone, returnOK;
|
||||
masm.branchTestObject(Assembler::Equal, JSReturnOperand, &derivedDone);
|
||||
masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, &returnOK);
|
||||
|
||||
// This is going to smash JSReturnOperand, but we don't care, because it's
|
||||
// also going to throw unconditionally.
|
||||
prepareVMCall();
|
||||
pushArg(JSReturnOperand);
|
||||
if (!callVM(ThrowBadDerivedReturnInfo))
|
||||
return false;
|
||||
masm.assumeUnreachable("Should throw on bad derived constructor return");
|
||||
|
||||
masm.bind(&returnOK);
|
||||
|
||||
if (!emitCheckThis())
|
||||
return false;
|
||||
|
||||
masm.bind(&derivedDone);
|
||||
}
|
||||
|
||||
if (compileDebugInstrumentation_) {
|
||||
// Move return value into the frame's rval slot.
|
||||
masm.storeValue(JSReturnOperand, frame.addressOfReturnValue());
|
||||
@ -3503,6 +3526,21 @@ BaselineCompiler::emit_JSOP_RETURN()
|
||||
return emitReturn();
|
||||
}
|
||||
|
||||
void
|
||||
BaselineCompiler::emitLoadReturnValue(ValueOperand val)
|
||||
{
|
||||
Label done, noRval;
|
||||
masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
|
||||
Imm32(BaselineFrame::HAS_RVAL), &noRval);
|
||||
masm.loadValue(frame.addressOfReturnValue(), val);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&noRval);
|
||||
masm.moveValue(UndefinedValue(), val);
|
||||
|
||||
masm.bind(&done);
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_RETRVAL()
|
||||
{
|
||||
@ -3653,21 +3691,9 @@ BaselineCompiler::emit_JSOP_GETRVAL()
|
||||
{
|
||||
frame.syncStack(0);
|
||||
|
||||
Label norval, done;
|
||||
Address flags = frame.addressOfFlags();
|
||||
masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), &norval);
|
||||
// Get the value from the return value slot, if any.
|
||||
masm.loadValue(frame.addressOfReturnValue(), R0);
|
||||
masm.jump(&done);
|
||||
emitLoadReturnValue(R0);
|
||||
|
||||
// Push undefined otherwise. When we throw an exception in the try
|
||||
// block, rval is not yet initialized when entering finally block.
|
||||
masm.bind(&norval);
|
||||
masm.moveValue(UndefinedValue(), R0);
|
||||
|
||||
masm.bind(&done);
|
||||
frame.push(R0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,6 @@ namespace jit {
|
||||
_(JSOP_UNDEFINED) \
|
||||
_(JSOP_HOLE) \
|
||||
_(JSOP_NULL) \
|
||||
_(JSOP_THIS) \
|
||||
_(JSOP_TRUE) \
|
||||
_(JSOP_FALSE) \
|
||||
_(JSOP_ZERO) \
|
||||
@ -205,6 +204,10 @@ namespace jit {
|
||||
_(JSOP_SETRVAL) \
|
||||
_(JSOP_RETRVAL) \
|
||||
_(JSOP_RETURN) \
|
||||
_(JSOP_FUNCTIONTHIS) \
|
||||
_(JSOP_GLOBALTHIS) \
|
||||
_(JSOP_CHECKTHIS) \
|
||||
_(JSOP_CHECKRETURN) \
|
||||
_(JSOP_NEWTARGET) \
|
||||
_(JSOP_SUPERCALL) \
|
||||
_(JSOP_SPREADSUPERCALL) \
|
||||
@ -260,6 +263,9 @@ class BaselineCompiler : public BaselineCompilerSpecific
|
||||
private:
|
||||
MethodStatus emitBody();
|
||||
|
||||
bool emitCheckThis(ValueOperand val);
|
||||
void emitLoadReturnValue(ValueOperand val);
|
||||
|
||||
void emitInitializeLocals(size_t n, const Value& v);
|
||||
bool emitPrologue();
|
||||
bool emitEpilogue();
|
||||
@ -319,7 +325,6 @@ class BaselineCompiler : public BaselineCompilerSpecific
|
||||
|
||||
bool emitThrowConstAssignment();
|
||||
bool emitUninitializedLexicalCheck(const ValueOperand& val);
|
||||
bool emitCheckThis();
|
||||
|
||||
bool addPCMappingEntry(bool addIndexEntry);
|
||||
|
||||
|
@ -512,35 +512,6 @@ typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, M
|
||||
static const VMFunction DoCallNativeGetterInfo =
|
||||
FunctionInfo<DoCallNativeGetterFn>(DoCallNativeGetter);
|
||||
|
||||
//
|
||||
// This_Fallback
|
||||
//
|
||||
|
||||
static bool
|
||||
DoThisFallback(JSContext* cx, ICThis_Fallback* stub, HandleValue thisv, MutableHandleValue ret)
|
||||
{
|
||||
FallbackICSpew(cx, stub, "This");
|
||||
return BoxNonStrictThis(cx, thisv, ret);
|
||||
}
|
||||
|
||||
typedef bool (*DoThisFallbackFn)(JSContext*, ICThis_Fallback*, HandleValue, MutableHandleValue);
|
||||
static const VMFunction DoThisFallbackInfo = FunctionInfo<DoThisFallbackFn>(DoThisFallback, TailCall);
|
||||
|
||||
bool
|
||||
ICThis_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
{
|
||||
MOZ_ASSERT(engine_ == Engine::Baseline);
|
||||
MOZ_ASSERT(R0 == JSReturnOperand);
|
||||
|
||||
// Restore the tail call register.
|
||||
EmitRestoreTailCallReg(masm);
|
||||
|
||||
masm.pushValue(R0);
|
||||
masm.push(ICStubReg);
|
||||
|
||||
return tailCallVM(DoThisFallbackInfo, masm);
|
||||
}
|
||||
|
||||
//
|
||||
// NewArray_Fallback
|
||||
//
|
||||
|
@ -190,32 +190,6 @@ class ICTypeUpdate_ObjectGroup : public ICStub
|
||||
};
|
||||
};
|
||||
|
||||
// This
|
||||
// JSOP_THIS
|
||||
|
||||
class ICThis_Fallback : public ICFallbackStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
explicit ICThis_Fallback(JitCode* stubCode)
|
||||
: ICFallbackStub(ICStub::This_Fallback, stubCode) {}
|
||||
|
||||
public:
|
||||
// Compiler for this stub kind.
|
||||
class Compiler : public ICStubCompiler {
|
||||
protected:
|
||||
bool generateStubCode(MacroAssembler& masm);
|
||||
|
||||
public:
|
||||
explicit Compiler(JSContext* cx)
|
||||
: ICStubCompiler(cx, ICStub::This_Fallback, Engine::Baseline) {}
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
return newStub<ICThis_Fallback>(space, getStubCode());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class ICNewArray_Fallback : public ICFallbackStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
@ -24,8 +24,6 @@ namespace jit {
|
||||
_(TypeUpdate_ObjectGroup) \
|
||||
_(TypeUpdate_PrimitiveSet) \
|
||||
\
|
||||
_(This_Fallback) \
|
||||
\
|
||||
_(NewArray_Fallback) \
|
||||
_(NewObject_Fallback) \
|
||||
_(NewObject_WithTemplate) \
|
||||
|
@ -1825,14 +1825,13 @@ class OutOfLineLambdaArrow : public OutOfLineCodeBase<CodeGenerator>
|
||||
}
|
||||
};
|
||||
|
||||
typedef JSObject* (*LambdaArrowFn)(JSContext*, HandleFunction, HandleObject, HandleValue, HandleValue);
|
||||
typedef JSObject* (*LambdaArrowFn)(JSContext*, HandleFunction, HandleObject, HandleValue);
|
||||
static const VMFunction LambdaArrowInfo = FunctionInfo<LambdaArrowFn>(js::LambdaArrow);
|
||||
|
||||
void
|
||||
CodeGenerator::visitOutOfLineLambdaArrow(OutOfLineLambdaArrow* ool)
|
||||
{
|
||||
Register scopeChain = ToRegister(ool->lir->scopeChain());
|
||||
ValueOperand thisv = ToValue(ool->lir, LLambdaArrow::ThisValue);
|
||||
ValueOperand newTarget = ToValue(ool->lir, LLambdaArrow::NewTargetValue);
|
||||
Register output = ToRegister(ool->lir->output());
|
||||
const LambdaFunctionInfo& info = ool->lir->mir()->info();
|
||||
@ -1846,7 +1845,6 @@ CodeGenerator::visitOutOfLineLambdaArrow(OutOfLineLambdaArrow* ool)
|
||||
saveLive(ool->lir);
|
||||
|
||||
pushArg(newTarget);
|
||||
pushArg(thisv);
|
||||
pushArg(scopeChain);
|
||||
pushArg(ImmGCPtr(info.fun));
|
||||
|
||||
@ -1862,7 +1860,6 @@ void
|
||||
CodeGenerator::visitLambdaArrow(LLambdaArrow* lir)
|
||||
{
|
||||
Register scopeChain = ToRegister(lir->scopeChain());
|
||||
ValueOperand thisv = ToValue(lir, LLambdaArrow::ThisValue);
|
||||
ValueOperand newTarget = ToValue(lir, LLambdaArrow::NewTargetValue);
|
||||
Register output = ToRegister(lir->output());
|
||||
const LambdaFunctionInfo& info = lir->mir()->info();
|
||||
@ -1895,11 +1892,10 @@ CodeGenerator::visitLambdaArrow(LLambdaArrow* lir)
|
||||
// Initialize extended slots. Lexical |this| is stored in the first one.
|
||||
MOZ_ASSERT(info.flags & JSFunction::EXTENDED);
|
||||
static_assert(FunctionExtended::NUM_EXTENDED_SLOTS == 2, "All slots must be initialized");
|
||||
static_assert(FunctionExtended::ARROW_THIS_SLOT == 0, "|this| must be stored in first slot");
|
||||
static_assert(FunctionExtended::ARROW_NEWTARGET_SLOT == 1,
|
||||
"|new.target| must be stored in second slot");
|
||||
masm.storeValue(thisv, Address(output, FunctionExtended::offsetOfExtendedSlot(0)));
|
||||
masm.storeValue(newTarget, Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
|
||||
static_assert(FunctionExtended::ARROW_NEWTARGET_SLOT == 0,
|
||||
"|new.target| must be stored in first slot");
|
||||
masm.storeValue(newTarget, Address(output, FunctionExtended::offsetOfExtendedSlot(0)));
|
||||
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
@ -3590,7 +3586,7 @@ CodeGenerator::visitGetDynamicName(LGetDynamicName* lir)
|
||||
bailoutFrom(&undefined, lir->snapshot());
|
||||
}
|
||||
|
||||
typedef bool (*DirectEvalSFn)(JSContext*, HandleObject, HandleScript, HandleValue, HandleValue,
|
||||
typedef bool (*DirectEvalSFn)(JSContext*, HandleObject, HandleScript, HandleValue,
|
||||
HandleString, jsbytecode*, MutableHandleValue);
|
||||
static const VMFunction DirectEvalStringInfo = FunctionInfo<DirectEvalSFn>(DirectEvalStringFromIon);
|
||||
|
||||
@ -3603,7 +3599,6 @@ CodeGenerator::visitCallDirectEval(LCallDirectEval* lir)
|
||||
pushArg(ImmPtr(lir->mir()->pc()));
|
||||
pushArg(string);
|
||||
pushArg(ToValue(lir, LCallDirectEval::NewTarget));
|
||||
pushArg(ToValue(lir, LCallDirectEval::ThisValue));
|
||||
pushArg(ImmGCPtr(gen->info().script()));
|
||||
pushArg(scopeChain);
|
||||
|
||||
@ -5117,14 +5112,6 @@ CodeGenerator::visitComputeThis(LComputeThis* lir)
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitLoadArrowThis(LLoadArrowThis* lir)
|
||||
{
|
||||
Register callee = ToRegister(lir->callee());
|
||||
ValueOperand output = ToOutValue(lir);
|
||||
masm.loadValue(Address(callee, FunctionExtended::offsetOfArrowThisSlot()), output);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitArrowNewTarget(LArrowNewTarget* lir)
|
||||
{
|
||||
|
@ -191,7 +191,6 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
void visitSetArgumentsObjectArg(LSetArgumentsObjectArg* lir);
|
||||
void visitReturnFromCtor(LReturnFromCtor* lir);
|
||||
void visitComputeThis(LComputeThis* lir);
|
||||
void visitLoadArrowThis(LLoadArrowThis* lir);
|
||||
void visitArrayLength(LArrayLength* lir);
|
||||
void visitSetArrayLength(LSetArrayLength* lir);
|
||||
void visitTypedArrayLength(LTypedArrayLength* lir);
|
||||
|
@ -1989,8 +1989,8 @@ IonBuilder::inspectOpcode(JSOp op)
|
||||
case JSOP_NOT:
|
||||
return jsop_not();
|
||||
|
||||
case JSOP_THIS:
|
||||
return jsop_this();
|
||||
case JSOP_FUNCTIONTHIS:
|
||||
return jsop_functionthis();
|
||||
|
||||
case JSOP_CALLEE: {
|
||||
MDefinition* callee = getCallee();
|
||||
@ -4492,15 +4492,6 @@ IonBuilder::processReturn(JSOp op)
|
||||
MOZ_CRASH("unknown return op");
|
||||
}
|
||||
|
||||
if (script()->isDerivedClassConstructor() &&
|
||||
def->type() != MIRType_Object)
|
||||
{
|
||||
MOZ_ASSERT(info().funMaybeLazy() && info().funMaybeLazy()->isClassConstructor());
|
||||
MCheckReturn* checkRet = MCheckReturn::New(alloc(), def, current->getSlot(info().thisSlot()));
|
||||
current->add(checkRet);
|
||||
def = checkRet;
|
||||
}
|
||||
|
||||
MReturn* ret = MReturn::New(alloc(), def);
|
||||
current->end(ret);
|
||||
|
||||
@ -6729,14 +6720,6 @@ IonBuilder::jsop_eval(uint32_t argc)
|
||||
if (info().funMaybeLazy()->isArrow())
|
||||
return abort("Direct eval from arrow function");
|
||||
|
||||
// The 'this' value for the outer and eval scripts must be the
|
||||
// same. This is not guaranteed if a primitive string/number/etc.
|
||||
// is passed through to the eval invoke as the primitive may be
|
||||
// boxed into different objects if accessed via 'this'.
|
||||
MIRType type = thisTypes ? thisTypes->getKnownMIRType() : MIRType_Value;
|
||||
if (type != MIRType_Object && type != MIRType_Null && type != MIRType_Undefined)
|
||||
return abort("Direct eval from script with maybe-primitive 'this'");
|
||||
|
||||
CallInfo callInfo(alloc(), /* constructing = */ false);
|
||||
if (!callInfo.init(current, argc))
|
||||
return false;
|
||||
@ -6755,9 +6738,6 @@ IonBuilder::jsop_eval(uint32_t argc)
|
||||
return pushTypeBarrier(string, types, BarrierKind::TypeSet);
|
||||
}
|
||||
|
||||
current->pushSlot(info().thisSlot());
|
||||
MDefinition* thisValue = current->pop();
|
||||
|
||||
if (!jsop_newtarget())
|
||||
return false;
|
||||
MDefinition* newTargetValue = current->pop();
|
||||
@ -6777,7 +6757,7 @@ IonBuilder::jsop_eval(uint32_t argc)
|
||||
current->add(dynamicName);
|
||||
|
||||
current->push(dynamicName);
|
||||
current->push(thisValue);
|
||||
current->push(constant(UndefinedValue())); // thisv
|
||||
|
||||
CallInfo evalCallInfo(alloc(), /* constructing = */ false);
|
||||
if (!evalCallInfo.init(current, /* argc = */ 0))
|
||||
@ -6788,7 +6768,7 @@ IonBuilder::jsop_eval(uint32_t argc)
|
||||
}
|
||||
|
||||
MInstruction* ins = MCallDirectEval::New(alloc(), scopeChain, string,
|
||||
thisValue, newTargetValue, pc);
|
||||
newTargetValue, pc);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
@ -12662,10 +12642,8 @@ IonBuilder::jsop_lambda_arrow(JSFunction* fun)
|
||||
MOZ_ASSERT(!fun->isNative());
|
||||
|
||||
MDefinition* newTargetDef = current->pop();
|
||||
MDefinition* thisDef = current->pop();
|
||||
|
||||
MLambdaArrow* ins = MLambdaArrow::New(alloc(), constraints(), current->scopeChain(),
|
||||
thisDef, newTargetDef, fun);
|
||||
newTargetDef, fun);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
@ -12849,42 +12827,17 @@ IonBuilder::jsop_checkaliasedlet(ScopeCoordinate sc)
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_this()
|
||||
IonBuilder::jsop_functionthis()
|
||||
{
|
||||
if (info().module()) {
|
||||
pushConstant(UndefinedValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!info().funMaybeLazy())
|
||||
return abort("JSOP_THIS outside of a JSFunction.");
|
||||
|
||||
if (info().funMaybeLazy()->isArrow()) {
|
||||
// Arrow functions store their lexical |this| in an extended slot.
|
||||
MLoadArrowThis* thisObj = MLoadArrowThis::New(alloc(), getCallee());
|
||||
current->add(thisObj);
|
||||
current->push(thisObj);
|
||||
return true;
|
||||
}
|
||||
MOZ_ASSERT(info().funMaybeLazy());
|
||||
MOZ_ASSERT(!info().funMaybeLazy()->isArrow());
|
||||
|
||||
if (script()->strict() || info().funMaybeLazy()->isSelfHostedBuiltin()) {
|
||||
// No need to wrap primitive |this| in strict mode or self-hosted code.
|
||||
MDefinition* thisVal = current->getSlot(info().thisSlot());
|
||||
if (script()->isDerivedClassConstructor()) {
|
||||
MOZ_ASSERT(info().funMaybeLazy()->isClassConstructor());
|
||||
MOZ_ASSERT(script()->strict());
|
||||
|
||||
MLexicalCheck* checkThis = MLexicalCheck::New(alloc(), thisVal, Bailout_UninitializedThis);
|
||||
current->add(checkThis);
|
||||
thisVal = checkThis;
|
||||
}
|
||||
|
||||
current->push(thisVal);
|
||||
current->pushSlot(info().thisSlot());
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!info().funMaybeLazy()->isClassConstructor());
|
||||
|
||||
if (thisTypes && (thisTypes->getKnownMIRType() == MIRType_Object ||
|
||||
(thisTypes->empty() && baselineFrame_ && baselineFrame_->thisType.isSomeObject())))
|
||||
{
|
||||
@ -12907,7 +12860,6 @@ IonBuilder::jsop_this()
|
||||
MDefinition* def = current->getSlot(info().thisSlot());
|
||||
|
||||
if (def->type() == MIRType_Object) {
|
||||
// If we already computed a |this| object, we can reuse it.
|
||||
current->push(def);
|
||||
return true;
|
||||
}
|
||||
@ -12916,8 +12868,6 @@ IonBuilder::jsop_this()
|
||||
current->add(thisObj);
|
||||
current->push(thisObj);
|
||||
|
||||
current->setSlot(info().thisSlot(), thisObj);
|
||||
|
||||
return resumeAfter(thisObj);
|
||||
}
|
||||
|
||||
|
@ -718,7 +718,7 @@ class IonBuilder
|
||||
bool jsop_object(JSObject* obj);
|
||||
bool jsop_lambda(JSFunction* fun);
|
||||
bool jsop_lambda_arrow(JSFunction* fun);
|
||||
bool jsop_this();
|
||||
bool jsop_functionthis();
|
||||
bool jsop_typeof();
|
||||
bool jsop_toid();
|
||||
bool jsop_iter(uint8_t flags);
|
||||
|
@ -395,16 +395,6 @@ LIRGenerator::visitComputeThis(MComputeThis* ins)
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitLoadArrowThis(MLoadArrowThis* ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->type() == MIRType_Value);
|
||||
MOZ_ASSERT(ins->callee()->type() == MIRType_Object);
|
||||
|
||||
LLoadArrowThis* lir = new(alloc()) LLoadArrowThis(useRegister(ins->callee()));
|
||||
defineBox(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitArrowNewTarget(MArrowNewTarget* ins)
|
||||
{
|
||||
@ -614,12 +604,10 @@ LIRGenerator::visitCallDirectEval(MCallDirectEval* ins)
|
||||
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);
|
||||
@ -2193,11 +2181,9 @@ void
|
||||
LIRGenerator::visitLambdaArrow(MLambdaArrow* ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->scopeChain()->type() == MIRType_Object);
|
||||
MOZ_ASSERT(ins->thisDef()->type() == MIRType_Value);
|
||||
MOZ_ASSERT(ins->newTargetDef()->type() == MIRType_Value);
|
||||
|
||||
LLambdaArrow* lir = new(alloc()) LLambdaArrow(useRegister(ins->scopeChain()));
|
||||
useBox(lir, LLambdaArrow::ThisValue, ins->thisDef());
|
||||
useBox(lir, LLambdaArrow::NewTargetValue, ins->newTargetDef());
|
||||
define(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
|
@ -100,7 +100,6 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
void visitSetArgumentsObjectArg(MSetArgumentsObjectArg* ins);
|
||||
void visitReturnFromCtor(MReturnFromCtor* ins);
|
||||
void visitComputeThis(MComputeThis* ins);
|
||||
void visitLoadArrowThis(MLoadArrowThis* ins);
|
||||
void visitCall(MCall* call);
|
||||
void visitApplyArgs(MApplyArgs* apply);
|
||||
void visitArraySplice(MArraySplice* splice);
|
||||
|
@ -4066,21 +4066,19 @@ class MGetDynamicName
|
||||
};
|
||||
|
||||
class MCallDirectEval
|
||||
: public MAryInstruction<4>,
|
||||
public Mix4Policy<ObjectPolicy<0>,
|
||||
: public MAryInstruction<3>,
|
||||
public Mix3Policy<ObjectPolicy<0>,
|
||||
StringPolicy<1>,
|
||||
BoxPolicy<2>,
|
||||
BoxPolicy<3> >::Data
|
||||
BoxPolicy<2> >::Data
|
||||
{
|
||||
protected:
|
||||
MCallDirectEval(MDefinition* scopeChain, MDefinition* string, MDefinition* thisValue,
|
||||
MCallDirectEval(MDefinition* scopeChain, MDefinition* string,
|
||||
MDefinition* newTargetValue, jsbytecode* pc)
|
||||
: pc_(pc)
|
||||
{
|
||||
initOperand(0, scopeChain);
|
||||
initOperand(1, string);
|
||||
initOperand(2, thisValue);
|
||||
initOperand(3, newTargetValue);
|
||||
initOperand(2, newTargetValue);
|
||||
setResultType(MIRType_Value);
|
||||
}
|
||||
|
||||
@ -4088,10 +4086,10 @@ class MCallDirectEval
|
||||
INSTRUCTION_HEADER(CallDirectEval)
|
||||
|
||||
static MCallDirectEval*
|
||||
New(TempAllocator& alloc, MDefinition* scopeChain, MDefinition* string, MDefinition* thisValue,
|
||||
New(TempAllocator& alloc, MDefinition* scopeChain, MDefinition* string,
|
||||
MDefinition* newTargetValue, jsbytecode* pc)
|
||||
{
|
||||
return new(alloc) MCallDirectEval(scopeChain, string, thisValue, newTargetValue, pc);
|
||||
return new(alloc) MCallDirectEval(scopeChain, string, newTargetValue, pc);
|
||||
}
|
||||
|
||||
MDefinition* getScopeChain() const {
|
||||
@ -4100,11 +4098,8 @@ class MCallDirectEval
|
||||
MDefinition* getString() const {
|
||||
return getOperand(1);
|
||||
}
|
||||
MDefinition* getThisValue() const {
|
||||
return getOperand(2);
|
||||
}
|
||||
MDefinition* getNewTargetValue() const {
|
||||
return getOperand(3);
|
||||
return getOperand(2);
|
||||
}
|
||||
|
||||
jsbytecode* pc() const {
|
||||
@ -6746,36 +6741,6 @@ class MComputeThis
|
||||
// Note: don't override getAliasSet: the thisValue hook can be effectful.
|
||||
};
|
||||
|
||||
// Load an arrow function's |this| value.
|
||||
class MLoadArrowThis
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
explicit MLoadArrowThis(MDefinition* callee)
|
||||
: MUnaryInstruction(callee)
|
||||
{
|
||||
setResultType(MIRType_Value);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(LoadArrowThis)
|
||||
|
||||
static MLoadArrowThis* New(TempAllocator& alloc, MDefinition* callee) {
|
||||
return new(alloc) MLoadArrowThis(callee);
|
||||
}
|
||||
MDefinition* callee() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
// An arrow function's lexical |this| value is immutable.
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
// Load an arrow function's |new.target| value.
|
||||
class MArrowNewTarget
|
||||
: public MUnaryInstruction,
|
||||
@ -7730,14 +7695,14 @@ class MLambda
|
||||
};
|
||||
|
||||
class MLambdaArrow
|
||||
: public MTernaryInstruction,
|
||||
public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >::Data
|
||||
: public MBinaryInstruction,
|
||||
public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>>::Data
|
||||
{
|
||||
const LambdaFunctionInfo info_;
|
||||
|
||||
MLambdaArrow(CompilerConstraintList* constraints, MDefinition* scopeChain,
|
||||
MDefinition* this_, MDefinition* newTarget_, JSFunction* fun)
|
||||
: MTernaryInstruction(scopeChain, this_, newTarget_), info_(fun)
|
||||
MDefinition* newTarget_, JSFunction* fun)
|
||||
: MBinaryInstruction(scopeChain, newTarget_), info_(fun)
|
||||
{
|
||||
setResultType(MIRType_Object);
|
||||
MOZ_ASSERT(!ObjectGroup::useSingletonForClone(fun));
|
||||
@ -7749,19 +7714,15 @@ class MLambdaArrow
|
||||
INSTRUCTION_HEADER(LambdaArrow)
|
||||
|
||||
static MLambdaArrow* New(TempAllocator& alloc, CompilerConstraintList* constraints,
|
||||
MDefinition* scopeChain, MDefinition* this_, MDefinition* newTarget_,
|
||||
JSFunction* fun)
|
||||
MDefinition* scopeChain, MDefinition* newTarget_, JSFunction* fun)
|
||||
{
|
||||
return new(alloc) MLambdaArrow(constraints, scopeChain, this_, newTarget_, fun);
|
||||
return new(alloc) MLambdaArrow(constraints, scopeChain, newTarget_, fun);
|
||||
}
|
||||
MDefinition* scopeChain() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
MDefinition* thisDef() const {
|
||||
return getOperand(1);
|
||||
}
|
||||
MDefinition* newTargetDef() const {
|
||||
return getOperand(2);
|
||||
return getOperand(1);
|
||||
}
|
||||
const LambdaFunctionInfo& info() const {
|
||||
return info_;
|
||||
|
@ -62,7 +62,6 @@ namespace jit {
|
||||
_(GetArgumentsObjectArg) \
|
||||
_(SetArgumentsObjectArg) \
|
||||
_(ComputeThis) \
|
||||
_(LoadArrowThis) \
|
||||
_(Call) \
|
||||
_(ApplyArgs) \
|
||||
_(ArraySplice) \
|
||||
|
@ -4621,24 +4621,32 @@ static bool
|
||||
DoTypeMonitorFallback(JSContext* cx, BaselineFrame* frame, ICTypeMonitor_Fallback* stub,
|
||||
HandleValue value, MutableHandleValue res)
|
||||
{
|
||||
// It's possible that we arrived here from bailing out of Ion, and that
|
||||
// Ion proved that the value is dead and optimized out. In such cases, do
|
||||
// nothing. However, it's also possible that we have an uninitialized this,
|
||||
// in which case we should not look for other magic values.
|
||||
if (stub->monitorsThis()) {
|
||||
MOZ_ASSERT_IF(value.isMagic(), value.isMagic(JS_UNINITIALIZED_LEXICAL));
|
||||
} else {
|
||||
if (value.isMagic(JS_OPTIMIZED_OUT)) {
|
||||
res.set(value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ICStubCompiler::Engine engine = SharedStubEngine(frame);
|
||||
RootedScript script(cx, SharedStubScript(frame, stub));
|
||||
jsbytecode* pc = stub->icEntry()->pc(script);
|
||||
TypeFallbackICSpew(cx, stub, "TypeMonitor");
|
||||
|
||||
if (value.isMagic()) {
|
||||
// It's possible that we arrived here from bailing out of Ion, and that
|
||||
// Ion proved that the value is dead and optimized out. In such cases,
|
||||
// do nothing. However, it's also possible that we have an uninitialized
|
||||
// this, in which case we should not look for other magic values.
|
||||
|
||||
if (value.whyMagic() == JS_OPTIMIZED_OUT) {
|
||||
MOZ_ASSERT(!stub->monitorsThis());
|
||||
res.set(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// In derived class constructors (including nested arrows/eval), the
|
||||
// |this| argument or GETALIASEDVAR can return the magic TDZ value.
|
||||
MOZ_ASSERT(value.isMagic(JS_UNINITIALIZED_LEXICAL));
|
||||
MOZ_ASSERT(frame->isFunctionFrame());
|
||||
MOZ_ASSERT(stub->monitorsThis() ||
|
||||
*GetNextPc(pc) == JSOP_CHECKTHIS ||
|
||||
*GetNextPc(pc) == JSOP_CHECKRETURN);
|
||||
}
|
||||
|
||||
uint32_t argument;
|
||||
if (stub->monitorsThis()) {
|
||||
MOZ_ASSERT(pc == script->code());
|
||||
@ -4648,9 +4656,13 @@ DoTypeMonitorFallback(JSContext* cx, BaselineFrame* frame, ICTypeMonitor_Fallbac
|
||||
TypeScript::SetThis(cx, script, value);
|
||||
} else if (stub->monitorsArgument(&argument)) {
|
||||
MOZ_ASSERT(pc == script->code());
|
||||
MOZ_ASSERT(!value.isMagic(JS_UNINITIALIZED_LEXICAL));
|
||||
TypeScript::SetArgument(cx, script, argument, value);
|
||||
} else {
|
||||
TypeScript::Monitor(cx, script, pc, value);
|
||||
if (value.isMagic(JS_UNINITIALIZED_LEXICAL))
|
||||
TypeScript::Monitor(cx, script, pc, TypeSet::UnknownType());
|
||||
else
|
||||
TypeScript::Monitor(cx, script, pc, value);
|
||||
}
|
||||
|
||||
if (!stub->addMonitorStubForValue(cx, script, value, engine))
|
||||
|
@ -1217,7 +1217,6 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins)
|
||||
_(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<ObjectPolicy<0>, IntPolicy<1>, TruncateToInt32Policy<2>, TruncateToInt32Policy<3> >) \
|
||||
_(Mix3Policy<ObjectPolicy<0>, CacheIdPolicy<1>, NoFloatPolicy<2>>) \
|
||||
|
@ -1309,5 +1309,11 @@ BaselineThrowUninitializedThis(JSContext* cx, BaselineFrame* frame)
|
||||
return ThrowUninitializedThis(cx, frame);
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue res)
|
||||
{
|
||||
return GetFunctionThis(cx, frame, res);
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
@ -737,6 +737,8 @@ bool ThrowRuntimeLexicalError(JSContext* cx, unsigned errorNumber);
|
||||
bool BaselineThrowUninitializedThis(JSContext* cx, BaselineFrame* frame);
|
||||
bool ThrowBadDerivedReturn(JSContext* cx, HandleValue v);
|
||||
|
||||
bool BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue res);
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
@ -1475,20 +1475,6 @@ class LComputeThis : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
|
||||
}
|
||||
};
|
||||
|
||||
class LLoadArrowThis : public LInstructionHelper<BOX_PIECES, 1, 0>
|
||||
{
|
||||
public:
|
||||
explicit LLoadArrowThis(const LAllocation& callee) {
|
||||
setOperand(0, callee);
|
||||
}
|
||||
|
||||
LIR_HEADER(LoadArrowThis)
|
||||
|
||||
const LAllocation* callee() {
|
||||
return getOperand(0);
|
||||
}
|
||||
};
|
||||
|
||||
// Writes a typed argument for a function call to the frame's argument vector.
|
||||
class LStackArgT : public LInstructionHelper<0, 1, 0>
|
||||
{
|
||||
@ -4033,13 +4019,12 @@ class LLambda : public LInstructionHelper<1, 1, 1>
|
||||
}
|
||||
};
|
||||
|
||||
class LLambdaArrow : public LInstructionHelper<1, 1 + (2 * BOX_PIECES), 0>
|
||||
class LLambdaArrow : public LInstructionHelper<1, 1 + BOX_PIECES, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(LambdaArrow)
|
||||
|
||||
static const size_t ThisValue = 1;
|
||||
static const size_t NewTargetValue = ThisValue + BOX_PIECES;
|
||||
static const size_t NewTargetValue = 1;
|
||||
|
||||
explicit LLambdaArrow(const LAllocation& scopeChain) {
|
||||
setOperand(0, scopeChain);
|
||||
|
@ -89,7 +89,6 @@
|
||||
_(SetArgumentsObjectArg) \
|
||||
_(ReturnFromCtor) \
|
||||
_(ComputeThis) \
|
||||
_(LoadArrowThis) \
|
||||
_(BitNotI) \
|
||||
_(BitNotV) \
|
||||
_(BitOpI) \
|
||||
|
@ -720,8 +720,12 @@ FormatFrame(JSContext* cx, const ScriptFrameIter& iter, char* buf, int num,
|
||||
funname = fun->displayAtom();
|
||||
|
||||
RootedValue thisVal(cx);
|
||||
if (iter.hasUsableAbstractFramePtr() && iter.computeThis(cx)) {
|
||||
thisVal = iter.computedThisValue();
|
||||
if (iter.hasUsableAbstractFramePtr() &&
|
||||
iter.isNonEvalFunctionFrame() &&
|
||||
fun && !fun->isArrow() && !fun->isDerivedClassConstructor())
|
||||
{
|
||||
if (!GetFunctionThis(cx, iter.abstractFramePtr(), &thisVal))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// print the frame number and function name
|
||||
|
@ -193,7 +193,7 @@ class JSFunction : public js::NativeObject
|
||||
bool hasScript() const { return flags() & INTERPRETED; }
|
||||
bool isBeingParsed() const { return flags() & BEING_PARSED; }
|
||||
|
||||
// Arrow functions store their lexical |this| in the first extended slot.
|
||||
// Arrow functions store their lexical new.target in the first extended slot.
|
||||
bool isArrow() const { return kind() == Arrow; }
|
||||
// Every class-constructor is also a method.
|
||||
bool isMethod() const { return kind() == Method || kind() == ClassConstructor; }
|
||||
@ -225,6 +225,10 @@ class JSFunction : public js::NativeObject
|
||||
return isLambda() && displayAtom() && !hasGuessedAtom();
|
||||
}
|
||||
|
||||
bool hasLexicalThis() const {
|
||||
return isArrow() || nonLazyScript()->isGeneratorExp();
|
||||
}
|
||||
|
||||
bool isBuiltinFunctionConstructor();
|
||||
|
||||
/* Returns the strictness of this function, which must be interpreted. */
|
||||
@ -669,9 +673,8 @@ class FunctionExtended : public JSFunction
|
||||
public:
|
||||
static const unsigned NUM_EXTENDED_SLOTS = 2;
|
||||
|
||||
/* Arrow functions store their lexical |this| in the first extended slot. */
|
||||
static const unsigned ARROW_THIS_SLOT = 0;
|
||||
static const unsigned ARROW_NEWTARGET_SLOT = 1;
|
||||
/* Arrow functions store their lexical new.target in the first extended slot. */
|
||||
static const unsigned ARROW_NEWTARGET_SLOT = 0;
|
||||
|
||||
static const unsigned METHOD_HOMEOBJECT_SLOT = 0;
|
||||
|
||||
@ -679,9 +682,6 @@ class FunctionExtended : public JSFunction
|
||||
MOZ_ASSERT(which < NUM_EXTENDED_SLOTS);
|
||||
return offsetof(FunctionExtended, extendedSlots) + which * sizeof(HeapValue);
|
||||
}
|
||||
static inline size_t offsetOfArrowThisSlot() {
|
||||
return offsetOfExtendedSlot(ARROW_THIS_SLOT);
|
||||
}
|
||||
static inline size_t offsetOfArrowNewTargetSlot() {
|
||||
return offsetOfExtendedSlot(ARROW_NEWTARGET_SLOT);
|
||||
}
|
||||
|
@ -3107,6 +3107,9 @@ js::GetThisValue(JSObject* obj)
|
||||
if (obj->is<DynamicWithObject>())
|
||||
return ObjectValue(*obj->as<DynamicWithObject>().withThis());
|
||||
|
||||
if (obj->is<NonSyntacticVariablesObject>())
|
||||
return GetThisValue(obj->enclosingScope());
|
||||
|
||||
return ObjectValue(*obj);
|
||||
}
|
||||
|
||||
@ -3478,7 +3481,8 @@ js::DumpInterpreterFrame(JSContext* cx, InterpreterFrame* start)
|
||||
fprintf(stderr, " current op: %s\n", js_CodeName[*pc]);
|
||||
MaybeDumpObject("staticScope", i.script()->getStaticBlockScope(pc));
|
||||
}
|
||||
MaybeDumpValue("this", i.thisv(cx));
|
||||
if (i.isNonEvalFunctionFrame())
|
||||
MaybeDumpValue("this", i.originalFunctionThis(cx));
|
||||
if (!i.isJit()) {
|
||||
fprintf(stderr, " rval: ");
|
||||
dumpValue(i.interpFrame()->returnValue());
|
||||
|
@ -1186,7 +1186,7 @@ ExpressionDecompiler::decompilePC(jsbytecode* pc)
|
||||
}
|
||||
case JSOP_UNDEFINED:
|
||||
return write(js_undefined_str);
|
||||
case JSOP_THIS:
|
||||
case JSOP_GLOBALTHIS:
|
||||
// |this| could convert to a very long object initialiser, so cite it by
|
||||
// its keyword name.
|
||||
return write(js_this_str);
|
||||
@ -1244,6 +1244,8 @@ ExpressionDecompiler::write(const char* s)
|
||||
bool
|
||||
ExpressionDecompiler::write(JSString* str)
|
||||
{
|
||||
if (str == cx->names().dotThis)
|
||||
return write("this");
|
||||
return sprinter.putString(str) >= 0;
|
||||
}
|
||||
|
||||
|
@ -62,14 +62,25 @@ using mozilla::PodCopy;
|
||||
using mozilla::PodZero;
|
||||
using mozilla::RotateLeft;
|
||||
|
||||
static BindingIter
|
||||
GetBinding(HandleScript script, HandlePropertyName name)
|
||||
{
|
||||
BindingIter bi(script);
|
||||
while (bi->name() != name)
|
||||
bi++;
|
||||
return bi;
|
||||
}
|
||||
|
||||
/* static */ BindingIter
|
||||
Bindings::argumentsBinding(ExclusiveContext* cx, HandleScript script)
|
||||
{
|
||||
HandlePropertyName arguments = cx->names().arguments;
|
||||
BindingIter bi(script);
|
||||
while (bi->name() != arguments)
|
||||
bi++;
|
||||
return bi;
|
||||
return GetBinding(script, cx->names().arguments);
|
||||
}
|
||||
|
||||
/* static */ BindingIter
|
||||
Bindings::thisBinding(ExclusiveContext* cx, HandleScript script)
|
||||
{
|
||||
return GetBinding(script, cx->names().dotThis);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -599,6 +610,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript
|
||||
ArgumentsHasVarBinding,
|
||||
NeedsArgsObj,
|
||||
HasMappedArgsObj,
|
||||
FunctionHasThisBinding,
|
||||
IsGeneratorExp,
|
||||
IsLegacyGenerator,
|
||||
IsStarGenerator,
|
||||
@ -729,6 +741,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript
|
||||
scriptBits |= (1 << NeedsArgsObj);
|
||||
if (script->hasMappedArgsObj())
|
||||
scriptBits |= (1 << HasMappedArgsObj);
|
||||
if (script->functionHasThisBinding())
|
||||
scriptBits |= (1 << FunctionHasThisBinding);
|
||||
if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
|
||||
scriptBits |= (1 << OwnSource);
|
||||
if (script->isGeneratorExp())
|
||||
@ -875,6 +889,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript
|
||||
script->setNeedsArgsObj(true);
|
||||
if (scriptBits & (1 << HasMappedArgsObj))
|
||||
script->hasMappedArgsObj_ = true;
|
||||
if (scriptBits & (1 << FunctionHasThisBinding))
|
||||
script->functionHasThisBinding_ = true;
|
||||
if (scriptBits & (1 << IsGeneratorExp))
|
||||
script->isGeneratorExp_ = true;
|
||||
if (scriptBits & (1 << HasSingleton))
|
||||
@ -2846,6 +2862,8 @@ JSScript::linkToFunctionFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScrip
|
||||
}
|
||||
script->hasMappedArgsObj_ = funbox->hasMappedArgsObj();
|
||||
|
||||
script->functionHasThisBinding_ = funbox->hasThisBinding();
|
||||
|
||||
script->funLength_ = funbox->length;
|
||||
|
||||
script->isGeneratorExp_ = funbox->inGenexpLambda;
|
||||
@ -2937,6 +2955,7 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco
|
||||
MOZ_ASSERT(script->isDerivedClassConstructor_ == funbox->isDerivedClassConstructor());
|
||||
MOZ_ASSERT(script->argumentsHasVarBinding() == funbox->argumentsHasLocalBinding());
|
||||
MOZ_ASSERT(script->hasMappedArgsObj() == funbox->hasMappedArgsObj());
|
||||
MOZ_ASSERT(script->functionHasThisBinding() == funbox->hasThisBinding());
|
||||
MOZ_ASSERT(script->functionNonDelazifying() == funbox->function());
|
||||
MOZ_ASSERT(script->isGeneratorExp_ == funbox->inGenexpLambda);
|
||||
MOZ_ASSERT(script->generatorKind() == funbox->generatorKind());
|
||||
@ -3496,6 +3515,7 @@ js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScri
|
||||
dst->setNeedsArgsObj(src->needsArgsObj());
|
||||
}
|
||||
dst->hasMappedArgsObj_ = src->hasMappedArgsObj();
|
||||
dst->functionHasThisBinding_ = src->functionHasThisBinding();
|
||||
dst->cloneHasArray(src);
|
||||
dst->strict_ = src->strict();
|
||||
dst->explicitUseStrict_ = src->explicitUseStrict();
|
||||
|
@ -331,8 +331,9 @@ class Bindings : public JS::Traceable
|
||||
/* Return the initial shape of call objects created for this scope. */
|
||||
Shape* callObjShape() const { return callObjShape_; }
|
||||
|
||||
/* Convenience method to get the var index of 'arguments'. */
|
||||
/* Convenience method to get the var index of 'arguments' or 'this'. */
|
||||
static BindingIter argumentsBinding(ExclusiveContext* cx, HandleScript script);
|
||||
static BindingIter thisBinding(ExclusiveContext* cx, HandleScript script);
|
||||
|
||||
/* Return whether the binding at bindingIndex is aliased. */
|
||||
bool bindingIsAliased(uint32_t bindingIndex);
|
||||
@ -1154,6 +1155,7 @@ class JSScript : public js::gc::TenuredCell
|
||||
bool argsHasVarBinding_:1;
|
||||
bool needsArgsAnalysis_:1;
|
||||
bool needsArgsObj_:1;
|
||||
bool functionHasThisBinding_:1;
|
||||
|
||||
// Whether the arguments object for this script, if it needs one, should be
|
||||
// mapped (alias formal parameters).
|
||||
@ -1482,6 +1484,10 @@ class JSScript : public js::gc::TenuredCell
|
||||
return hasMappedArgsObj_;
|
||||
}
|
||||
|
||||
bool functionHasThisBinding() const {
|
||||
return functionHasThisBinding_;
|
||||
}
|
||||
|
||||
/*
|
||||
* Arguments access (via JSOP_*ARG* opcodes) must access the canonical
|
||||
* location for the argument. If an arguments object exists AND it's mapped
|
||||
|
@ -65,6 +65,7 @@
|
||||
macro(displayURL, displayURL, "displayURL") \
|
||||
macro(done, done, "done") \
|
||||
macro(dotGenerator, dotGenerator, ".generator") \
|
||||
macro(dotThis, dotThis, ".this") \
|
||||
macro(each, each, "each") \
|
||||
macro(elementType, elementType, "elementType") \
|
||||
macro(empty, empty, "") \
|
||||
|
@ -6304,11 +6304,15 @@ DebuggerFrame_getThis(JSContext* cx, unsigned argc, Value* vp)
|
||||
THIS_FRAME_ITER(cx, argc, vp, "get this", args, thisobj, _, iter);
|
||||
RootedValue thisv(cx);
|
||||
{
|
||||
AutoCompartment ac(cx, iter.scopeChain(cx));
|
||||
if (!iter.computeThis(cx))
|
||||
AbstractFramePtr frame = iter.abstractFramePtr();
|
||||
AutoCompartment ac(cx, frame.scopeChain());
|
||||
|
||||
UpdateFrameIterPc(iter);
|
||||
|
||||
if (!GetThisValueForDebuggerMaybeOptimizedOut(cx, frame, iter.pc(), &thisv))
|
||||
return false;
|
||||
thisv = iter.computedThisValue();
|
||||
}
|
||||
|
||||
if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &thisv))
|
||||
return false;
|
||||
args.rval().set(thisv);
|
||||
@ -6596,12 +6600,11 @@ DebuggerFrame_setOnPop(JSContext* cx, unsigned argc, Value* vp)
|
||||
* go. In this case, |frame| must have a computed 'this' value, equal to |thisv|.
|
||||
*/
|
||||
static bool
|
||||
EvaluateInEnv(JSContext* cx, Handle<Env*> env, HandleValue thisv, AbstractFramePtr frame,
|
||||
EvaluateInEnv(JSContext* cx, Handle<Env*> env, AbstractFramePtr frame,
|
||||
jsbytecode* pc, mozilla::Range<const char16_t> chars, const char* filename,
|
||||
unsigned lineno, MutableHandleValue rval)
|
||||
{
|
||||
assertSameCompartment(cx, env, frame);
|
||||
MOZ_ASSERT_IF(frame, thisv.get() == frame.thisValue());
|
||||
MOZ_ASSERT_IF(frame, pc);
|
||||
|
||||
/*
|
||||
@ -6662,7 +6665,7 @@ EvaluateInEnv(JSContext* cx, Handle<Env*> env, HandleValue thisv, AbstractFrameP
|
||||
}
|
||||
|
||||
ExecuteType type = !frame ? EXECUTE_GLOBAL : EXECUTE_DEBUG;
|
||||
return ExecuteKernel(cx, script, *env, thisv, NullValue(), type, frame, rval.address());
|
||||
return ExecuteKernel(cx, script, *env, NullValue(), type, frame, rval.address());
|
||||
}
|
||||
|
||||
enum EvalBindings { EvalHasExtraBindings = true, EvalWithDefaultBindings = false };
|
||||
@ -6755,22 +6758,12 @@ DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code
|
||||
else
|
||||
ac.emplace(cx, scope);
|
||||
|
||||
RootedValue thisv(cx);
|
||||
Rooted<Env*> env(cx);
|
||||
if (iter) {
|
||||
/* ExecuteInEnv requires 'fp' to have a computed 'this" value. */
|
||||
if (!iter->computeThis(cx))
|
||||
return false;
|
||||
thisv = iter->computedThisValue();
|
||||
env = GetDebugScopeForFrame(cx, iter->abstractFramePtr(), iter->pc());
|
||||
if (!env)
|
||||
return false;
|
||||
} else {
|
||||
/*
|
||||
* Use the global lexical scope as 'this'. If the global is a Window
|
||||
* object, GetThisValue should return the WindowProxy.
|
||||
*/
|
||||
thisv = GetThisValue(scope);
|
||||
env = scope;
|
||||
}
|
||||
|
||||
@ -6810,7 +6803,7 @@ DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code
|
||||
return false;
|
||||
|
||||
mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
|
||||
bool ok = EvaluateInEnv(cx, env, thisv, frame, pc, chars, url ? url : "debugger eval code",
|
||||
bool ok = EvaluateInEnv(cx, env, frame, pc, chars, url ? url : "debugger eval code",
|
||||
lineNumber, &rval);
|
||||
return dbg->receiveCompletionValue(ac, ok, rval, vp);
|
||||
}
|
||||
|
@ -26,51 +26,6 @@
|
||||
|
||||
namespace js {
|
||||
|
||||
inline bool
|
||||
ComputeThis(JSContext* cx, AbstractFramePtr frame)
|
||||
{
|
||||
MOZ_ASSERT_IF(frame.isInterpreterFrame(), !frame.asInterpreterFrame()->runningInJit());
|
||||
|
||||
if (frame.isFunctionFrame() && frame.fun()->isArrow()) {
|
||||
/*
|
||||
* Arrow functions store their (lexical) |this| value in an
|
||||
* extended slot.
|
||||
*/
|
||||
frame.thisValue() = frame.fun()->getExtendedSlot(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (frame.isModuleFrame()) {
|
||||
MOZ_ASSERT(frame.thisValue().isUndefined());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (frame.thisValue().isObject())
|
||||
return true;
|
||||
RootedValue thisv(cx, frame.thisValue());
|
||||
if (frame.isFunctionFrame()) {
|
||||
if (frame.fun()->strict() || frame.fun()->isSelfHostedBuiltin())
|
||||
return true;
|
||||
/*
|
||||
* Eval function frames have their own |this| slot, which is a copy of the function's
|
||||
* |this| slot. If we lazily wrap a primitive |this| in an eval function frame, the
|
||||
* eval's frame will get the wrapper, but the function's frame will not. To prevent
|
||||
* this, we always wrap a function's |this| before pushing an eval frame, and should
|
||||
* thus never see an unwrapped primitive in a non-strict eval function frame. Null
|
||||
* and undefined |this| values will unwrap to the same object in the function and
|
||||
* eval frames, so are not required to be wrapped.
|
||||
*/
|
||||
MOZ_ASSERT_IF(frame.isEvalFrame(), thisv.isUndefined() || thisv.isNull());
|
||||
}
|
||||
|
||||
RootedValue result(cx);
|
||||
if (!BoxNonStrictThis(cx, thisv, &result))
|
||||
return false;
|
||||
|
||||
frame.thisValue() = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Every possible consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) (as determined
|
||||
* by ScriptAnalysis::needsArgsObj) must check for these magic values and, when
|
||||
|
@ -125,10 +125,6 @@ js::BoxNonStrictThis(JSContext* cx, HandleValue thisv, MutableHandleValue vp)
|
||||
bool
|
||||
js::BoxNonStrictThis(JSContext* cx, const CallReceiver& call)
|
||||
{
|
||||
/*
|
||||
* Check for SynthesizeFrame poisoning and fast constructors which
|
||||
* didn't check their callee properly.
|
||||
*/
|
||||
MOZ_ASSERT(!call.thisv().isMagic());
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -139,6 +135,46 @@ js::BoxNonStrictThis(JSContext* cx, const CallReceiver& call)
|
||||
return BoxNonStrictThis(cx, call.thisv(), call.mutableThisv());
|
||||
}
|
||||
|
||||
bool
|
||||
js::GetFunctionThis(JSContext* cx, AbstractFramePtr frame, MutableHandleValue res)
|
||||
{
|
||||
MOZ_ASSERT(frame.isNonEvalFunctionFrame());
|
||||
MOZ_ASSERT(!frame.fun()->isArrow());
|
||||
|
||||
if (frame.thisValue().isObject() ||
|
||||
frame.fun()->strict() ||
|
||||
frame.fun()->isSelfHostedBuiltin())
|
||||
{
|
||||
res.set(frame.thisValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedValue thisv(cx, frame.thisValue());
|
||||
return BoxNonStrictThis(cx, thisv, res);
|
||||
}
|
||||
|
||||
bool
|
||||
js::GetNonSyntacticGlobalThis(JSContext* cx, HandleObject scopeChain, MutableHandleValue res)
|
||||
{
|
||||
RootedObject scope(cx, scopeChain);
|
||||
while (true) {
|
||||
if (IsExtensibleLexicalScope(scope)) {
|
||||
res.set(scope->as<ClonedBlockObject>().thisValue());
|
||||
return true;
|
||||
}
|
||||
if (!scope->enclosingScope()) {
|
||||
// This can only happen in Debugger eval frames: in that case we
|
||||
// don't always have a global lexical scope, see EvaluateInEnv.
|
||||
MOZ_ASSERT(scope->is<GlobalObject>());
|
||||
res.set(GetThisValue(scope));
|
||||
return true;
|
||||
}
|
||||
scope = scope->enclosingScope();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
GetPropertyOperation(JSContext* cx, InterpreterFrame* fp, HandleScript script, jsbytecode* pc,
|
||||
MutableHandleValue lval, MutableHandleValue vp)
|
||||
@ -579,7 +615,7 @@ js::InvokeSetter(JSContext* cx, const Value& thisv, Value fval, HandleValue v)
|
||||
}
|
||||
|
||||
bool
|
||||
js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg, const Value& thisv,
|
||||
js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg,
|
||||
const Value& newTargetValue, ExecuteType type, AbstractFramePtr evalInFrame,
|
||||
Value* result)
|
||||
{
|
||||
@ -587,7 +623,6 @@ js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg, c
|
||||
MOZ_ASSERT_IF(type == EXECUTE_GLOBAL, IsGlobalLexicalScope(&scopeChainArg) ||
|
||||
!IsSyntacticScope(&scopeChainArg));
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT_IF(thisv.isObject(), !IsWindow(&thisv.toObject()));
|
||||
RootedObject terminatingScope(cx, &scopeChainArg);
|
||||
while (IsSyntacticScope(terminatingScope))
|
||||
terminatingScope = terminatingScope->enclosingScope();
|
||||
@ -610,7 +645,9 @@ js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg, c
|
||||
return true;
|
||||
}
|
||||
|
||||
TypeScript::SetThis(cx, script, thisv);
|
||||
// It doesn't matter what we pass as thisv, global/eval scripts get |this|
|
||||
// from the scope chain. TODO: remove thisv from ExecuteState.
|
||||
RootedValue thisv(cx);
|
||||
|
||||
probes::StartExecution(script);
|
||||
ExecuteState state(cx, script, thisv, newTargetValue, scopeChainArg, type, evalInFrame, result);
|
||||
@ -648,9 +685,7 @@ js::Execute(JSContext* cx, HandleScript script, JSObject& scopeChainArg, Value*
|
||||
|
||||
ExecuteType type = script->module() ? EXECUTE_MODULE : EXECUTE_GLOBAL;
|
||||
|
||||
RootedValue thisv(cx, GetThisValue(scopeChain));
|
||||
|
||||
return ExecuteKernel(cx, script, *scopeChain, thisv, NullValue(), type,
|
||||
return ExecuteKernel(cx, script, *scopeChain, NullValue(), type,
|
||||
NullFramePtr() /* evalInFrame */, rval);
|
||||
}
|
||||
|
||||
@ -1706,8 +1741,10 @@ CASE(EnableInterruptsPseudoOpcode)
|
||||
/* Various 1-byte no-ops. */
|
||||
CASE(JSOP_NOP)
|
||||
CASE(JSOP_UNUSED14)
|
||||
CASE(JSOP_UNUSED65)
|
||||
CASE(JSOP_BACKPATCH)
|
||||
CASE(JSOP_UNUSED145)
|
||||
CASE(JSOP_UNUSED163)
|
||||
CASE(JSOP_UNUSED177)
|
||||
CASE(JSOP_UNUSED178)
|
||||
CASE(JSOP_UNUSED179)
|
||||
@ -1715,12 +1752,7 @@ CASE(JSOP_UNUSED180)
|
||||
CASE(JSOP_UNUSED181)
|
||||
CASE(JSOP_UNUSED182)
|
||||
CASE(JSOP_UNUSED183)
|
||||
CASE(JSOP_UNUSED185)
|
||||
CASE(JSOP_UNUSED186)
|
||||
CASE(JSOP_UNUSED187)
|
||||
CASE(JSOP_UNUSED189)
|
||||
CASE(JSOP_UNUSED190)
|
||||
CASE(JSOP_UNUSED191)
|
||||
CASE(JSOP_UNUSED192)
|
||||
CASE(JSOP_UNUSED209)
|
||||
CASE(JSOP_UNUSED210)
|
||||
@ -1842,9 +1874,6 @@ CASE(JSOP_RETRVAL)
|
||||
*/
|
||||
CHECK_BRANCH();
|
||||
|
||||
if (!REGS.fp()->checkReturn(cx))
|
||||
goto error;
|
||||
|
||||
successful_return_continuation:
|
||||
interpReturnOK = true;
|
||||
|
||||
@ -2411,13 +2440,50 @@ CASE(JSOP_VOID)
|
||||
REGS.sp[-1].setUndefined();
|
||||
END_CASE(JSOP_VOID)
|
||||
|
||||
CASE(JSOP_THIS)
|
||||
if (!ComputeThis(cx, REGS.fp()))
|
||||
CASE(JSOP_FUNCTIONTHIS)
|
||||
PUSH_NULL();
|
||||
if (!GetFunctionThis(cx, REGS.fp(), REGS.stackHandleAt(-1)))
|
||||
goto error;
|
||||
if (!REGS.fp()->checkThis(cx))
|
||||
END_CASE(JSOP_FUNCTIONTHIS)
|
||||
|
||||
CASE(JSOP_GLOBALTHIS)
|
||||
{
|
||||
if (script->hasNonSyntacticScope()) {
|
||||
PUSH_NULL();
|
||||
if (!GetNonSyntacticGlobalThis(cx, REGS.fp()->scopeChain(), REGS.stackHandleAt(-1)))
|
||||
goto error;
|
||||
} else {
|
||||
ClonedBlockObject* lexicalScope = &cx->global()->lexicalScope();
|
||||
PUSH_COPY(lexicalScope->thisValue());
|
||||
}
|
||||
}
|
||||
END_CASE(JSOP_GLOBALTHIS)
|
||||
|
||||
CASE(JSOP_CHECKTHIS)
|
||||
{
|
||||
if (REGS.sp[-1].isMagic(JS_UNINITIALIZED_LEXICAL)) {
|
||||
MOZ_ALWAYS_FALSE(ThrowUninitializedThis(cx, REGS.fp()));
|
||||
goto error;
|
||||
PUSH_COPY(REGS.fp()->thisValue());
|
||||
END_CASE(JSOP_THIS)
|
||||
}
|
||||
}
|
||||
END_CASE(JSOP_CHECKTHIS)
|
||||
|
||||
CASE(JSOP_CHECKTHISREINIT)
|
||||
{
|
||||
if (!REGS.sp[-1].isMagic(JS_UNINITIALIZED_LEXICAL)) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_REINIT_THIS);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
END_CASE(JSOP_CHECKTHISREINIT)
|
||||
|
||||
CASE(JSOP_CHECKRETURN)
|
||||
{
|
||||
if (!REGS.fp()->checkReturn(cx, REGS.stackHandleAt(-1)))
|
||||
goto error;
|
||||
REGS.sp--;
|
||||
}
|
||||
END_CASE(JSOP_CHECKRETURN)
|
||||
|
||||
CASE(JSOP_GETPROP)
|
||||
CASE(JSOP_LENGTH)
|
||||
@ -3029,7 +3095,16 @@ CASE(JSOP_GETALIASEDVAR)
|
||||
{
|
||||
ScopeCoordinate sc = ScopeCoordinate(REGS.pc);
|
||||
ReservedRooted<Value> val(&rootValue0, REGS.fp()->aliasedVarScope(sc).aliasedVar(sc));
|
||||
MOZ_ASSERT(!IsUninitializedLexical(val));
|
||||
#ifdef DEBUG
|
||||
// Only the .this slot can hold the TDZ MagicValue.
|
||||
if (IsUninitializedLexical(val)) {
|
||||
PropertyName* name = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache,
|
||||
script, REGS.pc);
|
||||
MOZ_ASSERT(name == cx->names().dotThis);
|
||||
JSOp next = JSOp(*GetNextPc(REGS.pc));
|
||||
MOZ_ASSERT(next == JSOP_CHECKTHIS || next == JSOP_CHECKRETURN || next == JSOP_CHECKTHISREINIT);
|
||||
}
|
||||
#endif
|
||||
PUSH_COPY(val);
|
||||
TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]);
|
||||
}
|
||||
@ -3124,7 +3199,16 @@ CASE(JSOP_GETLOCAL)
|
||||
{
|
||||
uint32_t i = GET_LOCALNO(REGS.pc);
|
||||
PUSH_COPY_SKIP_CHECK(REGS.fp()->unaliasedLocal(i));
|
||||
MOZ_ASSERT(!IsUninitializedLexical(REGS.sp[-1]));
|
||||
|
||||
#ifdef DEBUG
|
||||
// Derived class constructors store the TDZ Value in the .this slot
|
||||
// before a super() call.
|
||||
if (IsUninitializedLexical(REGS.sp[-1])) {
|
||||
MOZ_ASSERT(script->isDerivedClassConstructor());
|
||||
JSOp next = JSOp(*GetNextPc(REGS.pc));
|
||||
MOZ_ASSERT(next == JSOP_CHECKTHIS || next == JSOP_CHECKRETURN || next == JSOP_CHECKTHISREINIT);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Skip the same-compartment assertion if the local will be immediately
|
||||
@ -3140,7 +3224,12 @@ END_CASE(JSOP_GETLOCAL)
|
||||
CASE(JSOP_SETLOCAL)
|
||||
{
|
||||
uint32_t i = GET_LOCALNO(REGS.pc);
|
||||
MOZ_ASSERT(!IsUninitializedLexical(REGS.fp()->unaliasedLocal(i)));
|
||||
|
||||
// Derived class constructors store the TDZ Value in the .this slot
|
||||
// before a super() call.
|
||||
MOZ_ASSERT_IF(!script->isDerivedClassConstructor(),
|
||||
!IsUninitializedLexical(REGS.fp()->unaliasedLocal(i)));
|
||||
|
||||
REGS.fp()->unaliasedLocal(i) = REGS.sp[-1];
|
||||
}
|
||||
END_CASE(JSOP_SETLOCAL)
|
||||
@ -3208,14 +3297,12 @@ CASE(JSOP_LAMBDA_ARROW)
|
||||
{
|
||||
/* Load the specified function object literal. */
|
||||
ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
|
||||
ReservedRooted<Value> thisv(&rootValue0, REGS.sp[-2]);
|
||||
ReservedRooted<Value> newTarget(&rootValue1, REGS.sp[-1]);
|
||||
JSObject* obj = LambdaArrow(cx, fun, REGS.fp()->scopeChain(), thisv, newTarget);
|
||||
JSObject* obj = LambdaArrow(cx, fun, REGS.fp()->scopeChain(), newTarget);
|
||||
if (!obj)
|
||||
goto error;
|
||||
MOZ_ASSERT(obj->getProto());
|
||||
REGS.sp[-2].setObject(*obj);
|
||||
REGS.sp--;
|
||||
REGS.sp[-1].setObject(*obj);
|
||||
}
|
||||
END_CASE(JSOP_LAMBDA_ARROW)
|
||||
|
||||
@ -3775,22 +3862,6 @@ CASE(JSOP_SUPERFUN)
|
||||
}
|
||||
END_CASE(JSOP_SUPERFUN)
|
||||
|
||||
CASE(JSOP_SETTHIS)
|
||||
{
|
||||
MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame());
|
||||
MOZ_ASSERT(REGS.fp()->script()->isDerivedClassConstructor());
|
||||
MOZ_ASSERT(REGS.fp()->callee().isClassConstructor());
|
||||
|
||||
if (!REGS.fp()->thisValue().isMagic(JS_UNINITIALIZED_LEXICAL)) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_REINIT_THIS);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ReservedRooted<JSObject*> thisv(&rootObject0, ®S.sp[-1].toObject());
|
||||
REGS.fp()->setDerivedConstructorThis(thisv);
|
||||
}
|
||||
END_CASE(JSOP_SETTHIS)
|
||||
|
||||
CASE(JSOP_DERIVEDCONSTRUCTOR)
|
||||
{
|
||||
MOZ_ASSERT(REGS.sp[-1].isObject());
|
||||
@ -3996,8 +4067,7 @@ js::Lambda(JSContext* cx, HandleFunction fun, HandleObject parent)
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::LambdaArrow(JSContext* cx, HandleFunction fun, HandleObject parent, HandleValue thisv,
|
||||
HandleValue newTargetv)
|
||||
js::LambdaArrow(JSContext* cx, HandleFunction fun, HandleObject parent, HandleValue newTargetv)
|
||||
{
|
||||
MOZ_ASSERT(fun->isArrow());
|
||||
|
||||
@ -4007,8 +4077,7 @@ js::LambdaArrow(JSContext* cx, HandleFunction fun, HandleObject parent, HandleVa
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(clone->as<JSFunction>().isArrow());
|
||||
clone->as<JSFunction>().setExtendedSlot(0, thisv);
|
||||
clone->as<JSFunction>().setExtendedSlot(1, newTargetv);
|
||||
clone->as<JSFunction>().setExtendedSlot(0, newTargetv);
|
||||
|
||||
MOZ_ASSERT(fun->global() == clone->global());
|
||||
return clone;
|
||||
|
@ -34,14 +34,11 @@ BoxNonStrictThis(JSContext* cx, const CallReceiver& call);
|
||||
extern bool
|
||||
BoxNonStrictThis(JSContext* cx, HandleValue thisv, MutableHandleValue vp);
|
||||
|
||||
/*
|
||||
* Ensure that fp->thisValue() is the correct value of |this| for the scripted
|
||||
* call represented by |fp|. ComputeThis is necessary because fp->thisValue()
|
||||
* may be set to 'undefined' when 'this' should really be the global object (as
|
||||
* an optimization to avoid global-this computation).
|
||||
*/
|
||||
inline bool
|
||||
ComputeThis(JSContext* cx, AbstractFramePtr frame);
|
||||
extern bool
|
||||
GetFunctionThis(JSContext* cx, AbstractFramePtr frame, MutableHandleValue res);
|
||||
|
||||
extern bool
|
||||
GetNonSyntacticGlobalThis(JSContext* cx, HandleObject scopeChain, MutableHandleValue res);
|
||||
|
||||
enum MaybeConstruct {
|
||||
NO_CONSTRUCT = INITIAL_NONE,
|
||||
@ -115,7 +112,7 @@ InternalConstructWithProvidedThis(JSContext* cx, HandleValue fval, HandleValue t
|
||||
* stack to simulate executing an eval in that frame.
|
||||
*/
|
||||
extern bool
|
||||
ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChain, const Value& thisv,
|
||||
ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChain,
|
||||
const Value& newTargetVal, ExecuteType type, AbstractFramePtr evalInFrame,
|
||||
Value* result);
|
||||
|
||||
@ -361,8 +358,7 @@ JSObject*
|
||||
Lambda(JSContext* cx, HandleFunction fun, HandleObject parent);
|
||||
|
||||
JSObject*
|
||||
LambdaArrow(JSContext* cx, HandleFunction fun, HandleObject parent, HandleValue thisv,
|
||||
HandleValue newTargetv);
|
||||
LambdaArrow(JSContext* cx, HandleFunction fun, HandleObject parent, HandleValue newTargetv);
|
||||
|
||||
bool
|
||||
GetElement(JSContext* cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue res);
|
||||
|
@ -606,14 +606,7 @@
|
||||
* Stack: => null
|
||||
*/ \
|
||||
macro(JSOP_NULL, 64, js_null_str, js_null_str, 1, 0, 1, JOF_BYTE) \
|
||||
/*
|
||||
* Pushes 'this' value for current stack frame onto the stack.
|
||||
* Category: Variables and Scopes
|
||||
* Type: This
|
||||
* Operands:
|
||||
* Stack: => this
|
||||
*/ \
|
||||
macro(JSOP_THIS, 65, js_this_str, js_this_str, 1, 0, 1, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED65, 65, "unused65", NULL, 1, 0, 1, JOF_BYTE) \
|
||||
/*
|
||||
* Pushes boolean value onto the stack.
|
||||
* Category: Literals
|
||||
@ -1333,14 +1326,14 @@
|
||||
*/ \
|
||||
macro(JSOP_LAMBDA, 130, "lambda", NULL, 5, 0, 1, JOF_OBJECT) \
|
||||
/*
|
||||
* Pops the top of stack value as 'this', pushes an arrow function with
|
||||
* 'this' onto the stack.
|
||||
* Pops the top of stack value as 'new.target', pushes an arrow function with
|
||||
* lexical 'new.target' onto the stack.
|
||||
* Category: Statements
|
||||
* Type: Function
|
||||
* Operands: uint32_t funcIndex
|
||||
* Stack: this => obj
|
||||
* Stack: new.target => obj
|
||||
*/ \
|
||||
macro(JSOP_LAMBDA_ARROW, 131, "lambda_arrow", NULL, 5, 2, 1, JOF_OBJECT) \
|
||||
macro(JSOP_LAMBDA_ARROW, 131, "lambda_arrow", NULL, 5, 1, 1, JOF_OBJECT) \
|
||||
\
|
||||
/*
|
||||
* Pushes current callee onto the stack.
|
||||
@ -1666,16 +1659,7 @@
|
||||
* Stack: =>
|
||||
*/ \
|
||||
macro(JSOP_DEFLET, 162,"deflet", NULL, 5, 0, 0, JOF_ATOM) \
|
||||
\
|
||||
/*
|
||||
* Bind the |this| value of a function to the supplied value.
|
||||
*
|
||||
* Category: Variables and Scopes
|
||||
* Type: This
|
||||
* Operands:
|
||||
* Stack: this => this
|
||||
*/ \
|
||||
macro(JSOP_SETTHIS, 163,"setthis", NULL, 1, 1, 1, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED163, 163,"unused163", NULL, 1, 0, 1, JOF_BYTE) \
|
||||
/*
|
||||
* Find the function to invoke with |super()| on the scope chain.
|
||||
*
|
||||
@ -1827,9 +1811,24 @@
|
||||
* Stack: obj => obj[name]
|
||||
*/ \
|
||||
macro(JSOP_CALLPROP, 184,"callprop", NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_TYPESET) \
|
||||
\
|
||||
macro(JSOP_UNUSED185, 185,"unused185", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED186, 186,"unused186", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
/*
|
||||
* Determines the 'this' value for current function frame and pushes it onto
|
||||
* the stack. Emitted in the prologue of functions with a this-binding.
|
||||
* Category: Variables and Scopes
|
||||
* Type: This
|
||||
* Operands:
|
||||
* Stack: => this
|
||||
*/ \
|
||||
macro(JSOP_FUNCTIONTHIS, 185,"functionthis",NULL, 1, 0, 1, JOF_BYTE) \
|
||||
/*
|
||||
* Pushes 'this' value for current stack frame onto the stack. Emitted when
|
||||
* 'this' refers to the global 'this'.
|
||||
* Category: Variables and Scopes
|
||||
* Type: This
|
||||
* Operands:
|
||||
* Stack: => this
|
||||
*/ \
|
||||
macro(JSOP_GLOBALTHIS, 186,"globalthis", NULL, 1, 0, 1, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED187, 187,"unused187", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
\
|
||||
/*
|
||||
@ -1840,10 +1839,34 @@
|
||||
* Stack: => val
|
||||
*/ \
|
||||
macro(JSOP_UINT24, 188,"uint24", NULL, 4, 0, 1, JOF_UINT24) \
|
||||
\
|
||||
macro(JSOP_UNUSED189, 189,"unused189", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED190, 190,"unused190", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED191, 191,"unused191", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
/*
|
||||
* Throw if the value on top of the stack is the TDZ MagicValue. Used in
|
||||
* derived class constructors.
|
||||
* Category: Variables and Scopes
|
||||
* Type: This
|
||||
* Operands:
|
||||
* Stack: this => this
|
||||
*/ \
|
||||
macro(JSOP_CHECKTHIS, 189,"checkthis", NULL, 1, 1, 1, JOF_BYTE) \
|
||||
/*
|
||||
* Check if a derived class constructor has a valid return value and 'this'
|
||||
* value before it returns. If the return value is not an object, stores
|
||||
* the 'this' value to the return value slot.
|
||||
* Category: Variables and Scopes
|
||||
* Type: This
|
||||
* Operands:
|
||||
* Stack: this =>
|
||||
*/ \
|
||||
macro(JSOP_CHECKRETURN, 190,"checkreturn", NULL, 1, 1, 0, JOF_BYTE) \
|
||||
/*
|
||||
* Throw an exception if the value on top of the stack is not the TDZ
|
||||
* MagicValue. Used in derived class constructors.
|
||||
* Category: Variables and Scopes
|
||||
* Type: This
|
||||
* Operands:
|
||||
* Stack: this => this
|
||||
*/ \
|
||||
macro(JSOP_CHECKTHISREINIT,191,"checkthisreinit",NULL,1, 1, 1, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED192, 192,"unused192", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
\
|
||||
/*
|
||||
|
@ -656,6 +656,11 @@ static bool
|
||||
with_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
MutableHandleObject objp, MutableHandleShape propp)
|
||||
{
|
||||
if (JSID_IS_ATOM(id, cx->names().dotThis)) {
|
||||
objp.set(nullptr);
|
||||
propp.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
|
||||
return LookupProperty(cx, actual, id, objp, propp);
|
||||
}
|
||||
@ -664,6 +669,7 @@ static bool
|
||||
with_DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result)
|
||||
{
|
||||
MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
|
||||
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
|
||||
return DefineProperty(cx, actual, id, desc, result);
|
||||
}
|
||||
@ -671,6 +677,7 @@ with_DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<Propert
|
||||
static bool
|
||||
with_HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
|
||||
{
|
||||
MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
|
||||
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
|
||||
return HasProperty(cx, actual, id, foundp);
|
||||
}
|
||||
@ -679,6 +686,7 @@ static bool
|
||||
with_GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
|
||||
MutableHandleValue vp)
|
||||
{
|
||||
MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
|
||||
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
|
||||
RootedValue actualReceiver(cx, receiver);
|
||||
if (receiver.isObject() && &receiver.toObject() == obj)
|
||||
@ -690,6 +698,7 @@ static bool
|
||||
with_SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result)
|
||||
{
|
||||
MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
|
||||
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
|
||||
RootedValue actualReceiver(cx, receiver);
|
||||
if (receiver.isObject() && &receiver.toObject() == obj)
|
||||
@ -701,6 +710,7 @@ static bool
|
||||
with_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc)
|
||||
{
|
||||
MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
|
||||
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
|
||||
return GetOwnPropertyDescriptor(cx, actual, id, desc);
|
||||
}
|
||||
@ -708,6 +718,7 @@ with_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
|
||||
static bool
|
||||
with_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
|
||||
{
|
||||
MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
|
||||
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
|
||||
return DeleteProperty(cx, actual, id, result);
|
||||
}
|
||||
@ -849,7 +860,12 @@ ClonedBlockObject::create(JSContext* cx, Handle<StaticBlockObject*> block, Handl
|
||||
|
||||
MOZ_ASSERT(obj->isDelegate());
|
||||
|
||||
return &obj->as<ClonedBlockObject>();
|
||||
ClonedBlockObject* res = &obj->as<ClonedBlockObject>();
|
||||
|
||||
if (res->isGlobal() || !res->isSyntactic())
|
||||
res->setReservedSlot(THIS_VALUE_SLOT, GetThisValue(enclosing));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* static */ ClonedBlockObject*
|
||||
@ -1014,13 +1030,18 @@ StaticBlockObject::addVar(ExclusiveContext* cx, Handle<StaticBlockObject*> block
|
||||
}
|
||||
|
||||
Value
|
||||
ClonedBlockObject::thisValue()
|
||||
ClonedBlockObject::thisValue() const
|
||||
{
|
||||
// No other block objects should ever get passed to GetThisValue
|
||||
// except the global lexical scope and non-syntactic ones.
|
||||
MOZ_ASSERT(isGlobal() || !isSyntactic());
|
||||
MOZ_ASSERT_IF(isGlobal(), enclosingScope() == JSObject::global());
|
||||
return GetThisValue(&enclosingScope());
|
||||
Value v = getReservedSlot(THIS_VALUE_SLOT);
|
||||
if (v.isObject()) {
|
||||
// If `v` is a Window, return the WindowProxy instead. We called
|
||||
// GetThisValue (which also does ToWindowProxyIfWindow) when storing
|
||||
// the value in THIS_VALUE_SLOT, but it's possible the WindowProxy was
|
||||
// attached to the global *after* we set THIS_VALUE_SLOT.
|
||||
return ObjectValue(*ToWindowProxyIfWindow(&v.toObject()));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
const Class BlockObject::class_ = {
|
||||
@ -1749,6 +1770,10 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
{
|
||||
return id == NameToId(cx->names().arguments);
|
||||
}
|
||||
static bool isThis(JSContext* cx, jsid id)
|
||||
{
|
||||
return id == NameToId(cx->names().dotThis);
|
||||
}
|
||||
|
||||
static bool isFunctionScope(const JSObject& scope)
|
||||
{
|
||||
@ -1767,6 +1792,16 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
!scope.as<CallObject>().callee().nonLazyScript()->argumentsHasVarBinding();
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar to 'arguments' above, we don't add a 'this' binding to functions
|
||||
* if it's not used.
|
||||
*/
|
||||
static bool isMissingThisBinding(ScopeObject& scope)
|
||||
{
|
||||
return isFunctionScopeWithThis(scope) &&
|
||||
!scope.as<CallObject>().callee().nonLazyScript()->functionHasThisBinding();
|
||||
}
|
||||
|
||||
/*
|
||||
* This function checks if an arguments object needs to be created when
|
||||
* the debugger requests 'arguments' for a function scope where the
|
||||
@ -1778,6 +1813,10 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
return isArguments(cx, id) && isFunctionScope(scope) &&
|
||||
!scope.as<CallObject>().callee().nonLazyScript()->needsArgsObj();
|
||||
}
|
||||
static bool isMissingThis(JSContext* cx, jsid id, ScopeObject& scope)
|
||||
{
|
||||
return isThis(cx, id) && isMissingThisBinding(scope);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the value is the magic value JS_OPTIMIZED_ARGUMENTS. The
|
||||
@ -1818,12 +1857,39 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
return !!argsObj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a missing this Value. If the function returns true but
|
||||
* *success is false, it means the scope is dead.
|
||||
*/
|
||||
static bool createMissingThis(JSContext* cx, ScopeObject& scope,
|
||||
MutableHandleValue thisv, bool* success)
|
||||
{
|
||||
*success = false;
|
||||
|
||||
LiveScopeVal* maybeScope = DebugScopes::hasLiveScope(scope);
|
||||
if (!maybeScope)
|
||||
return true;
|
||||
|
||||
if (!GetFunctionThis(cx, maybeScope->frame(), thisv))
|
||||
return false;
|
||||
|
||||
*success = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
static const char family;
|
||||
static const DebugScopeProxy singleton;
|
||||
|
||||
MOZ_CONSTEXPR DebugScopeProxy() : BaseProxyHandler(&family) {}
|
||||
|
||||
static bool isFunctionScopeWithThis(const JSObject& scope)
|
||||
{
|
||||
// All functions except arrows and generator expression lambdas should
|
||||
// have their own this binding.
|
||||
return isFunctionScope(scope) && !scope.as<CallObject>().callee().hasLexicalThis();
|
||||
}
|
||||
|
||||
bool preventExtensions(JSContext* cx, HandleObject proxy,
|
||||
ObjectOpResult& result) const override
|
||||
{
|
||||
@ -1867,6 +1933,29 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
desc.setSetter(nullptr);
|
||||
return true;
|
||||
}
|
||||
bool getMissingThisPropertyDescriptor(JSContext* cx,
|
||||
Handle<DebugScopeObject*> debugScope,
|
||||
ScopeObject& scope,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
RootedValue thisv(cx);
|
||||
bool success;
|
||||
if (!createMissingThis(cx, scope, &thisv, &success))
|
||||
return false;
|
||||
|
||||
if (!success) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
|
||||
"Debugger scope");
|
||||
return false;
|
||||
}
|
||||
|
||||
desc.object().set(debugScope);
|
||||
desc.setAttributes(JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);
|
||||
desc.value().set(thisv);
|
||||
desc.setGetter(nullptr);
|
||||
desc.setSetter(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const override
|
||||
@ -1877,6 +1966,9 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
if (isMissingArguments(cx, id, *scope))
|
||||
return getMissingArgumentsPropertyDescriptor(cx, debugScope, *scope, desc);
|
||||
|
||||
if (isMissingThis(cx, id, *scope))
|
||||
return getMissingThisPropertyDescriptor(cx, debugScope, *scope, desc);
|
||||
|
||||
RootedValue v(cx);
|
||||
AccessResult access;
|
||||
if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, &v, &access))
|
||||
@ -1918,6 +2010,23 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getMissingThis(JSContext* cx, ScopeObject& scope, MutableHandleValue vp) const
|
||||
{
|
||||
RootedValue thisv(cx);
|
||||
bool success;
|
||||
if (!createMissingThis(cx, scope, &thisv, &success))
|
||||
return false;
|
||||
|
||||
if (!success) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
|
||||
"Debugger scope");
|
||||
return false;
|
||||
}
|
||||
|
||||
vp.set(thisv);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
|
||||
MutableHandleValue vp) const override
|
||||
{
|
||||
@ -1927,6 +2036,9 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
if (isMissingArguments(cx, id, *scope))
|
||||
return getMissingArguments(cx, *scope, vp);
|
||||
|
||||
if (isMissingThis(cx, id, *scope))
|
||||
return getMissingThis(cx, *scope, vp);
|
||||
|
||||
AccessResult access;
|
||||
if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, vp, &access))
|
||||
return false;
|
||||
@ -1956,6 +2068,17 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getMissingThisMaybeSentinelValue(JSContext* cx, ScopeObject& scope,
|
||||
MutableHandleValue vp) const
|
||||
{
|
||||
RootedValue thisv(cx);
|
||||
bool success;
|
||||
if (!createMissingThis(cx, scope, &thisv, &success))
|
||||
return false;
|
||||
vp.set(success ? thisv : MagicValue(JS_OPTIMIZED_OUT));
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Like 'get', but returns sentinel values instead of throwing on
|
||||
* exceptional cases.
|
||||
@ -1967,6 +2090,8 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
|
||||
if (isMissingArguments(cx, id, *scope))
|
||||
return getMissingArgumentsMaybeSentinelValue(cx, *scope, vp);
|
||||
if (isMissingThis(cx, id, *scope))
|
||||
return getMissingThisMaybeSentinelValue(cx, *scope, vp);
|
||||
|
||||
AccessResult access;
|
||||
if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, vp, &access))
|
||||
@ -2037,6 +2162,10 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
if (!props.append(NameToId(cx->names().arguments)))
|
||||
return false;
|
||||
}
|
||||
if (isMissingThisBinding(*scope)) {
|
||||
if (!props.append(NameToId(cx->names().dotThis)))
|
||||
return false;
|
||||
}
|
||||
|
||||
// DynamicWithObject isn't a very good proxy. It doesn't have a
|
||||
// JSNewEnumerateOp implementation, because if it just delegated to the
|
||||
@ -2078,6 +2207,10 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
*bp = true;
|
||||
return true;
|
||||
}
|
||||
if (isThis(cx, id) && isFunctionScopeWithThis(scopeObj)) {
|
||||
*bp = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool found;
|
||||
RootedObject scope(cx, &scopeObj);
|
||||
@ -2174,6 +2307,12 @@ DebugScopeObject::getMaybeSentinelValue(JSContext* cx, HandleId id, MutableHandl
|
||||
return DebugScopeProxy::singleton.getMaybeSentinelValue(cx, self, id, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
DebugScopeObject::isFunctionScopeWithThis()
|
||||
{
|
||||
return DebugScopeProxy::isFunctionScopeWithThis(scope());
|
||||
}
|
||||
|
||||
bool
|
||||
DebugScopeObject::isOptimizedOut() const
|
||||
{
|
||||
@ -2951,6 +3090,52 @@ js::GetModuleEnvironmentForScript(JSScript* script)
|
||||
return ssi.module().environment();
|
||||
}
|
||||
|
||||
bool
|
||||
js::GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
|
||||
MutableHandleValue res)
|
||||
{
|
||||
for (ScopeIter si(cx, frame, pc); !si.done(); ++si) {
|
||||
if (si.type() == ScopeIter::Module) {
|
||||
res.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (si.type() != ScopeIter::Call || si.fun().hasLexicalThis())
|
||||
continue;
|
||||
|
||||
RootedScript script(cx, si.fun().nonLazyScript());
|
||||
|
||||
if (!script->functionHasThisBinding()) {
|
||||
MOZ_ASSERT(!script->isDerivedClassConstructor(),
|
||||
"Derived class constructors always have a this-binding");
|
||||
|
||||
// If we're still inside `frame`, we can use the this-value passed
|
||||
// to it, if it does not require boxing.
|
||||
if (si.withinInitialFrame() && (frame.thisValue().isObject() || script->strict()))
|
||||
res.set(frame.thisValue());
|
||||
else
|
||||
res.setMagic(JS_OPTIMIZED_OUT);
|
||||
return true;
|
||||
}
|
||||
|
||||
BindingIter bi = Bindings::thisBinding(cx, script);
|
||||
|
||||
if (script->bindingIsAliased(bi)) {
|
||||
RootedObject callObj(cx, &si.scope().as<CallObject>());
|
||||
return GetProperty(cx, callObj, callObj, cx->names().dotThis, res);
|
||||
}
|
||||
|
||||
if (si.withinInitialFrame())
|
||||
res.set(frame.unaliasedLocal(bi.frameIndex()));
|
||||
else
|
||||
res.setMagic(JS_OPTIMIZED_OUT);
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject scopeChain(cx, frame.scopeChain());
|
||||
return GetNonSyntacticGlobalThis(cx, scopeChain, res);
|
||||
}
|
||||
|
||||
bool
|
||||
js::CheckLexicalNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
|
||||
HandleObject varObj, HandlePropertyName name)
|
||||
|
@ -874,6 +874,8 @@ class StaticBlockObject : public BlockObject
|
||||
|
||||
class ClonedBlockObject : public BlockObject
|
||||
{
|
||||
static const unsigned THIS_VALUE_SLOT = 1;
|
||||
|
||||
static ClonedBlockObject* create(JSContext* cx, Handle<StaticBlockObject*> block,
|
||||
HandleObject enclosing);
|
||||
|
||||
@ -929,7 +931,7 @@ class ClonedBlockObject : public BlockObject
|
||||
*/
|
||||
static ClonedBlockObject* clone(JSContext* cx, Handle<ClonedBlockObject*> block);
|
||||
|
||||
Value thisValue();
|
||||
Value thisValue() const;
|
||||
};
|
||||
|
||||
// Internal scope object used by JSOP_BINDNAME upon encountering an
|
||||
@ -1181,6 +1183,10 @@ class DebugScopeObject : public ProxyObject
|
||||
// on exceptional cases.
|
||||
bool getMaybeSentinelValue(JSContext* cx, HandleId id, MutableHandleValue vp);
|
||||
|
||||
// Returns true iff this is a function scope with its own this-binding
|
||||
// (all functions except arrow functions and generator expression lambdas).
|
||||
bool isFunctionScopeWithThis();
|
||||
|
||||
// Does this debug scope not have a dynamic counterpart or was never live
|
||||
// (and thus does not have a synthesized ScopeObject or a snapshot)?
|
||||
bool isOptimizedOut() const;
|
||||
@ -1453,6 +1459,9 @@ uint32_t StaticScopeChainLength(JSObject* staticScope);
|
||||
|
||||
ModuleEnvironmentObject* GetModuleEnvironmentForScript(JSScript* script);
|
||||
|
||||
bool GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
|
||||
MutableHandleValue res);
|
||||
|
||||
bool CheckVarNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
|
||||
HandlePropertyName name);
|
||||
|
||||
|
@ -321,39 +321,25 @@ InterpreterFrame::epilogue(JSContext* cx)
|
||||
}
|
||||
|
||||
bool
|
||||
InterpreterFrame::checkThis(JSContext* cx)
|
||||
InterpreterFrame::checkReturn(JSContext* cx, HandleValue thisv)
|
||||
{
|
||||
if (script()->isDerivedClassConstructor()) {
|
||||
MOZ_ASSERT(isNonEvalFunctionFrame());
|
||||
MOZ_ASSERT(fun()->isClassConstructor());
|
||||
MOZ_ASSERT(script()->isDerivedClassConstructor());
|
||||
MOZ_ASSERT(isFunctionFrame());
|
||||
MOZ_ASSERT(callee().isClassConstructor());
|
||||
|
||||
if (thisValue().isMagic(JS_UNINITIALIZED_LEXICAL)) {
|
||||
RootedFunction func(cx, fun());
|
||||
return ThrowUninitializedThis(cx, this);
|
||||
}
|
||||
HandleValue retVal = returnValue();
|
||||
if (retVal.isObject())
|
||||
return true;
|
||||
|
||||
if (!retVal.isUndefined()) {
|
||||
ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, retVal, nullptr);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InterpreterFrame::checkReturn(JSContext* cx)
|
||||
{
|
||||
if (script()->isDerivedClassConstructor()) {
|
||||
MOZ_ASSERT(isNonEvalFunctionFrame());
|
||||
MOZ_ASSERT(callee().isClassConstructor());
|
||||
if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL))
|
||||
return ThrowUninitializedThis(cx, this);
|
||||
|
||||
HandleValue retVal = returnValue();
|
||||
if (retVal.isObject())
|
||||
return true;
|
||||
|
||||
if (!retVal.isUndefined()) {
|
||||
ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, retVal, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkThis(cx))
|
||||
return false;
|
||||
}
|
||||
setReturnValue(thisv);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1306,23 +1292,11 @@ FrameIter::argsObj() const
|
||||
return abstractFramePtr().argsObj();
|
||||
}
|
||||
|
||||
bool
|
||||
FrameIter::computeThis(JSContext* cx) const
|
||||
{
|
||||
MOZ_ASSERT(!done() && !isAsmJS());
|
||||
assertSameCompartment(cx, scopeChain(cx));
|
||||
return ComputeThis(cx, abstractFramePtr());
|
||||
}
|
||||
|
||||
Value
|
||||
FrameIter::computedThisValue() const
|
||||
FrameIter::originalFunctionThis(JSContext* cx) const
|
||||
{
|
||||
return abstractFramePtr().thisValue();
|
||||
}
|
||||
MOZ_ASSERT(isNonEvalFunctionFrame());
|
||||
|
||||
Value
|
||||
FrameIter::thisv(JSContext* cx) const
|
||||
{
|
||||
switch (data_.state_) {
|
||||
case DONE:
|
||||
case ASMJS:
|
||||
|
@ -453,8 +453,7 @@ class InterpreterFrame
|
||||
bool prologue(JSContext* cx);
|
||||
void epilogue(JSContext* cx);
|
||||
|
||||
bool checkReturn(JSContext* cx);
|
||||
bool checkThis(JSContext* cx);
|
||||
bool checkReturn(JSContext* cx, HandleValue thisv);
|
||||
|
||||
bool initFunctionScopeObjects(JSContext* cx);
|
||||
|
||||
@ -742,14 +741,6 @@ class InterpreterFrame
|
||||
return argv()[-1];
|
||||
}
|
||||
|
||||
void setDerivedConstructorThis(HandleObject thisv) {
|
||||
MOZ_ASSERT(isNonEvalFunctionFrame());
|
||||
MOZ_ASSERT(script()->isDerivedClassConstructor());
|
||||
MOZ_ASSERT(callee().isClassConstructor());
|
||||
MOZ_ASSERT(thisValue().isMagic(JS_UNINITIALIZED_LEXICAL));
|
||||
argv()[-1] = ObjectValue(*thisv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callee
|
||||
*
|
||||
@ -2011,17 +2002,11 @@ class FrameIter
|
||||
bool hasArgsObj() const;
|
||||
ArgumentsObject& argsObj() const;
|
||||
|
||||
// Ensure that computedThisValue is correct, see ComputeThis.
|
||||
bool computeThis(JSContext* cx) const;
|
||||
// thisv() may not always be correct, even after computeThis. In case when
|
||||
// the frame is an Ion frame, the computed this value cannot be saved to
|
||||
// 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;
|
||||
// Get the original |this| value passed to this function. May not be the
|
||||
// actual this-binding (for instance, derived class constructors will
|
||||
// change their this-value later and non-strict functions will box
|
||||
// primitives).
|
||||
Value originalFunctionThis(JSContext* cx) const;
|
||||
|
||||
Value newTarget() const;
|
||||
|
||||
|
@ -461,8 +461,8 @@ MarkObjectStateChange(ExclusiveContext* cx, JSObject* obj)
|
||||
}
|
||||
|
||||
/* Interface helpers for JSScript*. */
|
||||
extern void TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, TypeSet::Type type);
|
||||
extern void TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, const Value& rval);
|
||||
extern void TypeDynamicResult(JSContext* cx, JSScript* script, jsbytecode* pc, TypeSet::Type type);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Script interface functions
|
||||
@ -557,6 +557,12 @@ TypeScript::Monitor(JSContext* cx, JSScript* script, jsbytecode* pc, const js::V
|
||||
TypeMonitorResult(cx, script, pc, rval);
|
||||
}
|
||||
|
||||
/* static */ inline void
|
||||
TypeScript::Monitor(JSContext* cx, JSScript* script, jsbytecode* pc, TypeSet::Type type)
|
||||
{
|
||||
TypeMonitorResult(cx, script, pc, type);
|
||||
}
|
||||
|
||||
/* static */ inline void
|
||||
TypeScript::Monitor(JSContext* cx, const js::Value& rval)
|
||||
{
|
||||
|
@ -3221,6 +3221,20 @@ js::FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap)
|
||||
MOZ_ASSERT(added == script->nTypeSets());
|
||||
}
|
||||
|
||||
void
|
||||
js::TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, TypeSet::Type type)
|
||||
{
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
|
||||
if (types->hasType(type))
|
||||
return;
|
||||
|
||||
InferSpew(ISpewOps, "bytecodeType: %p %05u: %s",
|
||||
script, script->pcToOffset(pc), TypeSet::TypeString(type));
|
||||
types->addType(cx, type);
|
||||
}
|
||||
|
||||
void
|
||||
js::TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, const js::Value& rval)
|
||||
{
|
||||
@ -3231,16 +3245,7 @@ js::TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, const js:
|
||||
if (!script->hasBaselineScript())
|
||||
return;
|
||||
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
TypeSet::Type type = TypeSet::GetValueType(rval);
|
||||
StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
|
||||
if (types->hasType(type))
|
||||
return;
|
||||
|
||||
InferSpew(ISpewOps, "bytecodeType: %p %05u: %s",
|
||||
script, script->pcToOffset(pc), TypeSet::TypeString(type));
|
||||
types->addType(cx, type);
|
||||
TypeMonitorResult(cx, script, pc, TypeSet::GetValueType(rval));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
@ -3872,7 +3877,11 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g
|
||||
if (!iter.isConstructing() || !iter.matchCallee(cx, function))
|
||||
continue;
|
||||
|
||||
Value thisv = iter.thisv(cx);
|
||||
// Derived class constructors initialize their this-binding later and
|
||||
// we shouldn't run the definite properties analysis on them.
|
||||
MOZ_ASSERT(!iter.script()->isDerivedClassConstructor());
|
||||
|
||||
Value thisv = iter.originalFunctionThis(cx);
|
||||
if (!thisv.isObject() ||
|
||||
thisv.toObject().hasLazyGroup() ||
|
||||
thisv.toObject().group() != group)
|
||||
|
@ -1022,6 +1022,8 @@ class TypeScript
|
||||
*/
|
||||
static inline void Monitor(JSContext* cx, JSScript* script, jsbytecode* pc,
|
||||
const js::Value& val);
|
||||
static inline void Monitor(JSContext* cx, JSScript* script, jsbytecode* pc,
|
||||
TypeSet::Type type);
|
||||
static inline void Monitor(JSContext* cx, const js::Value& rval);
|
||||
|
||||
/* Monitor an assignment at a SETELEM on a non-integer identifier. */
|
||||
|
@ -29,7 +29,7 @@ namespace js {
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 321;
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 322;
|
||||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user