Bug 1071646 - Introduce JSOP_BINDVAR to support Annex B.3.3.3. (r=jorendorff)

This commit is contained in:
Shu-yu Guo 2015-12-18 13:18:19 -08:00 committed by Cameron Kaiser
parent 37bf861a92
commit 884bd2ab53
5 changed files with 44 additions and 20 deletions

View File

@ -4413,7 +4413,7 @@ BytecodeEmitter::emitVariables(ParseNode* pn, VarEmitOption emitOption)
* i' to be hoisted out of the loop.
*/
MOZ_ASSERT(binding->isOp(JSOP_NOP));
MOZ_ASSERT(emitOption != DefineVars);
MOZ_ASSERT(emitOption != DefineVars && emitOption != AnnexB);
/*
* To allow the front end to rewrite var f = x; as f = x; when a
@ -4474,13 +4474,18 @@ BytecodeEmitter::emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode
op == JSOP_STRICTSETGNAME)
{
MOZ_ASSERT(emitOption != PushInitialValues);
JSOp bindOp;
if (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME)
bindOp = JSOP_BINDNAME;
else
bindOp = JSOP_BINDGNAME;
if (!emitIndex32(bindOp, atomIndex))
return false;
if (op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME) {
if (!emitIndex32(JSOP_BINDGNAME, atomIndex))
return false;
} else if (emitOption == AnnexB) {
// Annex B vars always go on the nearest variable environment,
// even if scopes on the chain contain same-named bindings.
if (!emit1(JSOP_BINDVAR))
return false;
} else {
if (!emitIndex32(JSOP_BINDNAME, atomIndex))
return false;
}
}
bool oldEmittingForInit = emittingForInit;
@ -4504,7 +4509,7 @@ BytecodeEmitter::emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode
// If we are not initializing, nothing to pop. If we are initializing
// lets, we must emit the pops.
if (emitOption == InitializeVars) {
if (emitOption == InitializeVars || emitOption == AnnexB) {
MOZ_ASSERT_IF(binding->isDefn(), initializer == binding->pn_expr);
if (!binding->pn_scopecoord.isFree()) {
if (!emitVarOp(binding, op))
@ -6267,12 +6272,13 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
// definitions are seen for the second time, we need to emit the
// assignment that assigns the function to the outer 'var' binding.
if (assignmentForAnnexB) {
if (!emitTree(assignmentForAnnexB))
return false;
// If we did not synthesize a new binding and only a simple
// assignment, manually pop the result.
if (assignmentForAnnexB->isKind(PNK_ASSIGN)) {
if (assignmentForAnnexB->isKind(PNK_VAR)) {
if (!emitVariables(assignmentForAnnexB, AnnexB))
return false;
} else {
MOZ_ASSERT(assignmentForAnnexB->isKind(PNK_ASSIGN));
if (!emitTree(assignmentForAnnexB))
return false;
if (!emit1(JSOP_POP))
return false;
}

View File

@ -121,7 +121,12 @@ enum VarEmitOption {
// Emit code to evaluate initializer expressions and leave those values on
// the stack. This is used to implement `for (let/const ...;;)` and
// deprecated `let` blocks.
PushInitialValues
PushInitialValues,
// Like InitializeVars, but bind using BINDVAR instead of
// BINDNAME/BINDGNAME. Only used for emitting declarations synthesized for
// Annex B block-scoped function semantics.
AnnexB,
};
struct BytecodeEmitter

View File

@ -2368,8 +2368,8 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
// Under non-strict mode, try ES6 Annex B.3.3 semantics. If
// making an additional 'var' binding of the same name does
// not throw an early error, do so. This 'var' binding would
// be assigned the function object in situ, e.g., when its
// declaration is reached, not at the start of the block.
// be assigned the function object when its declaration is
// reached, not at the start of the block.
annexDef = pc->decls().lookupFirst(funName);
if (annexDef) {

View File

@ -1753,7 +1753,6 @@ CASE(JSOP_UNUSED209)
CASE(JSOP_UNUSED210)
CASE(JSOP_UNUSED211)
CASE(JSOP_UNUSED212)
CASE(JSOP_UNUSED213)
CASE(JSOP_UNUSED219)
CASE(JSOP_UNUSED220)
CASE(JSOP_UNUSED221)
@ -2089,6 +2088,12 @@ CASE(JSOP_BINDNAME)
}
END_CASE(JSOP_BINDNAME)
CASE(JSOP_BINDVAR)
{
PUSH_OBJECT(REGS.fp()->varObj());
}
END_CASE(JSOP_BINDVAR)
#define BITWISE_OP(OP) \
JS_BEGIN_MACRO \
int32_t i, j; \

View File

@ -2052,7 +2052,15 @@
macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \
macro(JSOP_UNUSED211, 211, "unused211", NULL, 1, 0, 0, JOF_BYTE) \
macro(JSOP_UNUSED212, 212, "unused212", NULL, 1, 0, 0, JOF_BYTE) \
macro(JSOP_UNUSED213, 213, "unused213", NULL, 1, 0, 0, JOF_BYTE) \
/*
* Pushes the nearest 'var' environment.
*
* Category: Variables and Scopes
* Type: Free Variables
* Operands:
* Stack: => scope
*/ \
macro(JSOP_BINDVAR, 213, "bindvar", NULL, 1, 0, 1, JOF_BYTE) \
/*
* Pushes the global scope onto the stack if the script doesn't have a
* non-syntactic global scope. Otherwise will act like JSOP_BINDNAME.