From 3bd300e2634d5f591aa97df1ff4e5b57b2673c43 Mon Sep 17 00:00:00 2001 From: Martok Date: Sun, 2 Apr 2023 00:45:27 +0200 Subject: [PATCH] Issue #2173 - Add accessors to LexicalScopeNode Based-on: m-c 1479659/7 --- js/src/builtin/ReflectParse.cpp | 41 +++++++------- js/src/frontend/BytecodeEmitter.cpp | 48 ++++++++-------- js/src/frontend/BytecodeEmitter.h | 2 +- js/src/frontend/FoldConstants.cpp | 19 ++++--- js/src/frontend/FullParseHandler.h | 10 ++-- js/src/frontend/NameFunctions.cpp | 11 ++-- js/src/frontend/ParseNode.cpp | 11 +--- js/src/frontend/ParseNode.h | 84 +++++++++++++++------------- js/src/frontend/Parser.cpp | 81 ++++++++++++++------------- js/src/frontend/Parser.h | 14 ++--- js/src/frontend/SyntaxParseHandler.h | 13 +++-- js/src/wasm/AsmJS.cpp | 17 +++--- 12 files changed, 180 insertions(+), 171 deletions(-) diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index 0cf36b1b71..5dd170b3eb 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2380,8 +2380,8 @@ ASTSerializer::tryStatement(TernaryNode* tryNode, MutableHandleValue dst) if (!guarded.reserve(catchList->count())) return false; - for (ParseNode* catchScope : catchList->contents()) { - MOZ_ASSERT(catchScope->isKind(PNK_LEXICALSCOPE)); + for (ParseNode* catchNode : catchList->contents()) { + LexicalScopeNode* catchScope = &catchNode->as(); RootedValue clause(cx); bool isGuarded; if (!catchClause(&catchScope->scopeBody()->as(), &isGuarded, &clause)) @@ -2480,7 +2480,7 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) return builder.emptyStatement(&pn->pn_pos, dst); case PNK_LEXICALSCOPE: - pn = pn->scopeBody(); + pn = pn->as().scopeBody(); if (!pn->isKind(PNK_STATEMENTLIST)) return statement(pn, dst); MOZ_FALLTHROUGH; @@ -2578,8 +2578,9 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) if (head->isKind(PNK_FORIN) || head->isKind(PNK_FOROF)) { RootedValue var(cx); - if (initNode->isKind(PNK_LEXICALSCOPE)) { - if (!variableDeclaration(&initNode->scopeBody()->as(), true, &var)) + if (initNode->is()) { + LexicalScopeNode* scopeNode = &initNode->as(); + if (!variableDeclaration(&scopeNode->scopeBody()->as(), true, &var)) return false; } else if (!initNode->isKind(PNK_VAR) && !initNode->isKind(PNK_LET) && @@ -2810,8 +2811,8 @@ ASTSerializer::comprehensionBlock(ForNode* forNode, MutableHandleValue dst) bool isForOf = in->isKind(PNK_FOROF); ListNode* decl; - if (in->kid1()->isKind(PNK_LEXICALSCOPE)) - decl = &in->kid1()->scopeBody()->as(); + if (in->kid1()->is()) + decl = &in->kid1()->as().scopeBody()->as(); else decl = &in->kid1()->as(); MOZ_ASSERT(decl->count() == 1); @@ -2840,8 +2841,8 @@ ASTSerializer::comprehension(ParseNode* pn, MutableHandleValue dst) // 1. The kind that was in ES4 for a while: [z for (x in y)] // 2. The kind that was in ES6 for a while: [for (x of y) z] // They have slightly different parse trees and scoping. - bool isLegacy = pn->isKind(PNK_LEXICALSCOPE); - ParseNode* next = isLegacy ? pn->scopeBody() : pn; + bool isLegacy = pn->is(); + ParseNode* next = isLegacy ? pn->as().scopeBody() : pn; LOCAL_ASSERT(next->isKind(PNK_COMPREHENSIONFOR)); NodeVector blocks(cx); @@ -2886,8 +2887,8 @@ ASTSerializer::generatorExpression(ParseNode* pn, MutableHandleValue dst) // Just as there are two kinds of array comprehension (see // ASTSerializer::comprehension), there are legacy and modern generator // expression. - bool isLegacy = pn->isKind(PNK_LEXICALSCOPE); - ParseNode* next = isLegacy ? pn->scopeBody() : pn; + bool isLegacy = pn->is(); + ParseNode* next = isLegacy ? pn->as().scopeBody() : pn; LOCAL_ASSERT(next->isKind(PNK_COMPREHENSIONFOR)); NodeVector blocks(cx); @@ -3653,29 +3654,29 @@ ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& MutableHandleValue body, MutableHandleValue rest) { ListNode* argsList; - ParseNode* pnbody; + ParseNode* bodyNode; /* Extract the args and body separately. */ if (pn->isKind(PNK_PARAMSBODY)) { argsList = &pn->as(); - pnbody = argsList->last(); + bodyNode = argsList->last(); } else { argsList = nullptr; - pnbody = pn; + bodyNode = pn; } - if (pnbody->isKind(PNK_LEXICALSCOPE)) - pnbody = pnbody->scopeBody(); + if (bodyNode->is()) + bodyNode = bodyNode->as().scopeBody(); /* Serialize the arguments and body. */ - switch (pnbody->getKind()) { + switch (bodyNode->getKind()) { case PNK_RETURN: /* expression closure, no destructured args */ return functionArgs(pn, argsList, args, defaults, rest) && - expression(pnbody->as().kid(), body); + expression(bodyNode->as().kid(), body); case PNK_STATEMENTLIST: /* statement closure */ { - ParseNode* firstNode = pnbody->as().head(); + ParseNode* firstNode = bodyNode->as().head(); // Skip over initial yield in generator. if (firstNode && firstNode->isKind(PNK_INITIALYIELD)) { @@ -3692,7 +3693,7 @@ ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& } return functionArgs(pn, argsList, args, defaults, rest) && - functionBody(firstNode, &pnbody->pn_pos, body); + functionBody(firstNode, &bodyNode->pn_pos, body); } default: diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 3d3f5ce670..b8f085b152 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -592,8 +592,8 @@ BytecodeEmitter::emitLoopHead(ParseNode* nextpn, JumpTarget* top) * instruction. nextpn is often a block, in which case the next * instruction typically comes from the first statement inside. */ - if (nextpn->isKind(PNK_LEXICALSCOPE)) - nextpn = nextpn->scopeBody(); + if (nextpn->is()) + nextpn = nextpn->as().scopeBody(); if (nextpn->isKind(PNK_STATEMENTLIST)) { if (ParseNode* firstStatement = nextpn->as().head()) { nextpn = firstStatement; @@ -612,8 +612,8 @@ BytecodeEmitter::emitLoopEntry(ParseNode* nextpn, JumpList entryJump) { if (nextpn) { /* Update the line number, as for LOOPHEAD. */ - if (nextpn->isKind(PNK_LEXICALSCOPE)) - nextpn = nextpn->scopeBody(); + if (nextpn->is()) + nextpn = nextpn->as().scopeBody(); if (nextpn->isKind(PNK_STATEMENTLIST)) { if (ParseNode* firstStatement = nextpn->as().head()) { nextpn = firstStatement; @@ -1487,7 +1487,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case PNK_LEXICALSCOPE: MOZ_ASSERT(pn->isArity(PN_SCOPE)); - return checkSideEffects(pn->scopeBody(), answer); + return checkSideEffects(pn->as().scopeBody(), answer); // We could methodically check every interpolated expression, but it's // probably not worth the trouble. Treat template strings as effect-free @@ -2080,8 +2080,7 @@ BytecodeEmitter::emitNumberOp(double dval) MOZ_NEVER_INLINE bool BytecodeEmitter::emitSwitch(SwitchStatement* switchStmt) { - ParseNode& lexical = switchStmt->lexicalForCaseList(); - MOZ_ASSERT(lexical.isKind(PNK_LEXICALSCOPE)); + LexicalScopeNode& lexical = switchStmt->lexicalForCaseList(); ListNode* cases = &lexical.scopeBody()->as(); MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST)); @@ -2343,20 +2342,21 @@ BytecodeEmitter::emitScript(ParseNode* body) setFunctionBodyEndPos(body->pn_pos); if (sc->isEvalContext() && !sc->strict() && - body->isKind(PNK_LEXICALSCOPE) && !body->isEmptyScope()) + body->is() && !body->as().isEmptyScope()) { // Sloppy eval scripts may need to emit DEFFUNs in the prologue. If there is // an immediately enclosed lexical scope, we need to enter the lexical // scope in the prologue for the DEFFUNs to pick up the right // environment chain. EmitterScope lexicalEmitterScope(this); + LexicalScopeNode* scope = &body->as(); switchToPrologue(); - if (!lexicalEmitterScope.enterLexical(this, ScopeKind::Lexical, body->scopeBindings())) + if (!lexicalEmitterScope.enterLexical(this, ScopeKind::Lexical, scope->scopeBindings())) return false; switchToMain(); - if (!emitLexicalScopeBody(body->scopeBody())) + if (!emitLexicalScopeBody(scope->scopeBody())) return false; if (!lexicalEmitterScope.leave(this)) @@ -4272,13 +4272,13 @@ BytecodeEmitter::emitTry(TernaryNode* tryNode) // code if appropriate, and is also used for the catch-all trynote for // capturing exceptions thrown from catch{} blocks. // - for (ParseNode* lexicalScope : catchList->as().contents()) { + for (ParseNode* scopeNode : catchList->as().contents()) { + LexicalScopeNode* catchScope = &scopeNode->as(); if (!tryCatch.emitCatch()) return false; // Emit the lexical scope and catch body. - MOZ_ASSERT(lexicalScope->isKind(PNK_LEXICALSCOPE)); - if (!emitTree(lexicalScope)) + if (!emitTree(catchScope)) return false; } } @@ -4387,14 +4387,12 @@ BytecodeEmitter::emitLexicalScopeBody(ParseNode* body, EmitLineNumberNote emitLi // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See // the comment on emitSwitch. MOZ_NEVER_INLINE bool -BytecodeEmitter::emitLexicalScope(ParseNode* pn) +BytecodeEmitter::emitLexicalScope(LexicalScopeNode* lexicalScope) { - MOZ_ASSERT(pn->isKind(PNK_LEXICALSCOPE)); - TDZCheckCache tdzCache(this); - ParseNode* body = pn->scopeBody(); - if (pn->isEmptyScope()) + ParseNode* body = lexicalScope->scopeBody(); + if (lexicalScope->isEmptyScope()) return emitLexicalScopeBody(body); // Update line number notes before emitting TDZ poison in @@ -4431,7 +4429,7 @@ BytecodeEmitter::emitLexicalScope(ParseNode* pn) } else kind = ScopeKind::Lexical; - if (!emitterScope.enterLexical(this, kind, pn->scopeBindings())) + if (!emitterScope.enterLexical(this, kind, lexicalScope->scopeBindings())) return false; if (body->isKind(PNK_FOR)) { @@ -5310,7 +5308,7 @@ BytecodeEmitter::emitComprehensionForInOrOfVariables(ParseNode* pn, bool* lexica // for-in/of loops, and we haven't extended these requirements to // comprehension syntax. - *lexicalScope = pn->isKind(PNK_LEXICALSCOPE); + *lexicalScope = pn->is(); if (*lexicalScope) { // This is initially-ES7-tracked syntax, now with considerably murkier // outlook. The scope work is done by the caller by instantiating an @@ -5369,9 +5367,10 @@ BytecodeEmitter::emitComprehensionForOf(ForNode* forNode) Maybe emitterScope; ParseNode* loopVariableName; if (lexicalScope) { - loopVariableName = parser->handler.singleBindingFromDeclaration(&loopDecl->scopeBody()->as()); + LexicalScopeNode* scopeNode = &loopDecl->as(); + loopVariableName = parser->handler.singleBindingFromDeclaration(&scopeNode->scopeBody()->as()); emitterScope.emplace(this); - if (!emitterScope->enterComprehensionFor(this, loopDecl->scopeBindings())) + if (!emitterScope->enterComprehensionFor(this, scopeNode->scopeBindings())) return false; } else { loopVariableName = parser->handler.singleBindingFromDeclaration(&loopDecl->as()); @@ -5515,7 +5514,8 @@ BytecodeEmitter::emitComprehensionForIn(ForNode* forNode) Maybe emitterScope; if (lexicalScope) { emitterScope.emplace(this); - if (!emitterScope->enterComprehensionFor(this, loopDecl->scopeBindings())) + LexicalScopeNode* scopeNode = &loopDecl->as(); + if (!emitterScope->enterComprehensionFor(this, scopeNode->scopeBindings())) return false; } @@ -8945,7 +8945,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: break; case PNK_LEXICALSCOPE: - if (!emitLexicalScope(pn)) + if (!emitLexicalScope(&pn->as())) return false; break; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 1d2628131c..d9334d3445 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -588,7 +588,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitWith(BinaryNode* withNode); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLabeledStatement(const LabeledStatement* pn); - MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(ParseNode* pn); + MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(LexicalScopeNode* lexicalScope); MOZ_MUST_USE bool emitLexicalScopeBody(ParseNode* body, EmitLineNumberNote emitLineNote = EMIT_LINENOTE); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitSwitch(SwitchStatement* switchStmt); diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index a5383c1c7c..5a3f6e8c9d 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -207,8 +207,8 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) if (tryNode->kid2()) { if (ListNode* catchList = &tryNode->kid2()->as()) { - for (ParseNode* lexicalScope : catchList->contents()) { - MOZ_ASSERT(lexicalScope->isKind(PNK_LEXICALSCOPE)); + for (ParseNode* scopeNode : catchList->contents()) { + LexicalScopeNode* lexicalScope = &scopeNode->as(); TernaryNode* catchNode = &lexicalScope->scopeBody()->as(); MOZ_ASSERT(catchNode->isKind(PNK_CATCH)); @@ -286,14 +286,14 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) } case PNK_LEXICALSCOPE: { - MOZ_ASSERT(node->isArity(PN_SCOPE)); - ParseNode* expr = node->scopeBody(); + LexicalScopeNode* scope = &node->as(); + ParseNode* expr = scope->scopeBody(); if (expr->isKind(PNK_FOR) || expr->isKind(PNK_FUNCTION)) return ContainsHoistedDeclaration(cx, expr, result); MOZ_ASSERT(expr->isKind(PNK_STATEMENTLIST)); - return ListContainsHoistedDeclaration(cx, &node->scopeBody()->as(), result); + return ListContainsHoistedDeclaration(cx, &scope->scopeBody()->as(), result); } // List nodes with all non-null children. @@ -1966,11 +1966,12 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo case PNK_DOT: return FoldDottedProperty(cx, &pn->as(), parser, inGenexpLambda); - case PNK_LEXICALSCOPE: - MOZ_ASSERT(pn->isArity(PN_SCOPE)); - if (!pn->scopeBody()) + case PNK_LEXICALSCOPE: { + LexicalScopeNode* node = &pn->as(); + if (!node->scopeBody()) return true; - return Fold(cx, &pn->pn_u.scope.body, parser, inGenexpLambda); + return Fold(cx, node->unsafeScopeBodyReference(), parser, inGenexpLambda); + } case PNK_NAME: return FoldName(cx, &pn->as(), parser, inGenexpLambda); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 6b81d153ab..e0fc057530 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -648,7 +648,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) } SwitchStatementType newSwitchStatement(uint32_t begin, Node discriminant, - Node lexicalForCaseList, bool hasDefault) + LexicalScopeNodeType lexicalForCaseList, bool hasDefault) { return new_(begin, discriminant, lexicalForCaseList, hasDefault); } @@ -714,7 +714,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) return new_(lhs, index, lhs->pn_pos.begin, end); } - inline MOZ_MUST_USE bool addCatchBlock(ListNodeType catchList, ParseNode* lexicalScope, + inline MOZ_MUST_USE bool addCatchBlock(ListNodeType catchList, LexicalScopeNodeType lexicalScope, ParseNode* catchBinding, ParseNode* catchGuard, ParseNode* catchBody); @@ -759,7 +759,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) void addFunctionFormalParameter(CodeNodeType funNode, Node argpn) { addList(/* list = */ funNode->body(), /* child = */ argpn); } - void setFunctionBody(CodeNodeType funNode, Node body) { + void setFunctionBody(CodeNodeType funNode, LexicalScopeNodeType body) { MOZ_ASSERT(funNode->body()->isKind(PNK_PARAMSBODY)); addList(/* list = */ funNode->body(), /* child = */ body); } @@ -772,7 +772,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) return new_(PNK_NEW, JSOP_NEW, TokenPos(begin, args->pn_pos.end), ctor, args); } - ParseNode* newLexicalScope(LexicalScope::Data* bindings, ParseNode* body) { + LexicalScopeNodeType newLexicalScope(LexicalScope::Data* bindings, Node body) { return new_(bindings, body); } @@ -1002,7 +1002,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) }; inline bool -FullParseHandler::addCatchBlock(ListNodeType catchList, ParseNode* lexicalScope, +FullParseHandler::addCatchBlock(ListNodeType catchList, LexicalScopeNodeType lexicalScope, ParseNode* catchBinding, ParseNode* catchGuard, ParseNode* catchBody) { ParseNode* catchpn = newTernary(PNK_CATCH, catchBinding, catchGuard, catchBody); diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index affad7a239..c3e709699f 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -828,10 +828,10 @@ class NameResolver case PNK_CATCHLIST: { ListNode* catchList = &cur->as(); for (ParseNode* catchNode : catchList->contents()) { - MOZ_ASSERT(catchNode->isKind(PNK_LEXICALSCOPE)); - MOZ_ASSERT(catchNode->scopeBody()->isKind(PNK_CATCH)); - MOZ_ASSERT(catchNode->scopeBody()->isArity(PN_TERNARY)); - if (!resolve(catchNode->scopeBody(), prefix)) + LexicalScopeNode* catchScope = &catchNode->as(); + MOZ_ASSERT(catchScope->scopeBody()->isKind(PNK_CATCH)); + MOZ_ASSERT(catchScope->scopeBody()->isArity(PN_TERNARY)); + if (!resolve(catchScope->scopeBody(), prefix)) return false; } break; @@ -862,8 +862,7 @@ class NameResolver break; case PNK_LEXICALSCOPE: - MOZ_ASSERT(cur->isArity(PN_SCOPE)); - if (!resolve(cur->scopeBody(), prefix)) + if (!resolve(cur->as().scopeBody(), prefix)) return false; break; diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 0e1492f0eb..31efe76558 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -96,8 +96,6 @@ enum class PushResult { Recyclable, CleanUpLater }; static PushResult PushCodeNodeChildren(CodeNode* node, NodeStack* stack) { - MOZ_ASSERT(node->isArity(PN_CODE)); - /* * Function nodes are linked into the function box tree, and may appear * on method lists. Both of those lists are singly-linked, so trying to @@ -133,10 +131,8 @@ PushNameNodeChildren(NameNode* node, NodeStack* stack) } static PushResult -PushScopeNodeChildren(ParseNode* node, NodeStack* stack) +PushScopeNodeChildren(LexicalScopeNode* node, NodeStack* stack) { - MOZ_ASSERT(node->isArity(PN_SCOPE)); - if (node->scopeBody()) stack->push(node->scopeBody()); node->setScopeBody(nullptr); @@ -146,7 +142,6 @@ PushScopeNodeChildren(ParseNode* node, NodeStack* stack) static PushResult PushListNodeChildren(ListNode* node, NodeStack* stack) { - MOZ_ASSERT(node->isArity(PN_LIST)); node->checkConsistency(); stack->pushList(node); @@ -508,7 +503,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) return PushNameNodeChildren(&pn->as(), stack); case PNK_LEXICALSCOPE: - return PushScopeNodeChildren(pn, stack); + return PushScopeNodeChildren(&pn->as(), stack); case PNK_FUNCTION: case PNK_MODULE: @@ -701,7 +696,7 @@ ParseNode::dump(int indent) as().dump(indent); return; case PN_SCOPE: - ((LexicalScopeNode*) this)->dump(indent); + as().dump(indent); return; } fprintf(stderr, "#", diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 12d79ab4f8..fffd28edd3 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -312,9 +312,9 @@ IsTypeofKind(ParseNodeKind kind) * kid1: try block * kid2: null or PNK_CATCHLIST list * kid3: null or finally block - * PNK_CATCHLIST list pn_head: list of PNK_LEXICALSCOPE nodes, one per - * catch-block, each with scopeBody pointing - * to a PNK_CATCH node + * PNK_CATCHLIST (ListNode) + * head: list of PNK_LEXICALSCOPE nodes, one per catch-block, + * each with scopeBody pointing to a PNK_CATCH node * PNK_CATCH (TernaryNode) * kid1: PNK_NAME, PNK_ARRAY, or PNK_OBJECT catch binding node (PNK_ARRAY or PNK_OBJECT if destructuring) or null if optional catch binding @@ -524,8 +524,9 @@ IsTypeofKind(ParseNodeKind kind) * PNK_SETTHIS (BinaryNode) * left: '.this' Name * right: SuperCall - * PNK_LEXICALSCOPE scope pn_u.scope.bindings: scope bindings - * pn_u.scope.body: scope body + * PNK_LEXICALSCOPE (LexicalScopeNode) + * scopeBindings: scope bindings + * scopeBody: scope body * PNK_GENERATOR (NullaryNode) * PNK_INITIALYIELD (UnaryNode) * kid: generator object @@ -567,6 +568,8 @@ enum ParseNodeArity \ macro(CodeNode, CodeNodeType, asCode) \ \ + macro(LexicalScopeNode, LexicalScopeNodeType, asLexicalScope) \ + \ macro(ListNode, ListNodeType, asList) \ macro(CallSiteNode, CallSiteNodeType, asCallSite) \ \ @@ -732,6 +735,8 @@ class ParseNode ParseNode* body; /* module or function body */ } code; struct { + private: + friend class LexicalScopeNode; LexicalScope::Data* bindings; ParseNode* body; } scope; @@ -759,28 +764,6 @@ class ParseNode inline PropertyName* name() const; - bool isEmptyScope() const { - MOZ_ASSERT(pn_arity == PN_SCOPE); - return !pn_u.scope.bindings; - } - - Handle scopeBindings() const { - MOZ_ASSERT(!isEmptyScope()); - // Bindings' GC safety depend on the presence of an AutoKeepAtoms that - // the rest of the frontend also depends on. - return Handle::fromMarkedLocation(&pn_u.scope.bindings); - } - - ParseNode* scopeBody() const { - MOZ_ASSERT(pn_arity == PN_SCOPE); - return pn_u.scope.body; - } - - void setScopeBody(ParseNode* body) { - MOZ_ASSERT(pn_arity == PN_SCOPE); - pn_u.scope.body = body; - } - /* True if pn is a parsenode representing a literal constant. */ bool isLiteral() const { return isKind(PNK_NUMBER) || @@ -1523,8 +1506,9 @@ class NumericLiteral : public ParseNode } }; -struct LexicalScopeNode : public ParseNode +class LexicalScopeNode : public ParseNode { + public: LexicalScopeNode(LexicalScope::Data* bindings, ParseNode* body) : ParseNode(PNK_LEXICALSCOPE, JSOP_NOP, PN_SCOPE, body->pn_pos) { @@ -1533,12 +1517,37 @@ struct LexicalScopeNode : public ParseNode } static bool test(const ParseNode& node) { - return node.isKind(PNK_LEXICALSCOPE); + bool match = node.isKind(PNK_LEXICALSCOPE); + MOZ_ASSERT_IF(match, node.isArity(PN_SCOPE)); + return match; } #ifdef DEBUG void dump(int indent); #endif + + Handle scopeBindings() const { + MOZ_ASSERT(!isEmptyScope()); + // Bindings' GC safety depend on the presence of an AutoKeepAtoms that + // the rest of the frontend also depends on. + return Handle::fromMarkedLocation(&pn_u.scope.bindings); + } + + ParseNode* scopeBody() const { + return pn_u.scope.body; + } + + void setScopeBody(ParseNode* body) { + pn_u.scope.body = body; + } + + bool isEmptyScope() const { + return !pn_u.scope.bindings; + } + + ParseNode** unsafeScopeBodyReference() { + return &pn_u.scope.body; + } }; class LabeledStatement : public NameNode @@ -1983,14 +1992,13 @@ class ClassMethod : public BinaryNode class SwitchStatement : public BinaryNode { public: - SwitchStatement(uint32_t begin, ParseNode* discriminant, ParseNode* lexicalForCaseList, + SwitchStatement(uint32_t begin, ParseNode* discriminant, LexicalScopeNode* lexicalForCaseList, bool hasDefault) : BinaryNode(PNK_SWITCH, JSOP_NOP, TokenPos(begin, lexicalForCaseList->pn_pos.end), discriminant, lexicalForCaseList) { #ifdef DEBUG - MOZ_ASSERT(lexicalForCaseList->isKind(PNK_LEXICALSCOPE)); ListNode* cases = &lexicalForCaseList->scopeBody()->as(); MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST)); bool found = false; @@ -2016,8 +2024,8 @@ class SwitchStatement : public BinaryNode ParseNode& discriminant() const { return *left(); } - ParseNode& lexicalForCaseList() const {; - return *right(); + LexicalScopeNode& lexicalForCaseList() const { + return right()->as(); } bool hasDefault() const { return pn_u.binary.hasDefault; @@ -2090,15 +2098,13 @@ class ClassNode : public TernaryNode if (methodsOrBlock->isKind(PNK_CLASSMETHODLIST)) return &methodsOrBlock->as(); - MOZ_ASSERT(methodsOrBlock->is()); - ListNode* list = &methodsOrBlock->scopeBody()->as(); + ListNode* list = &methodsOrBlock->as().scopeBody()->as(); MOZ_ASSERT(list->isKind(PNK_CLASSMETHODLIST)); return list; } Handle scopeBindings() const { ParseNode* scope = kid3(); - MOZ_ASSERT(scope->is()); - return scope->scopeBindings(); + return scope->as().scopeBindings(); } }; @@ -2221,8 +2227,8 @@ FunctionFormalParametersList(ParseNode* fn, unsigned* numFormals) MOZ_ASSERT(argsBody->isKind(PNK_PARAMSBODY)); *numFormals = argsBody->count(); if (*numFormals > 0 && - argsBody->last()->isKind(PNK_LEXICALSCOPE) && - argsBody->last()->scopeBody()->isKind(PNK_STATEMENTLIST)) + argsBody->last()->is() && + argsBody->last()->as().scopeBody()->isKind(PNK_STATEMENTLIST)) { (*numFormals)--; } diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 528d57adb2..24be7af2d2 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2066,16 +2066,16 @@ Parser::newLexicalScopeData(ParseContext::Scope& scope) } template <> -SyntaxParseHandler::Node +SyntaxParseHandler::LexicalScopeNodeType Parser::finishLexicalScope(ParseContext::Scope& scope, Node body) { if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) return null(); - return body; + return handler.newLexicalScope(body); } template <> -ParseNode* +LexicalScopeNode* Parser::finishLexicalScope(ParseContext::Scope& scope, ParseNode* body) { if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) @@ -2108,7 +2108,7 @@ IsArgumentsUsedInLegacyGenerator(ExclusiveContext* cx, Scope* scope) } template <> -ParseNode* +LexicalScopeNode* Parser::evalBody(EvalSharedContext* evalsc) { ParseContext evalpc(this, evalsc, /* newDirectives = */ nullptr); @@ -2119,21 +2119,24 @@ Parser::evalBody(EvalSharedContext* evalsc) if (!varScope.init(pc)) return nullptr; - // All evals have an implicit non-extensible lexical scope. - ParseContext::Scope lexicalScope(this); - if (!lexicalScope.init(pc)) - return nullptr; + LexicalScopeNode* body; + { + // All evals have an implicit non-extensible lexical scope. + ParseContext::Scope lexicalScope(this); + if (!lexicalScope.init(pc)) + return nullptr; - ParseNode* body = statementList(YieldIsName); - if (!body) - return nullptr; + ParseNode* list = statementList(YieldIsName); + if (!list) + return nullptr; - if (!checkStatementsEOF()) - return nullptr; + if (!checkStatementsEOF()) + return nullptr; - body = finishLexicalScope(lexicalScope, body); - if (!body) - return nullptr; + body = finishLexicalScope(lexicalScope, list); + if (!body) + return nullptr; + } // It's an error to use 'arguments' in a legacy generator expression. // @@ -2170,8 +2173,10 @@ Parser::evalBody(EvalSharedContext* evalsc) } #endif - if (!FoldConstants(context, &body, this)) + ParseNode* node = body; + if (!FoldConstants(context, &node, this)) return nullptr; + body = handler.asLexicalScope(node); Maybe bindings = newEvalScopeData(pc->varScope()); if (!bindings) @@ -2663,7 +2668,7 @@ Parser::declareFunctionArgumentsObject() } template -typename ParseHandler::Node +typename ParseHandler::LexicalScopeNodeType Parser::functionBody(InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, FunctionBodyType type) { @@ -3721,7 +3726,7 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, // Whereas the |yield| in the function body is always parsed as a name. // The same goes when parsing |await| in arrow functions. YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind()); - Node body; + LexicalScopeNodeType body; { AutoAwaitIsKeyword awaitIsKeyword(this, funbox->isAsync()); body = functionBody(inHandling, bodyYieldHandling, kind, bodyType); @@ -4620,7 +4625,7 @@ Parser::destructuringDeclarationWithoutYieldOrAwait(DeclarationKin } template -typename ParseHandler::Node +typename ParseHandler::LexicalScopeNodeType Parser::blockStatement(YieldHandling yieldHandling, unsigned errorNumber) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); @@ -6532,7 +6537,7 @@ Parser::switchStatement(YieldHandling yieldHandling) handler.addCaseStatementToList(caseList, caseClause); } - Node lexicalForCaseList = finishLexicalScope(scope, caseList); + LexicalScopeNodeType lexicalForCaseList = finishLexicalScope(scope, caseList); if (!lexicalForCaseList) return null(); @@ -6983,7 +6988,7 @@ Parser::tryStatement(YieldHandling yieldHandling) * finally nodes are TOK_LC statement lists. */ - Node innerBlock; + LexicalScopeNodeType innerBlock; { MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); @@ -6994,11 +6999,11 @@ Parser::tryStatement(YieldHandling yieldHandling) if (!scope.init(pc)) return null(); - innerBlock = statementList(yieldHandling); - if (!innerBlock) + ListNodeType list = statementList(yieldHandling); + if (!list) return null(); - innerBlock = finishLexicalScope(scope, innerBlock); + innerBlock = finishLexicalScope(scope, list); if (!innerBlock) return null(); @@ -7018,8 +7023,6 @@ Parser::tryStatement(YieldHandling yieldHandling) return null(); do { - Node pnblock; - /* Check for another catch after unconditional catch. */ if (hasUnconditionalCatch) { error(JSMSG_CATCH_AFTER_GENERAL); @@ -7100,28 +7103,28 @@ Parser::tryStatement(YieldHandling yieldHandling) MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); } - Node catchBody = catchBlockStatement(yieldHandling, scope); + LexicalScopeNodeType catchBody = catchBlockStatement(yieldHandling, scope); if (!catchBody) return null(); if (!catchGuard) hasUnconditionalCatch = true; - pnblock = finishLexicalScope(scope, catchBody); - if (!pnblock) + LexicalScopeNodeType catchScope = finishLexicalScope(scope, catchBody); + if (!catchScope) return null(); - if (!handler.addCatchBlock(catchList, pnblock, catchName, catchGuard, catchBody)) + if (!handler.addCatchBlock(catchList, catchScope, catchName, catchGuard, catchBody)) return null(); handler.setEndPosition(catchList, pos().end); - handler.setEndPosition(pnblock, pos().end); + handler.setEndPosition(catchScope, pos().end); if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); } while (tt == TOK_CATCH); } - Node finallyBlock = null(); + LexicalScopeNodeType finallyBlock = null(); if (tt == TOK_FINALLY) { MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY); @@ -7133,11 +7136,11 @@ Parser::tryStatement(YieldHandling yieldHandling) if (!scope.init(pc)) return null(); - finallyBlock = statementList(yieldHandling); - if (!finallyBlock) + ListNodeType list = statementList(yieldHandling); + if (!list) return null(); - finallyBlock = finishLexicalScope(scope, finallyBlock); + finallyBlock = finishLexicalScope(scope, list); if (!finallyBlock) return null(); @@ -7156,7 +7159,7 @@ Parser::tryStatement(YieldHandling yieldHandling) } template -typename ParseHandler::Node +typename ParseHandler::LexicalScopeNodeType Parser::catchBlockStatement(YieldHandling yieldHandling, ParseContext::Scope& catchParamScope) { @@ -7427,7 +7430,7 @@ Parser::classDefinition(YieldHandling yieldHandling, if (!innerName) return null(); - Node classBlock = finishLexicalScope(*innerScope, classMethods); + LexicalScopeNodeType classBlock = finishLexicalScope(*innerScope, classMethods); if (!classBlock) return null(); @@ -8937,7 +8940,7 @@ Parser::comprehensionFor(GeneratorKind comprehensionKind) return null(); // Finish the lexical scope after parsing the tail. - Node lexicalScope = finishLexicalScope(scope, decls); + LexicalScopeNodeType lexicalScope = finishLexicalScope(scope, decls); if (!lexicalScope) return null(); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 0a52c18701..1a35f321fe 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1132,7 +1132,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) // Eval scripts are distinguished from global scripts in that in ES6, per // 18.2.1.1 steps 9 and 10, all eval scripts are executed under a fresh // lexical scope. - Node evalBody(EvalSharedContext* evalsc); + LexicalScopeNodeType evalBody(EvalSharedContext* evalsc); // Parse the body of a global script. ListNodeType globalBody(GlobalSharedContext* globalsc); @@ -1196,8 +1196,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) ListNodeType statementList(YieldHandling yieldHandling); - Node blockStatement(YieldHandling yieldHandling, - unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND); + LexicalScopeNodeType blockStatement(YieldHandling yieldHandling, + unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND); BinaryNodeType doWhileStatement(YieldHandling yieldHandling); BinaryNodeType whileStatement(YieldHandling yieldHandling); @@ -1217,7 +1217,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) BinaryNodeType withStatement(YieldHandling yieldHandling); UnaryNodeType throwStatement(YieldHandling yieldHandling); TernaryNodeType tryStatement(YieldHandling yieldHandling); - Node catchBlockStatement(YieldHandling yieldHandling, ParseContext::Scope& catchParamScope); + LexicalScopeNodeType catchBlockStatement(YieldHandling yieldHandling, ParseContext::Scope& catchParamScope); DebuggerStatementType debuggerStatement(); Node variableStatement(YieldHandling yieldHandling); @@ -1363,8 +1363,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) // Parse a function body. Pass StatementListBody if the body is a list of // statements; pass ExpressionBody if the body is a single expression. enum FunctionBodyType { StatementListBody, ExpressionBody }; - Node functionBody(InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, - FunctionBodyType type); + LexicalScopeNodeType functionBody(InHandling inHandling, YieldHandling yieldHandling, + FunctionSyntaxKind kind, FunctionBodyType type); UnaryNodeType unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op, uint32_t begin); @@ -1514,7 +1514,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) bool hasParameterExprs); mozilla::Maybe newVarScopeData(ParseContext::Scope& scope); mozilla::Maybe newLexicalScopeData(ParseContext::Scope& scope); - Node finishLexicalScope(ParseContext::Scope& scope, Node body); + LexicalScopeNodeType finishLexicalScope(ParseContext::Scope& scope, Node body); Node propertyName(YieldHandling yieldHandling, const mozilla::Maybe& maybeDecl, ListNodeType propList, diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index dc59e7da93..6a69e41398 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -316,6 +316,10 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) { return NodeGeneric; } ClassNodeType newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; } + LexicalScopeNodeType newLexicalScope(Node body) { + return NodeLexicalDeclaration; + } + BinaryNodeType newNewTarget(NullaryNodeType newHolder, NullaryNodeType targetHolder) { return NodeGeneric; } @@ -362,7 +366,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) } BinaryNodeType newDoWhileStatement(Node body, Node cond, const TokenPos& pos) { return NodeGeneric; } BinaryNodeType newWhileStatement(uint32_t begin, Node cond, Node body) { return NodeGeneric; } - SwitchStatementType newSwitchStatement(uint32_t begin, Node discriminant, Node lexicalForCaseList, bool hasDefault) + SwitchStatementType newSwitchStatement(uint32_t begin, Node discriminant, + LexicalScopeNodeType lexicalForCaseList, bool hasDefault) { return NodeGeneric; } @@ -399,8 +404,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) Node newOptionalPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeOptionalElement; } - MOZ_MUST_USE bool addCatchBlock(ListNodeType catchList, Node letBlock, Node catchBinding, - Node catchGuard, Node catchBody) { return true; } + MOZ_MUST_USE bool addCatchBlock(ListNodeType catchList, LexicalScopeNodeType lexicalScope, + Node catchBinding, Node catchGuard, Node catchBody) { return true; } MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(CodeNodeType funNode, Node pn) { return true; } @@ -412,7 +417,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) bool setComprehensionLambdaBody(CodeNodeType funNode, ListNodeType body) { return true; } void setFunctionFormalParametersAndBody(CodeNodeType funNode, ListNodeType paramsBody) {} - void setFunctionBody(CodeNodeType funNode, Node body) {} + void setFunctionBody(CodeNodeType funNode, LexicalScopeNodeType body) {} void setFunctionBox(CodeNodeType funNode, FunctionBox* funbox) {} void addFunctionFormalParameter(CodeNodeType funNode, Node argpn) {} diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index ff943aed40..1a5f4dbccb 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -668,8 +668,7 @@ static inline ParseNode* FunctionStatementList(CodeNode* funNode) { MOZ_ASSERT(funNode->body()->isKind(PNK_PARAMSBODY)); - ParseNode* last = funNode->body()->as().last(); - MOZ_ASSERT(last->isKind(PNK_LEXICALSCOPE)); + LexicalScopeNode* last = &funNode->body()->as().last()->as(); MOZ_ASSERT(last->isEmptyScope()); ParseNode* body = last->scopeBody(); MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST)); @@ -6807,10 +6806,11 @@ CheckSwitch(FunctionValidator& f, ParseNode* switchStmt) ParseNode* switchExpr = BinaryLeft(switchStmt); ParseNode* switchBody = BinaryRight(switchStmt); - if (switchBody->isKind(PNK_LEXICALSCOPE)) { - if (!switchBody->isEmptyScope()) - return f.fail(switchBody, "switch body may not contain lexical declarations"); - switchBody = switchBody->scopeBody(); + if (switchBody->is()) { + LexicalScopeNode* scope = &switchBody->as(); + if (!scope->isEmptyScope()) + return f.fail(scope, "switch body may not contain lexical declarations"); + switchBody = scope->scopeBody(); } ParseNode* stmt = ListHead(switchBody); @@ -6984,10 +6984,9 @@ CheckStatementList(FunctionValidator& f, ParseNode* stmtList, const NameVector* } static bool -CheckLexicalScope(FunctionValidator& f, ParseNode* lexicalScope) +CheckLexicalScope(FunctionValidator& f, ParseNode* node) { - MOZ_ASSERT(lexicalScope->isKind(PNK_LEXICALSCOPE)); - + LexicalScopeNode* lexicalScope = &node->as(); if (!lexicalScope->isEmptyScope()) return f.fail(lexicalScope, "cannot have 'let' or 'const' declarations");