diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/frontend/SyntaxParseHandler.h | 733 |
1 files changed, 733 insertions, 0 deletions
diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h new file mode 100644 index 0000000000..0c93376700 --- /dev/null +++ b/js/src/frontend/SyntaxParseHandler.h @@ -0,0 +1,733 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef frontend_SyntaxParseHandler_h +#define frontend_SyntaxParseHandler_h + +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/Maybe.h" // mozilla::Maybe + +#include <string.h> + +#include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind +#include "frontend/NameAnalysisTypes.h" // PrivateNameKind +#include "frontend/ParseNode.h" +#include "frontend/TokenStream.h" +#include "js/GCAnnotations.h" +#include "vm/JSContext.h" + +namespace js { + +namespace frontend { + +// Parse handler used when processing the syntax in a block of code, to generate +// the minimal information which is required to detect syntax errors and allow +// bytecode to be emitted for outer functions. +// +// When parsing, we start at the top level with a full parse, and when possible +// only check the syntax for inner functions, so that they can be lazily parsed +// into bytecode when/if they first run. Checking the syntax of a function is +// several times faster than doing a full parse/emit, and lazy parsing improves +// both performance and memory usage significantly when pages contain large +// amounts of code that never executes (which happens often). +class SyntaxParseHandler { + // Remember the last encountered name or string literal during syntax parses. + const ParserAtom* lastAtom; + TokenPos lastStringPos; + + public: + enum Node { + NodeFailure = 0, + NodeGeneric, + NodeGetProp, + NodeStringExprStatement, + NodeReturn, + NodeBreak, + NodeThrow, + NodeEmptyStatement, + + NodeVarDeclaration, + NodeLexicalDeclaration, + + // A non-arrow function expression with block body, from bog-standard + // ECMAScript. + NodeFunctionExpression, + + NodeFunctionArrow, + NodeFunctionStatement, + + // This is needed for proper assignment-target handling. ES6 formally + // requires function calls *not* pass IsValidSimpleAssignmentTarget, + // but at last check there were still sites with |f() = 5| and similar + // in code not actually executed (or at least not executed enough to be + // noticed). + NodeFunctionCall, + + NodeOptionalFunctionCall, + + // Node representing normal names which don't require any special + // casing. + NodeName, + + // Nodes representing the names "arguments" and "eval". + NodeArgumentsName, + NodeEvalName, + + // Node representing the "async" name, which may actually be a + // contextual keyword. + NodePotentialAsyncKeyword, + + // Node representing private names. + NodePrivateName, + + NodeDottedProperty, + NodeOptionalDottedProperty, + NodeElement, + NodeOptionalElement, + // A distinct node for [PrivateName], to make detecting delete this.#x + // detectable in syntax parse + NodePrivateElement, + + // Destructuring target patterns can't be parenthesized: |([a]) = [3];| + // must be a syntax error. (We can't use NodeGeneric instead of these + // because that would trigger invalid-left-hand-side ReferenceError + // semantics when SyntaxError semantics are desired.) + NodeParenthesizedArray, + NodeParenthesizedObject, + + // In rare cases a parenthesized |node| doesn't have the same semantics + // as |node|. Each such node has a special Node value, and we use a + // different Node value to represent the parenthesized form. See also + // is{Unp,P}arenthesized*(Node), parenthesize(Node), and the various + // functions that deal in NodeUnparenthesized* below. + + // Valuable for recognizing potential destructuring patterns. + NodeUnparenthesizedArray, + NodeUnparenthesizedObject, + + // The directive prologue at the start of a FunctionBody or ScriptBody + // is the longest sequence (possibly empty) of string literal + // expression statements at the start of a function. Thus we need this + // to treat |"use strict";| as a possible Use Strict Directive and + // |("use strict");| as a useless statement. + NodeUnparenthesizedString, + + // For destructuring patterns an assignment element with + // an initializer expression is not allowed be parenthesized. + // i.e. |{x = 1} = obj| + NodeUnparenthesizedAssignment, + + // This node is necessary to determine if the base operand in an + // exponentiation operation is an unparenthesized unary expression. + // We want to reject |-2 ** 3|, but still need to allow |(-2) ** 3|. + NodeUnparenthesizedUnary, + + // This node is necessary to determine if the LHS of a property access is + // super related. + NodeSuperBase + }; + +#define DECLARE_TYPE(typeName, longTypeName, asMethodName) \ + using longTypeName = Node; + FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) +#undef DECLARE_TYPE + + using NullNode = Node; + + bool isNonArrowFunctionExpression(Node node) const { + return node == NodeFunctionExpression; + } + + bool isPropertyAccess(Node node) { + return node == NodeDottedProperty || node == NodeElement || + node == NodePrivateElement; + } + + bool isOptionalPropertyAccess(Node node) { + return node == NodeOptionalDottedProperty || node == NodeOptionalElement; + } + + bool isFunctionCall(Node node) { + // Note: super() is a special form, *not* a function call. + return node == NodeFunctionCall; + } + + static bool isUnparenthesizedDestructuringPattern(Node node) { + return node == NodeUnparenthesizedArray || + node == NodeUnparenthesizedObject; + } + + static bool isParenthesizedDestructuringPattern(Node node) { + // Technically this isn't a destructuring target at all -- the grammar + // doesn't treat it as such. But we need to know when this happens to + // consider it a SyntaxError rather than an invalid-left-hand-side + // ReferenceError. + return node == NodeParenthesizedArray || node == NodeParenthesizedObject; + } + + public: + SyntaxParseHandler(JSContext* cx, LifoAlloc& alloc, + BaseScript* lazyOuterFunction) + : lastAtom(nullptr) {} + + static NullNode null() { return NodeFailure; } + +#define DECLARE_AS(typeName, longTypeName, asMethodName) \ + static longTypeName asMethodName(Node node) { return node; } + FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) +#undef DECLARE_AS + + NameNodeType newName(const ParserName* name, const TokenPos& pos, + JSContext* cx) { + lastAtom = name; + if (name == cx->parserNames().arguments) { + return NodeArgumentsName; + } + if (pos.begin + strlen("async") == pos.end && + name == cx->parserNames().async) { + return NodePotentialAsyncKeyword; + } + if (name == cx->parserNames().eval) { + return NodeEvalName; + } + return NodeName; + } + + UnaryNodeType newComputedName(Node expr, uint32_t start, uint32_t end) { + return NodeGeneric; + } + + UnaryNodeType newSyntheticComputedName(Node expr, uint32_t start, + uint32_t end) { + return NodeGeneric; + } + + NameNodeType newObjectLiteralPropertyName(const ParserAtom* atom, + const TokenPos& pos) { + return NodeName; + } + + NameNodeType newPrivateName(const ParserAtom* atom, const TokenPos& pos) { + return NodePrivateName; + } + + NumericLiteralType newNumber(double value, DecimalPoint decimalPoint, + const TokenPos& pos) { + return NodeGeneric; + } + + BigIntLiteralType newBigInt() { return NodeGeneric; } + + BooleanLiteralType newBooleanLiteral(bool cond, const TokenPos& pos) { + return NodeGeneric; + } + + NameNodeType newStringLiteral(const ParserAtom* atom, const TokenPos& pos) { + lastAtom = atom; + lastStringPos = pos; + return NodeUnparenthesizedString; + } + + NameNodeType newTemplateStringLiteral(const ParserAtom* atom, + const TokenPos& pos) { + return NodeGeneric; + } + + CallSiteNodeType newCallSiteObject(uint32_t begin) { return NodeGeneric; } + + void addToCallSiteObject(CallSiteNodeType callSiteObj, Node rawNode, + Node cookedNode) {} + + ThisLiteralType newThisLiteral(const TokenPos& pos, Node thisName) { + return NodeGeneric; + } + NullLiteralType newNullLiteral(const TokenPos& pos) { return NodeGeneric; } + RawUndefinedLiteralType newRawUndefinedLiteral(const TokenPos& pos) { + return NodeGeneric; + } + + RegExpLiteralType newRegExp(Node reobj, const TokenPos& pos) { + return NodeGeneric; + } + + ConditionalExpressionType newConditional(Node cond, Node thenExpr, + Node elseExpr) { + return NodeGeneric; + } + + Node newElision() { return NodeGeneric; } + + UnaryNodeType newDelete(uint32_t begin, Node expr) { + return NodeUnparenthesizedUnary; + } + + UnaryNodeType newTypeof(uint32_t begin, Node kid) { + return NodeUnparenthesizedUnary; + } + + UnaryNodeType newUnary(ParseNodeKind kind, uint32_t begin, Node kid) { + return NodeUnparenthesizedUnary; + } + + UnaryNodeType newUpdate(ParseNodeKind kind, uint32_t begin, Node kid) { + return NodeGeneric; + } + + UnaryNodeType newSpread(uint32_t begin, Node kid) { return NodeGeneric; } + + Node appendOrCreateList(ParseNodeKind kind, Node left, Node right, + ParseContext* pc) { + return NodeGeneric; + } + + // Expressions + + ListNodeType newArrayLiteral(uint32_t begin) { + return NodeUnparenthesizedArray; + } + MOZ_MUST_USE bool addElision(ListNodeType literal, const TokenPos& pos) { + return true; + } + MOZ_MUST_USE bool addSpreadElement(ListNodeType literal, uint32_t begin, + Node inner) { + return true; + } + void addArrayElement(ListNodeType literal, Node element) {} + + ListNodeType newArguments(const TokenPos& pos) { return NodeGeneric; } + CallNodeType newCall(Node callee, Node args, JSOp callOp) { + return NodeFunctionCall; + } + + CallNodeType newOptionalCall(Node callee, Node args, JSOp callOp) { + return NodeOptionalFunctionCall; + } + + CallNodeType newSuperCall(Node callee, Node args, bool isSpread) { + return NodeGeneric; + } + CallNodeType newTaggedTemplate(Node tag, Node args, JSOp callOp) { + return NodeGeneric; + } + + ListNodeType newObjectLiteral(uint32_t begin) { + return NodeUnparenthesizedObject; + } + ListNodeType newClassMemberList(uint32_t begin) { return NodeGeneric; } + 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; + } + NullaryNodeType newPosHolder(const TokenPos& pos) { return NodeGeneric; } + UnaryNodeType newSuperBase(Node thisName, const TokenPos& pos) { + return NodeSuperBase; + } + + MOZ_MUST_USE bool addPrototypeMutation(ListNodeType literal, uint32_t begin, + Node expr) { + return true; + } + BinaryNodeType newPropertyDefinition(Node key, Node val) { + return NodeGeneric; + } + void addPropertyDefinition(ListNodeType literal, BinaryNodeType propdef) {} + MOZ_MUST_USE bool addPropertyDefinition(ListNodeType literal, Node key, + Node expr) { + return true; + } + MOZ_MUST_USE bool addShorthand(ListNodeType literal, NameNodeType name, + NameNodeType expr) { + return true; + } + MOZ_MUST_USE bool addSpreadProperty(ListNodeType literal, uint32_t begin, + Node inner) { + return true; + } + MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node key, + FunctionNodeType funNode, + AccessorType atype) { + return true; + } + MOZ_MUST_USE Node newClassMethodDefinition( + Node key, FunctionNodeType funNode, AccessorType atype, bool isStatic, + mozilla::Maybe<FunctionNodeType> initializerIfPrivate) { + return NodeGeneric; + } + MOZ_MUST_USE Node newClassFieldDefinition(Node name, + FunctionNodeType initializer, + bool isStatic) { + return NodeGeneric; + } + MOZ_MUST_USE bool addClassMemberDefinition(ListNodeType memberList, + Node member) { + return true; + } + UnaryNodeType newYieldExpression(uint32_t begin, Node value) { + return NodeGeneric; + } + UnaryNodeType newYieldStarExpression(uint32_t begin, Node value) { + return NodeGeneric; + } + UnaryNodeType newAwaitExpression(uint32_t begin, Node value) { + return NodeUnparenthesizedUnary; + } + UnaryNodeType newOptionalChain(uint32_t begin, Node value) { + return NodeGeneric; + } + + // Statements + + ListNodeType newStatementList(const TokenPos& pos) { return NodeGeneric; } + void addStatementToList(ListNodeType list, Node stmt) {} + void setListEndPosition(ListNodeType list, const TokenPos& pos) {} + void addCaseStatementToList(ListNodeType list, CaseClauseType caseClause) {} + MOZ_MUST_USE bool prependInitialYield(ListNodeType stmtList, Node genName) { + return true; + } + NullaryNodeType newEmptyStatement(const TokenPos& pos) { + return NodeEmptyStatement; + } + + UnaryNodeType newExportDeclaration(Node kid, const TokenPos& pos) { + return NodeGeneric; + } + BinaryNodeType newExportFromDeclaration(uint32_t begin, Node exportSpecSet, + Node moduleSpec) { + return NodeGeneric; + } + BinaryNodeType newExportDefaultDeclaration(Node kid, Node maybeBinding, + const TokenPos& pos) { + return NodeGeneric; + } + BinaryNodeType newExportSpec(Node bindingName, Node exportName) { + return NodeGeneric; + } + NullaryNodeType newExportBatchSpec(const TokenPos& pos) { + return NodeGeneric; + } + BinaryNodeType newImportMeta(NullaryNodeType importHolder, + NullaryNodeType metaHolder) { + return NodeGeneric; + } + BinaryNodeType newCallImport(NullaryNodeType importHolder, Node singleArg) { + return NodeGeneric; + } + + BinaryNodeType newSetThis(Node thisName, Node value) { return value; } + + UnaryNodeType newExprStatement(Node expr, uint32_t end) { + return expr == NodeUnparenthesizedString ? NodeStringExprStatement + : NodeGeneric; + } + + TernaryNodeType newIfStatement(uint32_t begin, Node cond, Node thenBranch, + Node elseBranch) { + return NodeGeneric; + } + 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, + LexicalScopeNodeType lexicalForCaseList, bool hasDefault) { + return NodeGeneric; + } + CaseClauseType newCaseOrDefault(uint32_t begin, Node expr, Node body) { + return NodeGeneric; + } + ContinueStatementType newContinueStatement(const ParserName* label, + const TokenPos& pos) { + return NodeGeneric; + } + BreakStatementType newBreakStatement(const ParserName* label, + const TokenPos& pos) { + return NodeBreak; + } + UnaryNodeType newReturnStatement(Node expr, const TokenPos& pos) { + return NodeReturn; + } + UnaryNodeType newExpressionBody(Node expr) { return NodeReturn; } + BinaryNodeType newWithStatement(uint32_t begin, Node expr, Node body) { + return NodeGeneric; + } + + LabeledStatementType newLabeledStatement(const ParserName* label, Node stmt, + uint32_t begin) { + return NodeGeneric; + } + + UnaryNodeType newThrowStatement(Node expr, const TokenPos& pos) { + return NodeThrow; + } + TernaryNodeType newTryStatement(uint32_t begin, Node body, + LexicalScopeNodeType catchScope, + Node finallyBlock) { + return NodeGeneric; + } + DebuggerStatementType newDebuggerStatement(const TokenPos& pos) { + return NodeGeneric; + } + + NameNodeType newPropertyName(const ParserName* name, const TokenPos& pos) { + lastAtom = name; + return NodeGeneric; + } + + PropertyAccessType newPropertyAccess(Node expr, NameNodeType key) { + return NodeDottedProperty; + } + + PropertyAccessType newOptionalPropertyAccess(Node expr, NameNodeType key) { + return NodeOptionalDottedProperty; + } + + PropertyByValueType newPropertyByValue(Node lhs, Node index, uint32_t end) { + if (isPrivateName(index)) { + return NodePrivateElement; + } + return NodeElement; + } + + PropertyByValueType newOptionalPropertyByValue(Node lhs, Node index, + uint32_t end) { + return NodeOptionalElement; + } + + MOZ_MUST_USE bool setupCatchScope(LexicalScopeNodeType lexicalScope, + Node catchName, Node catchBody) { + return true; + } + + MOZ_MUST_USE bool setLastFunctionFormalParameterDefault( + FunctionNodeType funNode, Node defaultValue) { + return true; + } + + void checkAndSetIsDirectRHSAnonFunction(Node pn) {} + + FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind, + const TokenPos& pos) { + switch (syntaxKind) { + case FunctionSyntaxKind::Statement: + return NodeFunctionStatement; + case FunctionSyntaxKind::Arrow: + return NodeFunctionArrow; + default: + // All non-arrow function expressions are initially presumed to have + // block body. This will be overridden later *if* the function + // expression permissibly has an AssignmentExpression body. + return NodeFunctionExpression; + } + } + + void setFunctionFormalParametersAndBody(FunctionNodeType funNode, + ListNodeType paramsBody) {} + void setFunctionBody(FunctionNodeType funNode, LexicalScopeNodeType body) {} + void setFunctionBox(FunctionNodeType funNode, FunctionBox* funbox) {} + void addFunctionFormalParameter(FunctionNodeType funNode, Node argpn) {} + + ForNodeType newForStatement(uint32_t begin, TernaryNodeType forHead, + Node body, unsigned iflags) { + return NodeGeneric; + } + + TernaryNodeType newForHead(Node init, Node test, Node update, + const TokenPos& pos) { + return NodeGeneric; + } + + TernaryNodeType newForInOrOfHead(ParseNodeKind kind, Node target, + Node iteratedExpr, const TokenPos& pos) { + return NodeGeneric; + } + + AssignmentNodeType finishInitializerAssignment(NameNodeType nameNode, + Node init) { + return NodeUnparenthesizedAssignment; + } + + void setBeginPosition(Node pn, Node oth) {} + void setBeginPosition(Node pn, uint32_t begin) {} + + void setEndPosition(Node pn, Node oth) {} + void setEndPosition(Node pn, uint32_t end) {} + + uint32_t getFunctionNameOffset(Node func, TokenStreamAnyChars& ts) { + // XXX This offset isn't relevant to the offending function name. But + // we may not *have* that function name around, because of how lazy + // parsing works -- the actual name could be outside + // |tokenStream.userbuf|'s observed region. So the current offset + // is the best we can do. + return ts.currentToken().pos.begin; + } + + ListNodeType newList(ParseNodeKind kind, const TokenPos& pos) { + MOZ_ASSERT(kind != ParseNodeKind::VarStmt); + MOZ_ASSERT(kind != ParseNodeKind::LetDecl); + MOZ_ASSERT(kind != ParseNodeKind::ConstDecl); + return NodeGeneric; + } + + ListNodeType newList(ParseNodeKind kind, Node kid) { + return newList(kind, TokenPos()); + } + + ListNodeType newDeclarationList(ParseNodeKind kind, const TokenPos& pos) { + if (kind == ParseNodeKind::VarStmt) { + return NodeVarDeclaration; + } + MOZ_ASSERT(kind == ParseNodeKind::LetDecl || + kind == ParseNodeKind::ConstDecl); + return NodeLexicalDeclaration; + } + + bool isDeclarationList(Node node) { + return node == NodeVarDeclaration || node == NodeLexicalDeclaration; + } + + // This method should only be called from parsers using FullParseHandler. + Node singleBindingFromDeclaration(ListNodeType decl) = delete; + + ListNodeType newCommaExpressionList(Node kid) { return NodeGeneric; } + + void addList(ListNodeType list, Node kid) { + MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedArray || + list == NodeUnparenthesizedObject || + list == NodeVarDeclaration || list == NodeLexicalDeclaration || + list == NodeFunctionCall); + } + + CallNodeType newNewExpression(uint32_t begin, Node ctor, Node args, + bool isSpread) { + return NodeGeneric; + } + + AssignmentNodeType newAssignment(ParseNodeKind kind, Node lhs, Node rhs) { + return kind == ParseNodeKind::AssignExpr ? NodeUnparenthesizedAssignment + : NodeGeneric; + } + + bool isUnparenthesizedAssignment(Node node) { + return node == NodeUnparenthesizedAssignment; + } + + bool isUnparenthesizedUnaryExpression(Node node) { + return node == NodeUnparenthesizedUnary; + } + + bool isReturnStatement(Node node) { return node == NodeReturn; } + + bool isStatementPermittedAfterReturnStatement(Node pn) { + return pn == NodeFunctionStatement || isNonArrowFunctionExpression(pn) || + pn == NodeVarDeclaration || pn == NodeBreak || pn == NodeThrow || + pn == NodeEmptyStatement; + } + + bool isSuperBase(Node pn) { return pn == NodeSuperBase; } + + void setListHasNonConstInitializer(ListNodeType literal) {} + MOZ_MUST_USE Node parenthesize(Node node) { + // A number of nodes have different behavior upon parenthesization, but + // only in some circumstances. Convert these nodes to special + // parenthesized forms. + if (node == NodeUnparenthesizedArray) { + return NodeParenthesizedArray; + } + if (node == NodeUnparenthesizedObject) { + return NodeParenthesizedObject; + } + + // Other nodes need not be recognizable after parenthesization; convert + // them to a generic node. + if (node == NodeUnparenthesizedString || + node == NodeUnparenthesizedAssignment || + node == NodeUnparenthesizedUnary) { + return NodeGeneric; + } + + // Convert parenthesized |async| to a normal name node. + if (node == NodePotentialAsyncKeyword) { + return NodeName; + } + + // In all other cases, the parenthesized form of |node| is equivalent + // to the unparenthesized form: return |node| unchanged. + return node; + } + template <typename NodeType> + MOZ_MUST_USE NodeType setLikelyIIFE(NodeType node) { + return node; // Remain in syntax-parse mode. + } + + bool isName(Node node) { + return node == NodeName || node == NodeArgumentsName || + node == NodeEvalName || node == NodePotentialAsyncKeyword; + } + + bool isArgumentsName(Node node, JSContext* cx) { + return node == NodeArgumentsName; + } + + bool isEvalName(Node node, JSContext* cx) { return node == NodeEvalName; } + + bool isAsyncKeyword(Node node, JSContext* cx) { + return node == NodePotentialAsyncKeyword; + } + + bool isPrivateName(Node node) { return node == NodePrivateName; } + bool isPrivateField(Node node) { return node == NodePrivateElement; } + + const ParserName* maybeDottedProperty(Node node) { + // Note: |super.apply(...)| is a special form that calls an "apply" + // method retrieved from one value, but using a *different* value as + // |this|. It's not really eligible for the funapply/funcall + // optimizations as they're currently implemented (assuming a single + // value is used for both retrieval and |this|). + if (node != NodeDottedProperty && node != NodeOptionalDottedProperty) { + return nullptr; + } + return lastAtom->asName(); + } + + const ParserAtom* isStringExprStatement(Node pn, TokenPos* pos) { + if (pn == NodeStringExprStatement) { + *pos = lastStringPos; + return lastAtom; + } + return nullptr; + } + + bool canSkipLazyInnerFunctions() { return false; } + bool canSkipLazyClosedOverBindings() { return false; } + JSAtom* nextLazyClosedOverBinding() { + MOZ_CRASH( + "SyntaxParseHandler::canSkipLazyClosedOverBindings must return false"); + } + + void setPrivateNameKind(Node node, PrivateNameKind kind) {} +}; + +} // namespace frontend +} // namespace js + +#endif /* frontend_SyntaxParseHandler_h */ |