Bug 1132183 - Make |this| a real binding, remove lazy this computation. r=efaust,shu

This commit is contained in:
Jan de Mooij 2015-11-21 14:33:13 +01:00
parent 4e3be28022
commit 8f55c26a1c
60 changed files with 1239 additions and 703 deletions

View File

@ -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;

View File

@ -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

View File

@ -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");
}

View File

@ -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;

View File

@ -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");

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
};

View File

@ -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());

View File

@ -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);

View File

@ -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; }

View File

@ -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;
}

View File

@ -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();
}
}

View 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); }");

View 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);

View 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.

View 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");

View 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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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
//

View File

@ -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;

View File

@ -24,8 +24,6 @@ namespace jit {
_(TypeUpdate_ObjectGroup) \
_(TypeUpdate_PrimitiveSet) \
\
_(This_Fallback) \
\
_(NewArray_Fallback) \
_(NewObject_Fallback) \
_(NewObject_WithTemplate) \

View File

@ -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)
{

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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_;

View File

@ -62,7 +62,6 @@ namespace jit {
_(GetArgumentsObjectArg) \
_(SetArgumentsObjectArg) \
_(ComputeThis) \
_(LoadArrowThis) \
_(Call) \
_(ApplyArgs) \
_(ArraySplice) \

View File

@ -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))

View File

@ -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>>) \

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -89,7 +89,6 @@
_(SetArgumentsObjectArg) \
_(ReturnFromCtor) \
_(ComputeThis) \
_(LoadArrowThis) \
_(BitNotI) \
_(BitNotV) \
_(BitOpI) \

View File

@ -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

View File

@ -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);
}

View File

@ -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());

View File

@ -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;
}

View File

@ -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();

View File

@ -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

View File

@ -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, "") \

View File

@ -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);
}

View File

@ -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

View File

@ -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, &REGS.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;

View File

@ -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);

View File

@ -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) \
\
/*

View File

@ -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)

View File

@ -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);

View File

@ -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:

View File

@ -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;

View File

@ -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)
{

View File

@ -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)

View File

@ -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. */

View File

@ -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);