mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-18 15:55:36 +00:00
Bug 1071646 - Support labelled function declarations in sloppy mode per Annex B.3.2. (r=jorendorff)
This commit is contained in:
parent
00a88f7e6e
commit
394e48b09b
@ -1372,20 +1372,20 @@ BytecodeEmitter::emitVarIncDec(ParseNode* pn)
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::atBodyLevel() const
|
||||
BytecodeEmitter::atBodyLevel(StmtInfoBCE* stmt) const
|
||||
{
|
||||
// 'eval' and non-syntactic scripts are always under an invisible lexical
|
||||
// scope, but since it is not syntactic, it should still be considered at
|
||||
// body level.
|
||||
if (sc->staticScope()->is<StaticEvalObject>()) {
|
||||
bool bl = !innermostStmt()->enclosing;
|
||||
MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK);
|
||||
MOZ_ASSERT_IF(bl, innermostStmt()->staticScope
|
||||
->as<StaticBlockObject>()
|
||||
.enclosingStaticScope() == sc->staticScope());
|
||||
bool bl = !stmt->enclosing;
|
||||
MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK);
|
||||
MOZ_ASSERT_IF(bl, stmt->staticScope
|
||||
->as<StaticBlockObject>()
|
||||
.enclosingStaticScope() == sc->staticScope());
|
||||
return bl;
|
||||
}
|
||||
return !innermostStmt() || sc->isModuleBox();
|
||||
return !stmt || sc->isModuleBox();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
@ -3098,11 +3098,23 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
|
||||
if (!enterBlockScope(&stmtInfo, cases->pn_objbox, JSOP_UNINITIALIZED, 0))
|
||||
return false;
|
||||
|
||||
stmtInfo.type = StmtType::SWITCH;
|
||||
stmtInfo.update = top = offset();
|
||||
|
||||
// Advance |cases| to refer to the switch case list.
|
||||
cases = cases->expr();
|
||||
|
||||
// A switch statement may contain hoisted functions inside its
|
||||
// cases. The PNX_FUNCDEFS flag is propagated from the STATEMENTLIST
|
||||
// bodies of the cases to the case list.
|
||||
if (cases->pn_xflags & PNX_FUNCDEFS) {
|
||||
for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) {
|
||||
if (caseNode->pn_right->pn_xflags & PNX_FUNCDEFS) {
|
||||
if (!emitHoistedFunctionsInList(caseNode->pn_right))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stmtInfo.type = StmtType::SWITCH;
|
||||
stmtInfo.update = top = offset();
|
||||
} else {
|
||||
MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST));
|
||||
top = offset();
|
||||
@ -5336,6 +5348,11 @@ BytecodeEmitter::emitHoistedFunctionsInList(ParseNode* list)
|
||||
MOZ_ASSERT(list->pn_xflags & PNX_FUNCDEFS);
|
||||
|
||||
for (ParseNode* pn = list->pn_head; pn; pn = pn->pn_next) {
|
||||
if (!sc->strict()) {
|
||||
while (pn->isKind(PNK_LABEL))
|
||||
pn = pn->as<LabeledStatement>().statement();
|
||||
}
|
||||
|
||||
if (pn->isKind(PNK_ANNEXB_FUNCTION) ||
|
||||
(pn->isKind(PNK_FUNCTION) && pn->functionIsHoisted()))
|
||||
{
|
||||
@ -6370,7 +6387,18 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
* For modules, we record the function and instantiate the binding during
|
||||
* ModuleDeclarationInstantiation(), before the script is run.
|
||||
*/
|
||||
if (!atBodyLevel()) {
|
||||
|
||||
// Check for functions that were parsed under labeled statements per ES6
|
||||
// Annex B.3.2.
|
||||
bool blockScopedFunction = !atBodyLevel();
|
||||
if (!sc->strict() && blockScopedFunction) {
|
||||
StmtInfoBCE* stmt = innermostStmt();
|
||||
while (stmt && stmt->type == StmtType::LABEL)
|
||||
stmt = stmt->enclosing;
|
||||
blockScopedFunction = !atBodyLevel(stmt);
|
||||
}
|
||||
|
||||
if (blockScopedFunction) {
|
||||
if (!emitIndexOp(JSOP_LAMBDA, index))
|
||||
return false;
|
||||
MOZ_ASSERT(pn->getOp() == JSOP_INITLEXICAL);
|
||||
@ -6381,7 +6409,6 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
} else if (sc->isGlobalContext()) {
|
||||
MOZ_ASSERT(pn->pn_scopecoord.isFree());
|
||||
MOZ_ASSERT(pn->getOp() == JSOP_NOP);
|
||||
MOZ_ASSERT(atBodyLevel());
|
||||
switchToPrologue();
|
||||
if (!emitIndex32(JSOP_DEFFUN, index))
|
||||
return false;
|
||||
|
@ -249,7 +249,10 @@ struct BytecodeEmitter
|
||||
return parser->blockScopes[dn->pn_blockid];
|
||||
}
|
||||
|
||||
bool atBodyLevel() const;
|
||||
bool atBodyLevel(StmtInfoBCE* stmt) const;
|
||||
bool atBodyLevel() const {
|
||||
return atBodyLevel(innermostStmt());
|
||||
}
|
||||
uint32_t computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut);
|
||||
bool isAliasedName(BytecodeEmitter* bceOfDef, ParseNode* pn);
|
||||
bool computeDefinitionIsAliased(BytecodeEmitter* bceOfDef, Definition* dn, JSOp* op);
|
||||
|
@ -444,18 +444,40 @@ class FullParseHandler
|
||||
return pn;
|
||||
}
|
||||
|
||||
template <typename PC>
|
||||
bool isFunctionStmt(ParseNode* stmt, PC* pc) {
|
||||
if (!pc->sc->strict()) {
|
||||
while (stmt->isKind(PNK_LABEL))
|
||||
stmt = stmt->as<LabeledStatement>().statement();
|
||||
}
|
||||
|
||||
return stmt->isKind(PNK_FUNCTION) || stmt->isKind(PNK_ANNEXB_FUNCTION);
|
||||
}
|
||||
|
||||
template <typename PC>
|
||||
void addStatementToList(ParseNode* list, ParseNode* stmt, PC* pc) {
|
||||
MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST));
|
||||
|
||||
if (stmt->isKind(PNK_FUNCTION) || stmt->isKind(PNK_ANNEXB_FUNCTION)) {
|
||||
list->append(stmt);
|
||||
|
||||
if (isFunctionStmt(stmt, pc)) {
|
||||
// PNX_FUNCDEFS notifies the emitter that the block contains
|
||||
// body-level function definitions that should be processed
|
||||
// before the rest of nodes.
|
||||
list->pn_xflags |= PNX_FUNCDEFS;
|
||||
}
|
||||
}
|
||||
|
||||
list->append(stmt);
|
||||
template <typename PC>
|
||||
void addCaseStatementToList(ParseNode* list, ParseNode* casepn, PC* pc) {
|
||||
MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST));
|
||||
MOZ_ASSERT(casepn->isKind(PNK_CASE));
|
||||
MOZ_ASSERT(casepn->pn_right->isKind(PNK_STATEMENTLIST));
|
||||
|
||||
list->append(casepn);
|
||||
|
||||
if (casepn->pn_right->pn_xflags & PNX_FUNCDEFS)
|
||||
list->pn_xflags |= PNX_FUNCDEFS;
|
||||
}
|
||||
|
||||
bool prependInitialYield(ParseNode* stmtList, ParseNode* genName) {
|
||||
|
@ -2330,7 +2330,30 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
MOZ_ASSERT(assignmentForAnnexBOut);
|
||||
*assignmentForAnnexBOut = nullptr;
|
||||
|
||||
if (pc->atBodyLevel()) {
|
||||
// In sloppy mode, ES6 Annex B.3.2 allows labelled function
|
||||
// declarations. Otherwise it is a parse error.
|
||||
bool bodyLevelFunction = pc->atBodyLevel();
|
||||
if (!bodyLevelFunction) {
|
||||
StmtInfoPC* stmt = pc->innermostStmt();
|
||||
if (stmt->type == StmtType::LABEL) {
|
||||
if (pc->sc->strict()) {
|
||||
report(ParseError, false, null(), JSMSG_FUNCTION_LABEL);
|
||||
return false;
|
||||
}
|
||||
|
||||
stmt = pc->innermostNonLabelStmt();
|
||||
// A switch statement is always braced, so it's okay to label
|
||||
// functions in sloppy mode under switch.
|
||||
if (stmt && stmt->type != StmtType::BLOCK && stmt->type != StmtType::SWITCH) {
|
||||
report(ParseError, false, null(), JSMSG_SLOPPY_FUNCTION_LABEL);
|
||||
return false;
|
||||
}
|
||||
|
||||
bodyLevelFunction = pc->atBodyLevel(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
if (bodyLevelFunction) {
|
||||
if (!bindBodyLevelFunctionName(funName, pn_))
|
||||
return false;
|
||||
} else {
|
||||
@ -2338,11 +2361,11 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
Node synthesizedDeclarationList = null();
|
||||
|
||||
if (!pc->sc->strict()) {
|
||||
// Under non-strict mode, try 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.
|
||||
// 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.
|
||||
|
||||
annexDef = pc->decls().lookupFirst(funName);
|
||||
if (annexDef) {
|
||||
@ -3113,16 +3136,16 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
|
||||
Maybe<AutoPushStmtInfoPC> synthesizedStmtInfoForAnnexB;
|
||||
Node synthesizedBlockForAnnexB = null();
|
||||
StmtInfoPC *stmt = pc->innermostStmt();
|
||||
if (!pc->sc->strict() && stmt &&
|
||||
(stmt->type == StmtType::IF || stmt->type == StmtType::ELSE))
|
||||
{
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
if (!pc->sc->strict() && stmt) {
|
||||
if (stmt->type == StmtType::IF || stmt->type == StmtType::ELSE) {
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
|
||||
synthesizedStmtInfoForAnnexB.emplace(*this, StmtType::BLOCK);
|
||||
synthesizedBlockForAnnexB = pushLexicalScope(*synthesizedStmtInfoForAnnexB);
|
||||
if (!synthesizedBlockForAnnexB)
|
||||
return null();
|
||||
synthesizedStmtInfoForAnnexB.emplace(*this, StmtType::BLOCK);
|
||||
synthesizedBlockForAnnexB = pushLexicalScope(*synthesizedStmtInfoForAnnexB);
|
||||
if (!synthesizedBlockForAnnexB)
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
RootedPropertyName name(context);
|
||||
@ -4554,28 +4577,45 @@ Parser<ParseHandler>::variables(YieldHandling yieldHandling,
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::checkAndPrepareLexical(bool isConst, const TokenPos& errorPos)
|
||||
Parser<FullParseHandler>::checkAndPrepareLexical(PrepareLexicalKind prepareWhat,
|
||||
const TokenPos& errorPos)
|
||||
{
|
||||
/*
|
||||
* This is a lexical declaration. We must be directly under a block, but
|
||||
* not an implicit block created due to 'for (let ...)'. If we pass this
|
||||
* error test, make the enclosing StmtInfoPC be our scope. Further let
|
||||
* declarations in this block will find this scope statement and use the
|
||||
* same block object.
|
||||
* This is a lexical declaration. We must be directly under a block for
|
||||
* 'let' and 'const' declarations. If we pass this error test, make the
|
||||
* enclosing StmtInfoPC be our scope. Further let declarations in this
|
||||
* block will find this scope statement and use the same block object.
|
||||
*
|
||||
* Function declarations behave like 'let', except that they are allowed
|
||||
* per ES6 Annex B.3.2 to be labeled, unlike plain 'let' and 'const'
|
||||
* declarations.
|
||||
*
|
||||
* If we are the first let declaration in this block (i.e., when the
|
||||
* enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then
|
||||
* we also need to set pc->blockNode to be our PNK_LEXICALSCOPE.
|
||||
*/
|
||||
StmtInfoPC* stmt = pc->innermostStmt();
|
||||
|
||||
// ES6 Annex B.3.2 does not apply in strict mode, and labeled functions in
|
||||
// strict mode should have been rejected by checkFunctionDefinition.
|
||||
MOZ_ASSERT_IF(pc->innermostStmt() &&
|
||||
pc->innermostStmt()->type == StmtType::LABEL &&
|
||||
prepareWhat == PrepareFunction,
|
||||
!pc->sc->strict());
|
||||
|
||||
StmtInfoPC* stmt = prepareWhat == PrepareFunction
|
||||
? pc->innermostNonLabelStmt()
|
||||
: pc->innermostStmt();
|
||||
if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) {
|
||||
reportWithOffset(ParseError, false, errorPos.begin, JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
|
||||
isConst ? "const" : "lexical");
|
||||
reportWithOffset(ParseError, false, errorPos.begin,
|
||||
stmt->type == StmtType::LABEL
|
||||
? JSMSG_LEXICAL_DECL_LABEL
|
||||
: JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
|
||||
prepareWhat == PrepareConst ? "const" : "lexical");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt) {
|
||||
MOZ_ASSERT(pc->atBodyLevel());
|
||||
MOZ_ASSERT_IF(prepareWhat != PrepareFunction, pc->atBodyLevel());
|
||||
|
||||
/*
|
||||
* Self-hosted code must be usable against *any* global object,
|
||||
@ -4586,7 +4626,7 @@ Parser<FullParseHandler>::checkAndPrepareLexical(bool isConst, const TokenPos& e
|
||||
bool isGlobal = !pc->sc->isFunctionBox() && stmt == pc->innermostScopeStmt();
|
||||
if (options().selfHostingMode && isGlobal) {
|
||||
report(ParseError, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL,
|
||||
isConst ? "'const'" : "'let'");
|
||||
prepareWhat == PrepareConst ? "'const'" : "'let'");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -4612,8 +4652,12 @@ Parser<FullParseHandler>::checkAndPrepareLexical(bool isConst, const TokenPos& e
|
||||
* catch block (catch is a lexical scope by definition).
|
||||
*/
|
||||
MOZ_ASSERT(stmt->canBeBlockScope() && stmt->type != StmtType::CATCH);
|
||||
|
||||
pc->stmtStack.makeInnermostLexicalScope(*blockObj);
|
||||
if (prepareWhat == PrepareFunction) {
|
||||
stmt->isBlockScope = true;
|
||||
pc->stmtStack.linkAsInnermostScopeStmt(stmt, *blockObj);
|
||||
} else {
|
||||
pc->stmtStack.makeInnermostLexicalScope(*blockObj);
|
||||
}
|
||||
MOZ_ASSERT(!blockScopes[stmt->blockid]);
|
||||
blockScopes[stmt->blockid].set(blockObj);
|
||||
|
||||
@ -4645,21 +4689,22 @@ CurrentLexicalStaticBlock(ParseContext<FullParseHandler>* pc)
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::prepareAndBindInitializedLexicalWithNode(HandlePropertyName name,
|
||||
bool isConst,
|
||||
PrepareLexicalKind prepareWhat,
|
||||
ParseNode* pn,
|
||||
const TokenPos& pos)
|
||||
{
|
||||
BindData<FullParseHandler> data(context);
|
||||
if (!checkAndPrepareLexical(isConst, pos))
|
||||
if (!checkAndPrepareLexical(prepareWhat, pos))
|
||||
return false;
|
||||
data.initLexical(HoistVars, isConst ? JSOP_DEFCONST : JSOP_DEFLET,
|
||||
data.initLexical(HoistVars, prepareWhat == PrepareConst ? JSOP_DEFCONST : JSOP_DEFLET,
|
||||
CurrentLexicalStaticBlock(pc), JSMSG_TOO_MANY_LOCALS);
|
||||
return bindInitialized(&data, name, pn);
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode*
|
||||
Parser<FullParseHandler>::makeInitializedLexicalBinding(HandlePropertyName name, bool isConst,
|
||||
Parser<FullParseHandler>::makeInitializedLexicalBinding(HandlePropertyName name,
|
||||
PrepareLexicalKind prepareWhat,
|
||||
const TokenPos& pos)
|
||||
{
|
||||
ParseNode* dn = newBindingNode(name, false);
|
||||
@ -4667,7 +4712,7 @@ Parser<FullParseHandler>::makeInitializedLexicalBinding(HandlePropertyName name,
|
||||
return null();
|
||||
handler.setPosition(dn, pos);
|
||||
|
||||
if (!prepareAndBindInitializedLexicalWithNode(name, isConst, dn, pos))
|
||||
if (!prepareAndBindInitializedLexicalWithNode(name, prepareWhat, dn, pos))
|
||||
return null();
|
||||
|
||||
return dn;
|
||||
@ -4680,7 +4725,7 @@ Parser<FullParseHandler>::bindLexicalFunctionName(HandlePropertyName funName,
|
||||
{
|
||||
MOZ_ASSERT(!pc->atBodyLevel());
|
||||
pn->pn_blockid = pc->blockid();
|
||||
return prepareAndBindInitializedLexicalWithNode(funName, /* isConst = */ false, pn, pos());
|
||||
return prepareAndBindInitializedLexicalWithNode(funName, PrepareFunction, pn, pos());
|
||||
}
|
||||
|
||||
template <>
|
||||
@ -4689,7 +4734,7 @@ Parser<FullParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, bool i
|
||||
{
|
||||
handler.disableSyntaxParser();
|
||||
|
||||
if (!checkAndPrepareLexical(isConst, pos()))
|
||||
if (!checkAndPrepareLexical(isConst ? PrepareConst : PrepareLet, pos()))
|
||||
return null();
|
||||
|
||||
/*
|
||||
@ -5218,7 +5263,7 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
default:
|
||||
tokenStream.ungetToken();
|
||||
RootedPropertyName name(context, context->names().starDefaultStar);
|
||||
binding = makeInitializedLexicalBinding(name, true, pos());
|
||||
binding = makeInitializedLexicalBinding(name, PrepareConst, pos());
|
||||
if (!binding)
|
||||
return null();
|
||||
kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
|
||||
@ -5967,7 +6012,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
|
||||
afterReturn = true;
|
||||
}
|
||||
}
|
||||
handler.addList(body, stmt);
|
||||
handler.addStatementToList(body, stmt, pc);
|
||||
}
|
||||
|
||||
// In ES6, lexical bindings cannot be accessed until initialized. If
|
||||
@ -5986,7 +6031,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
|
||||
Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body);
|
||||
if (!casepn)
|
||||
return null();
|
||||
handler.addList(caseList, casepn);
|
||||
handler.addCaseStatementToList(caseList, casepn, pc);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -6813,7 +6858,7 @@ Parser<FullParseHandler>::classDefinition(YieldHandling yieldHandling,
|
||||
ParseNode* nameNode = null();
|
||||
ParseNode* methodsOrBlock = classMethods;
|
||||
if (name) {
|
||||
ParseNode* innerBinding = makeInitializedLexicalBinding(name, true, namePos);
|
||||
ParseNode* innerBinding = makeInitializedLexicalBinding(name, PrepareConst, namePos);
|
||||
if (!innerBinding)
|
||||
return null();
|
||||
|
||||
@ -6824,7 +6869,7 @@ Parser<FullParseHandler>::classDefinition(YieldHandling yieldHandling,
|
||||
|
||||
ParseNode* outerBinding = null();
|
||||
if (classContext == ClassStatement) {
|
||||
outerBinding = makeInitializedLexicalBinding(name, false, namePos);
|
||||
outerBinding = makeInitializedLexicalBinding(name, PrepareLet, namePos);
|
||||
if (!outerBinding)
|
||||
return null();
|
||||
}
|
||||
|
@ -289,6 +289,7 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
||||
|
||||
StmtInfoPC* innermostStmt() const { return stmtStack.innermost(); }
|
||||
StmtInfoPC* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); }
|
||||
StmtInfoPC* innermostNonLabelStmt() const { return stmtStack.innermostNonLabel(); }
|
||||
JSObject* innermostStaticScope() const {
|
||||
if (StmtInfoPC* stmt = innermostScopeStmt())
|
||||
return stmt->staticScope;
|
||||
@ -302,19 +303,23 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
||||
// function f1() { function f2() { } }
|
||||
// if (cond) { function f3() { if (cond) { function f4() { } } } }
|
||||
//
|
||||
bool atBodyLevel() {
|
||||
bool atBodyLevel(StmtInfoPC* stmt) {
|
||||
// 'eval' and non-syntactic scripts are always under an invisible
|
||||
// lexical scope, but since it is not syntactic, it should still be
|
||||
// considered at body level.
|
||||
if (sc->staticScope()->is<StaticEvalObject>()) {
|
||||
bool bl = !innermostStmt()->enclosing;
|
||||
MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK);
|
||||
MOZ_ASSERT_IF(bl, innermostStmt()->staticScope
|
||||
->template as<StaticBlockObject>()
|
||||
.enclosingStaticScope() == sc->staticScope());
|
||||
bool bl = !stmt->enclosing;
|
||||
MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK);
|
||||
MOZ_ASSERT_IF(bl, stmt->staticScope
|
||||
->template as<StaticBlockObject>()
|
||||
.enclosingStaticScope() == sc->staticScope());
|
||||
return bl;
|
||||
}
|
||||
return !innermostStmt();
|
||||
return !stmt;
|
||||
}
|
||||
|
||||
bool atBodyLevel() {
|
||||
return atBodyLevel(innermostStmt());
|
||||
}
|
||||
|
||||
bool atGlobalLevel() {
|
||||
@ -842,10 +847,17 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
|
||||
Node objectLiteral(YieldHandling yieldHandling);
|
||||
|
||||
bool checkAndPrepareLexical(bool isConst, const TokenPos& errorPos);
|
||||
bool prepareAndBindInitializedLexicalWithNode(HandlePropertyName name, bool isConst,
|
||||
enum PrepareLexicalKind {
|
||||
PrepareLet,
|
||||
PrepareConst,
|
||||
PrepareFunction
|
||||
};
|
||||
bool checkAndPrepareLexical(PrepareLexicalKind prepareWhat, const TokenPos& errorPos);
|
||||
bool prepareAndBindInitializedLexicalWithNode(HandlePropertyName name,
|
||||
PrepareLexicalKind prepareWhat,
|
||||
ParseNode* pn, const TokenPos& pos);
|
||||
Node makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, const TokenPos& pos);
|
||||
Node makeInitializedLexicalBinding(HandlePropertyName name, PrepareLexicalKind prepareWhat,
|
||||
const TokenPos& pos);
|
||||
|
||||
Node newBindingNode(PropertyName* name, bool functionScope, VarContext varContext = HoistVars);
|
||||
|
||||
|
@ -587,6 +587,12 @@ class MOZ_STACK_CLASS StmtInfoStack
|
||||
|
||||
StmtInfo* innermost() const { return innermostStmt_; }
|
||||
StmtInfo* innermostScopeStmt() const { return innermostScopeStmt_; }
|
||||
StmtInfo* innermostNonLabel() const {
|
||||
StmtInfo* stmt = innermost();
|
||||
while (stmt && stmt->type == StmtType::LABEL)
|
||||
stmt = stmt->enclosing;
|
||||
return stmt;
|
||||
}
|
||||
|
||||
void push(StmtInfo* stmt, StmtType type) {
|
||||
stmt->type = type;
|
||||
|
@ -290,6 +290,7 @@ class SyntaxParseHandler
|
||||
|
||||
Node newStatementList(unsigned blockid, const TokenPos& pos) { return NodeGeneric; }
|
||||
void addStatementToList(Node list, Node stmt, ParseContext<SyntaxParseHandler>* pc) {}
|
||||
void addCaseStatementToList(Node list, Node stmt, ParseContext<SyntaxParseHandler>* pc) {}
|
||||
bool prependInitialYield(Node stmtList, Node gen) { return true; }
|
||||
Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; }
|
||||
|
||||
|
@ -270,6 +270,9 @@ MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found")
|
||||
MSG_DEF(JSMSG_LET_CLASS_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a class")
|
||||
MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
|
||||
MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
|
||||
MSG_DEF(JSMSG_LEXICAL_DECL_LABEL, 1, JSEXN_SYNTAXERR, "{0} declarations cannot be labelled")
|
||||
MSG_DEF(JSMSG_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions cannot be labelled")
|
||||
MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks")
|
||||
MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW, 0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
|
||||
MSG_DEF(JSMSG_MALFORMED_ESCAPE, 1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence")
|
||||
MSG_DEF(JSMSG_MISSING_BINARY_DIGITS, 0, JSEXN_SYNTAXERR, "missing binary digits after '0b'")
|
||||
|
@ -29,11 +29,11 @@ namespace js {
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 337;
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 338;
|
||||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
static_assert(JSErr_Limit == 429,
|
||||
static_assert(JSErr_Limit == 432,
|
||||
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
||||
"removed MSG_DEFs from js.msg, you should increment "
|
||||
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
||||
|
Loading…
Reference in New Issue
Block a user