diff options
| author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-10-15 09:45:50 +0000 |
|---|---|---|
| committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-10-15 09:45:50 +0000 |
| commit | e15dd966d523731101f70ccf768bba12435a0208 (patch) | |
| tree | ae9cb828a24ded2585a41af3f21411523b47897d /Source/JavaScriptCore/parser | |
| download | WebKitGtk-tarball-e15dd966d523731101f70ccf768bba12435a0208.tar.gz | |
webkitgtk-2.10.2webkitgtk-2.10.2
Diffstat (limited to 'Source/JavaScriptCore/parser')
31 files changed, 15294 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/parser/ASTBuilder.h b/Source/JavaScriptCore/parser/ASTBuilder.h new file mode 100644 index 000000000..7b587730a --- /dev/null +++ b/Source/JavaScriptCore/parser/ASTBuilder.h @@ -0,0 +1,1253 @@ +/* + * Copyright (C) 2010, 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ASTBuilder_h +#define ASTBuilder_h + +#include "BuiltinNames.h" +#include "BytecodeIntrinsicRegistry.h" +#include "NodeConstructors.h" +#include "SyntaxChecker.h" +#include "VariableEnvironment.h" +#include <utility> + +namespace JSC { + +class ASTBuilder { + struct BinaryOpInfo { + BinaryOpInfo() {} + BinaryOpInfo(const JSTextPosition& otherStart, const JSTextPosition& otherDivot, const JSTextPosition& otherEnd, bool rhsHasAssignment) + : start(otherStart) + , divot(otherDivot) + , end(otherEnd) + , hasAssignment(rhsHasAssignment) + { + } + BinaryOpInfo(const BinaryOpInfo& lhs, const BinaryOpInfo& rhs) + : start(lhs.start) + , divot(rhs.start) + , end(rhs.end) + , hasAssignment(lhs.hasAssignment || rhs.hasAssignment) + { + } + JSTextPosition start; + JSTextPosition divot; + JSTextPosition end; + bool hasAssignment; + }; + + + struct AssignmentInfo { + AssignmentInfo() {} + AssignmentInfo(ExpressionNode* node, const JSTextPosition& start, const JSTextPosition& divot, int initAssignments, Operator op) + : m_node(node) + , m_start(start) + , m_divot(divot) + , m_initAssignments(initAssignments) + , m_op(op) + { + ASSERT(m_divot.offset >= m_divot.lineStartOffset); + ASSERT(m_start.offset >= m_start.lineStartOffset); + } + ExpressionNode* m_node; + JSTextPosition m_start; + JSTextPosition m_divot; + int m_initAssignments; + Operator m_op; + }; +public: + ASTBuilder(VM* vm, ParserArena& parserArena, SourceCode* sourceCode) + : m_vm(vm) + , m_parserArena(parserArena) + , m_sourceCode(sourceCode) + , m_evalCount(0) + { + } + + struct BinaryExprContext { + BinaryExprContext(ASTBuilder&) {} + }; + struct UnaryExprContext { + UnaryExprContext(ASTBuilder&) {} + }; + + typedef ExpressionNode* Expression; + typedef JSC::SourceElements* SourceElements; + typedef ArgumentsNode* Arguments; + typedef CommaNode* Comma; + typedef PropertyNode* Property; + typedef PropertyListNode* PropertyList; + typedef ElementNode* ElementList; + typedef ArgumentListNode* ArgumentsList; +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + typedef TemplateExpressionListNode* TemplateExpressionList; + typedef TemplateStringNode* TemplateString; + typedef TemplateStringListNode* TemplateStringList; + typedef TemplateLiteralNode* TemplateLiteral; +#endif + typedef FunctionParameters* FormalParameterList; + typedef FunctionMetadataNode* FunctionBody; +#if ENABLE(ES6_CLASS_SYNTAX) + typedef ClassExprNode* ClassExpression; +#endif + typedef ModuleNameNode* ModuleName; + typedef ImportSpecifierNode* ImportSpecifier; + typedef ImportSpecifierListNode* ImportSpecifierList; + typedef ExportSpecifierNode* ExportSpecifier; + typedef ExportSpecifierListNode* ExportSpecifierList; + typedef StatementNode* Statement; + typedef ClauseListNode* ClauseList; + typedef CaseClauseNode* Clause; + typedef std::pair<ExpressionNode*, BinaryOpInfo> BinaryOperand; + typedef DestructuringPatternNode* DestructuringPattern; + typedef ArrayPatternNode* ArrayPattern; + typedef ObjectPatternNode* ObjectPattern; + typedef BindingNode* BindingPattern; + static const bool CreatesAST = true; + static const bool NeedsFreeVariableInfo = true; + static const bool CanUseFunctionCache = true; + static const int DontBuildKeywords = 0; + static const int DontBuildStrings = 0; + + ExpressionNode* makeBinaryNode(const JSTokenLocation&, int token, std::pair<ExpressionNode*, BinaryOpInfo>, std::pair<ExpressionNode*, BinaryOpInfo>); + ExpressionNode* makeFunctionCallNode(const JSTokenLocation&, ExpressionNode* func, ArgumentsNode* args, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd); + + JSC::SourceElements* createSourceElements() { return new (m_parserArena) JSC::SourceElements(); } + + DeclarationStacks::FunctionStack& funcDeclarations() { return m_scope.m_funcDeclarations; } + int features() const { return m_scope.m_features; } + int numConstants() const { return m_scope.m_numConstants; } + + ExpressionNode* makeAssignNode(const JSTokenLocation&, ExpressionNode* left, Operator, ExpressionNode* right, bool leftHasAssignments, bool rightHasAssignments, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end); + ExpressionNode* makePrefixNode(const JSTokenLocation&, ExpressionNode*, Operator, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end); + ExpressionNode* makePostfixNode(const JSTokenLocation&, ExpressionNode*, Operator, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end); + ExpressionNode* makeTypeOfNode(const JSTokenLocation&, ExpressionNode*); + ExpressionNode* makeDeleteNode(const JSTokenLocation&, ExpressionNode*, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end); + ExpressionNode* makeNegateNode(const JSTokenLocation&, ExpressionNode*); + ExpressionNode* makeBitwiseNotNode(const JSTokenLocation&, ExpressionNode*); + ExpressionNode* makeMultNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); + ExpressionNode* makeDivNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); + ExpressionNode* makeModNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); + ExpressionNode* makeAddNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); + ExpressionNode* makeSubNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); + ExpressionNode* makeBitXOrNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); + ExpressionNode* makeBitAndNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); + ExpressionNode* makeBitOrNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); + ExpressionNode* makeLeftShiftNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); + ExpressionNode* makeRightShiftNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); + ExpressionNode* makeURightShiftNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); + + ExpressionNode* createLogicalNot(const JSTokenLocation& location, ExpressionNode* expr) + { + if (expr->isNumber()) + return createBoolean(location, isZeroOrUnordered(static_cast<NumberNode*>(expr)->value())); + + return new (m_parserArena) LogicalNotNode(location, expr); + } + ExpressionNode* createUnaryPlus(const JSTokenLocation& location, ExpressionNode* expr) { return new (m_parserArena) UnaryPlusNode(location, expr); } + ExpressionNode* createVoid(const JSTokenLocation& location, ExpressionNode* expr) + { + incConstants(); + return new (m_parserArena) VoidNode(location, expr); + } + ExpressionNode* createThisExpr(const JSTokenLocation& location, ThisTDZMode thisTDZMode) + { + usesThis(); + return new (m_parserArena) ThisNode(location, thisTDZMode); + } + ExpressionNode* createSuperExpr(const JSTokenLocation& location) + { + return new (m_parserArena) SuperNode(location); + } + ExpressionNode* createNewTargetExpr(const JSTokenLocation location) + { + return new (m_parserArena) NewTargetNode(location); + } + ExpressionNode* createResolve(const JSTokenLocation& location, const Identifier* ident, const JSTextPosition& start) + { + if (m_vm->propertyNames->arguments == *ident) + usesArguments(); + return new (m_parserArena) ResolveNode(location, *ident, start); + } + ExpressionNode* createObjectLiteral(const JSTokenLocation& location) { return new (m_parserArena) ObjectLiteralNode(location); } + ExpressionNode* createObjectLiteral(const JSTokenLocation& location, PropertyListNode* properties) { return new (m_parserArena) ObjectLiteralNode(location, properties); } + + ExpressionNode* createArray(const JSTokenLocation& location, int elisions) + { + if (elisions) + incConstants(); + return new (m_parserArena) ArrayNode(location, elisions); + } + + ExpressionNode* createArray(const JSTokenLocation& location, ElementNode* elems) { return new (m_parserArena) ArrayNode(location, elems); } + ExpressionNode* createArray(const JSTokenLocation& location, int elisions, ElementNode* elems) + { + if (elisions) + incConstants(); + return new (m_parserArena) ArrayNode(location, elisions, elems); + } + ExpressionNode* createDoubleExpr(const JSTokenLocation& location, double d) + { + incConstants(); + return new (m_parserArena) DoubleNode(location, d); + } + ExpressionNode* createIntegerExpr(const JSTokenLocation& location, double d) + { + incConstants(); + return new (m_parserArena) IntegerNode(location, d); + } + + ExpressionNode* createString(const JSTokenLocation& location, const Identifier* string) + { + incConstants(); + return new (m_parserArena) StringNode(location, *string); + } + + ExpressionNode* createBoolean(const JSTokenLocation& location, bool b) + { + incConstants(); + return new (m_parserArena) BooleanNode(location, b); + } + + ExpressionNode* createNull(const JSTokenLocation& location) + { + incConstants(); + return new (m_parserArena) NullNode(location); + } + + ExpressionNode* createBracketAccess(const JSTokenLocation& location, ExpressionNode* base, ExpressionNode* property, bool propertyHasAssignments, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) + { + BracketAccessorNode* node = new (m_parserArena) BracketAccessorNode(location, base, property, propertyHasAssignments); + setExceptionLocation(node, start, divot, end); + return node; + } + + ExpressionNode* createDotAccess(const JSTokenLocation& location, ExpressionNode* base, const Identifier* property, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) + { + DotAccessorNode* node = new (m_parserArena) DotAccessorNode(location, base, *property); + setExceptionLocation(node, start, divot, end); + return node; + } + + ExpressionNode* createSpreadExpression(const JSTokenLocation& location, ExpressionNode* expression, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) + { + auto node = new (m_parserArena) SpreadExpressionNode(location, expression); + setExceptionLocation(node, start, divot, end); + return node; + } + +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + TemplateStringNode* createTemplateString(const JSTokenLocation& location, const Identifier& cooked, const Identifier& raw) + { + return new (m_parserArena) TemplateStringNode(location, cooked, raw); + } + + TemplateStringListNode* createTemplateStringList(TemplateStringNode* templateString) + { + return new (m_parserArena) TemplateStringListNode(templateString); + } + + TemplateStringListNode* createTemplateStringList(TemplateStringListNode* templateStringList, TemplateStringNode* templateString) + { + return new (m_parserArena) TemplateStringListNode(templateStringList, templateString); + } + + TemplateExpressionListNode* createTemplateExpressionList(ExpressionNode* expression) + { + return new (m_parserArena) TemplateExpressionListNode(expression); + } + + TemplateExpressionListNode* createTemplateExpressionList(TemplateExpressionListNode* templateExpressionListNode, ExpressionNode* expression) + { + return new (m_parserArena) TemplateExpressionListNode(templateExpressionListNode, expression); + } + + TemplateLiteralNode* createTemplateLiteral(const JSTokenLocation& location, TemplateStringListNode* templateStringList) + { + return new (m_parserArena) TemplateLiteralNode(location, templateStringList); + } + + TemplateLiteralNode* createTemplateLiteral(const JSTokenLocation& location, TemplateStringListNode* templateStringList, TemplateExpressionListNode* templateExpressionList) + { + return new (m_parserArena) TemplateLiteralNode(location, templateStringList, templateExpressionList); + } + + ExpressionNode* createTaggedTemplate(const JSTokenLocation& location, ExpressionNode* base, TemplateLiteralNode* templateLiteral, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) + { + auto node = new (m_parserArena) TaggedTemplateNode(location, base, templateLiteral); + setExceptionLocation(node, start, divot, end); + return node; + } +#endif + + ExpressionNode* createRegExp(const JSTokenLocation& location, const Identifier& pattern, const Identifier& flags, const JSTextPosition& start) + { + if (Yarr::checkSyntax(pattern.string())) + return 0; + RegExpNode* node = new (m_parserArena) RegExpNode(location, pattern, flags); + int size = pattern.length() + 2; // + 2 for the two /'s + JSTextPosition end = start + size; + setExceptionLocation(node, start, end, end); + return node; + } + + ExpressionNode* createNewExpr(const JSTokenLocation& location, ExpressionNode* expr, ArgumentsNode* arguments, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) + { + NewExprNode* node = new (m_parserArena) NewExprNode(location, expr, arguments); + setExceptionLocation(node, start, divot, end); + return node; + } + + ExpressionNode* createNewExpr(const JSTokenLocation& location, ExpressionNode* expr, const JSTextPosition& start, const JSTextPosition& end) + { + NewExprNode* node = new (m_parserArena) NewExprNode(location, expr); + setExceptionLocation(node, start, end, end); + return node; + } + + ExpressionNode* createConditionalExpr(const JSTokenLocation& location, ExpressionNode* condition, ExpressionNode* lhs, ExpressionNode* rhs) + { + return new (m_parserArena) ConditionalNode(location, condition, lhs, rhs); + } + + ExpressionNode* createAssignResolve(const JSTokenLocation& location, const Identifier& ident, ExpressionNode* rhs, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end, AssignmentContext assignmentContext) + { + if (rhs->isFuncExprNode()) + static_cast<FuncExprNode*>(rhs)->metadata()->setInferredName(ident); + AssignResolveNode* node = new (m_parserArena) AssignResolveNode(location, ident, rhs, assignmentContext); + setExceptionLocation(node, start, divot, end); + return node; + } + +#if ENABLE(ES6_CLASS_SYNTAX) + ClassExprNode* createClassExpr(const JSTokenLocation& location, const Identifier& name, ExpressionNode* constructor, + ExpressionNode* parentClass, PropertyListNode* instanceMethods, PropertyListNode* staticMethods) + { + return new (m_parserArena) ClassExprNode(location, name, constructor, parentClass, instanceMethods, staticMethods); + } +#endif + + ExpressionNode* createFunctionExpr(const JSTokenLocation& location, const ParserFunctionInfo<ASTBuilder>& functionInfo) + { + FuncExprNode* result = new (m_parserArena) FuncExprNode(location, *functionInfo.name, functionInfo.body, + m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.bodyStartColumn)); + functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset); + return result; + } + + FunctionMetadataNode* createFunctionMetadata( + const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, + unsigned startColumn, unsigned endColumn, int functionKeywordStart, + int functionNameStart, int parametersStart, bool inStrictContext, + ConstructorKind constructorKind, unsigned parameterCount, SourceParseMode mode) + { + return new (m_parserArena) FunctionMetadataNode( + m_parserArena, startLocation, endLocation, startColumn, endColumn, + functionKeywordStart, functionNameStart, parametersStart, + inStrictContext, constructorKind, parameterCount, mode); + } + + ExpressionNode* createArrowFunctionExpr(const JSTokenLocation& location, const ParserFunctionInfo<ASTBuilder>& functionInfo) + { + SourceCode source = m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.bodyStartColumn); + + FuncExprNode* result = new (m_parserArena) FuncExprNode(location, *functionInfo.name, functionInfo.body, source); + functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset); + return result; + } + + NEVER_INLINE PropertyNode* createGetterOrSetterProperty(const JSTokenLocation& location, PropertyNode::Type type, bool, + const Identifier* name, const ParserFunctionInfo<ASTBuilder>& functionInfo, SuperBinding superBinding) + { + ASSERT(name); + functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset); + functionInfo.body->setInferredName(*name); + SourceCode source = m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.bodyStartColumn); + FuncExprNode* funcExpr = new (m_parserArena) FuncExprNode(location, m_vm->propertyNames->nullIdentifier, functionInfo.body, source); + return new (m_parserArena) PropertyNode(*name, funcExpr, type, PropertyNode::Unknown, superBinding); + } + + NEVER_INLINE PropertyNode* createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation& location, PropertyNode::Type type, bool, + double name, const ParserFunctionInfo<ASTBuilder>& functionInfo, SuperBinding superBinding) + { + functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset); + const Identifier& ident = parserArena.identifierArena().makeNumericIdentifier(vm, name); + SourceCode source = m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.bodyStartColumn); + FuncExprNode* funcExpr = new (m_parserArena) FuncExprNode(location, vm->propertyNames->nullIdentifier, functionInfo.body, source); + return new (m_parserArena) PropertyNode(ident, funcExpr, type, PropertyNode::Unknown, superBinding); + } + + ArgumentsNode* createArguments() { return new (m_parserArena) ArgumentsNode(); } + ArgumentsNode* createArguments(ArgumentListNode* args) { return new (m_parserArena) ArgumentsNode(args); } + ArgumentListNode* createArgumentsList(const JSTokenLocation& location, ExpressionNode* arg) { return new (m_parserArena) ArgumentListNode(location, arg); } + ArgumentListNode* createArgumentsList(const JSTokenLocation& location, ArgumentListNode* args, ExpressionNode* arg) { return new (m_parserArena) ArgumentListNode(location, args, arg); } + + PropertyNode* createProperty(const Identifier* propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool, SuperBinding superBinding = SuperBinding::NotNeeded) + { + if (node->isFuncExprNode()) + static_cast<FuncExprNode*>(node)->metadata()->setInferredName(*propertyName); + return new (m_parserArena) PropertyNode(*propertyName, node, type, putType, superBinding); + } + PropertyNode* createProperty(VM* vm, ParserArena& parserArena, double propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool) + { + return new (m_parserArena) PropertyNode(parserArena.identifierArena().makeNumericIdentifier(vm, propertyName), node, type, putType); + } + PropertyNode* createProperty(ExpressionNode* propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool) { return new (m_parserArena) PropertyNode(propertyName, node, type, putType); } + PropertyListNode* createPropertyList(const JSTokenLocation& location, PropertyNode* property) { return new (m_parserArena) PropertyListNode(location, property); } + PropertyListNode* createPropertyList(const JSTokenLocation& location, PropertyNode* property, PropertyListNode* tail) { return new (m_parserArena) PropertyListNode(location, property, tail); } + + ElementNode* createElementList(int elisions, ExpressionNode* expr) { return new (m_parserArena) ElementNode(elisions, expr); } + ElementNode* createElementList(ElementNode* elems, int elisions, ExpressionNode* expr) { return new (m_parserArena) ElementNode(elems, elisions, expr); } + + FormalParameterList createFormalParameterList() { return new (m_parserArena) FunctionParameters(); } + void appendParameter(FormalParameterList list, DestructuringPattern pattern, ExpressionNode* defaultValue) + { + list->append(pattern, defaultValue); + } + + CaseClauseNode* createClause(ExpressionNode* expr, JSC::SourceElements* statements) { return new (m_parserArena) CaseClauseNode(expr, statements); } + ClauseListNode* createClauseList(CaseClauseNode* clause) { return new (m_parserArena) ClauseListNode(clause); } + ClauseListNode* createClauseList(ClauseListNode* tail, CaseClauseNode* clause) { return new (m_parserArena) ClauseListNode(tail, clause); } + + StatementNode* createFuncDeclStatement(const JSTokenLocation& location, const ParserFunctionInfo<ASTBuilder>& functionInfo) + { + FuncDeclNode* decl = new (m_parserArena) FuncDeclNode(location, *functionInfo.name, functionInfo.body, + m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.bodyStartColumn)); + if (*functionInfo.name == m_vm->propertyNames->arguments) + usesArguments(); + m_scope.m_funcDeclarations.append(decl->metadata()); + functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset); + return decl; + } + +#if ENABLE(ES6_CLASS_SYNTAX) + StatementNode* createClassDeclStatement(const JSTokenLocation& location, ClassExprNode* classExpression, + const JSTextPosition& classStart, const JSTextPosition& classEnd, unsigned startLine, unsigned endLine) + { + ExpressionNode* assign = createAssignResolve(location, classExpression->name(), classExpression, classStart, classStart + 1, classEnd, AssignmentContext::DeclarationStatement); + ClassDeclNode* decl = new (m_parserArena) ClassDeclNode(location, assign); + decl->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset); + return decl; + } +#endif + + StatementNode* createBlockStatement(const JSTokenLocation& location, JSC::SourceElements* elements, int startLine, int endLine, VariableEnvironment& lexicalVariables) + { + BlockNode* block = new (m_parserArena) BlockNode(location, elements, lexicalVariables); + block->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset); + return block; + } + + StatementNode* createExprStatement(const JSTokenLocation& location, ExpressionNode* expr, const JSTextPosition& start, int end) + { + ExprStatementNode* result = new (m_parserArena) ExprStatementNode(location, expr); + result->setLoc(start.line, end, start.offset, start.lineStartOffset); + return result; + } + + StatementNode* createIfStatement(const JSTokenLocation& location, ExpressionNode* condition, StatementNode* trueBlock, StatementNode* falseBlock, int start, int end) + { + IfElseNode* result = new (m_parserArena) IfElseNode(location, condition, trueBlock, falseBlock); + result->setLoc(start, end, location.startOffset, location.lineStartOffset); + return result; + } + + StatementNode* createForLoop(const JSTokenLocation& location, ExpressionNode* initializer, ExpressionNode* condition, ExpressionNode* iter, StatementNode* statements, int start, int end, VariableEnvironment& lexicalVariables) + { + ForNode* result = new (m_parserArena) ForNode(location, initializer, condition, iter, statements, lexicalVariables); + result->setLoc(start, end, location.startOffset, location.lineStartOffset); + return result; + } + + StatementNode* createForInLoop(const JSTokenLocation& location, ExpressionNode* lhs, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end, VariableEnvironment& lexicalVariables) + { + ForInNode* result = new (m_parserArena) ForInNode(location, lhs, iter, statements, lexicalVariables); + result->setLoc(start, end, location.startOffset, location.lineStartOffset); + setExceptionLocation(result, eStart, eDivot, eEnd); + return result; + } + + StatementNode* createForInLoop(const JSTokenLocation& location, DestructuringPatternNode* pattern, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end, VariableEnvironment& lexicalVariables) + { + auto lexpr = new (m_parserArena) DestructuringAssignmentNode(location, pattern, 0); + return createForInLoop(location, lexpr, iter, statements, eStart, eDivot, eEnd, start, end, lexicalVariables); + } + + StatementNode* createForOfLoop(const JSTokenLocation& location, ExpressionNode* lhs, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end, VariableEnvironment& lexicalVariables) + { + ForOfNode* result = new (m_parserArena) ForOfNode(location, lhs, iter, statements, lexicalVariables); + result->setLoc(start, end, location.startOffset, location.lineStartOffset); + setExceptionLocation(result, eStart, eDivot, eEnd); + return result; + } + + StatementNode* createForOfLoop(const JSTokenLocation& location, DestructuringPatternNode* pattern, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end, VariableEnvironment& lexicalVariables) + { + auto lexpr = new (m_parserArena) DestructuringAssignmentNode(location, pattern, 0); + return createForOfLoop(location, lexpr, iter, statements, eStart, eDivot, eEnd, start, end, lexicalVariables); + } + + bool isBindingNode(const DestructuringPattern& pattern) + { + return pattern->isBindingNode(); + } + + StatementNode* createEmptyStatement(const JSTokenLocation& location) { return new (m_parserArena) EmptyStatementNode(location); } + + StatementNode* createDeclarationStatement(const JSTokenLocation& location, ExpressionNode* expr, int start, int end) + { + StatementNode* result; + result = new (m_parserArena) DeclarationStatement(location, expr); + result->setLoc(start, end, location.startOffset, location.lineStartOffset); + return result; + } + + ExpressionNode* createEmptyVarExpression(const JSTokenLocation& location, const Identifier& identifier) + { + return new (m_parserArena) EmptyVarExpression(location, identifier); + } + + ExpressionNode* createEmptyLetExpression(const JSTokenLocation& location, const Identifier& identifier) + { + return new (m_parserArena) EmptyLetExpression(location, identifier); + } + + StatementNode* createReturnStatement(const JSTokenLocation& location, ExpressionNode* expression, const JSTextPosition& start, const JSTextPosition& end) + { + ReturnNode* result = new (m_parserArena) ReturnNode(location, expression); + setExceptionLocation(result, start, end, end); + result->setLoc(start.line, end.line, start.offset, start.lineStartOffset); + return result; + } + + StatementNode* createBreakStatement(const JSTokenLocation& location, const Identifier* ident, const JSTextPosition& start, const JSTextPosition& end) + { + BreakNode* result = new (m_parserArena) BreakNode(location, *ident); + setExceptionLocation(result, start, end, end); + result->setLoc(start.line, end.line, start.offset, start.lineStartOffset); + return result; + } + + StatementNode* createContinueStatement(const JSTokenLocation& location, const Identifier* ident, const JSTextPosition& start, const JSTextPosition& end) + { + ContinueNode* result = new (m_parserArena) ContinueNode(location, *ident); + setExceptionLocation(result, start, end, end); + result->setLoc(start.line, end.line, start.offset, start.lineStartOffset); + return result; + } + + StatementNode* createTryStatement(const JSTokenLocation& location, StatementNode* tryBlock, const Identifier* ident, StatementNode* catchBlock, StatementNode* finallyBlock, int startLine, int endLine, VariableEnvironment& catchEnvironment) + { + TryNode* result = new (m_parserArena) TryNode(location, tryBlock, *ident, catchBlock, catchEnvironment, finallyBlock); + if (catchBlock) + usesCatch(); + result->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset); + return result; + } + + StatementNode* createSwitchStatement(const JSTokenLocation& location, ExpressionNode* expr, ClauseListNode* firstClauses, CaseClauseNode* defaultClause, ClauseListNode* secondClauses, int startLine, int endLine, VariableEnvironment& lexicalVariables) + { + CaseBlockNode* cases = new (m_parserArena) CaseBlockNode(firstClauses, defaultClause, secondClauses); + SwitchNode* result = new (m_parserArena) SwitchNode(location, expr, cases, lexicalVariables); + result->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset); + return result; + } + + StatementNode* createWhileStatement(const JSTokenLocation& location, ExpressionNode* expr, StatementNode* statement, int startLine, int endLine) + { + WhileNode* result = new (m_parserArena) WhileNode(location, expr, statement); + result->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset); + return result; + } + + StatementNode* createDoWhileStatement(const JSTokenLocation& location, StatementNode* statement, ExpressionNode* expr, int startLine, int endLine) + { + DoWhileNode* result = new (m_parserArena) DoWhileNode(location, statement, expr); + result->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset); + return result; + } + + StatementNode* createLabelStatement(const JSTokenLocation& location, const Identifier* ident, StatementNode* statement, const JSTextPosition& start, const JSTextPosition& end) + { + LabelNode* result = new (m_parserArena) LabelNode(location, *ident, statement); + setExceptionLocation(result, start, end, end); + return result; + } + + StatementNode* createWithStatement(const JSTokenLocation& location, ExpressionNode* expr, StatementNode* statement, unsigned start, const JSTextPosition& end, unsigned startLine, unsigned endLine) + { + usesWith(); + WithNode* result = new (m_parserArena) WithNode(location, expr, statement, end, end - start); + result->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset); + return result; + } + + StatementNode* createThrowStatement(const JSTokenLocation& location, ExpressionNode* expr, const JSTextPosition& start, const JSTextPosition& end) + { + ThrowNode* result = new (m_parserArena) ThrowNode(location, expr); + result->setLoc(start.line, end.line, start.offset, start.lineStartOffset); + setExceptionLocation(result, start, end, end); + return result; + } + + StatementNode* createDebugger(const JSTokenLocation& location, int startLine, int endLine) + { + DebuggerStatementNode* result = new (m_parserArena) DebuggerStatementNode(location); + result->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset); + return result; + } + + ModuleNameNode* createModuleName(const JSTokenLocation& location, const Identifier& moduleName) + { + return new (m_parserArena) ModuleNameNode(location, moduleName); + } + + ImportSpecifierNode* createImportSpecifier(const JSTokenLocation& location, const Identifier& importedName, const Identifier& localName) + { + return new (m_parserArena) ImportSpecifierNode(location, importedName, localName); + } + + ImportSpecifierListNode* createImportSpecifierList() + { + return new (m_parserArena) ImportSpecifierListNode(); + } + + void appendImportSpecifier(ImportSpecifierListNode* specifierList, ImportSpecifierNode* specifier) + { + specifierList->append(specifier); + } + + StatementNode* createImportDeclaration(const JSTokenLocation& location, ImportSpecifierListNode* importSpecifierList, ModuleNameNode* moduleName) + { + return new (m_parserArena) ImportDeclarationNode(location, importSpecifierList, moduleName); + } + + StatementNode* createExportAllDeclaration(const JSTokenLocation& location, ModuleNameNode* moduleName) + { + return new (m_parserArena) ExportAllDeclarationNode(location, moduleName); + } + + StatementNode* createExportDefaultDeclaration(const JSTokenLocation& location, StatementNode* declaration, const Identifier& localName) + { + return new (m_parserArena) ExportDefaultDeclarationNode(location, declaration, localName); + } + + StatementNode* createExportLocalDeclaration(const JSTokenLocation& location, StatementNode* declaration) + { + return new (m_parserArena) ExportLocalDeclarationNode(location, declaration); + } + + StatementNode* createExportNamedDeclaration(const JSTokenLocation& location, ExportSpecifierListNode* exportSpecifierList, ModuleNameNode* moduleName) + { + return new (m_parserArena) ExportNamedDeclarationNode(location, exportSpecifierList, moduleName); + } + + ExportSpecifierNode* createExportSpecifier(const JSTokenLocation& location, const Identifier& localName, const Identifier& exportedName) + { + return new (m_parserArena) ExportSpecifierNode(location, localName, exportedName); + } + + ExportSpecifierListNode* createExportSpecifierList() + { + return new (m_parserArena) ExportSpecifierListNode(); + } + + void appendExportSpecifier(ExportSpecifierListNode* specifierList, ExportSpecifierNode* specifier) + { + specifierList->append(specifier); + } + + void appendStatement(JSC::SourceElements* elements, JSC::StatementNode* statement) + { + elements->append(statement); + } + + CommaNode* createCommaExpr(const JSTokenLocation& location, ExpressionNode* node) + { + return new (m_parserArena) CommaNode(location, node); + } + + CommaNode* appendToCommaExpr(const JSTokenLocation& location, ExpressionNode*, ExpressionNode* tail, ExpressionNode* next) + { + ASSERT(tail->isCommaNode()); + CommaNode* newTail = new (m_parserArena) CommaNode(location, next); + static_cast<CommaNode*>(tail)->setNext(newTail); + return newTail; + } + + int evalCount() const { return m_evalCount; } + + void appendBinaryExpressionInfo(int& operandStackDepth, ExpressionNode* current, const JSTextPosition& exprStart, const JSTextPosition& lhs, const JSTextPosition& rhs, bool hasAssignments) + { + operandStackDepth++; + m_binaryOperandStack.append(std::make_pair(current, BinaryOpInfo(exprStart, lhs, rhs, hasAssignments))); + } + + // Logic to handle datastructures used during parsing of binary expressions + void operatorStackPop(int& operatorStackDepth) + { + operatorStackDepth--; + m_binaryOperatorStack.removeLast(); + } + bool operatorStackHasHigherPrecedence(int&, int precedence) + { + return precedence <= m_binaryOperatorStack.last().second; + } + const BinaryOperand& getFromOperandStack(int i) { return m_binaryOperandStack[m_binaryOperandStack.size() + i]; } + void shrinkOperandStackBy(int& operandStackDepth, int amount) + { + operandStackDepth -= amount; + ASSERT(operandStackDepth >= 0); + m_binaryOperandStack.resize(m_binaryOperandStack.size() - amount); + } + void appendBinaryOperation(const JSTokenLocation& location, int& operandStackDepth, int&, const BinaryOperand& lhs, const BinaryOperand& rhs) + { + operandStackDepth++; + m_binaryOperandStack.append(std::make_pair(makeBinaryNode(location, m_binaryOperatorStack.last().first, lhs, rhs), BinaryOpInfo(lhs.second, rhs.second))); + } + void operatorStackAppend(int& operatorStackDepth, int op, int precedence) + { + operatorStackDepth++; + m_binaryOperatorStack.append(std::make_pair(op, precedence)); + } + ExpressionNode* popOperandStack(int&) + { + ExpressionNode* result = m_binaryOperandStack.last().first; + m_binaryOperandStack.removeLast(); + return result; + } + + void appendUnaryToken(int& tokenStackDepth, int type, const JSTextPosition& start) + { + tokenStackDepth++; + m_unaryTokenStack.append(std::make_pair(type, start)); + } + + int unaryTokenStackLastType(int&) + { + return m_unaryTokenStack.last().first; + } + + const JSTextPosition& unaryTokenStackLastStart(int&) + { + return m_unaryTokenStack.last().second; + } + + void unaryTokenStackRemoveLast(int& tokenStackDepth) + { + tokenStackDepth--; + m_unaryTokenStack.removeLast(); + } + + void assignmentStackAppend(int& assignmentStackDepth, ExpressionNode* node, const JSTextPosition& start, const JSTextPosition& divot, int assignmentCount, Operator op) + { + assignmentStackDepth++; + ASSERT(start.offset >= start.lineStartOffset); + ASSERT(divot.offset >= divot.lineStartOffset); + m_assignmentInfoStack.append(AssignmentInfo(node, start, divot, assignmentCount, op)); + } + + ExpressionNode* createAssignment(const JSTokenLocation& location, int& assignmentStackDepth, ExpressionNode* rhs, int initialAssignmentCount, int currentAssignmentCount, const JSTextPosition& lastTokenEnd) + { + AssignmentInfo& info = m_assignmentInfoStack.last(); + ExpressionNode* result = makeAssignNode(location, info.m_node, info.m_op, rhs, info.m_initAssignments != initialAssignmentCount, info.m_initAssignments != currentAssignmentCount, info.m_start, info.m_divot + 1, lastTokenEnd); + m_assignmentInfoStack.removeLast(); + assignmentStackDepth--; + return result; + } + + const Identifier* getName(const Property& property) const { return property->name(); } + PropertyNode::Type getType(const Property& property) const { return property->type(); } + + bool isResolve(ExpressionNode* expr) const { return expr->isResolveNode(); } + + ExpressionNode* createDestructuringAssignment(const JSTokenLocation& location, DestructuringPattern pattern, ExpressionNode* initializer) + { + return new (m_parserArena) DestructuringAssignmentNode(location, pattern, initializer); + } + + ArrayPattern createArrayPattern(const JSTokenLocation&) + { + return new (m_parserArena) ArrayPatternNode(); + } + + void appendArrayPatternSkipEntry(ArrayPattern node, const JSTokenLocation& location) + { + node->appendIndex(ArrayPatternNode::BindingType::Elision, location, 0, nullptr); + } + + void appendArrayPatternEntry(ArrayPattern node, const JSTokenLocation& location, DestructuringPattern pattern, ExpressionNode* defaultValue) + { + node->appendIndex(ArrayPatternNode::BindingType::Element, location, pattern, defaultValue); + } + + void appendArrayPatternRestEntry(ArrayPattern node, const JSTokenLocation& location, DestructuringPattern pattern) + { + node->appendIndex(ArrayPatternNode::BindingType::RestElement, location, pattern, nullptr); + } + + void finishArrayPattern(ArrayPattern node, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd) + { + setExceptionLocation(node, divotStart, divot, divotEnd); + } + + ObjectPattern createObjectPattern(const JSTokenLocation&) + { + return new (m_parserArena) ObjectPatternNode(); + } + + void appendObjectPatternEntry(ObjectPattern node, const JSTokenLocation& location, bool wasString, const Identifier& identifier, DestructuringPattern pattern, ExpressionNode* defaultValue) + { + node->appendEntry(location, identifier, wasString, pattern, defaultValue); + } + + BindingPattern createBindingLocation(const JSTokenLocation&, const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end, AssignmentContext context) + { + return new (m_parserArena) BindingNode(boundProperty, start, end, context); + } + + void setEndOffset(Node* node, int offset) + { + node->setEndOffset(offset); + } + + int endOffset(Node* node) + { + return node->endOffset(); + } + + void setStartOffset(CaseClauseNode* node, int offset) + { + node->setStartOffset(offset); + } + + void setStartOffset(Node* node, int offset) + { + node->setStartOffset(offset); + } + +private: + struct Scope { + Scope() + : m_features(0) + , m_numConstants(0) + { + } + DeclarationStacks::FunctionStack m_funcDeclarations; + int m_features; + int m_numConstants; + }; + + static void setExceptionLocation(ThrowableExpressionData* node, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd) + { + ASSERT(divot.offset >= divot.lineStartOffset); + node->setExceptionSourceCode(divot, divotStart, divotEnd); + } + + void incConstants() { m_scope.m_numConstants++; } + void usesThis() { m_scope.m_features |= ThisFeature; } + void usesCatch() { m_scope.m_features |= CatchFeature; } + void usesArguments() { m_scope.m_features |= ArgumentsFeature; } + void usesWith() { m_scope.m_features |= WithFeature; } + void usesEval() + { + m_evalCount++; + m_scope.m_features |= EvalFeature; + } + ExpressionNode* createIntegerLikeNumber(const JSTokenLocation& location, double d) + { + return new (m_parserArena) IntegerNode(location, d); + } + ExpressionNode* createDoubleLikeNumber(const JSTokenLocation& location, double d) + { + return new (m_parserArena) DoubleNode(location, d); + } + ExpressionNode* createNumberFromBinaryOperation(const JSTokenLocation& location, double value, const NumberNode& originalNodeA, const NumberNode& originalNodeB) + { + if (originalNodeA.isIntegerNode() && originalNodeB.isIntegerNode()) + return createIntegerLikeNumber(location, value); + return createDoubleLikeNumber(location, value); + } + ExpressionNode* createNumberFromUnaryOperation(const JSTokenLocation& location, double value, const NumberNode& originalNode) + { + if (originalNode.isIntegerNode()) + return createIntegerLikeNumber(location, value); + return createDoubleLikeNumber(location, value); + } + + VM* m_vm; + ParserArena& m_parserArena; + SourceCode* m_sourceCode; + Scope m_scope; + Vector<BinaryOperand, 10, UnsafeVectorOverflow> m_binaryOperandStack; + Vector<AssignmentInfo, 10, UnsafeVectorOverflow> m_assignmentInfoStack; + Vector<std::pair<int, int>, 10, UnsafeVectorOverflow> m_binaryOperatorStack; + Vector<std::pair<int, JSTextPosition>, 10, UnsafeVectorOverflow> m_unaryTokenStack; + int m_evalCount; +}; + +ExpressionNode* ASTBuilder::makeTypeOfNode(const JSTokenLocation& location, ExpressionNode* expr) +{ + if (expr->isResolveNode()) { + ResolveNode* resolve = static_cast<ResolveNode*>(expr); + return new (m_parserArena) TypeOfResolveNode(location, resolve->identifier()); + } + return new (m_parserArena) TypeOfValueNode(location, expr); +} + +ExpressionNode* ASTBuilder::makeDeleteNode(const JSTokenLocation& location, ExpressionNode* expr, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) +{ + if (!expr->isLocation()) + return new (m_parserArena) DeleteValueNode(location, expr); + if (expr->isResolveNode()) { + ResolveNode* resolve = static_cast<ResolveNode*>(expr); + return new (m_parserArena) DeleteResolveNode(location, resolve->identifier(), divot, start, end); + } + if (expr->isBracketAccessorNode()) { + BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(expr); + return new (m_parserArena) DeleteBracketNode(location, bracket->base(), bracket->subscript(), divot, start, end); + } + ASSERT(expr->isDotAccessorNode()); + DotAccessorNode* dot = static_cast<DotAccessorNode*>(expr); + return new (m_parserArena) DeleteDotNode(location, dot->base(), dot->identifier(), divot, start, end); +} + +ExpressionNode* ASTBuilder::makeNegateNode(const JSTokenLocation& location, ExpressionNode* n) +{ + if (n->isNumber()) { + const NumberNode& numberNode = static_cast<const NumberNode&>(*n); + return createNumberFromUnaryOperation(location, -numberNode.value(), numberNode); + } + + return new (m_parserArena) NegateNode(location, n); +} + +ExpressionNode* ASTBuilder::makeBitwiseNotNode(const JSTokenLocation& location, ExpressionNode* expr) +{ + if (expr->isNumber()) + return createIntegerLikeNumber(location, ~toInt32(static_cast<NumberNode*>(expr)->value())); + return new (m_parserArena) BitwiseNotNode(location, expr); +} + +ExpressionNode* ASTBuilder::makeMultNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + expr1 = expr1->stripUnaryPlus(); + expr2 = expr2->stripUnaryPlus(); + + if (expr1->isNumber() && expr2->isNumber()) { + const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1); + const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2); + return createNumberFromBinaryOperation(location, numberExpr1.value() * numberExpr2.value(), numberExpr1, numberExpr2); + } + + if (expr1->isNumber() && static_cast<NumberNode*>(expr1)->value() == 1) + return new (m_parserArena) UnaryPlusNode(location, expr2); + + if (expr2->isNumber() && static_cast<NumberNode*>(expr2)->value() == 1) + return new (m_parserArena) UnaryPlusNode(location, expr1); + + return new (m_parserArena) MultNode(location, expr1, expr2, rightHasAssignments); +} + +ExpressionNode* ASTBuilder::makeDivNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + expr1 = expr1->stripUnaryPlus(); + expr2 = expr2->stripUnaryPlus(); + + if (expr1->isNumber() && expr2->isNumber()) { + const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1); + const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2); + double result = numberExpr1.value() / numberExpr2.value(); + if (static_cast<int64_t>(result) == result) + return createNumberFromBinaryOperation(location, result, numberExpr1, numberExpr2); + return createDoubleLikeNumber(location, result); + } + return new (m_parserArena) DivNode(location, expr1, expr2, rightHasAssignments); +} + +ExpressionNode* ASTBuilder::makeModNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + expr1 = expr1->stripUnaryPlus(); + expr2 = expr2->stripUnaryPlus(); + + if (expr1->isNumber() && expr2->isNumber()) { + const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1); + const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2); + return createIntegerLikeNumber(location, fmod(numberExpr1.value(), numberExpr2.value())); + } + return new (m_parserArena) ModNode(location, expr1, expr2, rightHasAssignments); +} + +ExpressionNode* ASTBuilder::makeAddNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + + if (expr1->isNumber() && expr2->isNumber()) { + const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1); + const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2); + return createNumberFromBinaryOperation(location, numberExpr1.value() + numberExpr2.value(), numberExpr1, numberExpr2); + } + return new (m_parserArena) AddNode(location, expr1, expr2, rightHasAssignments); +} + +ExpressionNode* ASTBuilder::makeSubNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + expr1 = expr1->stripUnaryPlus(); + expr2 = expr2->stripUnaryPlus(); + + if (expr1->isNumber() && expr2->isNumber()) { + const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1); + const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2); + return createNumberFromBinaryOperation(location, numberExpr1.value() - numberExpr2.value(), numberExpr1, numberExpr2); + } + return new (m_parserArena) SubNode(location, expr1, expr2, rightHasAssignments); +} + +ExpressionNode* ASTBuilder::makeLeftShiftNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + if (expr1->isNumber() && expr2->isNumber()) { + const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1); + const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2); + return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) << (toUInt32(numberExpr2.value()) & 0x1f)); + } + return new (m_parserArena) LeftShiftNode(location, expr1, expr2, rightHasAssignments); +} + +ExpressionNode* ASTBuilder::makeRightShiftNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + if (expr1->isNumber() && expr2->isNumber()) { + const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1); + const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2); + return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) >> (toUInt32(numberExpr2.value()) & 0x1f)); + } + return new (m_parserArena) RightShiftNode(location, expr1, expr2, rightHasAssignments); +} + +ExpressionNode* ASTBuilder::makeURightShiftNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + if (expr1->isNumber() && expr2->isNumber()) { + const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1); + const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2); + return createIntegerLikeNumber(location, toUInt32(numberExpr1.value()) >> (toUInt32(numberExpr2.value()) & 0x1f)); + } + return new (m_parserArena) UnsignedRightShiftNode(location, expr1, expr2, rightHasAssignments); +} + +ExpressionNode* ASTBuilder::makeBitOrNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + if (expr1->isNumber() && expr2->isNumber()) { + const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1); + const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2); + return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) | toInt32(numberExpr2.value())); + } + return new (m_parserArena) BitOrNode(location, expr1, expr2, rightHasAssignments); +} + +ExpressionNode* ASTBuilder::makeBitAndNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + if (expr1->isNumber() && expr2->isNumber()) { + const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1); + const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2); + return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) & toInt32(numberExpr2.value())); + } + return new (m_parserArena) BitAndNode(location, expr1, expr2, rightHasAssignments); +} + +ExpressionNode* ASTBuilder::makeBitXOrNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + if (expr1->isNumber() && expr2->isNumber()) { + const NumberNode& numberExpr1 = static_cast<NumberNode&>(*expr1); + const NumberNode& numberExpr2 = static_cast<NumberNode&>(*expr2); + return createIntegerLikeNumber(location, toInt32(numberExpr1.value()) ^ toInt32(numberExpr2.value())); + } + return new (m_parserArena) BitXOrNode(location, expr1, expr2, rightHasAssignments); +} + +ExpressionNode* ASTBuilder::makeFunctionCallNode(const JSTokenLocation& location, ExpressionNode* func, ArgumentsNode* args, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd) +{ + ASSERT(divot.offset >= divot.lineStartOffset); + if (!func->isLocation()) + return new (m_parserArena) FunctionCallValueNode(location, func, args, divot, divotStart, divotEnd); + if (func->isResolveNode()) { + ResolveNode* resolve = static_cast<ResolveNode*>(func); + const Identifier& identifier = resolve->identifier(); + if (identifier == m_vm->propertyNames->eval) { + usesEval(); + return new (m_parserArena) EvalFunctionCallNode(location, args, divot, divotStart, divotEnd); + } + if (BytecodeIntrinsicNode::EmitterType emitter = m_vm->propertyNames->bytecodeIntrinsicRegistry().lookup(identifier)) + return new (m_parserArena) BytecodeIntrinsicNode(location, emitter, identifier, args, divot, divotStart, divotEnd); + return new (m_parserArena) FunctionCallResolveNode(location, identifier, args, divot, divotStart, divotEnd); + } + if (func->isBracketAccessorNode()) { + BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(func); + FunctionCallBracketNode* node = new (m_parserArena) FunctionCallBracketNode(location, bracket->base(), bracket->subscript(), bracket->subscriptHasAssignments(), args, divot, divotStart, divotEnd); + node->setSubexpressionInfo(bracket->divot(), bracket->divotEnd().offset); + return node; + } + ASSERT(func->isDotAccessorNode()); + DotAccessorNode* dot = static_cast<DotAccessorNode*>(func); + FunctionCallDotNode* node; + if (dot->identifier() == m_vm->propertyNames->builtinNames().callPublicName() || dot->identifier() == m_vm->propertyNames->builtinNames().callPrivateName()) + node = new (m_parserArena) CallFunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd); + else if (dot->identifier() == m_vm->propertyNames->builtinNames().applyPublicName() || dot->identifier() == m_vm->propertyNames->builtinNames().applyPrivateName()) + node = new (m_parserArena) ApplyFunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd); + else + node = new (m_parserArena) FunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd); + node->setSubexpressionInfo(dot->divot(), dot->divotEnd().offset); + return node; +} + +ExpressionNode* ASTBuilder::makeBinaryNode(const JSTokenLocation& location, int token, std::pair<ExpressionNode*, BinaryOpInfo> lhs, std::pair<ExpressionNode*, BinaryOpInfo> rhs) +{ + switch (token) { + case OR: + return new (m_parserArena) LogicalOpNode(location, lhs.first, rhs.first, OpLogicalOr); + + case AND: + return new (m_parserArena) LogicalOpNode(location, lhs.first, rhs.first, OpLogicalAnd); + + case BITOR: + return makeBitOrNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case BITXOR: + return makeBitXOrNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case BITAND: + return makeBitAndNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case EQEQ: + return new (m_parserArena) EqualNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case NE: + return new (m_parserArena) NotEqualNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case STREQ: + return new (m_parserArena) StrictEqualNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case STRNEQ: + return new (m_parserArena) NotStrictEqualNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case LT: + return new (m_parserArena) LessNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case GT: + return new (m_parserArena) GreaterNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case LE: + return new (m_parserArena) LessEqNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case GE: + return new (m_parserArena) GreaterEqNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case INSTANCEOF: { + InstanceOfNode* node = new (m_parserArena) InstanceOfNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + setExceptionLocation(node, lhs.second.start, rhs.second.start, rhs.second.end); + return node; + } + + case INTOKEN: { + InNode* node = new (m_parserArena) InNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + setExceptionLocation(node, lhs.second.start, rhs.second.start, rhs.second.end); + return node; + } + + case LSHIFT: + return makeLeftShiftNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case RSHIFT: + return makeRightShiftNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case URSHIFT: + return makeURightShiftNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case PLUS: + return makeAddNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case MINUS: + return makeSubNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case TIMES: + return makeMultNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case DIVIDE: + return makeDivNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case MOD: + return makeModNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + } + CRASH(); + return 0; +} + +ExpressionNode* ASTBuilder::makeAssignNode(const JSTokenLocation& location, ExpressionNode* loc, Operator op, ExpressionNode* expr, bool locHasAssignments, bool exprHasAssignments, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) +{ + if (!loc->isLocation()) + return new (m_parserArena) AssignErrorNode(location, divot, start, end); + + if (loc->isResolveNode()) { + ResolveNode* resolve = static_cast<ResolveNode*>(loc); + if (op == OpEqual) { + if (expr->isFuncExprNode()) + static_cast<FuncExprNode*>(expr)->metadata()->setInferredName(resolve->identifier()); + AssignResolveNode* node = new (m_parserArena) AssignResolveNode(location, resolve->identifier(), expr, AssignmentContext::AssignmentExpression); + setExceptionLocation(node, start, divot, end); + return node; + } + return new (m_parserArena) ReadModifyResolveNode(location, resolve->identifier(), op, expr, exprHasAssignments, divot, start, end); + } + if (loc->isBracketAccessorNode()) { + BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(loc); + if (op == OpEqual) + return new (m_parserArena) AssignBracketNode(location, bracket->base(), bracket->subscript(), expr, locHasAssignments, exprHasAssignments, bracket->divot(), start, end); + ReadModifyBracketNode* node = new (m_parserArena) ReadModifyBracketNode(location, bracket->base(), bracket->subscript(), op, expr, locHasAssignments, exprHasAssignments, divot, start, end); + node->setSubexpressionInfo(bracket->divot(), bracket->divotEnd().offset); + return node; + } + ASSERT(loc->isDotAccessorNode()); + DotAccessorNode* dot = static_cast<DotAccessorNode*>(loc); + if (op == OpEqual) { + if (expr->isFuncExprNode()) + static_cast<FuncExprNode*>(expr)->metadata()->setInferredName(dot->identifier()); + return new (m_parserArena) AssignDotNode(location, dot->base(), dot->identifier(), expr, exprHasAssignments, dot->divot(), start, end); + } + + ReadModifyDotNode* node = new (m_parserArena) ReadModifyDotNode(location, dot->base(), dot->identifier(), op, expr, exprHasAssignments, divot, start, end); + node->setSubexpressionInfo(dot->divot(), dot->divotEnd().offset); + return node; +} + +ExpressionNode* ASTBuilder::makePrefixNode(const JSTokenLocation& location, ExpressionNode* expr, Operator op, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) +{ + return new (m_parserArena) PrefixNode(location, expr, op, divot, start, end); +} + +ExpressionNode* ASTBuilder::makePostfixNode(const JSTokenLocation& location, ExpressionNode* expr, Operator op, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) +{ + return new (m_parserArena) PostfixNode(location, expr, op, divot, start, end); +} + +} + +#endif diff --git a/Source/JavaScriptCore/parser/Keywords.table b/Source/JavaScriptCore/parser/Keywords.table new file mode 100644 index 000000000..e2ba528a4 --- /dev/null +++ b/Source/JavaScriptCore/parser/Keywords.table @@ -0,0 +1,57 @@ +# Main keywords. +@begin mainTable 47 + +# Types. +null NULLTOKEN +true TRUETOKEN +false FALSETOKEN + +# Keywords. +break BREAK +case CASE +catch CATCH +class CLASSTOKEN +const CONSTTOKEN +default DEFAULT +extends EXTENDS +finally FINALLY +for FOR +instanceof INSTANCEOF +new NEW +var VAR +let LET +continue CONTINUE +function FUNCTION +return RETURN +void VOIDTOKEN +delete DELETETOKEN +if IF +this THISTOKEN +do DO +while WHILE +else ELSE +in INTOKEN +super SUPER +switch SWITCH +throw THROW +try TRY +typeof TYPEOF +with WITH +debugger DEBUGGER + +# Reserved for future use. +enum RESERVED +export EXPORT +import IMPORT + +# Reserved for future use in strict code. +implements RESERVED_IF_STRICT +interface RESERVED_IF_STRICT +package RESERVED_IF_STRICT +private RESERVED_IF_STRICT +protected RESERVED_IF_STRICT +public RESERVED_IF_STRICT +static RESERVED_IF_STRICT +yield RESERVED_IF_STRICT + +@end diff --git a/Source/JavaScriptCore/parser/Lexer.cpp b/Source/JavaScriptCore/parser/Lexer.cpp new file mode 100644 index 000000000..c4db1618a --- /dev/null +++ b/Source/JavaScriptCore/parser/Lexer.cpp @@ -0,0 +1,2417 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012, 2013 Apple Inc. All Rights Reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2010 Zoltan Herczeg (zherczeg@inf.u-szeged.hu) + * Copyright (C) 2012 Mathias Bynens (mathias@qiwi.be) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "Lexer.h" + +#include "JSFunctionInlines.h" + +#include "BuiltinNames.h" +#include "JSGlobalObjectFunctions.h" +#include "Identifier.h" +#include "Nodes.h" +#include "JSCInlines.h" +#include <wtf/dtoa.h> +#include <ctype.h> +#include <limits.h> +#include <string.h> +#include <wtf/Assertions.h> + +#include "KeywordLookup.h" +#include "Lexer.lut.h" +#include "Parser.h" + +namespace JSC { + +bool isLexerKeyword(const Identifier& identifier) +{ + return JSC::mainTable.entry(identifier); +} + +enum CharacterType { + // Types for the main switch + + // The first three types are fixed, and also used for identifying + // ASCII alpha and alphanumeric characters (see isIdentStart and isIdentPart). + CharacterIdentifierStart, + CharacterZero, + CharacterNumber, + + CharacterInvalid, + CharacterLineTerminator, + CharacterExclamationMark, + CharacterOpenParen, + CharacterCloseParen, + CharacterOpenBracket, + CharacterCloseBracket, + CharacterComma, + CharacterColon, + CharacterQuestion, + CharacterTilde, + CharacterQuote, + CharacterBackQuote, + CharacterDot, + CharacterSlash, + CharacterBackSlash, + CharacterSemicolon, + CharacterOpenBrace, + CharacterCloseBrace, + + CharacterAdd, + CharacterSub, + CharacterMultiply, + CharacterModulo, + CharacterAnd, + CharacterXor, + CharacterOr, + CharacterLess, + CharacterGreater, + CharacterEqual, + + // Other types (only one so far) + CharacterWhiteSpace, + CharacterPrivateIdentifierStart +}; + +// 256 Latin-1 codes +static const unsigned short typesOfLatin1Characters[256] = { +/* 0 - Null */ CharacterInvalid, +/* 1 - Start of Heading */ CharacterInvalid, +/* 2 - Start of Text */ CharacterInvalid, +/* 3 - End of Text */ CharacterInvalid, +/* 4 - End of Transm. */ CharacterInvalid, +/* 5 - Enquiry */ CharacterInvalid, +/* 6 - Acknowledgment */ CharacterInvalid, +/* 7 - Bell */ CharacterInvalid, +/* 8 - Back Space */ CharacterInvalid, +/* 9 - Horizontal Tab */ CharacterWhiteSpace, +/* 10 - Line Feed */ CharacterLineTerminator, +/* 11 - Vertical Tab */ CharacterWhiteSpace, +/* 12 - Form Feed */ CharacterWhiteSpace, +/* 13 - Carriage Return */ CharacterLineTerminator, +/* 14 - Shift Out */ CharacterInvalid, +/* 15 - Shift In */ CharacterInvalid, +/* 16 - Data Line Escape */ CharacterInvalid, +/* 17 - Device Control 1 */ CharacterInvalid, +/* 18 - Device Control 2 */ CharacterInvalid, +/* 19 - Device Control 3 */ CharacterInvalid, +/* 20 - Device Control 4 */ CharacterInvalid, +/* 21 - Negative Ack. */ CharacterInvalid, +/* 22 - Synchronous Idle */ CharacterInvalid, +/* 23 - End of Transmit */ CharacterInvalid, +/* 24 - Cancel */ CharacterInvalid, +/* 25 - End of Medium */ CharacterInvalid, +/* 26 - Substitute */ CharacterInvalid, +/* 27 - Escape */ CharacterInvalid, +/* 28 - File Separator */ CharacterInvalid, +/* 29 - Group Separator */ CharacterInvalid, +/* 30 - Record Separator */ CharacterInvalid, +/* 31 - Unit Separator */ CharacterInvalid, +/* 32 - Space */ CharacterWhiteSpace, +/* 33 - ! */ CharacterExclamationMark, +/* 34 - " */ CharacterQuote, +/* 35 - # */ CharacterInvalid, +/* 36 - $ */ CharacterIdentifierStart, +/* 37 - % */ CharacterModulo, +/* 38 - & */ CharacterAnd, +/* 39 - ' */ CharacterQuote, +/* 40 - ( */ CharacterOpenParen, +/* 41 - ) */ CharacterCloseParen, +/* 42 - * */ CharacterMultiply, +/* 43 - + */ CharacterAdd, +/* 44 - , */ CharacterComma, +/* 45 - - */ CharacterSub, +/* 46 - . */ CharacterDot, +/* 47 - / */ CharacterSlash, +/* 48 - 0 */ CharacterZero, +/* 49 - 1 */ CharacterNumber, +/* 50 - 2 */ CharacterNumber, +/* 51 - 3 */ CharacterNumber, +/* 52 - 4 */ CharacterNumber, +/* 53 - 5 */ CharacterNumber, +/* 54 - 6 */ CharacterNumber, +/* 55 - 7 */ CharacterNumber, +/* 56 - 8 */ CharacterNumber, +/* 57 - 9 */ CharacterNumber, +/* 58 - : */ CharacterColon, +/* 59 - ; */ CharacterSemicolon, +/* 60 - < */ CharacterLess, +/* 61 - = */ CharacterEqual, +/* 62 - > */ CharacterGreater, +/* 63 - ? */ CharacterQuestion, +/* 64 - @ */ CharacterPrivateIdentifierStart, +/* 65 - A */ CharacterIdentifierStart, +/* 66 - B */ CharacterIdentifierStart, +/* 67 - C */ CharacterIdentifierStart, +/* 68 - D */ CharacterIdentifierStart, +/* 69 - E */ CharacterIdentifierStart, +/* 70 - F */ CharacterIdentifierStart, +/* 71 - G */ CharacterIdentifierStart, +/* 72 - H */ CharacterIdentifierStart, +/* 73 - I */ CharacterIdentifierStart, +/* 74 - J */ CharacterIdentifierStart, +/* 75 - K */ CharacterIdentifierStart, +/* 76 - L */ CharacterIdentifierStart, +/* 77 - M */ CharacterIdentifierStart, +/* 78 - N */ CharacterIdentifierStart, +/* 79 - O */ CharacterIdentifierStart, +/* 80 - P */ CharacterIdentifierStart, +/* 81 - Q */ CharacterIdentifierStart, +/* 82 - R */ CharacterIdentifierStart, +/* 83 - S */ CharacterIdentifierStart, +/* 84 - T */ CharacterIdentifierStart, +/* 85 - U */ CharacterIdentifierStart, +/* 86 - V */ CharacterIdentifierStart, +/* 87 - W */ CharacterIdentifierStart, +/* 88 - X */ CharacterIdentifierStart, +/* 89 - Y */ CharacterIdentifierStart, +/* 90 - Z */ CharacterIdentifierStart, +/* 91 - [ */ CharacterOpenBracket, +/* 92 - \ */ CharacterBackSlash, +/* 93 - ] */ CharacterCloseBracket, +/* 94 - ^ */ CharacterXor, +/* 95 - _ */ CharacterIdentifierStart, +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) +/* 96 - ` */ CharacterBackQuote, +#else +/* 96 - ` */ CharacterInvalid, +#endif +/* 97 - a */ CharacterIdentifierStart, +/* 98 - b */ CharacterIdentifierStart, +/* 99 - c */ CharacterIdentifierStart, +/* 100 - d */ CharacterIdentifierStart, +/* 101 - e */ CharacterIdentifierStart, +/* 102 - f */ CharacterIdentifierStart, +/* 103 - g */ CharacterIdentifierStart, +/* 104 - h */ CharacterIdentifierStart, +/* 105 - i */ CharacterIdentifierStart, +/* 106 - j */ CharacterIdentifierStart, +/* 107 - k */ CharacterIdentifierStart, +/* 108 - l */ CharacterIdentifierStart, +/* 109 - m */ CharacterIdentifierStart, +/* 110 - n */ CharacterIdentifierStart, +/* 111 - o */ CharacterIdentifierStart, +/* 112 - p */ CharacterIdentifierStart, +/* 113 - q */ CharacterIdentifierStart, +/* 114 - r */ CharacterIdentifierStart, +/* 115 - s */ CharacterIdentifierStart, +/* 116 - t */ CharacterIdentifierStart, +/* 117 - u */ CharacterIdentifierStart, +/* 118 - v */ CharacterIdentifierStart, +/* 119 - w */ CharacterIdentifierStart, +/* 120 - x */ CharacterIdentifierStart, +/* 121 - y */ CharacterIdentifierStart, +/* 122 - z */ CharacterIdentifierStart, +/* 123 - { */ CharacterOpenBrace, +/* 124 - | */ CharacterOr, +/* 125 - } */ CharacterCloseBrace, +/* 126 - ~ */ CharacterTilde, +/* 127 - Delete */ CharacterInvalid, +/* 128 - Cc category */ CharacterInvalid, +/* 129 - Cc category */ CharacterInvalid, +/* 130 - Cc category */ CharacterInvalid, +/* 131 - Cc category */ CharacterInvalid, +/* 132 - Cc category */ CharacterInvalid, +/* 133 - Cc category */ CharacterInvalid, +/* 134 - Cc category */ CharacterInvalid, +/* 135 - Cc category */ CharacterInvalid, +/* 136 - Cc category */ CharacterInvalid, +/* 137 - Cc category */ CharacterInvalid, +/* 138 - Cc category */ CharacterInvalid, +/* 139 - Cc category */ CharacterInvalid, +/* 140 - Cc category */ CharacterInvalid, +/* 141 - Cc category */ CharacterInvalid, +/* 142 - Cc category */ CharacterInvalid, +/* 143 - Cc category */ CharacterInvalid, +/* 144 - Cc category */ CharacterInvalid, +/* 145 - Cc category */ CharacterInvalid, +/* 146 - Cc category */ CharacterInvalid, +/* 147 - Cc category */ CharacterInvalid, +/* 148 - Cc category */ CharacterInvalid, +/* 149 - Cc category */ CharacterInvalid, +/* 150 - Cc category */ CharacterInvalid, +/* 151 - Cc category */ CharacterInvalid, +/* 152 - Cc category */ CharacterInvalid, +/* 153 - Cc category */ CharacterInvalid, +/* 154 - Cc category */ CharacterInvalid, +/* 155 - Cc category */ CharacterInvalid, +/* 156 - Cc category */ CharacterInvalid, +/* 157 - Cc category */ CharacterInvalid, +/* 158 - Cc category */ CharacterInvalid, +/* 159 - Cc category */ CharacterInvalid, +/* 160 - Zs category (nbsp) */ CharacterWhiteSpace, +/* 161 - Po category */ CharacterInvalid, +/* 162 - Sc category */ CharacterInvalid, +/* 163 - Sc category */ CharacterInvalid, +/* 164 - Sc category */ CharacterInvalid, +/* 165 - Sc category */ CharacterInvalid, +/* 166 - So category */ CharacterInvalid, +/* 167 - So category */ CharacterInvalid, +/* 168 - Sk category */ CharacterInvalid, +/* 169 - So category */ CharacterInvalid, +/* 170 - Ll category */ CharacterIdentifierStart, +/* 171 - Pi category */ CharacterInvalid, +/* 172 - Sm category */ CharacterInvalid, +/* 173 - Cf category */ CharacterInvalid, +/* 174 - So category */ CharacterInvalid, +/* 175 - Sk category */ CharacterInvalid, +/* 176 - So category */ CharacterInvalid, +/* 177 - Sm category */ CharacterInvalid, +/* 178 - No category */ CharacterInvalid, +/* 179 - No category */ CharacterInvalid, +/* 180 - Sk category */ CharacterInvalid, +/* 181 - Ll category */ CharacterIdentifierStart, +/* 182 - So category */ CharacterInvalid, +/* 183 - Po category */ CharacterInvalid, +/* 184 - Sk category */ CharacterInvalid, +/* 185 - No category */ CharacterInvalid, +/* 186 - Ll category */ CharacterIdentifierStart, +/* 187 - Pf category */ CharacterInvalid, +/* 188 - No category */ CharacterInvalid, +/* 189 - No category */ CharacterInvalid, +/* 190 - No category */ CharacterInvalid, +/* 191 - Po category */ CharacterInvalid, +/* 192 - Lu category */ CharacterIdentifierStart, +/* 193 - Lu category */ CharacterIdentifierStart, +/* 194 - Lu category */ CharacterIdentifierStart, +/* 195 - Lu category */ CharacterIdentifierStart, +/* 196 - Lu category */ CharacterIdentifierStart, +/* 197 - Lu category */ CharacterIdentifierStart, +/* 198 - Lu category */ CharacterIdentifierStart, +/* 199 - Lu category */ CharacterIdentifierStart, +/* 200 - Lu category */ CharacterIdentifierStart, +/* 201 - Lu category */ CharacterIdentifierStart, +/* 202 - Lu category */ CharacterIdentifierStart, +/* 203 - Lu category */ CharacterIdentifierStart, +/* 204 - Lu category */ CharacterIdentifierStart, +/* 205 - Lu category */ CharacterIdentifierStart, +/* 206 - Lu category */ CharacterIdentifierStart, +/* 207 - Lu category */ CharacterIdentifierStart, +/* 208 - Lu category */ CharacterIdentifierStart, +/* 209 - Lu category */ CharacterIdentifierStart, +/* 210 - Lu category */ CharacterIdentifierStart, +/* 211 - Lu category */ CharacterIdentifierStart, +/* 212 - Lu category */ CharacterIdentifierStart, +/* 213 - Lu category */ CharacterIdentifierStart, +/* 214 - Lu category */ CharacterIdentifierStart, +/* 215 - Sm category */ CharacterInvalid, +/* 216 - Lu category */ CharacterIdentifierStart, +/* 217 - Lu category */ CharacterIdentifierStart, +/* 218 - Lu category */ CharacterIdentifierStart, +/* 219 - Lu category */ CharacterIdentifierStart, +/* 220 - Lu category */ CharacterIdentifierStart, +/* 221 - Lu category */ CharacterIdentifierStart, +/* 222 - Lu category */ CharacterIdentifierStart, +/* 223 - Ll category */ CharacterIdentifierStart, +/* 224 - Ll category */ CharacterIdentifierStart, +/* 225 - Ll category */ CharacterIdentifierStart, +/* 226 - Ll category */ CharacterIdentifierStart, +/* 227 - Ll category */ CharacterIdentifierStart, +/* 228 - Ll category */ CharacterIdentifierStart, +/* 229 - Ll category */ CharacterIdentifierStart, +/* 230 - Ll category */ CharacterIdentifierStart, +/* 231 - Ll category */ CharacterIdentifierStart, +/* 232 - Ll category */ CharacterIdentifierStart, +/* 233 - Ll category */ CharacterIdentifierStart, +/* 234 - Ll category */ CharacterIdentifierStart, +/* 235 - Ll category */ CharacterIdentifierStart, +/* 236 - Ll category */ CharacterIdentifierStart, +/* 237 - Ll category */ CharacterIdentifierStart, +/* 238 - Ll category */ CharacterIdentifierStart, +/* 239 - Ll category */ CharacterIdentifierStart, +/* 240 - Ll category */ CharacterIdentifierStart, +/* 241 - Ll category */ CharacterIdentifierStart, +/* 242 - Ll category */ CharacterIdentifierStart, +/* 243 - Ll category */ CharacterIdentifierStart, +/* 244 - Ll category */ CharacterIdentifierStart, +/* 245 - Ll category */ CharacterIdentifierStart, +/* 246 - Ll category */ CharacterIdentifierStart, +/* 247 - Sm category */ CharacterInvalid, +/* 248 - Ll category */ CharacterIdentifierStart, +/* 249 - Ll category */ CharacterIdentifierStart, +/* 250 - Ll category */ CharacterIdentifierStart, +/* 251 - Ll category */ CharacterIdentifierStart, +/* 252 - Ll category */ CharacterIdentifierStart, +/* 253 - Ll category */ CharacterIdentifierStart, +/* 254 - Ll category */ CharacterIdentifierStart, +/* 255 - Ll category */ CharacterIdentifierStart +}; + +// This table provides the character that results from \X where X is the index in the table beginning +// with SPACE. A table value of 0 means that more processing needs to be done. +static const LChar singleCharacterEscapeValuesForASCII[128] = { +/* 0 - Null */ 0, +/* 1 - Start of Heading */ 0, +/* 2 - Start of Text */ 0, +/* 3 - End of Text */ 0, +/* 4 - End of Transm. */ 0, +/* 5 - Enquiry */ 0, +/* 6 - Acknowledgment */ 0, +/* 7 - Bell */ 0, +/* 8 - Back Space */ 0, +/* 9 - Horizontal Tab */ 0, +/* 10 - Line Feed */ 0, +/* 11 - Vertical Tab */ 0, +/* 12 - Form Feed */ 0, +/* 13 - Carriage Return */ 0, +/* 14 - Shift Out */ 0, +/* 15 - Shift In */ 0, +/* 16 - Data Line Escape */ 0, +/* 17 - Device Control 1 */ 0, +/* 18 - Device Control 2 */ 0, +/* 19 - Device Control 3 */ 0, +/* 20 - Device Control 4 */ 0, +/* 21 - Negative Ack. */ 0, +/* 22 - Synchronous Idle */ 0, +/* 23 - End of Transmit */ 0, +/* 24 - Cancel */ 0, +/* 25 - End of Medium */ 0, +/* 26 - Substitute */ 0, +/* 27 - Escape */ 0, +/* 28 - File Separator */ 0, +/* 29 - Group Separator */ 0, +/* 30 - Record Separator */ 0, +/* 31 - Unit Separator */ 0, +/* 32 - Space */ ' ', +/* 33 - ! */ '!', +/* 34 - " */ '"', +/* 35 - # */ '#', +/* 36 - $ */ '$', +/* 37 - % */ '%', +/* 38 - & */ '&', +/* 39 - ' */ '\'', +/* 40 - ( */ '(', +/* 41 - ) */ ')', +/* 42 - * */ '*', +/* 43 - + */ '+', +/* 44 - , */ ',', +/* 45 - - */ '-', +/* 46 - . */ '.', +/* 47 - / */ '/', +/* 48 - 0 */ 0, +/* 49 - 1 */ 0, +/* 50 - 2 */ 0, +/* 51 - 3 */ 0, +/* 52 - 4 */ 0, +/* 53 - 5 */ 0, +/* 54 - 6 */ 0, +/* 55 - 7 */ 0, +/* 56 - 8 */ 0, +/* 57 - 9 */ 0, +/* 58 - : */ ':', +/* 59 - ; */ ';', +/* 60 - < */ '<', +/* 61 - = */ '=', +/* 62 - > */ '>', +/* 63 - ? */ '?', +/* 64 - @ */ '@', +/* 65 - A */ 'A', +/* 66 - B */ 'B', +/* 67 - C */ 'C', +/* 68 - D */ 'D', +/* 69 - E */ 'E', +/* 70 - F */ 'F', +/* 71 - G */ 'G', +/* 72 - H */ 'H', +/* 73 - I */ 'I', +/* 74 - J */ 'J', +/* 75 - K */ 'K', +/* 76 - L */ 'L', +/* 77 - M */ 'M', +/* 78 - N */ 'N', +/* 79 - O */ 'O', +/* 80 - P */ 'P', +/* 81 - Q */ 'Q', +/* 82 - R */ 'R', +/* 83 - S */ 'S', +/* 84 - T */ 'T', +/* 85 - U */ 'U', +/* 86 - V */ 'V', +/* 87 - W */ 'W', +/* 88 - X */ 'X', +/* 89 - Y */ 'Y', +/* 90 - Z */ 'Z', +/* 91 - [ */ '[', +/* 92 - \ */ '\\', +/* 93 - ] */ ']', +/* 94 - ^ */ '^', +/* 95 - _ */ '_', +/* 96 - ` */ '`', +/* 97 - a */ 'a', +/* 98 - b */ 0x08, +/* 99 - c */ 'c', +/* 100 - d */ 'd', +/* 101 - e */ 'e', +/* 102 - f */ 0x0C, +/* 103 - g */ 'g', +/* 104 - h */ 'h', +/* 105 - i */ 'i', +/* 106 - j */ 'j', +/* 107 - k */ 'k', +/* 108 - l */ 'l', +/* 109 - m */ 'm', +/* 110 - n */ 0x0A, +/* 111 - o */ 'o', +/* 112 - p */ 'p', +/* 113 - q */ 'q', +/* 114 - r */ 0x0D, +/* 115 - s */ 's', +/* 116 - t */ 0x09, +/* 117 - u */ 0, +/* 118 - v */ 0x0B, +/* 119 - w */ 'w', +/* 120 - x */ 0, +/* 121 - y */ 'y', +/* 122 - z */ 'z', +/* 123 - { */ '{', +/* 124 - | */ '|', +/* 125 - } */ '}', +/* 126 - ~ */ '~', +/* 127 - Delete */ 0 +}; + +template <typename T> +Lexer<T>::Lexer(VM* vm, JSParserBuiltinMode builtinMode) + : m_isReparsingFunction(false) + , m_vm(vm) + , m_parsingBuiltinFunction(builtinMode == JSParserBuiltinMode::Builtin) +{ +} + +static inline JSTokenType tokenTypeForIntegerLikeToken(double doubleValue) +{ + if ((doubleValue || !std::signbit(doubleValue)) && static_cast<int64_t>(doubleValue) == doubleValue) + return INTEGER; + return DOUBLE; +} + +template <typename T> +Lexer<T>::~Lexer() +{ +} + +template <typename T> +String Lexer<T>::invalidCharacterMessage() const +{ + switch (m_current) { + case 0: + return ASCIILiteral("Invalid character: '\\0'"); + case 10: + return ASCIILiteral("Invalid character: '\\n'"); + case 11: + return ASCIILiteral("Invalid character: '\\v'"); + case 13: + return ASCIILiteral("Invalid character: '\\r'"); + case 35: + return ASCIILiteral("Invalid character: '#'"); + case 64: + return ASCIILiteral("Invalid character: '@'"); + case 96: + return ASCIILiteral("Invalid character: '`'"); + default: + return String::format("Invalid character '\\u%04u'", static_cast<unsigned>(m_current)); + } +} + +template <typename T> +ALWAYS_INLINE const T* Lexer<T>::currentSourcePtr() const +{ + ASSERT(m_code <= m_codeEnd); + return m_code; +} + +template <typename T> +void Lexer<T>::setCode(const SourceCode& source, ParserArena* arena) +{ + m_arena = &arena->identifierArena(); + + m_lineNumber = source.firstLine(); + m_lastToken = -1; + + const String& sourceString = source.provider()->source(); + + if (!sourceString.isNull()) + setCodeStart(sourceString.impl()); + else + m_codeStart = 0; + + m_source = &source; + m_sourceOffset = source.startOffset(); + m_codeStartPlusOffset = m_codeStart + source.startOffset(); + m_code = m_codeStartPlusOffset; + m_codeEnd = m_codeStart + source.endOffset(); + m_error = false; + m_atLineStart = true; + m_lineStart = m_code; + m_lexErrorMessage = String(); + + m_buffer8.reserveInitialCapacity(initialReadBufferCapacity); + m_buffer16.reserveInitialCapacity((m_codeEnd - m_code) / 2); + m_bufferForRawTemplateString16.reserveInitialCapacity(initialReadBufferCapacity); + + if (LIKELY(m_code < m_codeEnd)) + m_current = *m_code; + else + m_current = 0; + ASSERT(currentOffset() == source.startOffset()); +} + +template <typename T> +template <int shiftAmount> ALWAYS_INLINE void Lexer<T>::internalShift() +{ + m_code += shiftAmount; + ASSERT(currentOffset() >= currentLineStartOffset()); + m_current = *m_code; +} + +template <typename T> +ALWAYS_INLINE void Lexer<T>::shift() +{ + // At one point timing showed that setting m_current to 0 unconditionally was faster than an if-else sequence. + m_current = 0; + ++m_code; + if (LIKELY(m_code < m_codeEnd)) + m_current = *m_code; +} + +template <typename T> +ALWAYS_INLINE bool Lexer<T>::atEnd() const +{ + ASSERT(!m_current || m_code < m_codeEnd); + return UNLIKELY(UNLIKELY(!m_current) && m_code == m_codeEnd); +} + +template <typename T> +ALWAYS_INLINE T Lexer<T>::peek(int offset) const +{ + ASSERT(offset > 0 && offset < 5); + const T* code = m_code + offset; + return (code < m_codeEnd) ? *code : 0; +} + +struct ParsedUnicodeEscapeValue { + ParsedUnicodeEscapeValue(UChar32 value) + : m_value(value) + { + ASSERT(isValid()); + } + + enum SpecialValueType { Incomplete = -2, Invalid = -1 }; + ParsedUnicodeEscapeValue(SpecialValueType type) + : m_value(type) + { + } + + bool isValid() const { return m_value >= 0; } + bool isIncomplete() const { return m_value == Incomplete; } + + UChar32 value() const + { + ASSERT(isValid()); + return m_value; + } + +private: + UChar32 m_value; +}; + +template<typename CharacterType> ParsedUnicodeEscapeValue Lexer<CharacterType>::parseUnicodeEscape() +{ + if (m_current == '{') { + shift(); + UChar32 codePoint = 0; + do { + if (!isASCIIHexDigit(m_current)) + return m_current ? ParsedUnicodeEscapeValue::Invalid : ParsedUnicodeEscapeValue::Incomplete; + codePoint = (codePoint << 4) | toASCIIHexValue(m_current); + if (codePoint > UCHAR_MAX_VALUE) + return ParsedUnicodeEscapeValue::Invalid; + shift(); + } while (m_current != '}'); + shift(); + return codePoint; + } + + auto character2 = peek(1); + auto character3 = peek(2); + auto character4 = peek(3); + if (UNLIKELY(!isASCIIHexDigit(m_current) || !isASCIIHexDigit(character2) || !isASCIIHexDigit(character3) || !isASCIIHexDigit(character4))) + return (m_code + 4) >= m_codeEnd ? ParsedUnicodeEscapeValue::Incomplete : ParsedUnicodeEscapeValue::Invalid; + auto result = convertUnicode(m_current, character2, character3, character4); + shift(); + shift(); + shift(); + shift(); + return result; +} + +template <typename T> +void Lexer<T>::shiftLineTerminator() +{ + ASSERT(isLineTerminator(m_current)); + + m_positionBeforeLastNewline = currentPosition(); + T prev = m_current; + shift(); + + // Allow both CRLF and LFCR. + if (prev + m_current == '\n' + '\r') + shift(); + + ++m_lineNumber; +} + +template <typename T> +ALWAYS_INLINE bool Lexer<T>::lastTokenWasRestrKeyword() const +{ + return m_lastToken == CONTINUE || m_lastToken == BREAK || m_lastToken == RETURN || m_lastToken == THROW; +} + +static NEVER_INLINE bool isNonLatin1IdentStart(UChar c) +{ + return U_GET_GC_MASK(c) & U_GC_L_MASK; +} + +static ALWAYS_INLINE bool isLatin1(LChar) +{ + return true; +} + +static ALWAYS_INLINE bool isLatin1(UChar c) +{ + return c < 256; +} + +static ALWAYS_INLINE bool isLatin1(UChar32 c) +{ + return !(c & ~0xFF); +} + +static inline bool isIdentStart(LChar c) +{ + return typesOfLatin1Characters[c] == CharacterIdentifierStart; +} + +static inline bool isIdentStart(UChar32 c) +{ + return isLatin1(c) ? isIdentStart(static_cast<LChar>(c)) : isNonLatin1IdentStart(c); +} + +static NEVER_INLINE bool isNonLatin1IdentPart(UChar32 c) +{ + // FIXME: ES6 says this should be based on the Unicode property ID_Continue now instead. + return (U_GET_GC_MASK(c) & (U_GC_L_MASK | U_GC_MN_MASK | U_GC_MC_MASK | U_GC_ND_MASK | U_GC_PC_MASK)) || c == 0x200C || c == 0x200D; +} + +static ALWAYS_INLINE bool isIdentPart(LChar c) +{ + // Character types are divided into two groups depending on whether they can be part of an + // identifier or not. Those whose type value is less or equal than CharacterNumber can be + // part of an identifier. (See the CharacterType definition for more details.) + return typesOfLatin1Characters[c] <= CharacterNumber; +} + +static ALWAYS_INLINE bool isIdentPart(UChar32 c) +{ + return isLatin1(c) ? isIdentPart(static_cast<LChar>(c)) : isNonLatin1IdentPart(c); +} + +static ALWAYS_INLINE bool isIdentPart(UChar c) +{ + return isIdentPart(static_cast<UChar32>(c)); +} + +template<typename CharacterType> ALWAYS_INLINE bool isIdentPartIncludingEscapeTemplate(const CharacterType* code, const CharacterType* codeEnd) +{ + if (isIdentPart(code[0])) + return true; + + // Shortest sequence handled below is \u{0}, which is 5 characters. + if (!(code[0] == '\\' && codeEnd - code >= 5 && code[1] == 'u')) + return false; + + if (code[2] == '{') { + UChar32 codePoint = 0; + const CharacterType* pointer; + for (pointer = &code[3]; pointer < codeEnd; ++pointer) { + auto digit = *pointer; + if (!isASCIIHexDigit(digit)) + break; + codePoint = (codePoint << 4) | toASCIIHexValue(digit); + if (codePoint > UCHAR_MAX_VALUE) + return false; + } + return isIdentPart(codePoint) && pointer < codeEnd && *pointer == '}'; + } + + // Shortest sequence handled below is \uXXXX, which is 6 characters. + if (codeEnd - code < 6) + return false; + + auto character1 = code[2]; + auto character2 = code[3]; + auto character3 = code[4]; + auto character4 = code[5]; + return isASCIIHexDigit(character1) && isASCIIHexDigit(character2) && isASCIIHexDigit(character3) && isASCIIHexDigit(character4) + && isIdentPart(Lexer<LChar>::convertUnicode(character1, character2, character3, character4)); +} + +static ALWAYS_INLINE bool isIdentPartIncludingEscape(const LChar* code, const LChar* codeEnd) +{ + return isIdentPartIncludingEscapeTemplate(code, codeEnd); +} + +static ALWAYS_INLINE bool isIdentPartIncludingEscape(const UChar* code, const UChar* codeEnd) +{ + return isIdentPartIncludingEscapeTemplate(code, codeEnd); +} + +static inline LChar singleEscape(int c) +{ + if (c < 128) { + ASSERT(static_cast<size_t>(c) < ARRAY_SIZE(singleCharacterEscapeValuesForASCII)); + return singleCharacterEscapeValuesForASCII[c]; + } + return 0; +} + +template <typename T> +inline void Lexer<T>::record8(int c) +{ + ASSERT(c >= 0); + ASSERT(c <= 0xFF); + m_buffer8.append(static_cast<LChar>(c)); +} + +template <typename T> +inline void assertCharIsIn8BitRange(T c) +{ + UNUSED_PARAM(c); + ASSERT(c >= 0); + ASSERT(c <= 0xFF); +} + +template <> +inline void assertCharIsIn8BitRange(UChar c) +{ + UNUSED_PARAM(c); + ASSERT(c <= 0xFF); +} + +template <> +inline void assertCharIsIn8BitRange(LChar) +{ +} + +template <typename T> +inline void Lexer<T>::append8(const T* p, size_t length) +{ + size_t currentSize = m_buffer8.size(); + m_buffer8.grow(currentSize + length); + LChar* rawBuffer = m_buffer8.data() + currentSize; + + for (size_t i = 0; i < length; i++) { + T c = p[i]; + assertCharIsIn8BitRange(c); + rawBuffer[i] = c; + } +} + +template <typename T> +inline void Lexer<T>::append16(const LChar* p, size_t length) +{ + size_t currentSize = m_buffer16.size(); + m_buffer16.grow(currentSize + length); + UChar* rawBuffer = m_buffer16.data() + currentSize; + + for (size_t i = 0; i < length; i++) + rawBuffer[i] = p[i]; +} + +template <typename T> +inline void Lexer<T>::record16(T c) +{ + m_buffer16.append(c); +} + +template <typename T> +inline void Lexer<T>::record16(int c) +{ + ASSERT(c >= 0); + ASSERT(c <= static_cast<int>(USHRT_MAX)); + m_buffer16.append(static_cast<UChar>(c)); +} + +template<typename CharacterType> inline void Lexer<CharacterType>::recordUnicodeCodePoint(UChar32 codePoint) +{ + ASSERT(codePoint >= 0); + ASSERT(codePoint <= UCHAR_MAX_VALUE); + if (U_IS_BMP(codePoint)) + record16(codePoint); + else { + UChar codeUnits[2] = { U16_LEAD(codePoint), U16_TRAIL(codePoint) }; + append16(codeUnits, 2); + } +} + +#if !ASSERT_DISABLED +bool isSafeBuiltinIdentifier(VM& vm, const Identifier* ident) +{ + if (!ident) + return true; + /* Just block any use of suspicious identifiers. This is intended to + * be used as a safety net while implementing builtins. + */ + // FIXME: How can a debug-only assertion be a safety net? + if (*ident == vm.propertyNames->builtinNames().callPublicName()) + return false; + if (*ident == vm.propertyNames->builtinNames().applyPublicName()) + return false; + if (*ident == vm.propertyNames->eval) + return false; + if (*ident == vm.propertyNames->Function) + return false; + return true; +} +#endif + +template <> +template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<LChar>::parseIdentifier(JSTokenData* tokenData, unsigned lexerFlags, bool strictMode) +{ + const ptrdiff_t remaining = m_codeEnd - m_code; + if ((remaining >= maxTokenLength) && !(lexerFlags & LexerFlagsIgnoreReservedWords)) { + JSTokenType keyword = parseKeyword<shouldCreateIdentifier>(tokenData); + if (keyword != IDENT) { + ASSERT((!shouldCreateIdentifier) || tokenData->ident); + return keyword == RESERVED_IF_STRICT && !strictMode ? IDENT : keyword; + } + } + + bool isPrivateName = m_current == '@' && m_parsingBuiltinFunction; + if (isPrivateName) + shift(); + + const LChar* identifierStart = currentSourcePtr(); + unsigned identifierLineStart = currentLineStartOffset(); + + while (isIdentPart(m_current)) + shift(); + + if (UNLIKELY(m_current == '\\')) { + setOffsetFromSourcePtr(identifierStart, identifierLineStart); + return parseIdentifierSlowCase<shouldCreateIdentifier>(tokenData, lexerFlags, strictMode); + } + + const Identifier* ident = 0; + + if (shouldCreateIdentifier || m_parsingBuiltinFunction) { + int identifierLength = currentSourcePtr() - identifierStart; + ident = makeIdentifier(identifierStart, identifierLength); + if (m_parsingBuiltinFunction) { + if (!isSafeBuiltinIdentifier(*m_vm, ident) && !isPrivateName) { + m_lexErrorMessage = makeString("The use of '", ident->string(), "' is disallowed in builtin functions."); + return ERRORTOK; + } + if (isPrivateName) + ident = m_vm->propertyNames->getPrivateName(*ident); + else if (*ident == m_vm->propertyNames->undefinedKeyword) + tokenData->ident = &m_vm->propertyNames->undefinedPrivateName; + if (!ident) + return INVALID_PRIVATE_NAME_ERRORTOK; + } + tokenData->ident = ident; + } else + tokenData->ident = 0; + + if (UNLIKELY((remaining < maxTokenLength) && !(lexerFlags & LexerFlagsIgnoreReservedWords)) && !isPrivateName) { + ASSERT(shouldCreateIdentifier); + if (remaining < maxTokenLength) { + const HashTableValue* entry = JSC::mainTable.entry(*ident); + ASSERT((remaining < maxTokenLength) || !entry); + if (!entry) + return IDENT; + JSTokenType token = static_cast<JSTokenType>(entry->lexerValue()); + return (token != RESERVED_IF_STRICT) || strictMode ? token : IDENT; + } + return IDENT; + } + + return IDENT; +} + +template <> +template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<UChar>::parseIdentifier(JSTokenData* tokenData, unsigned lexerFlags, bool strictMode) +{ + const ptrdiff_t remaining = m_codeEnd - m_code; + if ((remaining >= maxTokenLength) && !(lexerFlags & LexerFlagsIgnoreReservedWords)) { + JSTokenType keyword = parseKeyword<shouldCreateIdentifier>(tokenData); + if (keyword != IDENT) { + ASSERT((!shouldCreateIdentifier) || tokenData->ident); + return keyword == RESERVED_IF_STRICT && !strictMode ? IDENT : keyword; + } + } + + bool isPrivateName = m_current == '@' && m_parsingBuiltinFunction; + if (isPrivateName) + shift(); + + const UChar* identifierStart = currentSourcePtr(); + int identifierLineStart = currentLineStartOffset(); + + UChar orAllChars = 0; + + while (isIdentPart(m_current)) { + orAllChars |= m_current; + shift(); + } + + if (UNLIKELY(m_current == '\\')) { + ASSERT(!isPrivateName); + setOffsetFromSourcePtr(identifierStart, identifierLineStart); + return parseIdentifierSlowCase<shouldCreateIdentifier>(tokenData, lexerFlags, strictMode); + } + + bool isAll8Bit = false; + + if (!(orAllChars & ~0xff)) + isAll8Bit = true; + + const Identifier* ident = 0; + + if (shouldCreateIdentifier || m_parsingBuiltinFunction) { + int identifierLength = currentSourcePtr() - identifierStart; + if (isAll8Bit) + ident = makeIdentifierLCharFromUChar(identifierStart, identifierLength); + else + ident = makeIdentifier(identifierStart, identifierLength); + if (m_parsingBuiltinFunction) { + if (!isSafeBuiltinIdentifier(*m_vm, ident) && !isPrivateName) { + m_lexErrorMessage = makeString("The use of '", ident->string(), "' is disallowed in builtin functions."); + return ERRORTOK; + } + if (isPrivateName) + ident = m_vm->propertyNames->getPrivateName(*ident); + else if (*ident == m_vm->propertyNames->undefinedKeyword) + tokenData->ident = &m_vm->propertyNames->undefinedPrivateName; + if (!ident) + return INVALID_PRIVATE_NAME_ERRORTOK; + } + tokenData->ident = ident; + } else + tokenData->ident = 0; + + if (UNLIKELY((remaining < maxTokenLength) && !(lexerFlags & LexerFlagsIgnoreReservedWords)) && !isPrivateName) { + ASSERT(shouldCreateIdentifier); + if (remaining < maxTokenLength) { + const HashTableValue* entry = JSC::mainTable.entry(*ident); + ASSERT((remaining < maxTokenLength) || !entry); + if (!entry) + return IDENT; + JSTokenType token = static_cast<JSTokenType>(entry->lexerValue()); + return (token != RESERVED_IF_STRICT) || strictMode ? token : IDENT; + } + return IDENT; + } + + return IDENT; +} + +template<typename CharacterType> template<bool shouldCreateIdentifier> JSTokenType Lexer<CharacterType>::parseIdentifierSlowCase(JSTokenData* tokenData, unsigned lexerFlags, bool strictMode) +{ + auto identifierStart = currentSourcePtr(); + bool bufferRequired = false; + + while (true) { + if (LIKELY(isIdentPart(m_current))) { + shift(); + continue; + } + if (LIKELY(m_current != '\\')) + break; + + // \uXXXX unicode characters. + bufferRequired = true; + if (identifierStart != currentSourcePtr()) + m_buffer16.append(identifierStart, currentSourcePtr() - identifierStart); + shift(); + if (UNLIKELY(m_current != 'u')) + return atEnd() ? UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK : INVALID_IDENTIFIER_ESCAPE_ERRORTOK; + shift(); + auto character = parseUnicodeEscape(); + if (UNLIKELY(!character.isValid())) + return character.isIncomplete() ? UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK : INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK; + if (UNLIKELY(m_buffer16.size() ? !isIdentPart(character.value()) : !isIdentStart(character.value()))) + return INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK; + if (shouldCreateIdentifier) + recordUnicodeCodePoint(character.value()); + identifierStart = currentSourcePtr(); + } + + int identifierLength; + const Identifier* ident = nullptr; + if (shouldCreateIdentifier) { + if (!bufferRequired) { + identifierLength = currentSourcePtr() - identifierStart; + ident = makeIdentifier(identifierStart, identifierLength); + } else { + if (identifierStart != currentSourcePtr()) + m_buffer16.append(identifierStart, currentSourcePtr() - identifierStart); + ident = makeIdentifier(m_buffer16.data(), m_buffer16.size()); + } + + tokenData->ident = ident; + } else + tokenData->ident = nullptr; + + m_buffer16.shrink(0); + + if (LIKELY(!(lexerFlags & LexerFlagsIgnoreReservedWords))) { + ASSERT(shouldCreateIdentifier); + const HashTableValue* entry = JSC::mainTable.entry(*ident); + if (!entry) + return IDENT; + JSTokenType token = static_cast<JSTokenType>(entry->lexerValue()); + return (token != RESERVED_IF_STRICT) || strictMode ? token : IDENT; + } + + return IDENT; +} + +static ALWAYS_INLINE bool characterRequiresParseStringSlowCase(LChar character) +{ + return character < 0xE; +} + +static ALWAYS_INLINE bool characterRequiresParseStringSlowCase(UChar character) +{ + return character < 0xE || character > 0xFF; +} + +template <typename T> +template <bool shouldBuildStrings> ALWAYS_INLINE typename Lexer<T>::StringParseResult Lexer<T>::parseString(JSTokenData* tokenData, bool strictMode) +{ + int startingOffset = currentOffset(); + int startingLineStartOffset = currentLineStartOffset(); + int startingLineNumber = lineNumber(); + T stringQuoteCharacter = m_current; + shift(); + + const T* stringStart = currentSourcePtr(); + + while (m_current != stringQuoteCharacter) { + if (UNLIKELY(m_current == '\\')) { + if (stringStart != currentSourcePtr() && shouldBuildStrings) + append8(stringStart, currentSourcePtr() - stringStart); + shift(); + + LChar escape = singleEscape(m_current); + + // Most common escape sequences first. + if (escape) { + if (shouldBuildStrings) + record8(escape); + shift(); + } else if (UNLIKELY(isLineTerminator(m_current))) + shiftLineTerminator(); + else if (m_current == 'x') { + shift(); + if (!isASCIIHexDigit(m_current) || !isASCIIHexDigit(peek(1))) { + m_lexErrorMessage = ASCIILiteral("\\x can only be followed by a hex character sequence"); + return (atEnd() || (isASCIIHexDigit(m_current) && (m_code + 1 == m_codeEnd))) ? StringUnterminated : StringCannotBeParsed; + } + T prev = m_current; + shift(); + if (shouldBuildStrings) + record8(convertHex(prev, m_current)); + shift(); + } else { + setOffset(startingOffset, startingLineStartOffset); + setLineNumber(startingLineNumber); + m_buffer8.shrink(0); + return parseStringSlowCase<shouldBuildStrings>(tokenData, strictMode); + } + stringStart = currentSourcePtr(); + continue; + } + + if (UNLIKELY(characterRequiresParseStringSlowCase(m_current))) { + setOffset(startingOffset, startingLineStartOffset); + setLineNumber(startingLineNumber); + m_buffer8.shrink(0); + return parseStringSlowCase<shouldBuildStrings>(tokenData, strictMode); + } + + shift(); + } + + if (currentSourcePtr() != stringStart && shouldBuildStrings) + append8(stringStart, currentSourcePtr() - stringStart); + if (shouldBuildStrings) { + tokenData->ident = makeIdentifier(m_buffer8.data(), m_buffer8.size()); + m_buffer8.shrink(0); + } else + tokenData->ident = 0; + + return StringParsedSuccessfully; +} + +template <typename T> +template <bool shouldBuildStrings> ALWAYS_INLINE auto Lexer<T>::parseComplexEscape(EscapeParseMode escapeParseMode, bool strictMode, T stringQuoteCharacter) -> StringParseResult +{ + if (m_current == 'x') { + shift(); + if (!isASCIIHexDigit(m_current) || !isASCIIHexDigit(peek(1))) { + m_lexErrorMessage = ASCIILiteral("\\x can only be followed by a hex character sequence"); + return StringCannotBeParsed; + } + T prev = m_current; + shift(); + if (shouldBuildStrings) + record16(convertHex(prev, m_current)); + shift(); + return StringParsedSuccessfully; + } + + if (m_current == 'u') { + shift(); + + if (escapeParseMode == EscapeParseMode::String && m_current == stringQuoteCharacter) { + if (shouldBuildStrings) + record16('u'); + return StringParsedSuccessfully; + } + + auto character = parseUnicodeEscape(); + if (character.isValid()) { + if (shouldBuildStrings) + recordUnicodeCodePoint(character.value()); + return StringParsedSuccessfully; + } + + m_lexErrorMessage = ASCIILiteral("\\u can only be followed by a Unicode character sequence"); + return character.isIncomplete() ? StringUnterminated : StringCannotBeParsed; + } + + if (strictMode) { + if (isASCIIDigit(m_current)) { + // The only valid numeric escape in strict mode is '\0', and this must not be followed by a decimal digit. + int character1 = m_current; + shift(); + if (character1 != '0' || isASCIIDigit(m_current)) { + m_lexErrorMessage = ASCIILiteral("The only valid numeric escape in strict mode is '\\0'"); + return StringCannotBeParsed; + } + if (shouldBuildStrings) + record16(0); + return StringParsedSuccessfully; + } + } else { + if (isASCIIOctalDigit(m_current)) { + // Octal character sequences + T character1 = m_current; + shift(); + if (isASCIIOctalDigit(m_current)) { + // Two octal characters + T character2 = m_current; + shift(); + if (character1 >= '0' && character1 <= '3' && isASCIIOctalDigit(m_current)) { + if (shouldBuildStrings) + record16((character1 - '0') * 64 + (character2 - '0') * 8 + m_current - '0'); + shift(); + } else { + if (shouldBuildStrings) + record16((character1 - '0') * 8 + character2 - '0'); + } + } else { + if (shouldBuildStrings) + record16(character1 - '0'); + } + return StringParsedSuccessfully; + } + } + + if (!atEnd()) { + if (shouldBuildStrings) + record16(m_current); + shift(); + return StringParsedSuccessfully; + } + + m_lexErrorMessage = ASCIILiteral("Unterminated string constant"); + return StringUnterminated; +} + +template <typename T> +template <bool shouldBuildStrings> auto Lexer<T>::parseStringSlowCase(JSTokenData* tokenData, bool strictMode) -> StringParseResult +{ + T stringQuoteCharacter = m_current; + shift(); + + const T* stringStart = currentSourcePtr(); + + while (m_current != stringQuoteCharacter) { + if (UNLIKELY(m_current == '\\')) { + if (stringStart != currentSourcePtr() && shouldBuildStrings) + append16(stringStart, currentSourcePtr() - stringStart); + shift(); + + LChar escape = singleEscape(m_current); + + // Most common escape sequences first + if (escape) { + if (shouldBuildStrings) + record16(escape); + shift(); + } else if (UNLIKELY(isLineTerminator(m_current))) + shiftLineTerminator(); + else { + StringParseResult result = parseComplexEscape<shouldBuildStrings>(EscapeParseMode::String, strictMode, stringQuoteCharacter); + if (result != StringParsedSuccessfully) + return result; + } + + stringStart = currentSourcePtr(); + continue; + } + // Fast check for characters that require special handling. + // Catches 0, \n, \r, 0x2028, and 0x2029 as efficiently + // as possible, and lets through all common ASCII characters. + if (UNLIKELY(((static_cast<unsigned>(m_current) - 0xE) & 0x2000))) { + // New-line or end of input is not allowed + if (atEnd() || isLineTerminator(m_current)) { + m_lexErrorMessage = ASCIILiteral("Unexpected EOF"); + return atEnd() ? StringUnterminated : StringCannotBeParsed; + } + // Anything else is just a normal character + } + shift(); + } + + if (currentSourcePtr() != stringStart && shouldBuildStrings) + append16(stringStart, currentSourcePtr() - stringStart); + if (shouldBuildStrings) + tokenData->ident = makeIdentifier(m_buffer16.data(), m_buffer16.size()); + else + tokenData->ident = 0; + + m_buffer16.shrink(0); + return StringParsedSuccessfully; +} + +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) +// While the lexer accepts <LF><CR> (not <CR><LF>) sequence +// as one line terminator and increments one line number, +// TemplateLiteral considers it as two line terminators <LF> and <CR>. +// +// TemplateLiteral normalizes line terminators as follows. +// +// <LF> => <LF> +// <CR> => <LF> +// <CR><LF> => <LF> +// <\u2028> => <\u2028> +// <\u2029> => <\u2029> +// +// So, <LF><CR> should be normalized to <LF><LF>. +// However, the lexer should increment the line number only once for <LF><CR>. +// +// To achieve this, LineNumberAdder holds the current status of line terminator sequence. +// When TemplateLiteral lexer encounters a line terminator, it notifies to LineNumberAdder. +// LineNumberAdder maintains the status and increments the line number when it's necessary. +// For example, LineNumberAdder increments the line number only once for <LF><CR> and <CR><LF>. +template<typename CharacterType> +class LineNumberAdder { +public: + LineNumberAdder(int& lineNumber) + : m_lineNumber(lineNumber) + { + } + + void clear() + { + m_previous = 0; + } + + void add(CharacterType character) + { + ASSERT(Lexer<CharacterType>::isLineTerminator(character)); + if ((character + m_previous) == ('\n' + '\r')) + m_previous = 0; + else { + ++m_lineNumber; + m_previous = character; + } + } + +private: + int& m_lineNumber; + CharacterType m_previous { 0 }; +}; + +template <typename T> +template <bool shouldBuildStrings> typename Lexer<T>::StringParseResult Lexer<T>::parseTemplateLiteral(JSTokenData* tokenData, RawStringsBuildMode rawStringsBuildMode) +{ + const T* stringStart = currentSourcePtr(); + const T* rawStringStart = currentSourcePtr(); + + LineNumberAdder<T> lineNumberAdder(m_lineNumber); + + while (m_current != '`') { + if (UNLIKELY(m_current == '\\')) { + lineNumberAdder.clear(); + if (stringStart != currentSourcePtr() && shouldBuildStrings) + append16(stringStart, currentSourcePtr() - stringStart); + shift(); + + LChar escape = singleEscape(m_current); + + // Most common escape sequences first. + if (escape) { + if (shouldBuildStrings) + record16(escape); + shift(); + } else if (UNLIKELY(isLineTerminator(m_current))) { + if (m_current == '\r') { + lineNumberAdder.add(m_current); + shift(); + if (m_current == '\n') { + lineNumberAdder.add(m_current); + shift(); + } + } else { + lineNumberAdder.add(m_current); + shift(); + } + } else { + bool strictMode = true; + StringParseResult result = parseComplexEscape<shouldBuildStrings>(EscapeParseMode::Template, strictMode, '`'); + if (result != StringParsedSuccessfully) + return result; + } + + stringStart = currentSourcePtr(); + continue; + } + + if (m_current == '$' && peek(1) == '{') + break; + + // Fast check for characters that require special handling. + // Catches 0, \n, \r, 0x2028, and 0x2029 as efficiently + // as possible, and lets through all common ASCII characters. + if (UNLIKELY(((static_cast<unsigned>(m_current) - 0xE) & 0x2000))) { + // End of input is not allowed. + // Unlike String, line terminator is allowed. + if (atEnd()) { + m_lexErrorMessage = ASCIILiteral("Unexpected EOF"); + return atEnd() ? StringUnterminated : StringCannotBeParsed; + } + + if (isLineTerminator(m_current)) { + if (m_current == '\r') { + // Normalize <CR>, <CR><LF> to <LF>. + if (shouldBuildStrings) { + if (stringStart != currentSourcePtr()) + append16(stringStart, currentSourcePtr() - stringStart); + if (rawStringStart != currentSourcePtr() && rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings) + m_bufferForRawTemplateString16.append(rawStringStart, currentSourcePtr() - rawStringStart); + + record16('\n'); + if (rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings) + m_bufferForRawTemplateString16.append('\n'); + } + lineNumberAdder.add(m_current); + shift(); + if (m_current == '\n') { + lineNumberAdder.add(m_current); + shift(); + } + stringStart = currentSourcePtr(); + rawStringStart = currentSourcePtr(); + } else { + lineNumberAdder.add(m_current); + shift(); + } + continue; + } + // Anything else is just a normal character + } + + lineNumberAdder.clear(); + shift(); + } + + bool isTail = m_current == '`'; + + if (shouldBuildStrings) { + if (currentSourcePtr() != stringStart) + append16(stringStart, currentSourcePtr() - stringStart); + if (rawStringStart != currentSourcePtr() && rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings) + m_bufferForRawTemplateString16.append(rawStringStart, currentSourcePtr() - rawStringStart); + } + + if (shouldBuildStrings) { + tokenData->cooked = makeIdentifier(m_buffer16.data(), m_buffer16.size()); + // Line terminator normalization (e.g. <CR> => <LF>) should be applied to both the raw and cooked representations. + if (rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings) + tokenData->raw = makeIdentifier(m_bufferForRawTemplateString16.data(), m_bufferForRawTemplateString16.size()); + else + tokenData->raw = makeEmptyIdentifier(); + } else { + tokenData->cooked = makeEmptyIdentifier(); + tokenData->raw = makeEmptyIdentifier(); + } + tokenData->isTail = isTail; + + m_buffer16.shrink(0); + m_bufferForRawTemplateString16.shrink(0); + + if (isTail) { + // Skip ` + shift(); + } else { + // Skip $ and { + shift(); + shift(); + } + + return StringParsedSuccessfully; +} +#endif + +template <typename T> +ALWAYS_INLINE void Lexer<T>::parseHex(double& returnValue) +{ + // Optimization: most hexadecimal values fit into 4 bytes. + uint32_t hexValue = 0; + int maximumDigits = 7; + + do { + hexValue = (hexValue << 4) + toASCIIHexValue(m_current); + shift(); + --maximumDigits; + } while (isASCIIHexDigit(m_current) && maximumDigits >= 0); + + if (maximumDigits >= 0) { + returnValue = hexValue; + return; + } + + // No more place in the hexValue buffer. + // The values are shifted out and placed into the m_buffer8 vector. + for (int i = 0; i < 8; ++i) { + int digit = hexValue >> 28; + if (digit < 10) + record8(digit + '0'); + else + record8(digit - 10 + 'a'); + hexValue <<= 4; + } + + while (isASCIIHexDigit(m_current)) { + record8(m_current); + shift(); + } + + returnValue = parseIntOverflow(m_buffer8.data(), m_buffer8.size(), 16); +} + +template <typename T> +ALWAYS_INLINE bool Lexer<T>::parseBinary(double& returnValue) +{ + // Optimization: most binary values fit into 4 bytes. + uint32_t binaryValue = 0; + const unsigned maximumDigits = 32; + int digit = maximumDigits - 1; + // Temporary buffer for the digits. Makes easier + // to reconstruct the input characters when needed. + LChar digits[maximumDigits]; + + do { + binaryValue = (binaryValue << 1) + (m_current - '0'); + digits[digit] = m_current; + shift(); + --digit; + } while (isASCIIBinaryDigit(m_current) && digit >= 0); + + if (!isASCIIDigit(m_current) && digit >= 0) { + returnValue = binaryValue; + return true; + } + + for (int i = maximumDigits - 1; i > digit; --i) + record8(digits[i]); + + while (isASCIIBinaryDigit(m_current)) { + record8(m_current); + shift(); + } + + if (isASCIIDigit(m_current)) + return false; + + returnValue = parseIntOverflow(m_buffer8.data(), m_buffer8.size(), 2); + return true; +} + +template <typename T> +ALWAYS_INLINE bool Lexer<T>::parseOctal(double& returnValue) +{ + // Optimization: most octal values fit into 4 bytes. + uint32_t octalValue = 0; + const unsigned maximumDigits = 10; + int digit = maximumDigits - 1; + // Temporary buffer for the digits. Makes easier + // to reconstruct the input characters when needed. + LChar digits[maximumDigits]; + + do { + octalValue = octalValue * 8 + (m_current - '0'); + digits[digit] = m_current; + shift(); + --digit; + } while (isASCIIOctalDigit(m_current) && digit >= 0); + + if (!isASCIIDigit(m_current) && digit >= 0) { + returnValue = octalValue; + return true; + } + + for (int i = maximumDigits - 1; i > digit; --i) + record8(digits[i]); + + while (isASCIIOctalDigit(m_current)) { + record8(m_current); + shift(); + } + + if (isASCIIDigit(m_current)) + return false; + + returnValue = parseIntOverflow(m_buffer8.data(), m_buffer8.size(), 8); + return true; +} + +template <typename T> +ALWAYS_INLINE bool Lexer<T>::parseDecimal(double& returnValue) +{ + // Optimization: most decimal values fit into 4 bytes. + uint32_t decimalValue = 0; + + // Since parseOctal may be executed before parseDecimal, + // the m_buffer8 may hold ascii digits. + if (!m_buffer8.size()) { + const unsigned maximumDigits = 10; + int digit = maximumDigits - 1; + // Temporary buffer for the digits. Makes easier + // to reconstruct the input characters when needed. + LChar digits[maximumDigits]; + + do { + decimalValue = decimalValue * 10 + (m_current - '0'); + digits[digit] = m_current; + shift(); + --digit; + } while (isASCIIDigit(m_current) && digit >= 0); + + if (digit >= 0 && m_current != '.' && (m_current | 0x20) != 'e') { + returnValue = decimalValue; + return true; + } + + for (int i = maximumDigits - 1; i > digit; --i) + record8(digits[i]); + } + + while (isASCIIDigit(m_current)) { + record8(m_current); + shift(); + } + + return false; +} + +template <typename T> +ALWAYS_INLINE void Lexer<T>::parseNumberAfterDecimalPoint() +{ + record8('.'); + while (isASCIIDigit(m_current)) { + record8(m_current); + shift(); + } +} + +template <typename T> +ALWAYS_INLINE bool Lexer<T>::parseNumberAfterExponentIndicator() +{ + record8('e'); + shift(); + if (m_current == '+' || m_current == '-') { + record8(m_current); + shift(); + } + + if (!isASCIIDigit(m_current)) + return false; + + do { + record8(m_current); + shift(); + } while (isASCIIDigit(m_current)); + return true; +} + +template <typename T> +ALWAYS_INLINE bool Lexer<T>::parseMultilineComment() +{ + while (true) { + while (UNLIKELY(m_current == '*')) { + shift(); + if (m_current == '/') { + shift(); + return true; + } + } + + if (atEnd()) + return false; + + if (isLineTerminator(m_current)) { + shiftLineTerminator(); + m_terminator = true; + } else + shift(); + } +} + +template <typename T> +bool Lexer<T>::nextTokenIsColon() +{ + const T* code = m_code; + while (code < m_codeEnd && (isWhiteSpace(*code) || isLineTerminator(*code))) + code++; + + return code < m_codeEnd && *code == ':'; +} + +template <typename T> +void Lexer<T>::setTokenPosition(JSToken* tokenRecord) +{ + JSTokenData* tokenData = &tokenRecord->m_data; + tokenData->line = lineNumber(); + tokenData->offset = currentOffset(); + tokenData->lineStartOffset = currentLineStartOffset(); + ASSERT(tokenData->offset >= tokenData->lineStartOffset); +} + +template <typename T> +JSTokenType Lexer<T>::lex(JSToken* tokenRecord, unsigned lexerFlags, bool strictMode) +{ + JSTokenData* tokenData = &tokenRecord->m_data; + JSTokenLocation* tokenLocation = &tokenRecord->m_location; + m_lastTockenLocation = JSTokenLocation(tokenRecord->m_location); + + ASSERT(!m_error); + ASSERT(m_buffer8.isEmpty()); + ASSERT(m_buffer16.isEmpty()); + + JSTokenType token = ERRORTOK; + m_terminator = false; + +start: + while (isWhiteSpace(m_current)) + shift(); + + if (atEnd()) + return EOFTOK; + + tokenLocation->startOffset = currentOffset(); + ASSERT(currentOffset() >= currentLineStartOffset()); + tokenRecord->m_startPosition = currentPosition(); + + CharacterType type; + if (LIKELY(isLatin1(m_current))) + type = static_cast<CharacterType>(typesOfLatin1Characters[m_current]); + else if (isNonLatin1IdentStart(m_current)) + type = CharacterIdentifierStart; + else if (isLineTerminator(m_current)) + type = CharacterLineTerminator; + else + type = CharacterInvalid; + + switch (type) { + case CharacterGreater: + shift(); + if (m_current == '>') { + shift(); + if (m_current == '>') { + shift(); + if (m_current == '=') { + shift(); + token = URSHIFTEQUAL; + break; + } + token = URSHIFT; + break; + } + if (m_current == '=') { + shift(); + token = RSHIFTEQUAL; + break; + } + token = RSHIFT; + break; + } + if (m_current == '=') { + shift(); + token = GE; + break; + } + token = GT; + break; + case CharacterEqual: { +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + if (peek(1) == '>') { + token = ARROWFUNCTION; + tokenData->line = lineNumber(); + tokenData->offset = currentOffset(); + tokenData->lineStartOffset = currentLineStartOffset(); + ASSERT(tokenData->offset >= tokenData->lineStartOffset); + shift(); + shift(); + break; + } +#endif + shift(); + if (m_current == '=') { + shift(); + if (m_current == '=') { + shift(); + token = STREQ; + break; + } + token = EQEQ; + break; + } + token = EQUAL; + break; + } + case CharacterLess: + shift(); + if (m_current == '!' && peek(1) == '-' && peek(2) == '-') { + // <!-- marks the beginning of a line comment (for www usage) + goto inSingleLineComment; + } + if (m_current == '<') { + shift(); + if (m_current == '=') { + shift(); + token = LSHIFTEQUAL; + break; + } + token = LSHIFT; + break; + } + if (m_current == '=') { + shift(); + token = LE; + break; + } + token = LT; + break; + case CharacterExclamationMark: + shift(); + if (m_current == '=') { + shift(); + if (m_current == '=') { + shift(); + token = STRNEQ; + break; + } + token = NE; + break; + } + token = EXCLAMATION; + break; + case CharacterAdd: + shift(); + if (m_current == '+') { + shift(); + token = (!m_terminator) ? PLUSPLUS : AUTOPLUSPLUS; + break; + } + if (m_current == '=') { + shift(); + token = PLUSEQUAL; + break; + } + token = PLUS; + break; + case CharacterSub: + shift(); + if (m_current == '-') { + shift(); + if (m_atLineStart && m_current == '>') { + shift(); + goto inSingleLineComment; + } + token = (!m_terminator) ? MINUSMINUS : AUTOMINUSMINUS; + break; + } + if (m_current == '=') { + shift(); + token = MINUSEQUAL; + break; + } + token = MINUS; + break; + case CharacterMultiply: + shift(); + if (m_current == '=') { + shift(); + token = MULTEQUAL; + break; + } + token = TIMES; + break; + case CharacterSlash: + shift(); + if (m_current == '/') { + shift(); + goto inSingleLineComment; + } + if (m_current == '*') { + shift(); + if (parseMultilineComment()) + goto start; + m_lexErrorMessage = ASCIILiteral("Multiline comment was not closed properly"); + token = UNTERMINATED_MULTILINE_COMMENT_ERRORTOK; + goto returnError; + } + if (m_current == '=') { + shift(); + token = DIVEQUAL; + break; + } + token = DIVIDE; + break; + case CharacterAnd: + shift(); + if (m_current == '&') { + shift(); + token = AND; + break; + } + if (m_current == '=') { + shift(); + token = ANDEQUAL; + break; + } + token = BITAND; + break; + case CharacterXor: + shift(); + if (m_current == '=') { + shift(); + token = XOREQUAL; + break; + } + token = BITXOR; + break; + case CharacterModulo: + shift(); + if (m_current == '=') { + shift(); + token = MODEQUAL; + break; + } + token = MOD; + break; + case CharacterOr: + shift(); + if (m_current == '=') { + shift(); + token = OREQUAL; + break; + } + if (m_current == '|') { + shift(); + token = OR; + break; + } + token = BITOR; + break; + case CharacterOpenParen: + token = OPENPAREN; + shift(); + break; + case CharacterCloseParen: + token = CLOSEPAREN; + shift(); + break; + case CharacterOpenBracket: + token = OPENBRACKET; + shift(); + break; + case CharacterCloseBracket: + token = CLOSEBRACKET; + shift(); + break; + case CharacterComma: + token = COMMA; + shift(); + break; + case CharacterColon: + token = COLON; + shift(); + break; + case CharacterQuestion: + token = QUESTION; + shift(); + break; + case CharacterTilde: + token = TILDE; + shift(); + break; + case CharacterSemicolon: + shift(); + token = SEMICOLON; + break; + case CharacterOpenBrace: + tokenData->line = lineNumber(); + tokenData->offset = currentOffset(); + tokenData->lineStartOffset = currentLineStartOffset(); + ASSERT(tokenData->offset >= tokenData->lineStartOffset); + shift(); + token = OPENBRACE; + break; + case CharacterCloseBrace: + tokenData->line = lineNumber(); + tokenData->offset = currentOffset(); + tokenData->lineStartOffset = currentLineStartOffset(); + ASSERT(tokenData->offset >= tokenData->lineStartOffset); + shift(); + token = CLOSEBRACE; + break; + case CharacterDot: + shift(); + if (!isASCIIDigit(m_current)) { + if (UNLIKELY((m_current == '.') && (peek(1) == '.'))) { + shift(); + shift(); + token = DOTDOTDOT; + break; + } + token = DOT; + break; + } + goto inNumberAfterDecimalPoint; + case CharacterZero: + shift(); + if ((m_current | 0x20) == 'x') { + if (!isASCIIHexDigit(peek(1))) { + m_lexErrorMessage = ASCIILiteral("No hexadecimal digits after '0x'"); + token = INVALID_HEX_NUMBER_ERRORTOK; + goto returnError; + } + + // Shift out the 'x' prefix. + shift(); + + parseHex(tokenData->doubleValue); + if (isIdentStart(m_current)) { + m_lexErrorMessage = ASCIILiteral("No space between hexadecimal literal and identifier"); + token = INVALID_HEX_NUMBER_ERRORTOK; + goto returnError; + } + token = tokenTypeForIntegerLikeToken(tokenData->doubleValue); + m_buffer8.shrink(0); + break; + } + if ((m_current | 0x20) == 'b') { + if (!isASCIIBinaryDigit(peek(1))) { + m_lexErrorMessage = ASCIILiteral("No binary digits after '0b'"); + token = INVALID_BINARY_NUMBER_ERRORTOK; + goto returnError; + } + + // Shift out the 'b' prefix. + shift(); + + parseBinary(tokenData->doubleValue); + if (isIdentStart(m_current)) { + m_lexErrorMessage = ASCIILiteral("No space between binary literal and identifier"); + token = INVALID_BINARY_NUMBER_ERRORTOK; + goto returnError; + } + token = tokenTypeForIntegerLikeToken(tokenData->doubleValue); + m_buffer8.shrink(0); + break; + } + + if ((m_current | 0x20) == 'o') { + if (!isASCIIOctalDigit(peek(1))) { + m_lexErrorMessage = ASCIILiteral("No octal digits after '0o'"); + token = INVALID_OCTAL_NUMBER_ERRORTOK; + goto returnError; + } + + // Shift out the 'o' prefix. + shift(); + + parseOctal(tokenData->doubleValue); + if (isIdentStart(m_current)) { + m_lexErrorMessage = ASCIILiteral("No space between octal literal and identifier"); + token = INVALID_OCTAL_NUMBER_ERRORTOK; + goto returnError; + } + token = tokenTypeForIntegerLikeToken(tokenData->doubleValue); + m_buffer8.shrink(0); + break; + } + + record8('0'); + if (strictMode && isASCIIDigit(m_current)) { + m_lexErrorMessage = ASCIILiteral("Decimal integer literals with a leading zero are forbidden in strict mode"); + token = INVALID_OCTAL_NUMBER_ERRORTOK; + goto returnError; + } + if (isASCIIOctalDigit(m_current)) { + if (parseOctal(tokenData->doubleValue)) { + token = tokenTypeForIntegerLikeToken(tokenData->doubleValue); + } + } + FALLTHROUGH; + case CharacterNumber: + if (LIKELY(token != INTEGER && token != DOUBLE)) { + if (!parseDecimal(tokenData->doubleValue)) { + token = INTEGER; + if (m_current == '.') { + shift(); +inNumberAfterDecimalPoint: + parseNumberAfterDecimalPoint(); + token = DOUBLE; + } + if ((m_current | 0x20) == 'e') { + if (!parseNumberAfterExponentIndicator()) { + m_lexErrorMessage = ASCIILiteral("Non-number found after exponent indicator"); + token = atEnd() ? UNTERMINATED_NUMERIC_LITERAL_ERRORTOK : INVALID_NUMERIC_LITERAL_ERRORTOK; + goto returnError; + } + } + size_t parsedLength; + tokenData->doubleValue = parseDouble(m_buffer8.data(), m_buffer8.size(), parsedLength); + if (token == INTEGER) + token = tokenTypeForIntegerLikeToken(tokenData->doubleValue); + } else + token = tokenTypeForIntegerLikeToken(tokenData->doubleValue); + } + + if (UNLIKELY(isIdentStart(m_current))) { + m_lexErrorMessage = ASCIILiteral("No identifiers allowed directly after numeric literal"); + token = atEnd() ? UNTERMINATED_NUMERIC_LITERAL_ERRORTOK : INVALID_NUMERIC_LITERAL_ERRORTOK; + goto returnError; + } + m_buffer8.shrink(0); + break; + case CharacterQuote: { + StringParseResult result = StringCannotBeParsed; + if (lexerFlags & LexerFlagsDontBuildStrings) + result = parseString<false>(tokenData, strictMode); + else + result = parseString<true>(tokenData, strictMode); + + if (UNLIKELY(result != StringParsedSuccessfully)) { + token = result == StringUnterminated ? UNTERMINATED_STRING_LITERAL_ERRORTOK : INVALID_STRING_LITERAL_ERRORTOK; + goto returnError; + } + shift(); + token = STRING; + break; + } +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + case CharacterBackQuote: { + // Skip backquote. + shift(); + StringParseResult result = StringCannotBeParsed; + if (lexerFlags & LexerFlagsDontBuildStrings) + result = parseTemplateLiteral<false>(tokenData, RawStringsBuildMode::BuildRawStrings); + else + result = parseTemplateLiteral<true>(tokenData, RawStringsBuildMode::BuildRawStrings); + + if (UNLIKELY(result != StringParsedSuccessfully)) { + token = result == StringUnterminated ? UNTERMINATED_TEMPLATE_LITERAL_ERRORTOK : INVALID_TEMPLATE_LITERAL_ERRORTOK; + goto returnError; + } + token = TEMPLATE; + break; + } +#endif + case CharacterIdentifierStart: + ASSERT(isIdentStart(m_current)); + FALLTHROUGH; + case CharacterBackSlash: + parseIdent: + if (lexerFlags & LexexFlagsDontBuildKeywords) + token = parseIdentifier<false>(tokenData, lexerFlags, strictMode); + else + token = parseIdentifier<true>(tokenData, lexerFlags, strictMode); + break; + case CharacterLineTerminator: + ASSERT(isLineTerminator(m_current)); + shiftLineTerminator(); + m_atLineStart = true; + m_terminator = true; + m_lineStart = m_code; + goto start; + case CharacterPrivateIdentifierStart: + if (m_parsingBuiltinFunction) + goto parseIdent; + + FALLTHROUGH; + case CharacterInvalid: + m_lexErrorMessage = invalidCharacterMessage(); + token = ERRORTOK; + goto returnError; + default: + RELEASE_ASSERT_NOT_REACHED(); + m_lexErrorMessage = ASCIILiteral("Internal Error"); + token = ERRORTOK; + goto returnError; + } + + m_atLineStart = false; + goto returnToken; + +inSingleLineComment: + while (!isLineTerminator(m_current)) { + if (atEnd()) + return EOFTOK; + shift(); + } + shiftLineTerminator(); + m_atLineStart = true; + m_terminator = true; + m_lineStart = m_code; + if (!lastTokenWasRestrKeyword()) + goto start; + + token = SEMICOLON; + // Fall through into returnToken. + +returnToken: + tokenLocation->line = m_lineNumber; + tokenLocation->endOffset = currentOffset(); + tokenLocation->lineStartOffset = currentLineStartOffset(); + ASSERT(tokenLocation->endOffset >= tokenLocation->lineStartOffset); + tokenRecord->m_endPosition = currentPosition(); + m_lastToken = token; + return token; + +returnError: + m_error = true; + tokenLocation->line = m_lineNumber; + tokenLocation->endOffset = currentOffset(); + tokenLocation->lineStartOffset = currentLineStartOffset(); + ASSERT(tokenLocation->endOffset >= tokenLocation->lineStartOffset); + tokenRecord->m_endPosition = currentPosition(); + RELEASE_ASSERT(token & ErrorTokenFlag); + return token; +} + +template <typename T> +static inline void orCharacter(UChar&, UChar); + +template <> +inline void orCharacter<LChar>(UChar&, UChar) { } + +template <> +inline void orCharacter<UChar>(UChar& orAccumulator, UChar character) +{ + orAccumulator |= character; +} + +template <typename T> +bool Lexer<T>::scanRegExp(const Identifier*& pattern, const Identifier*& flags, UChar patternPrefix) +{ + ASSERT(m_buffer16.isEmpty()); + + bool lastWasEscape = false; + bool inBrackets = false; + UChar charactersOredTogether = 0; + + if (patternPrefix) { + ASSERT(!isLineTerminator(patternPrefix)); + ASSERT(patternPrefix != '/'); + ASSERT(patternPrefix != '['); + record16(patternPrefix); + } + + while (true) { + if (isLineTerminator(m_current) || atEnd()) { + m_buffer16.shrink(0); + return false; + } + + T prev = m_current; + + shift(); + + if (prev == '/' && !lastWasEscape && !inBrackets) + break; + + record16(prev); + orCharacter<T>(charactersOredTogether, prev); + + if (lastWasEscape) { + lastWasEscape = false; + continue; + } + + switch (prev) { + case '[': + inBrackets = true; + break; + case ']': + inBrackets = false; + break; + case '\\': + lastWasEscape = true; + break; + } + } + + pattern = makeRightSizedIdentifier(m_buffer16.data(), m_buffer16.size(), charactersOredTogether); + + m_buffer16.shrink(0); + charactersOredTogether = 0; + + while (isIdentPart(m_current)) { + record16(m_current); + orCharacter<T>(charactersOredTogether, m_current); + shift(); + } + + flags = makeRightSizedIdentifier(m_buffer16.data(), m_buffer16.size(), charactersOredTogether); + m_buffer16.shrink(0); + + return true; +} + +template <typename T> +bool Lexer<T>::skipRegExp() +{ + bool lastWasEscape = false; + bool inBrackets = false; + + while (true) { + if (isLineTerminator(m_current) || atEnd()) + return false; + + T prev = m_current; + + shift(); + + if (prev == '/' && !lastWasEscape && !inBrackets) + break; + + if (lastWasEscape) { + lastWasEscape = false; + continue; + } + + switch (prev) { + case '[': + inBrackets = true; + break; + case ']': + inBrackets = false; + break; + case '\\': + lastWasEscape = true; + break; + } + } + + while (isIdentPart(m_current)) + shift(); + + return true; +} + +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) +template <typename T> +JSTokenType Lexer<T>::scanTrailingTemplateString(JSToken* tokenRecord, RawStringsBuildMode rawStringsBuildMode) +{ + JSTokenData* tokenData = &tokenRecord->m_data; + JSTokenLocation* tokenLocation = &tokenRecord->m_location; + ASSERT(!m_error); + ASSERT(m_buffer16.isEmpty()); + + // Leading closing brace } is already shifted in the previous token scan. + // So in this re-scan phase, shift() is not needed here. + StringParseResult result = parseTemplateLiteral<true>(tokenData, rawStringsBuildMode); + JSTokenType token = ERRORTOK; + if (UNLIKELY(result != StringParsedSuccessfully)) { + token = result == StringUnterminated ? UNTERMINATED_TEMPLATE_LITERAL_ERRORTOK : INVALID_TEMPLATE_LITERAL_ERRORTOK; + m_error = true; + } else { + token = TEMPLATE; + m_lastToken = token; + } + + // Since TemplateString always ends with ` or }, m_atLineStart always becomes false. + m_atLineStart = false; + + // Adjust current tokenLocation data for TemplateString. + tokenLocation->line = m_lineNumber; + tokenLocation->endOffset = currentOffset(); + tokenLocation->lineStartOffset = currentLineStartOffset(); + ASSERT(tokenLocation->endOffset >= tokenLocation->lineStartOffset); + tokenRecord->m_endPosition = currentPosition(); + return token; +} +#endif + +template <typename T> +void Lexer<T>::clear() +{ + m_arena = 0; + + Vector<LChar> newBuffer8; + m_buffer8.swap(newBuffer8); + + Vector<UChar> newBuffer16; + m_buffer16.swap(newBuffer16); + + Vector<UChar> newBufferForRawTemplateString16; + m_bufferForRawTemplateString16.swap(newBufferForRawTemplateString16); + + m_isReparsingFunction = false; +} + +// Instantiate the two flavors of Lexer we need instead of putting most of this file in Lexer.h +template class Lexer<LChar>; +template class Lexer<UChar>; + +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/Lexer.h b/Source/JavaScriptCore/parser/Lexer.h new file mode 100644 index 000000000..e5f56cca4 --- /dev/null +++ b/Source/JavaScriptCore/parser/Lexer.h @@ -0,0 +1,389 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2010 Zoltan Herczeg (zherczeg@inf.u-szeged.hu) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef Lexer_h +#define Lexer_h + +#include "Lookup.h" +#include "ParserArena.h" +#include "ParserTokens.h" +#include "SourceCode.h" +#include <wtf/ASCIICType.h> +#include <wtf/SegmentedVector.h> +#include <wtf/Vector.h> + +namespace JSC { + +enum LexerFlags { + LexerFlagsIgnoreReservedWords = 1, + LexerFlagsDontBuildStrings = 2, + LexexFlagsDontBuildKeywords = 4 +}; + +struct ParsedUnicodeEscapeValue; + +bool isLexerKeyword(const Identifier&); + +template <typename T> +class Lexer { + WTF_MAKE_NONCOPYABLE(Lexer); + WTF_MAKE_FAST_ALLOCATED; + +public: + Lexer(VM*, JSParserBuiltinMode); + ~Lexer(); + + // Character manipulation functions. + static bool isWhiteSpace(T character); + static bool isLineTerminator(T character); + static unsigned char convertHex(int c1, int c2); + static UChar convertUnicode(int c1, int c2, int c3, int c4); + + // Functions to set up parsing. + void setCode(const SourceCode&, ParserArena*); + void setIsReparsingFunction() { m_isReparsingFunction = true; } + bool isReparsingFunction() const { return m_isReparsingFunction; } + + void setTokenPosition(JSToken* tokenRecord); + JSTokenType lex(JSToken*, unsigned, bool strictMode); + bool nextTokenIsColon(); + int lineNumber() const { return m_lineNumber; } + ALWAYS_INLINE int currentOffset() const { return offsetFromSourcePtr(m_code); } + ALWAYS_INLINE int currentLineStartOffset() const { return offsetFromSourcePtr(m_lineStart); } + ALWAYS_INLINE JSTextPosition currentPosition() const + { + return JSTextPosition(m_lineNumber, currentOffset(), currentLineStartOffset()); + } + JSTextPosition positionBeforeLastNewline() const { return m_positionBeforeLastNewline; } + JSTokenLocation lastTokenLocation() const { return m_lastTockenLocation; } + void setLastLineNumber(int lastLineNumber) { m_lastLineNumber = lastLineNumber; } + int lastLineNumber() const { return m_lastLineNumber; } + bool prevTerminator() const { return m_terminator; } + bool scanRegExp(const Identifier*& pattern, const Identifier*& flags, UChar patternPrefix = 0); +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + enum class RawStringsBuildMode { BuildRawStrings, DontBuildRawStrings }; + JSTokenType scanTrailingTemplateString(JSToken*, RawStringsBuildMode); +#endif + bool skipRegExp(); + + // Functions for use after parsing. + bool sawError() const { return m_error; } + String getErrorMessage() const { return m_lexErrorMessage; } + void clear(); + void setOffset(int offset, int lineStartOffset) + { + m_error = 0; + m_lexErrorMessage = String(); + + m_code = sourcePtrFromOffset(offset); + m_lineStart = sourcePtrFromOffset(lineStartOffset); + ASSERT(currentOffset() >= currentLineStartOffset()); + + m_buffer8.resize(0); + m_buffer16.resize(0); + if (LIKELY(m_code < m_codeEnd)) + m_current = *m_code; + else + m_current = 0; + } + void setLineNumber(int line) + { + m_lineNumber = line; + } + void setTerminator(bool terminator) + { + m_terminator = terminator; + } + + SourceProvider* sourceProvider() const { return m_source->provider(); } + + JSTokenType lexExpectIdentifier(JSToken*, unsigned, bool strictMode); + +private: + void record8(int); + void append8(const T*, size_t); + void record16(int); + void record16(T); + void recordUnicodeCodePoint(UChar32); + void append16(const LChar*, size_t); + void append16(const UChar* characters, size_t length) { m_buffer16.append(characters, length); } + + ALWAYS_INLINE void shift(); + ALWAYS_INLINE bool atEnd() const; + ALWAYS_INLINE T peek(int offset) const; + + ParsedUnicodeEscapeValue parseUnicodeEscape(); + void shiftLineTerminator(); + + ALWAYS_INLINE int offsetFromSourcePtr(const T* ptr) const { return ptr - m_codeStart; } + ALWAYS_INLINE const T* sourcePtrFromOffset(int offset) const { return m_codeStart + offset; } + + String invalidCharacterMessage() const; + ALWAYS_INLINE const T* currentSourcePtr() const; + ALWAYS_INLINE void setOffsetFromSourcePtr(const T* sourcePtr, unsigned lineStartOffset) { setOffset(offsetFromSourcePtr(sourcePtr), lineStartOffset); } + + ALWAYS_INLINE void setCodeStart(const StringImpl*); + + ALWAYS_INLINE const Identifier* makeIdentifier(const LChar* characters, size_t length); + ALWAYS_INLINE const Identifier* makeIdentifier(const UChar* characters, size_t length); + ALWAYS_INLINE const Identifier* makeLCharIdentifier(const LChar* characters, size_t length); + ALWAYS_INLINE const Identifier* makeLCharIdentifier(const UChar* characters, size_t length); + ALWAYS_INLINE const Identifier* makeRightSizedIdentifier(const UChar* characters, size_t length, UChar orAllChars); + ALWAYS_INLINE const Identifier* makeIdentifierLCharFromUChar(const UChar* characters, size_t length); + ALWAYS_INLINE const Identifier* makeEmptyIdentifier(); + + ALWAYS_INLINE bool lastTokenWasRestrKeyword() const; + + template <int shiftAmount> void internalShift(); + template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType parseKeyword(JSTokenData*); + template <bool shouldBuildIdentifiers> ALWAYS_INLINE JSTokenType parseIdentifier(JSTokenData*, unsigned lexerFlags, bool strictMode); + template <bool shouldBuildIdentifiers> NEVER_INLINE JSTokenType parseIdentifierSlowCase(JSTokenData*, unsigned lexerFlags, bool strictMode); + enum StringParseResult { + StringParsedSuccessfully, + StringUnterminated, + StringCannotBeParsed + }; + template <bool shouldBuildStrings> ALWAYS_INLINE StringParseResult parseString(JSTokenData*, bool strictMode); + template <bool shouldBuildStrings> NEVER_INLINE StringParseResult parseStringSlowCase(JSTokenData*, bool strictMode); + + enum class EscapeParseMode { Template, String }; + template <bool shouldBuildStrings> ALWAYS_INLINE StringParseResult parseComplexEscape(EscapeParseMode, bool strictMode, T stringQuoteCharacter); +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + template <bool shouldBuildStrings> ALWAYS_INLINE StringParseResult parseTemplateLiteral(JSTokenData*, RawStringsBuildMode); +#endif + ALWAYS_INLINE void parseHex(double& returnValue); + ALWAYS_INLINE bool parseBinary(double& returnValue); + ALWAYS_INLINE bool parseOctal(double& returnValue); + ALWAYS_INLINE bool parseDecimal(double& returnValue); + ALWAYS_INLINE void parseNumberAfterDecimalPoint(); + ALWAYS_INLINE bool parseNumberAfterExponentIndicator(); + ALWAYS_INLINE bool parseMultilineComment(); + + static const size_t initialReadBufferCapacity = 32; + + int m_lineNumber; + int m_lastLineNumber; + + Vector<LChar> m_buffer8; + Vector<UChar> m_buffer16; + Vector<UChar> m_bufferForRawTemplateString16; + bool m_terminator; + int m_lastToken; + + const SourceCode* m_source; + unsigned m_sourceOffset; + const T* m_code; + const T* m_codeStart; + const T* m_codeEnd; + const T* m_codeStartPlusOffset; + const T* m_lineStart; + JSTextPosition m_positionBeforeLastNewline; + JSTokenLocation m_lastTockenLocation; + bool m_isReparsingFunction; + bool m_atLineStart; + bool m_error; + String m_lexErrorMessage; + + T m_current; + + IdentifierArena* m_arena; + + VM* m_vm; + bool m_parsingBuiltinFunction; +}; + +template <> +ALWAYS_INLINE bool Lexer<LChar>::isWhiteSpace(LChar ch) +{ + return ch == ' ' || ch == '\t' || ch == 0xB || ch == 0xC || ch == 0xA0; +} + +template <> +ALWAYS_INLINE bool Lexer<UChar>::isWhiteSpace(UChar ch) +{ + // 0x180E used to be in Zs category before Unicode 6.3, and EcmaScript says that we should keep treating it as such. + return (ch < 256) ? Lexer<LChar>::isWhiteSpace(static_cast<LChar>(ch)) : (u_charType(ch) == U_SPACE_SEPARATOR || ch == 0x180E || ch == 0xFEFF); +} + +template <> +ALWAYS_INLINE bool Lexer<LChar>::isLineTerminator(LChar ch) +{ + return ch == '\r' || ch == '\n'; +} + +template <> +ALWAYS_INLINE bool Lexer<UChar>::isLineTerminator(UChar ch) +{ + return ch == '\r' || ch == '\n' || (ch & ~1) == 0x2028; +} + +template <typename T> +inline unsigned char Lexer<T>::convertHex(int c1, int c2) +{ + return (toASCIIHexValue(c1) << 4) | toASCIIHexValue(c2); +} + +template <typename T> +inline UChar Lexer<T>::convertUnicode(int c1, int c2, int c3, int c4) +{ + return (convertHex(c1, c2) << 8) | convertHex(c3, c4); +} + +template <typename T> +ALWAYS_INLINE const Identifier* Lexer<T>::makeIdentifier(const LChar* characters, size_t length) +{ + return &m_arena->makeIdentifier(m_vm, characters, length); +} + +template <typename T> +ALWAYS_INLINE const Identifier* Lexer<T>::makeIdentifier(const UChar* characters, size_t length) +{ + return &m_arena->makeIdentifier(m_vm, characters, length); +} + +template <> +ALWAYS_INLINE const Identifier* Lexer<LChar>::makeRightSizedIdentifier(const UChar* characters, size_t length, UChar) +{ + return &m_arena->makeIdentifierLCharFromUChar(m_vm, characters, length); +} + +template <> +ALWAYS_INLINE const Identifier* Lexer<UChar>::makeRightSizedIdentifier(const UChar* characters, size_t length, UChar orAllChars) +{ + if (!(orAllChars & ~0xff)) + return &m_arena->makeIdentifierLCharFromUChar(m_vm, characters, length); + + return &m_arena->makeIdentifier(m_vm, characters, length); +} + +template <typename T> +ALWAYS_INLINE const Identifier* Lexer<T>::makeEmptyIdentifier() +{ + return &m_arena->makeEmptyIdentifier(m_vm); +} + +template <> +ALWAYS_INLINE void Lexer<LChar>::setCodeStart(const StringImpl* sourceString) +{ + ASSERT(sourceString->is8Bit()); + m_codeStart = sourceString->characters8(); +} + +template <> +ALWAYS_INLINE void Lexer<UChar>::setCodeStart(const StringImpl* sourceString) +{ + ASSERT(!sourceString->is8Bit()); + m_codeStart = sourceString->characters16(); +} + +template <typename T> +ALWAYS_INLINE const Identifier* Lexer<T>::makeIdentifierLCharFromUChar(const UChar* characters, size_t length) +{ + return &m_arena->makeIdentifierLCharFromUChar(m_vm, characters, length); +} + +template <typename T> +ALWAYS_INLINE const Identifier* Lexer<T>::makeLCharIdentifier(const LChar* characters, size_t length) +{ + return &m_arena->makeIdentifier(m_vm, characters, length); +} + +template <typename T> +ALWAYS_INLINE const Identifier* Lexer<T>::makeLCharIdentifier(const UChar* characters, size_t length) +{ + return &m_arena->makeIdentifierLCharFromUChar(m_vm, characters, length); +} + +#if ASSERT_DISABLED +ALWAYS_INLINE bool isSafeBuiltinIdentifier(VM&, const Identifier*) { return true; } +#else +bool isSafeBuiltinIdentifier(VM&, const Identifier*); +#endif + +template <typename T> +ALWAYS_INLINE JSTokenType Lexer<T>::lexExpectIdentifier(JSToken* tokenRecord, unsigned lexerFlags, bool strictMode) +{ + JSTokenData* tokenData = &tokenRecord->m_data; + JSTokenLocation* tokenLocation = &tokenRecord->m_location; + ASSERT((lexerFlags & LexerFlagsIgnoreReservedWords)); + const T* start = m_code; + const T* ptr = start; + const T* end = m_codeEnd; + JSTextPosition startPosition = currentPosition(); + if (ptr >= end) { + ASSERT(ptr == end); + goto slowCase; + } + if (!WTF::isASCIIAlpha(*ptr)) + goto slowCase; + ++ptr; + while (ptr < end) { + if (!WTF::isASCIIAlphanumeric(*ptr)) + break; + ++ptr; + } + + // Here's the shift + if (ptr < end) { + if ((!WTF::isASCII(*ptr)) || (*ptr == '\\') || (*ptr == '_') || (*ptr == '$')) + goto slowCase; + m_current = *ptr; + } else + m_current = 0; + + m_code = ptr; + ASSERT(currentOffset() >= currentLineStartOffset()); + + // Create the identifier if needed + if (lexerFlags & LexexFlagsDontBuildKeywords +#if !ASSERT_DISABLED + && !m_parsingBuiltinFunction +#endif + ) + tokenData->ident = 0; + else + tokenData->ident = makeLCharIdentifier(start, ptr - start); + + tokenLocation->line = m_lineNumber; + tokenLocation->lineStartOffset = currentLineStartOffset(); + tokenLocation->startOffset = offsetFromSourcePtr(start); + tokenLocation->endOffset = currentOffset(); + ASSERT(tokenLocation->startOffset >= tokenLocation->lineStartOffset); + tokenRecord->m_startPosition = startPosition; + tokenRecord->m_endPosition = currentPosition(); +#if !ASSERT_DISABLED + if (m_parsingBuiltinFunction) { + if (!isSafeBuiltinIdentifier(*m_vm, tokenData->ident)) + return ERRORTOK; + } +#endif + + m_lastToken = IDENT; + return IDENT; + +slowCase: + return lex(tokenRecord, lexerFlags, strictMode); +} + +} // namespace JSC + +#endif // Lexer_h diff --git a/Source/JavaScriptCore/parser/ModuleAnalyzer.cpp b/Source/JavaScriptCore/parser/ModuleAnalyzer.cpp new file mode 100644 index 000000000..b2de960b3 --- /dev/null +++ b/Source/JavaScriptCore/parser/ModuleAnalyzer.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ModuleAnalyzer.h" + +#include "IdentifierInlines.h" +#include "ModuleRecord.h" + +namespace JSC { + + +ModuleAnalyzer::ModuleAnalyzer(VM& vm, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables) + : m_vm(&vm) + , m_moduleRecord(ModuleRecord::create()) + , m_declaredVariables(declaredVariables) + , m_lexicalVariables(lexicalVariables) +{ +} + +Identifier ModuleAnalyzer::exportedBinding(const RefPtr<UniquedStringImpl>& ident) +{ + const auto iterator = m_aliasMap.find(ident); + if (iterator != m_aliasMap.end()) + return iterator->value; + return Identifier::fromUid(&vm(), ident.get()); +} + +void ModuleAnalyzer::declareExportAlias(const Identifier& localName, const Identifier& exportName) +{ + m_aliasMap.add(localName.impl(), exportName); +} + +void ModuleAnalyzer::exportVariable(const RefPtr<UniquedStringImpl>& localName, const VariableEnvironmentEntry& variable) +{ + // In the parser, we already marked the variables as Exported and Imported. + // By leveraging this information, we collect the information that is needed + // to construct the module environment. + // + // I E + // * = exported module local variable + // * = imported binding + // = non-exported module local variable + // * * = indirect exported binding + // + // One exception is namespace binding (like import * as ns from "mod"). + // This is annotated as an imported, but the actual binding is locate in the + // current module. + + if (!variable.isExported()) + return; + + const Identifier exportName = exportedBinding(localName); + + // Exported module local variable. + if (!variable.isImported()) { + moduleRecord().addExportEntry(ModuleRecord::ExportEntry::createLocal(exportName, Identifier::fromUid(m_vm, localName.get()), variable)); + return; + } + + const auto& importEntry = moduleRecord().lookUpImportEntry(localName); + if (importEntry.isNamespace(vm())) { + // Exported namespace binding. + // import * as namespace from "mod" + // export { namespace } + moduleRecord().addExportEntry(ModuleRecord::ExportEntry::createNamespace(exportName, importEntry.moduleRequest)); + return; + } + + // Indirectly exported binding. + // import a from "mod" + // export { a } + moduleRecord().addExportEntry(ModuleRecord::ExportEntry::createIndirect(exportName, importEntry.importName, importEntry.moduleRequest)); +} + + + +Ref<ModuleRecord> ModuleAnalyzer::analyze(ModuleProgramNode& moduleProgramNode) +{ + // Traverse the module AST and collect + // * Import entries + // * Export entries that have FromClause (e.g. export { a } from "mod") + // * Export entries that have star (e.g. export * from "mod") + // * Aliased export names (e.g. export { a as b }) + moduleProgramNode.analyzeModule(*this); + + // Based on the collected information, categorize export entries into 3 types. + // 1. Local export entries + // This references the local variable in the current module. + // This variable should be allocated in the current module environment as a heap variable. + // + // const variable = 20 + // export { variable } + // + // 2. Namespace export entries + // This references the namespace object imported by some import entries. + // This variable itself should be allocated in the current module environment as a heap variable. + // But when the other modules attempt to resolve this export name in this module, this module + // should tell the link to the original module. + // + // import * as namespace from "mod" + // export { namespace as mod } + // + // 3. Indirect export entries + // This references the imported binding name from the other module. + // This module environment itself should hold the pointer to (1) the original module and + // (2) the binding in the original module. The variable itself is allocated in the original + // module. This indirect binding is resolved when the CodeBlock resolves the references. + // + // import mod from "mod" + // export { mod } + // + // export { a } from "mod" + // + // And separeted from the above 3 types, we also collect the star export entries. + // + // 4. Star export entries + // This exports all the names from the specified external module as the current module's name. + // + // export * from "mod" + for (const auto& pair : m_declaredVariables) + exportVariable(pair.key, pair.value); + + for (const auto& pair : m_lexicalVariables) + exportVariable(pair.key, pair.value); + + if (Options::dumpModuleRecord()) + m_moduleRecord->dump(); + + return *m_moduleRecord; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/ModuleAnalyzer.h b/Source/JavaScriptCore/parser/ModuleAnalyzer.h new file mode 100644 index 000000000..027648476 --- /dev/null +++ b/Source/JavaScriptCore/parser/ModuleAnalyzer.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ModuleAnalyzer_h +#define ModuleAnalyzer_h + +#include "ModuleRecord.h" +#include "Nodes.h" + +namespace JSC { + +class ModuleAnalyzer { +public: + ModuleAnalyzer(VM&, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables); + + Ref<ModuleRecord> analyze(ModuleProgramNode&); + + VM& vm() { return *m_vm; } + + ModuleRecord& moduleRecord() { return *m_moduleRecord; } + + void declareExportAlias(const Identifier& localName, const Identifier& exportName); + +private: + typedef HashMap<RefPtr<UniquedStringImpl>, Identifier, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> IdentifierAliasMap; + + void exportVariable(const RefPtr<UniquedStringImpl>&, const VariableEnvironmentEntry&); + + Identifier exportedBinding(const RefPtr<UniquedStringImpl>& ident); + + VM* m_vm; + RefPtr<ModuleRecord> m_moduleRecord; + VariableEnvironment m_declaredVariables; + VariableEnvironment m_lexicalVariables; + IdentifierAliasMap m_aliasMap; +}; + +} // namespace JSC + +#endif // ModuleAnalyzer_h diff --git a/Source/JavaScriptCore/parser/ModuleRecord.cpp b/Source/JavaScriptCore/parser/ModuleRecord.cpp new file mode 100644 index 000000000..ec973568b --- /dev/null +++ b/Source/JavaScriptCore/parser/ModuleRecord.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ModuleRecord.h" + +#include "IdentifierInlines.h" + +namespace JSC { + + +auto ModuleRecord::ExportEntry::createLocal(const Identifier& exportName, const Identifier& localName, const VariableEnvironmentEntry& variable) -> ExportEntry +{ + return ExportEntry { Type::Local, exportName, Identifier(), Identifier(), localName, variable }; +} + +auto ModuleRecord::ExportEntry::createNamespace(const Identifier& exportName, const Identifier& moduleName) -> ExportEntry +{ + return ExportEntry { Type::Namespace, exportName, moduleName, Identifier(), Identifier(), VariableEnvironmentEntry() }; +} + +auto ModuleRecord::ExportEntry::createIndirect(const Identifier& exportName, const Identifier& importName, const Identifier& moduleName) -> ExportEntry +{ + return ExportEntry { Type::Indirect, exportName, moduleName, importName, Identifier(), VariableEnvironmentEntry() }; +} + +static String printableName(const RefPtr<UniquedStringImpl>& uid) +{ + if (uid->isSymbol()) + return uid.get(); + return WTF::makeString("'", String(uid.get()), "'"); +} + +static String printableName(const Identifier& ident) +{ + return printableName(ident.impl()); +} + + +void ModuleRecord::dump() +{ + dataLog("\nAnalyzing ModuleRecord\n"); + + dataLog(" Dependencies: ", m_requestedModules.size(), " modules\n"); + for (const auto& moduleName : m_requestedModules) + dataLog(" module(", printableName(moduleName), ")\n"); + + dataLog(" Import: ", m_importEntries.size(), " entries\n"); + for (const auto& pair : m_importEntries) + dataLog(" import(", printableName(pair.value.importName), "), local(", printableName(pair.value.localName), "), module(", printableName(pair.value.moduleRequest), ")\n"); + + dataLog(" Export: ", m_exportEntries.size(), " entries\n"); + for (const auto& pair : m_exportEntries) { + const ExportEntry& entry = pair.value; + switch (entry.type) { + case ExportEntry::Type::Local: + dataLog(" [Local] ", "export(", printableName(entry.exportName), "), local(", printableName(entry.localName), ")\n"); + break; + + case ExportEntry::Type::Namespace: + dataLog(" [Namespace] ", "export(", printableName(entry.exportName), "), module(", printableName(entry.moduleName), ")\n"); + break; + + case ExportEntry::Type::Indirect: + dataLog(" [Indirect] ", "export(", printableName(entry.exportName), "), import(", printableName(entry.importName), "), module(", printableName(entry.moduleName), ")\n"); + break; + } + } + for (const auto& moduleName : m_starExportEntries) + dataLog(" [Star] module(", printableName(moduleName.get()), ")\n"); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/ModuleRecord.h b/Source/JavaScriptCore/parser/ModuleRecord.h new file mode 100644 index 000000000..ef081c03b --- /dev/null +++ b/Source/JavaScriptCore/parser/ModuleRecord.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ModuleRecord_h +#define ModuleRecord_h + +#include "Nodes.h" +#include <wtf/HashMap.h> +#include <wtf/ListHashSet.h> + +namespace JSC { + +// Based on the Source Text Module Record +// http://www.ecma-international.org/ecma-262/6.0/#sec-source-text-module-records +class ModuleRecord : public RefCounted<ModuleRecord> { +public: + struct ExportEntry { + enum class Type { + Local, + Namespace, + Indirect + }; + + static ExportEntry createLocal(const Identifier& exportName, const Identifier& localName, const VariableEnvironmentEntry&); + static ExportEntry createNamespace(const Identifier& exportName, const Identifier& moduleName); + static ExportEntry createIndirect(const Identifier& exportName, const Identifier& importName, const Identifier& moduleName); + + Type type; + Identifier exportName; + Identifier moduleName; + Identifier importName; + Identifier localName; + VariableEnvironmentEntry variable; + }; + + struct ImportEntry { + Identifier moduleRequest; + Identifier importName; + Identifier localName; + + bool isNamespace(VM& vm) const + { + return importName == vm.propertyNames->timesIdentifier; + } + }; + + typedef WTF::ListHashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> OrderedIdentifierSet; + typedef HashMap<RefPtr<UniquedStringImpl>, ImportEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> ImportMap; + typedef HashMap<RefPtr<UniquedStringImpl>, ExportEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> ExportMap; + + static Ref<ModuleRecord> create() + { + return adoptRef(*new ModuleRecord); + } + + void appendRequestedModule(const Identifier&); + void addStarExportEntry(const Identifier&); + void addImportEntry(const ImportEntry&); + void addExportEntry(const ExportEntry&); + + const ImportEntry& lookUpImportEntry(const RefPtr<UniquedStringImpl>& localName); + + void dump(); + +private: + // Map localName -> ImportEntry. + ImportMap m_importEntries; + + // Map exportName -> ExportEntry. + ExportMap m_exportEntries; + + IdentifierSet m_starExportEntries; + + // Save the occurrence order since the module loader loads and runs the modules in this order. + // http://www.ecma-international.org/ecma-262/6.0/#sec-moduleevaluation + OrderedIdentifierSet m_requestedModules; +}; + +inline void ModuleRecord::appendRequestedModule(const Identifier& moduleName) +{ + m_requestedModules.add(moduleName.impl()); +} + +inline void ModuleRecord::addImportEntry(const ImportEntry& entry) +{ + m_importEntries.add(entry.localName.impl(), entry); +} + +inline void ModuleRecord::addExportEntry(const ExportEntry& entry) +{ + m_exportEntries.add(entry.exportName.impl(), entry); +} + +inline void ModuleRecord::addStarExportEntry(const Identifier& moduleName) +{ + m_starExportEntries.add(moduleName.impl()); +} + +inline auto ModuleRecord::lookUpImportEntry(const RefPtr<UniquedStringImpl>& localName) -> const ImportEntry& +{ + return (*m_importEntries.find(localName)).value; +} + +} // namespace JSC + +#endif // ModuleRecord_h diff --git a/Source/JavaScriptCore/parser/NodeConstructors.h b/Source/JavaScriptCore/parser/NodeConstructors.h new file mode 100644 index 000000000..abb72a74e --- /dev/null +++ b/Source/JavaScriptCore/parser/NodeConstructors.h @@ -0,0 +1,1018 @@ +/* + * Copyright (C) 2009, 2013 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef NodeConstructors_h +#define NodeConstructors_h + +#include "Nodes.h" +#include "Lexer.h" +#include "Parser.h" + +namespace JSC { + + inline void* ParserArenaFreeable::operator new(size_t size, ParserArena& parserArena) + { + return parserArena.allocateFreeable(size); + } + + inline void* ParserArenaDeletable::operator new(size_t size, ParserArena& parserArena) + { + return parserArena.allocateDeletable(size); + } + + inline ParserArenaRoot::ParserArenaRoot(ParserArena& parserArena) + { + m_arena.swap(parserArena); + } + + inline Node::Node(const JSTokenLocation& location) + : m_position(location.line, location.startOffset, location.lineStartOffset) + , m_endOffset(-1) + { + ASSERT(location.startOffset >= location.lineStartOffset); + } + + inline ExpressionNode::ExpressionNode(const JSTokenLocation& location, ResultType resultType) + : Node(location) + , m_resultType(resultType) + { + } + + inline StatementNode::StatementNode(const JSTokenLocation& location) + : Node(location) + , m_next(nullptr) + , m_lastLine(-1) + { + } + + inline ConstantNode::ConstantNode(const JSTokenLocation& location, ResultType resultType) + : ExpressionNode(location, resultType) + { + } + + inline NullNode::NullNode(const JSTokenLocation& location) + : ConstantNode(location, ResultType::nullType()) + { + } + + inline BooleanNode::BooleanNode(const JSTokenLocation& location, bool value) + : ConstantNode(location, ResultType::booleanType()) + , m_value(value) + { + } + + inline NumberNode::NumberNode(const JSTokenLocation& location, double value) + : ConstantNode(location, JSValue(value).isInt32() ? ResultType::numberTypeIsInt32() : ResultType::numberType()) + , m_value(value) + { + } + + inline DoubleNode::DoubleNode(const JSTokenLocation& location, double value) + : NumberNode(location, value) + { + } + + inline IntegerNode::IntegerNode(const JSTokenLocation& location, double value) + : DoubleNode(location, value) + { + } + + inline StringNode::StringNode(const JSTokenLocation& location, const Identifier& value) + : ConstantNode(location, ResultType::stringType()) + , m_value(value) + { + } + +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + inline TemplateExpressionListNode::TemplateExpressionListNode(ExpressionNode* node) + : m_node(node) + { + } + + inline TemplateExpressionListNode::TemplateExpressionListNode(TemplateExpressionListNode* previous, ExpressionNode* node) + : m_node(node) + { + previous->m_next = this; + } + + inline TemplateStringNode::TemplateStringNode(const JSTokenLocation& location, const Identifier& cooked, const Identifier& raw) + : ExpressionNode(location) + , m_cooked(cooked) + , m_raw(raw) + { + } + + inline TemplateStringListNode::TemplateStringListNode(TemplateStringNode* node) + : m_node(node) + { + } + + inline TemplateStringListNode::TemplateStringListNode(TemplateStringListNode* previous, TemplateStringNode* node) + : m_node(node) + { + previous->m_next = this; + } + + inline TemplateLiteralNode::TemplateLiteralNode(const JSTokenLocation& location, TemplateStringListNode* templateStrings) + : ExpressionNode(location) + , m_templateStrings(templateStrings) + , m_templateExpressions(nullptr) + { + } + + inline TemplateLiteralNode::TemplateLiteralNode(const JSTokenLocation& location, TemplateStringListNode* templateStrings, TemplateExpressionListNode* templateExpressions) + : ExpressionNode(location) + , m_templateStrings(templateStrings) + , m_templateExpressions(templateExpressions) + { + } + + inline TaggedTemplateNode::TaggedTemplateNode(const JSTokenLocation& location, ExpressionNode* tag, TemplateLiteralNode* templateLiteral) + : ExpressionNode(location) + , m_tag(tag) + , m_templateLiteral(templateLiteral) + { + } +#endif + + inline RegExpNode::RegExpNode(const JSTokenLocation& location, const Identifier& pattern, const Identifier& flags) + : ExpressionNode(location) + , m_pattern(pattern) + , m_flags(flags) + { + } + + inline ThisNode::ThisNode(const JSTokenLocation& location, ThisTDZMode thisTDZMode) + : ExpressionNode(location) + , m_shouldAlwaysEmitTDZCheck(thisTDZMode == ThisTDZMode::AlwaysCheck) + { + } + + inline SuperNode::SuperNode(const JSTokenLocation& location) + : ExpressionNode(location) + { + } + + inline NewTargetNode::NewTargetNode(const JSTokenLocation& location) + : ExpressionNode(location) + { + } + + inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifier& ident, const JSTextPosition& start) + : ExpressionNode(location) + , m_ident(ident) + , m_start(start) + { + ASSERT(m_start.offset >= m_start.lineStartOffset); + } + + inline ElementNode::ElementNode(int elision, ExpressionNode* node) + : m_next(0) + , m_elision(elision) + , m_node(node) + { + } + + inline ElementNode::ElementNode(ElementNode* l, int elision, ExpressionNode* node) + : m_next(0) + , m_elision(elision) + , m_node(node) + { + l->m_next = this; + } + + inline ArrayNode::ArrayNode(const JSTokenLocation& location, int elision) + : ExpressionNode(location) + , m_element(0) + , m_elision(elision) + , m_optional(true) + { + } + + inline ArrayNode::ArrayNode(const JSTokenLocation& location, ElementNode* element) + : ExpressionNode(location) + , m_element(element) + , m_elision(0) + , m_optional(false) + { + } + + inline ArrayNode::ArrayNode(const JSTokenLocation& location, int elision, ElementNode* element) + : ExpressionNode(location) + , m_element(element) + , m_elision(elision) + , m_optional(true) + { + } + + inline PropertyNode::PropertyNode(const Identifier& name, ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding = SuperBinding::NotNeeded) + : m_name(&name) + , m_assign(assign) + , m_type(type) + , m_needsSuperBinding(superBinding == SuperBinding::Needed) + , m_putType(putType) + { + } + + inline PropertyNode::PropertyNode(ExpressionNode* name, ExpressionNode* assign, Type type, PutType putType) + : m_name(0) + , m_expression(name) + , m_assign(assign) + , m_type(type) + , m_needsSuperBinding(false) + , m_putType(putType) + { + } + + inline PropertyListNode::PropertyListNode(const JSTokenLocation& location, PropertyNode* node) + : ExpressionNode(location) + , m_node(node) + , m_next(0) + { + } + + inline PropertyListNode::PropertyListNode(const JSTokenLocation& location, PropertyNode* node, PropertyListNode* list) + : ExpressionNode(location) + , m_node(node) + , m_next(0) + { + list->m_next = this; + } + + inline ObjectLiteralNode::ObjectLiteralNode(const JSTokenLocation& location) + : ExpressionNode(location) + , m_list(0) + { + } + + inline ObjectLiteralNode::ObjectLiteralNode(const JSTokenLocation& location, PropertyListNode* list) + : ExpressionNode(location) + , m_list(list) + { + } + + inline BracketAccessorNode::BracketAccessorNode(const JSTokenLocation& location, ExpressionNode* base, ExpressionNode* subscript, bool subscriptHasAssignments) + : ExpressionNode(location) + , m_base(base) + , m_subscript(subscript) + , m_subscriptHasAssignments(subscriptHasAssignments) + { + } + + inline DotAccessorNode::DotAccessorNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident) + : ExpressionNode(location) + , m_base(base) + , m_ident(ident) + { + } + + + inline SpreadExpressionNode::SpreadExpressionNode(const JSTokenLocation& location, ExpressionNode* expression) + : ExpressionNode(location) + , m_expression(expression) + { + } + + inline ArgumentListNode::ArgumentListNode(const JSTokenLocation& location, ExpressionNode* expr) + : ExpressionNode(location) + , m_next(0) + , m_expr(expr) + { + } + + inline ArgumentListNode::ArgumentListNode(const JSTokenLocation& location, ArgumentListNode* listNode, ExpressionNode* expr) + : ExpressionNode(location) + , m_next(0) + , m_expr(expr) + { + listNode->m_next = this; + } + + inline ArgumentsNode::ArgumentsNode() + : m_listNode(0) + { + } + + inline ArgumentsNode::ArgumentsNode(ArgumentListNode* listNode) + : m_listNode(listNode) + { + } + + inline NewExprNode::NewExprNode(const JSTokenLocation& location, ExpressionNode* expr) + : ExpressionNode(location) + , m_expr(expr) + , m_args(0) + { + } + + inline NewExprNode::NewExprNode(const JSTokenLocation& location, ExpressionNode* expr, ArgumentsNode* args) + : ExpressionNode(location) + , m_expr(expr) + , m_args(args) + { + } + + inline EvalFunctionCallNode::EvalFunctionCallNode(const JSTokenLocation& location, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableExpressionData(divot, divotStart, divotEnd) + , m_args(args) + { + } + + inline FunctionCallValueNode::FunctionCallValueNode(const JSTokenLocation& location, ExpressionNode* expr, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableExpressionData(divot, divotStart, divotEnd) + , m_expr(expr) + , m_args(args) + { + ASSERT(divot.offset >= divotStart.offset); + } + + inline FunctionCallResolveNode::FunctionCallResolveNode(const JSTokenLocation& location, const Identifier& ident, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableExpressionData(divot, divotStart, divotEnd) + , m_ident(ident) + , m_args(args) + { + } + + inline FunctionCallBracketNode::FunctionCallBracketNode(const JSTokenLocation& location, ExpressionNode* base, ExpressionNode* subscript, bool subscriptHasAssignments, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableSubExpressionData(divot, divotStart, divotEnd) + , m_base(base) + , m_subscript(subscript) + , m_args(args) + , m_subscriptHasAssignments(subscriptHasAssignments) + { + } + + inline FunctionCallDotNode::FunctionCallDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableSubExpressionData(divot, divotStart, divotEnd) + , m_base(base) + , m_ident(ident) + , m_args(args) + { + } + + inline BytecodeIntrinsicNode::BytecodeIntrinsicNode(const JSTokenLocation& location, EmitterType emitter, const Identifier& ident, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableExpressionData(divot, divotStart, divotEnd) + , m_emitter(emitter) + , m_ident(ident) + , m_args(args) + { + } + + inline CallFunctionCallDotNode::CallFunctionCallDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : FunctionCallDotNode(location, base, ident, args, divot, divotStart, divotEnd) + { + } + + inline ApplyFunctionCallDotNode::ApplyFunctionCallDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : FunctionCallDotNode(location, base, ident, args, divot, divotStart, divotEnd) + { + } + + inline PostfixNode::PostfixNode(const JSTokenLocation& location, ExpressionNode* expr, Operator oper, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : PrefixNode(location, expr, oper, divot, divotStart, divotEnd) + { + } + + inline DeleteResolveNode::DeleteResolveNode(const JSTokenLocation& location, const Identifier& ident, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableExpressionData(divot, divotStart, divotEnd) + , m_ident(ident) + { + } + + inline DeleteBracketNode::DeleteBracketNode(const JSTokenLocation& location, ExpressionNode* base, ExpressionNode* subscript, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableExpressionData(divot, divotStart, divotEnd) + , m_base(base) + , m_subscript(subscript) + { + } + + inline DeleteDotNode::DeleteDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableExpressionData(divot, divotStart, divotEnd) + , m_base(base) + , m_ident(ident) + { + } + + inline DeleteValueNode::DeleteValueNode(const JSTokenLocation& location, ExpressionNode* expr) + : ExpressionNode(location) + , m_expr(expr) + { + } + + inline VoidNode::VoidNode(const JSTokenLocation& location, ExpressionNode* expr) + : ExpressionNode(location) + , m_expr(expr) + { + } + + inline TypeOfResolveNode::TypeOfResolveNode(const JSTokenLocation& location, const Identifier& ident) + : ExpressionNode(location, ResultType::stringType()) + , m_ident(ident) + { + } + + inline TypeOfValueNode::TypeOfValueNode(const JSTokenLocation& location, ExpressionNode* expr) + : ExpressionNode(location, ResultType::stringType()) + , m_expr(expr) + { + } + + inline PrefixNode::PrefixNode(const JSTokenLocation& location, ExpressionNode* expr, Operator oper, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowablePrefixedSubExpressionData(divot, divotStart, divotEnd) + , m_expr(expr) + , m_operator(oper) + { + } + + inline UnaryOpNode::UnaryOpNode(const JSTokenLocation& location, ResultType type, ExpressionNode* expr, OpcodeID opcodeID) + : ExpressionNode(location, type) + , m_expr(expr) + , m_opcodeID(opcodeID) + { + } + + inline UnaryPlusNode::UnaryPlusNode(const JSTokenLocation& location, ExpressionNode* expr) + : UnaryOpNode(location, ResultType::numberType(), expr, op_to_number) + { + } + + inline NegateNode::NegateNode(const JSTokenLocation& location, ExpressionNode* expr) + : UnaryOpNode(location, ResultType::numberType(), expr, op_negate) + { + } + + inline BitwiseNotNode::BitwiseNotNode(const JSTokenLocation& location, ExpressionNode* expr) + : ExpressionNode(location, ResultType::forBitOp()) + , m_expr(expr) + { + } + + inline LogicalNotNode::LogicalNotNode(const JSTokenLocation& location, ExpressionNode* expr) + : UnaryOpNode(location, ResultType::booleanType(), expr, op_not) + { + } + + inline BinaryOpNode::BinaryOpNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, OpcodeID opcodeID, bool rightHasAssignments) + : ExpressionNode(location) + , m_expr1(expr1) + , m_expr2(expr2) + , m_opcodeID(opcodeID) + , m_rightHasAssignments(rightHasAssignments) + { + } + + inline BinaryOpNode::BinaryOpNode(const JSTokenLocation& location, ResultType type, ExpressionNode* expr1, ExpressionNode* expr2, OpcodeID opcodeID, bool rightHasAssignments) + : ExpressionNode(location, type) + , m_expr1(expr1) + , m_expr2(expr2) + , m_opcodeID(opcodeID) + , m_rightHasAssignments(rightHasAssignments) + { + } + + inline MultNode::MultNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_mul, rightHasAssignments) + { + } + + inline DivNode::DivNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_div, rightHasAssignments) + { + } + + + inline ModNode::ModNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_mod, rightHasAssignments) + { + } + + inline AddNode::AddNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::forAdd(expr1->resultDescriptor(), expr2->resultDescriptor()), expr1, expr2, op_add, rightHasAssignments) + { + } + + inline SubNode::SubNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_sub, rightHasAssignments) + { + } + + inline LeftShiftNode::LeftShiftNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::forBitOp(), expr1, expr2, op_lshift, rightHasAssignments) + { + } + + inline RightShiftNode::RightShiftNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::forBitOp(), expr1, expr2, op_rshift, rightHasAssignments) + { + } + + inline UnsignedRightShiftNode::UnsignedRightShiftNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_urshift, rightHasAssignments) + { + } + + inline LessNode::LessNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::booleanType(), expr1, expr2, op_less, rightHasAssignments) + { + } + + inline GreaterNode::GreaterNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::booleanType(), expr1, expr2, op_greater, rightHasAssignments) + { + } + + inline LessEqNode::LessEqNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::booleanType(), expr1, expr2, op_lesseq, rightHasAssignments) + { + } + + inline GreaterEqNode::GreaterEqNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::booleanType(), expr1, expr2, op_greatereq, rightHasAssignments) + { + } + + inline ThrowableBinaryOpNode::ThrowableBinaryOpNode(const JSTokenLocation& location, ResultType type, ExpressionNode* expr1, ExpressionNode* expr2, OpcodeID opcodeID, bool rightHasAssignments) + : BinaryOpNode(location, type, expr1, expr2, opcodeID, rightHasAssignments) + { + } + + inline ThrowableBinaryOpNode::ThrowableBinaryOpNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, OpcodeID opcodeID, bool rightHasAssignments) + : BinaryOpNode(location, expr1, expr2, opcodeID, rightHasAssignments) + { + } + + inline InstanceOfNode::InstanceOfNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : ThrowableBinaryOpNode(location, ResultType::booleanType(), expr1, expr2, op_instanceof, rightHasAssignments) + { + } + + inline InNode::InNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : ThrowableBinaryOpNode(location, expr1, expr2, op_in, rightHasAssignments) + { + } + + inline EqualNode::EqualNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::booleanType(), expr1, expr2, op_eq, rightHasAssignments) + { + } + + inline NotEqualNode::NotEqualNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::booleanType(), expr1, expr2, op_neq, rightHasAssignments) + { + } + + inline StrictEqualNode::StrictEqualNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::booleanType(), expr1, expr2, op_stricteq, rightHasAssignments) + { + } + + inline NotStrictEqualNode::NotStrictEqualNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::booleanType(), expr1, expr2, op_nstricteq, rightHasAssignments) + { + } + + inline BitAndNode::BitAndNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::forBitOp(), expr1, expr2, op_bitand, rightHasAssignments) + { + } + + inline BitOrNode::BitOrNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::forBitOp(), expr1, expr2, op_bitor, rightHasAssignments) + { + } + + inline BitXOrNode::BitXOrNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::forBitOp(), expr1, expr2, op_bitxor, rightHasAssignments) + { + } + + inline LogicalOpNode::LogicalOpNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, LogicalOperator oper) + : ExpressionNode(location, ResultType::forLogicalOp(expr1->resultDescriptor(), expr2->resultDescriptor())) + , m_expr1(expr1) + , m_expr2(expr2) + , m_operator(oper) + { + } + + inline ConditionalNode::ConditionalNode(const JSTokenLocation& location, ExpressionNode* logical, ExpressionNode* expr1, ExpressionNode* expr2) + : ExpressionNode(location) + , m_logical(logical) + , m_expr1(expr1) + , m_expr2(expr2) + { + } + + inline ReadModifyResolveNode::ReadModifyResolveNode(const JSTokenLocation& location, const Identifier& ident, Operator oper, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableExpressionData(divot, divotStart, divotEnd) + , m_ident(ident) + , m_right(right) + , m_operator(oper) + , m_rightHasAssignments(rightHasAssignments) + { + } + + inline AssignResolveNode::AssignResolveNode(const JSTokenLocation& location, const Identifier& ident, ExpressionNode* right, AssignmentContext assignmentContext) + : ExpressionNode(location) + , m_ident(ident) + , m_right(right) + , m_assignmentContext(assignmentContext) + { + } + + + inline ReadModifyBracketNode::ReadModifyBracketNode(const JSTokenLocation& location, ExpressionNode* base, ExpressionNode* subscript, Operator oper, ExpressionNode* right, bool subscriptHasAssignments, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableSubExpressionData(divot, divotStart, divotEnd) + , m_base(base) + , m_subscript(subscript) + , m_right(right) + , m_operator(oper) + , m_subscriptHasAssignments(subscriptHasAssignments) + , m_rightHasAssignments(rightHasAssignments) + { + } + + inline AssignBracketNode::AssignBracketNode(const JSTokenLocation& location, ExpressionNode* base, ExpressionNode* subscript, ExpressionNode* right, bool subscriptHasAssignments, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableExpressionData(divot, divotStart, divotEnd) + , m_base(base) + , m_subscript(subscript) + , m_right(right) + , m_subscriptHasAssignments(subscriptHasAssignments) + , m_rightHasAssignments(rightHasAssignments) + { + } + + inline AssignDotNode::AssignDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableExpressionData(divot, divotStart, divotEnd) + , m_base(base) + , m_ident(ident) + , m_right(right) + , m_rightHasAssignments(rightHasAssignments) + { + } + + inline ReadModifyDotNode::ReadModifyDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, Operator oper, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableSubExpressionData(divot, divotStart, divotEnd) + , m_base(base) + , m_ident(ident) + , m_right(right) + , m_operator(oper) + , m_rightHasAssignments(rightHasAssignments) + { + } + + inline AssignErrorNode::AssignErrorNode(const JSTokenLocation& location, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ExpressionNode(location) + , ThrowableExpressionData(divot, divotStart, divotEnd) + { + } + + inline CommaNode::CommaNode(const JSTokenLocation& location, ExpressionNode* expr) + : ExpressionNode(location) + , m_expr(expr) + , m_next(nullptr) + { + } + + inline SourceElements::SourceElements() + : m_head(nullptr) + , m_tail(nullptr) + { + } + + inline EmptyStatementNode::EmptyStatementNode(const JSTokenLocation& location) + : StatementNode(location) + { + } + + inline DebuggerStatementNode::DebuggerStatementNode(const JSTokenLocation& location) + : StatementNode(location) + { + } + + inline ExprStatementNode::ExprStatementNode(const JSTokenLocation& location, ExpressionNode* expr) + : StatementNode(location) + , m_expr(expr) + { + } + + inline DeclarationStatement::DeclarationStatement(const JSTokenLocation& location, ExpressionNode* expr) + : StatementNode(location) + , m_expr(expr) + { + } + + inline ModuleDeclarationNode::ModuleDeclarationNode(const JSTokenLocation& location) + : StatementNode(location) + { + } + + inline ModuleNameNode::ModuleNameNode(const JSTokenLocation& location, const Identifier& moduleName) + : Node(location) + , m_moduleName(moduleName) + { + } + + inline ImportSpecifierNode::ImportSpecifierNode(const JSTokenLocation& location, const Identifier& importedName, const Identifier& localName) + : Node(location) + , m_importedName(importedName) + , m_localName(localName) + { + } + + inline ImportDeclarationNode::ImportDeclarationNode(const JSTokenLocation& location, ImportSpecifierListNode* importSpecifierList, ModuleNameNode* moduleName) + : ModuleDeclarationNode(location) + , m_specifierList(importSpecifierList) + , m_moduleName(moduleName) + { + } + + inline ExportAllDeclarationNode::ExportAllDeclarationNode(const JSTokenLocation& location, ModuleNameNode* moduleName) + : ModuleDeclarationNode(location) + , m_moduleName(moduleName) + { + } + + inline ExportDefaultDeclarationNode::ExportDefaultDeclarationNode(const JSTokenLocation& location, StatementNode* declaration, const Identifier& localName) + : ModuleDeclarationNode(location) + , m_declaration(declaration) + , m_localName(localName) + { + } + + inline ExportLocalDeclarationNode::ExportLocalDeclarationNode(const JSTokenLocation& location, StatementNode* declaration) + : ModuleDeclarationNode(location) + , m_declaration(declaration) + { + } + + inline ExportNamedDeclarationNode::ExportNamedDeclarationNode(const JSTokenLocation& location, ExportSpecifierListNode* exportSpecifierList, ModuleNameNode* moduleName) + : ModuleDeclarationNode(location) + , m_specifierList(exportSpecifierList) + , m_moduleName(moduleName) + { + } + + inline ExportSpecifierNode::ExportSpecifierNode(const JSTokenLocation& location, const Identifier& localName, const Identifier& exportedName) + : Node(location) + , m_localName(localName) + , m_exportedName(exportedName) + { + } + + inline EmptyVarExpression::EmptyVarExpression(const JSTokenLocation& location, const Identifier& ident) + : ExpressionNode(location) + , m_ident(ident) + { + } + + inline EmptyLetExpression::EmptyLetExpression(const JSTokenLocation& location, const Identifier& ident) + : ExpressionNode(location) + , m_ident(ident) + { + } + + inline IfElseNode::IfElseNode(const JSTokenLocation& location, ExpressionNode* condition, StatementNode* ifBlock, StatementNode* elseBlock) + : StatementNode(location) + , m_condition(condition) + , m_ifBlock(ifBlock) + , m_elseBlock(elseBlock) + { + } + + inline DoWhileNode::DoWhileNode(const JSTokenLocation& location, StatementNode* statement, ExpressionNode* expr) + : StatementNode(location) + , m_statement(statement) + , m_expr(expr) + { + } + + inline WhileNode::WhileNode(const JSTokenLocation& location, ExpressionNode* expr, StatementNode* statement) + : StatementNode(location) + , m_expr(expr) + , m_statement(statement) + { + } + + inline ForNode::ForNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, ExpressionNode* expr3, StatementNode* statement, VariableEnvironment& lexicalVariables) + : StatementNode(location) + , VariableEnvironmentNode(lexicalVariables) + , m_expr1(expr1) + , m_expr2(expr2) + , m_expr3(expr3) + , m_statement(statement) + { + ASSERT(statement); + } + + inline ContinueNode::ContinueNode(const JSTokenLocation& location, const Identifier& ident) + : StatementNode(location) + , m_ident(ident) + { + } + + inline BreakNode::BreakNode(const JSTokenLocation& location, const Identifier& ident) + : StatementNode(location) + , m_ident(ident) + { + } + + inline ReturnNode::ReturnNode(const JSTokenLocation& location, ExpressionNode* value) + : StatementNode(location) + , m_value(value) + { + } + + inline WithNode::WithNode(const JSTokenLocation& location, ExpressionNode* expr, StatementNode* statement, const JSTextPosition& divot, uint32_t expressionLength) + : StatementNode(location) + , m_expr(expr) + , m_statement(statement) + , m_divot(divot) + , m_expressionLength(expressionLength) + { + } + + inline LabelNode::LabelNode(const JSTokenLocation& location, const Identifier& name, StatementNode* statement) + : StatementNode(location) + , m_name(name) + , m_statement(statement) + { + } + + inline ThrowNode::ThrowNode(const JSTokenLocation& location, ExpressionNode* expr) + : StatementNode(location) + , m_expr(expr) + { + } + + inline TryNode::TryNode(const JSTokenLocation& location, StatementNode* tryBlock, const Identifier& thrownValueIdent, StatementNode* catchBlock, VariableEnvironment& catchEnvironment, StatementNode* finallyBlock) + : StatementNode(location) + , m_tryBlock(tryBlock) + , m_thrownValueIdent(thrownValueIdent) + , m_catchBlock(catchBlock) + , m_finallyBlock(finallyBlock) + { + m_catchEnvironment.swap(catchEnvironment); + } + + inline FunctionParameters::FunctionParameters() + { + } + + inline FuncExprNode::FuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* m_metadata, const SourceCode& source) + : ExpressionNode(location) + , m_metadata(m_metadata) + { + m_metadata->finishParsing(source, ident, FunctionExpression); + } + + inline FuncDeclNode::FuncDeclNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* m_metadata, const SourceCode& source) + : StatementNode(location) + , m_metadata(m_metadata) + { + m_metadata->finishParsing(source, ident, FunctionDeclaration); + } + +#if ENABLE(ES6_CLASS_SYNTAX) + inline ClassDeclNode::ClassDeclNode(const JSTokenLocation& location, ExpressionNode* classDeclaration) + : StatementNode(location) + , m_classDeclaration(classDeclaration) + { + } + + inline ClassExprNode::ClassExprNode(const JSTokenLocation& location, const Identifier& name, ExpressionNode* constructorExpression, ExpressionNode* classHeritage, PropertyListNode* instanceMethods, PropertyListNode* staticMethods) + : ExpressionNode(location) + , m_name(name) + , m_constructorExpression(constructorExpression) + , m_classHeritage(classHeritage) + , m_instanceMethods(instanceMethods) + , m_staticMethods(staticMethods) + { + } +#endif + + inline CaseClauseNode::CaseClauseNode(ExpressionNode* expr, SourceElements* statements) + : m_expr(expr) + , m_statements(statements) + { + } + + inline ClauseListNode::ClauseListNode(CaseClauseNode* clause) + : m_clause(clause) + , m_next(0) + { + } + + inline ClauseListNode::ClauseListNode(ClauseListNode* clauseList, CaseClauseNode* clause) + : m_clause(clause) + , m_next(0) + { + clauseList->m_next = this; + } + + inline CaseBlockNode::CaseBlockNode(ClauseListNode* list1, CaseClauseNode* defaultClause, ClauseListNode* list2) + : m_list1(list1) + , m_defaultClause(defaultClause) + , m_list2(list2) + { + } + + inline SwitchNode::SwitchNode(const JSTokenLocation& location, ExpressionNode* expr, CaseBlockNode* block, VariableEnvironment& lexicalVariables) + : StatementNode(location) + , VariableEnvironmentNode(lexicalVariables) + , m_expr(expr) + , m_block(block) + { + } + + inline BlockNode::BlockNode(const JSTokenLocation& location, SourceElements* statements, VariableEnvironment& lexicalVariables) + : StatementNode(location) + , VariableEnvironmentNode(lexicalVariables) + , m_statements(statements) + { + } + + inline EnumerationNode::EnumerationNode(const JSTokenLocation& location, ExpressionNode* lexpr, ExpressionNode* expr, StatementNode* statement, VariableEnvironment& lexicalVariables) + : StatementNode(location) + , VariableEnvironmentNode(lexicalVariables) + , m_lexpr(lexpr) + , m_expr(expr) + , m_statement(statement) + { + ASSERT(lexpr); + } + + inline ForInNode::ForInNode(const JSTokenLocation& location, ExpressionNode* lexpr, ExpressionNode* expr, StatementNode* statement, VariableEnvironment& lexicalVariables) + : EnumerationNode(location, lexpr, expr, statement, lexicalVariables) + { + } + + inline ForOfNode::ForOfNode(const JSTokenLocation& location, ExpressionNode* lexpr, ExpressionNode* expr, StatementNode* statement, VariableEnvironment& lexicalVariables) + : EnumerationNode(location, lexpr, expr, statement, lexicalVariables) + { + } + + inline DestructuringPatternNode::DestructuringPatternNode() + { + } + + inline ArrayPatternNode::ArrayPatternNode() + : DestructuringPatternNode() + { + } + + inline ObjectPatternNode::ObjectPatternNode() + : DestructuringPatternNode() + { + } + + inline BindingNode::BindingNode(const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end, AssignmentContext context) + : DestructuringPatternNode() + , m_divotStart(start) + , m_divotEnd(end) + , m_boundProperty(boundProperty) + , m_bindingContext(context) + { + } + + inline DestructuringAssignmentNode::DestructuringAssignmentNode(const JSTokenLocation& location, DestructuringPatternNode* bindings, ExpressionNode* initializer) + : ExpressionNode(location) + , m_bindings(bindings) + , m_initializer(initializer) + { + } + +} // namespace JSC + +#endif // NodeConstructors_h diff --git a/Source/JavaScriptCore/parser/Nodes.cpp b/Source/JavaScriptCore/parser/Nodes.cpp new file mode 100644 index 000000000..cf4fe450a --- /dev/null +++ b/Source/JavaScriptCore/parser/Nodes.cpp @@ -0,0 +1,205 @@ +/* +* Copyright (C) 1999-2002 Harri Porten (porten@kde.org) +* Copyright (C) 2001 Peter Kelly (pmk@post.com) +* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved. +* Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) +* Copyright (C) 2007 Maks Orlovich +* Copyright (C) 2007 Eric Seidel <eric@webkit.org> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public License +* along with this library; see the file COPYING.LIB. If not, write to +* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +* +*/ + +#include "config.h" +#include "Nodes.h" +#include "NodeConstructors.h" + +#include "CallFrame.h" +#include "Debugger.h" +#include "JIT.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "LabelScope.h" +#include "Lexer.h" +#include "JSCInlines.h" +#include "Parser.h" +#include "PropertyNameArray.h" +#include "RegExpObject.h" +#include "SamplingTool.h" +#include <wtf/Assertions.h> +#include <wtf/RefCountedLeakCounter.h> +#include <wtf/Threading.h> + +using namespace WTF; + +namespace JSC { + + +// ------------------------------ StatementNode -------------------------------- + +void StatementNode::setLoc(unsigned firstLine, unsigned lastLine, int startOffset, int lineStartOffset) +{ + m_lastLine = lastLine; + m_position = JSTextPosition(firstLine, startOffset, lineStartOffset); + ASSERT(m_position.offset >= m_position.lineStartOffset); +} + +// ------------------------------ SourceElements -------------------------------- + +void SourceElements::append(StatementNode* statement) +{ + if (statement->isEmptyStatement()) + return; + + if (!m_head) { + m_head = statement; + m_tail = statement; + return; + } + + m_tail->setNext(statement); + m_tail = statement; +} + +StatementNode* SourceElements::singleStatement() const +{ + return m_head == m_tail ? m_head : nullptr; +} + +// ------------------------------ ScopeNode ----------------------------- + +ScopeNode::ScopeNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, bool inStrictContext) + : StatementNode(endLocation) + , ParserArenaRoot(parserArena) + , m_startLineNumber(startLocation.line) + , m_startStartOffset(startLocation.startOffset) + , m_startLineStartOffset(startLocation.lineStartOffset) + , m_features(inStrictContext ? StrictModeFeature : NoFeatures) + , m_numConstants(0) + , m_statements(0) +{ +} + +ScopeNode::ScopeNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, const SourceCode& source, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack& funcStack, VariableEnvironment& lexicalVariables, CodeFeatures features, int numConstants) + : StatementNode(endLocation) + , ParserArenaRoot(parserArena) + , VariableEnvironmentNode(lexicalVariables) + , m_startLineNumber(startLocation.line) + , m_startStartOffset(startLocation.startOffset) + , m_startLineStartOffset(startLocation.lineStartOffset) + , m_features(features) + , m_source(source) + , m_numConstants(numConstants) + , m_statements(children) +{ + m_varDeclarations.swap(varEnvironment); + m_functionStack.swap(funcStack); +} + +StatementNode* ScopeNode::singleStatement() const +{ + return m_statements ? m_statements->singleStatement() : 0; +} + +// ------------------------------ ProgramNode ----------------------------- + +ProgramNode::ProgramNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack& funcStack, VariableEnvironment& lexicalVariables, FunctionParameters*, const SourceCode& source, CodeFeatures features, int numConstants) + : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, funcStack, lexicalVariables, features, numConstants) + , m_startColumn(startColumn) + , m_endColumn(endColumn) +{ +} + +void ProgramNode::setClosedVariables(Vector<RefPtr<UniquedStringImpl>>&& closedVariables) +{ + m_closedVariables = WTF::move(closedVariables); +} + +// ------------------------------ ModuleProgramNode ----------------------------- + +ModuleProgramNode::ModuleProgramNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack& funcStack, VariableEnvironment& lexicalVariables, FunctionParameters*, const SourceCode& source, CodeFeatures features, int numConstants) + : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, funcStack, lexicalVariables, features, numConstants) + , m_startColumn(startColumn) + , m_endColumn(endColumn) +{ +} + +// ------------------------------ EvalNode ----------------------------- + +EvalNode::EvalNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack& funcStack, VariableEnvironment& lexicalVariables, FunctionParameters*, const SourceCode& source, CodeFeatures features, int numConstants) + : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, funcStack, lexicalVariables, features, numConstants) + , m_endColumn(endColumn) +{ +} + +// ------------------------------ FunctionMetadataNode ----------------------------- + +FunctionMetadataNode::FunctionMetadataNode( + ParserArena&, const JSTokenLocation& startLocation, + const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, + int functionKeywordStart, int functionNameStart, int parametersStart, bool isInStrictContext, + ConstructorKind constructorKind, unsigned parameterCount, SourceParseMode mode) + : Node(endLocation) + , m_startColumn(startColumn) + , m_endColumn(endColumn) + , m_functionKeywordStart(functionKeywordStart) + , m_functionNameStart(functionNameStart) + , m_parametersStart(parametersStart) + , m_startStartOffset(startLocation.startOffset) + , m_parameterCount(parameterCount) + , m_parseMode(mode) + , m_isInStrictContext(isInStrictContext) + , m_constructorKind(static_cast<unsigned>(constructorKind)) +{ + ASSERT(m_constructorKind == static_cast<unsigned>(constructorKind)); +} + +void FunctionMetadataNode::finishParsing(const SourceCode& source, const Identifier& ident, enum FunctionMode functionMode) +{ + m_source = source; + m_ident = ident; + m_functionMode = functionMode; +} + +void FunctionMetadataNode::setEndPosition(JSTextPosition position) +{ + m_lastLine = position.line; + m_endColumn = position.offset - position.lineStartOffset; +} + +// ------------------------------ FunctionNode ----------------------------- + +FunctionNode::FunctionNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack& funcStack, VariableEnvironment& lexicalVariables, FunctionParameters* parameters, const SourceCode& sourceCode, CodeFeatures features, int numConstants) + : ScopeNode(parserArena, startLocation, endLocation, sourceCode, children, varEnvironment, funcStack, lexicalVariables, features, numConstants) + , m_parameters(parameters) + , m_startColumn(startColumn) + , m_endColumn(endColumn) +{ +} + +void FunctionNode::finishParsing(const Identifier& ident, enum FunctionMode functionMode) +{ + ASSERT(!source().isNull()); + m_ident = ident; + m_functionMode = functionMode; +} + +VariableEnvironmentNode::VariableEnvironmentNode(VariableEnvironment& lexicalVariables) +{ + m_lexicalVariables.swap(lexicalVariables); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/Nodes.h b/Source/JavaScriptCore/parser/Nodes.h new file mode 100644 index 000000000..1d6b03d77 --- /dev/null +++ b/Source/JavaScriptCore/parser/Nodes.h @@ -0,0 +1,2127 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) + * Copyright (C) 2007 Maks Orlovich + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef Nodes_h +#define Nodes_h + +#include "Error.h" +#include "JITCode.h" +#include "Opcode.h" +#include "ParserArena.h" +#include "ParserTokens.h" +#include "ResultType.h" +#include "SourceCode.h" +#include "SymbolTable.h" +#include "VariableEnvironment.h" +#include <wtf/MathExtras.h> + +namespace JSC { + + class ArgumentListNode; + class BytecodeGenerator; + class FunctionMetadataNode; + class FunctionParameters; + class Label; + class PropertyListNode; + class ReadModifyResolveNode; + class RegisterID; + class JSScope; + class ScopeNode; + class ModuleAnalyzer; + + enum Operator { + OpEqual, + OpPlusEq, + OpMinusEq, + OpMultEq, + OpDivEq, + OpPlusPlus, + OpMinusMinus, + OpAndEq, + OpXOrEq, + OpOrEq, + OpModEq, + OpLShift, + OpRShift, + OpURShift + }; + + enum LogicalOperator { + OpLogicalAnd, + OpLogicalOr + }; + + enum FallThroughMode { + FallThroughMeansTrue = 0, + FallThroughMeansFalse = 1 + }; + inline FallThroughMode invert(FallThroughMode fallThroughMode) { return static_cast<FallThroughMode>(!fallThroughMode); } + + typedef HashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> IdentifierSet; + + namespace DeclarationStacks { + typedef Vector<FunctionMetadataNode*> FunctionStack; + } + + struct SwitchInfo { + enum SwitchType { SwitchNone, SwitchImmediate, SwitchCharacter, SwitchString }; + uint32_t bytecodeOffset; + SwitchType switchType; + }; + + enum class AssignmentContext { + DeclarationStatement, + ConstDeclarationStatement, + AssignmentExpression + }; + + class ParserArenaFreeable { + public: + // ParserArenaFreeable objects are are freed when the arena is deleted. + // Destructors are not called. Clients must not call delete on such objects. + void* operator new(size_t, ParserArena&); + }; + + class ParserArenaDeletable { + public: + virtual ~ParserArenaDeletable() { } + + // ParserArenaDeletable objects are deleted when the arena is deleted. + // Clients must not call delete directly on such objects. + void* operator new(size_t, ParserArena&); + }; + + class ParserArenaRoot { + WTF_MAKE_FAST_ALLOCATED; + protected: + ParserArenaRoot(ParserArena&); + + public: + ParserArena& parserArena() { return m_arena; } + virtual ~ParserArenaRoot() { } + + protected: + ParserArena m_arena; + }; + + class Node : public ParserArenaFreeable { + protected: + Node(const JSTokenLocation&); + + public: + virtual ~Node() { } + + int firstLine() const { return m_position.line; } + int startOffset() const { return m_position.offset; } + int endOffset() const { return m_endOffset; } + int lineStartOffset() const { return m_position.lineStartOffset; } + const JSTextPosition& position() const { return m_position; } + void setEndOffset(int offset) { m_endOffset = offset; } + void setStartOffset(int offset) { m_position.offset = offset; } + + protected: + JSTextPosition m_position; + int m_endOffset; + }; + + class ExpressionNode : public Node { + protected: + ExpressionNode(const JSTokenLocation&, ResultType = ResultType::unknownType()); + + public: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* destination = 0) = 0; + + virtual bool isNumber() const { return false; } + virtual bool isString() const { return false; } + virtual bool isNull() const { return false; } + virtual bool isPure(BytecodeGenerator&) const { return false; } + virtual bool isConstant() const { return false; } + virtual bool isLocation() const { return false; } + virtual bool isAssignmentLocation() const { return isLocation(); } + virtual bool isResolveNode() const { return false; } + virtual bool isBracketAccessorNode() const { return false; } + virtual bool isDotAccessorNode() const { return false; } + virtual bool isDestructuringNode() const { return false; } + virtual bool isFuncExprNode() const { return false; } + virtual bool isCommaNode() const { return false; } + virtual bool isSimpleArray() const { return false; } + virtual bool isAdd() const { return false; } + virtual bool isSubtract() const { return false; } + virtual bool isBoolean() const { return false; } + virtual bool isSpreadExpression() const { return false; } + virtual bool isSuperNode() const { return false; } + + virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label*, Label*, FallThroughMode); + + virtual ExpressionNode* stripUnaryPlus() { return this; } + + ResultType resultDescriptor() const { return m_resultType; } + + private: + ResultType m_resultType; + }; + + class StatementNode : public Node { + protected: + StatementNode(const JSTokenLocation&); + + public: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* destination = 0) = 0; + + void setLoc(unsigned firstLine, unsigned lastLine, int startOffset, int lineStartOffset); + unsigned lastLine() const { return m_lastLine; } + + StatementNode* next() { return m_next; } + void setNext(StatementNode* next) { m_next = next; } + + virtual bool isEmptyStatement() const { return false; } + virtual bool isReturnNode() const { return false; } + virtual bool isExprStatement() const { return false; } + virtual bool isBreak() const { return false; } + virtual bool isContinue() const { return false; } + virtual bool isBlock() const { return false; } + virtual bool isFuncDeclNode() const { return false; } + virtual bool isModuleDeclarationNode() const { return false; } + + protected: + StatementNode* m_next; + int m_lastLine; + }; + + class VariableEnvironmentNode { + public: + VariableEnvironmentNode() + { + } + + VariableEnvironmentNode(VariableEnvironment& lexicalDeclaredVariables); + + VariableEnvironment& lexicalVariables() { return m_lexicalVariables; } + + protected: + VariableEnvironment m_lexicalVariables; + }; + + class ConstantNode : public ExpressionNode { + public: + ConstantNode(const JSTokenLocation&, ResultType); + virtual bool isPure(BytecodeGenerator&) const override { return true; } + virtual bool isConstant() const override { return true; } + virtual JSValue jsValue(BytecodeGenerator&) const = 0; + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode) override; + }; + + class NullNode : public ConstantNode { + public: + NullNode(const JSTokenLocation&); + + private: + virtual bool isNull() const override { return true; } + virtual JSValue jsValue(BytecodeGenerator&) const override { return jsNull(); } + }; + + class BooleanNode : public ConstantNode { + public: + BooleanNode(const JSTokenLocation&, bool value); + bool value() { return m_value; } + + private: + virtual bool isBoolean() const override { return true; } + virtual JSValue jsValue(BytecodeGenerator&) const override { return jsBoolean(m_value); } + + bool m_value; + }; + + class NumberNode : public ConstantNode { + public: + NumberNode(const JSTokenLocation&, double value); + double value() const { return m_value; } + virtual bool isIntegerNode() const = 0; + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override final; + + private: + virtual bool isNumber() const override final { return true; } + virtual JSValue jsValue(BytecodeGenerator&) const override { return jsNumber(m_value); } + + double m_value; + }; + + class DoubleNode : public NumberNode { + public: + DoubleNode(const JSTokenLocation&, double value); + + private: + virtual bool isIntegerNode() const override { return false; } + }; + + // An integer node represent a number represented as an integer (e.g. 42 instead of 42., 42.0, 42e0) + class IntegerNode : public DoubleNode { + public: + IntegerNode(const JSTokenLocation&, double value); + virtual bool isIntegerNode() const override final { return true; } + }; + + class StringNode : public ConstantNode { + public: + StringNode(const JSTokenLocation&, const Identifier&); + const Identifier& value() { return m_value; } + + private: + virtual bool isString() const override { return true; } + virtual JSValue jsValue(BytecodeGenerator&) const override; + + const Identifier& m_value; + }; + + class ThrowableExpressionData { + public: + ThrowableExpressionData() + : m_divot(-1, -1, -1) + , m_divotStart(-1, -1, -1) + , m_divotEnd(-1, -1, -1) + { + } + + ThrowableExpressionData(const JSTextPosition& divot, const JSTextPosition& start, const JSTextPosition& end) + : m_divot(divot) + , m_divotStart(start) + , m_divotEnd(end) + { + ASSERT(m_divot.offset >= m_divot.lineStartOffset); + ASSERT(m_divotStart.offset >= m_divotStart.lineStartOffset); + ASSERT(m_divotEnd.offset >= m_divotEnd.lineStartOffset); + } + + void setExceptionSourceCode(const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + { + ASSERT(divot.offset >= divot.lineStartOffset); + ASSERT(divotStart.offset >= divotStart.lineStartOffset); + ASSERT(divotEnd.offset >= divotEnd.lineStartOffset); + m_divot = divot; + m_divotStart = divotStart; + m_divotEnd = divotEnd; + } + + const JSTextPosition& divot() const { return m_divot; } + const JSTextPosition& divotStart() const { return m_divotStart; } + const JSTextPosition& divotEnd() const { return m_divotEnd; } + + protected: + RegisterID* emitThrowReferenceError(BytecodeGenerator&, const String& message); + + private: + JSTextPosition m_divot; + JSTextPosition m_divotStart; + JSTextPosition m_divotEnd; + }; + + class ThrowableSubExpressionData : public ThrowableExpressionData { + public: + ThrowableSubExpressionData() + : m_subexpressionDivotOffset(0) + , m_subexpressionEndOffset(0) + , m_subexpressionLineOffset(0) + , m_subexpressionLineStartOffset(0) + { + } + + ThrowableSubExpressionData(const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + : ThrowableExpressionData(divot, divotStart, divotEnd) + , m_subexpressionDivotOffset(0) + , m_subexpressionEndOffset(0) + , m_subexpressionLineOffset(0) + , m_subexpressionLineStartOffset(0) + { + } + + void setSubexpressionInfo(const JSTextPosition& subexpressionDivot, int subexpressionOffset) + { + ASSERT(subexpressionDivot.offset <= divot().offset); + // Overflow means we can't do this safely, so just point at the primary divot, + // divotLine, or divotLineStart. + if ((divot() - subexpressionDivot.offset) & ~0xFFFF) + return; + if ((divot().line - subexpressionDivot.line) & ~0xFFFF) + return; + if ((divot().lineStartOffset - subexpressionDivot.lineStartOffset) & ~0xFFFF) + return; + if ((divotEnd() - subexpressionOffset) & ~0xFFFF) + return; + m_subexpressionDivotOffset = divot() - subexpressionDivot.offset; + m_subexpressionEndOffset = divotEnd() - subexpressionOffset; + m_subexpressionLineOffset = divot().line - subexpressionDivot.line; + m_subexpressionLineStartOffset = divot().lineStartOffset - subexpressionDivot.lineStartOffset; + } + + JSTextPosition subexpressionDivot() + { + int newLine = divot().line - m_subexpressionLineOffset; + int newOffset = divot().offset - m_subexpressionDivotOffset; + int newLineStartOffset = divot().lineStartOffset - m_subexpressionLineStartOffset; + return JSTextPosition(newLine, newOffset, newLineStartOffset); + } + JSTextPosition subexpressionStart() { return divotStart(); } + JSTextPosition subexpressionEnd() { return divotEnd() - static_cast<int>(m_subexpressionEndOffset); } + + protected: + uint16_t m_subexpressionDivotOffset; + uint16_t m_subexpressionEndOffset; + uint16_t m_subexpressionLineOffset; + uint16_t m_subexpressionLineStartOffset; + }; + + class ThrowablePrefixedSubExpressionData : public ThrowableExpressionData { + public: + ThrowablePrefixedSubExpressionData() + : m_subexpressionDivotOffset(0) + , m_subexpressionStartOffset(0) + , m_subexpressionLineOffset(0) + , m_subexpressionLineStartOffset(0) + { + } + + ThrowablePrefixedSubExpressionData(const JSTextPosition& divot, const JSTextPosition& start, const JSTextPosition& end) + : ThrowableExpressionData(divot, start, end) + , m_subexpressionDivotOffset(0) + , m_subexpressionStartOffset(0) + , m_subexpressionLineOffset(0) + , m_subexpressionLineStartOffset(0) + { + } + + void setSubexpressionInfo(const JSTextPosition& subexpressionDivot, int subexpressionOffset) + { + ASSERT(subexpressionDivot.offset >= divot().offset); + // Overflow means we can't do this safely, so just point at the primary divot, + // divotLine, or divotLineStart. + if ((subexpressionDivot.offset - divot()) & ~0xFFFF) + return; + if ((subexpressionDivot.line - divot().line) & ~0xFFFF) + return; + if ((subexpressionDivot.lineStartOffset - divot().lineStartOffset) & ~0xFFFF) + return; + if ((subexpressionOffset - divotStart()) & ~0xFFFF) + return; + m_subexpressionDivotOffset = subexpressionDivot.offset - divot(); + m_subexpressionStartOffset = subexpressionOffset - divotStart(); + m_subexpressionLineOffset = subexpressionDivot.line - divot().line; + m_subexpressionLineStartOffset = subexpressionDivot.lineStartOffset - divot().lineStartOffset; + } + + JSTextPosition subexpressionDivot() + { + int newLine = divot().line + m_subexpressionLineOffset; + int newOffset = divot().offset + m_subexpressionDivotOffset; + int newLineStartOffset = divot().lineStartOffset + m_subexpressionLineStartOffset; + return JSTextPosition(newLine, newOffset, newLineStartOffset); + } + JSTextPosition subexpressionStart() { return divotStart() + static_cast<int>(m_subexpressionStartOffset); } + JSTextPosition subexpressionEnd() { return divotEnd(); } + + protected: + uint16_t m_subexpressionDivotOffset; + uint16_t m_subexpressionStartOffset; + uint16_t m_subexpressionLineOffset; + uint16_t m_subexpressionLineStartOffset; + }; + +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + class TemplateExpressionListNode : public ParserArenaFreeable { + public: + TemplateExpressionListNode(ExpressionNode*); + TemplateExpressionListNode(TemplateExpressionListNode*, ExpressionNode*); + + ExpressionNode* value() { return m_node; } + TemplateExpressionListNode* next() { return m_next; } + + private: + TemplateExpressionListNode* m_next { nullptr }; + ExpressionNode* m_node { nullptr }; + }; + + class TemplateStringNode : public ExpressionNode { + public: + TemplateStringNode(const JSTokenLocation&, const Identifier& cooked, const Identifier& raw); + + const Identifier& cooked() { return m_cooked; } + const Identifier& raw() { return m_raw; } + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_cooked; + const Identifier& m_raw; + }; + + class TemplateStringListNode : public ParserArenaFreeable { + public: + TemplateStringListNode(TemplateStringNode*); + TemplateStringListNode(TemplateStringListNode*, TemplateStringNode*); + + TemplateStringNode* value() { return m_node; } + TemplateStringListNode* next() { return m_next; } + + private: + TemplateStringListNode* m_next { nullptr }; + TemplateStringNode* m_node { nullptr }; + }; + + class TemplateLiteralNode : public ExpressionNode { + public: + TemplateLiteralNode(const JSTokenLocation&, TemplateStringListNode*); + TemplateLiteralNode(const JSTokenLocation&, TemplateStringListNode*, TemplateExpressionListNode*); + + TemplateStringListNode* templateStrings() const { return m_templateStrings; } + TemplateExpressionListNode* templateExpressions() const { return m_templateExpressions; } + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + TemplateStringListNode* m_templateStrings; + TemplateExpressionListNode* m_templateExpressions; + }; + + class TaggedTemplateNode : public ExpressionNode, public ThrowableExpressionData { + public: + TaggedTemplateNode(const JSTokenLocation&, ExpressionNode*, TemplateLiteralNode*); + + TemplateLiteralNode* templateLiteral() const { return m_templateLiteral; } + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_tag; + TemplateLiteralNode* m_templateLiteral; + }; +#endif + + class RegExpNode : public ExpressionNode, public ThrowableExpressionData { + public: + RegExpNode(const JSTokenLocation&, const Identifier& pattern, const Identifier& flags); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_pattern; + const Identifier& m_flags; + }; + + class ThisNode : public ExpressionNode { + public: + ThisNode(const JSTokenLocation&, ThisTDZMode); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + bool m_shouldAlwaysEmitTDZCheck; + }; + + class SuperNode final : public ExpressionNode { + public: + SuperNode(const JSTokenLocation&); + + private: + virtual bool isSuperNode() const override { return true; } + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class NewTargetNode final : public ExpressionNode { + public: + NewTargetNode(const JSTokenLocation&); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class ResolveNode : public ExpressionNode { + public: + ResolveNode(const JSTokenLocation&, const Identifier&, const JSTextPosition& start); + + const Identifier& identifier() const { return m_ident; } + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + virtual bool isPure(BytecodeGenerator&) const override; + virtual bool isLocation() const override { return true; } + virtual bool isResolveNode() const override { return true; } + + const Identifier& m_ident; + JSTextPosition m_start; + }; + + class ElementNode : public ParserArenaFreeable { + public: + ElementNode(int elision, ExpressionNode*); + ElementNode(ElementNode*, int elision, ExpressionNode*); + + int elision() const { return m_elision; } + ExpressionNode* value() { return m_node; } + ElementNode* next() { return m_next; } + + private: + ElementNode* m_next; + int m_elision; + ExpressionNode* m_node; + }; + + class ArrayNode : public ExpressionNode { + public: + ArrayNode(const JSTokenLocation&, int elision); + ArrayNode(const JSTokenLocation&, ElementNode*); + ArrayNode(const JSTokenLocation&, int elision, ElementNode*); + + ArgumentListNode* toArgumentList(ParserArena&, int, int) const; + + ElementNode* elements() const { ASSERT(isSimpleArray()); return m_element; } + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + virtual bool isSimpleArray() const override; + + ElementNode* m_element; + int m_elision; + bool m_optional; + }; + + class PropertyNode : public ParserArenaFreeable { + public: + enum Type { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16 }; + enum PutType { Unknown, KnownDirect }; + + PropertyNode(const Identifier&, ExpressionNode*, Type, PutType, SuperBinding); + PropertyNode(ExpressionNode* propertyName, ExpressionNode*, Type, PutType); + + ExpressionNode* expressionName() const { return m_expression; } + const Identifier* name() const { return m_name; } + + Type type() const { return static_cast<Type>(m_type); } + bool needsSuperBinding() const { return m_needsSuperBinding; } + PutType putType() const { return static_cast<PutType>(m_putType); } + + private: + friend class PropertyListNode; + const Identifier* m_name; + ExpressionNode* m_expression; + ExpressionNode* m_assign; + unsigned m_type : 5; + unsigned m_needsSuperBinding : 1; + unsigned m_putType : 1; + }; + + class PropertyListNode : public ExpressionNode { + public: + PropertyListNode(const JSTokenLocation&, PropertyNode*); + PropertyListNode(const JSTokenLocation&, PropertyNode*, PropertyListNode*); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitPutConstantProperty(BytecodeGenerator&, RegisterID*, PropertyNode&); + + PropertyNode* m_node; + PropertyListNode* m_next; + }; + + class ObjectLiteralNode : public ExpressionNode { + public: + ObjectLiteralNode(const JSTokenLocation&); + ObjectLiteralNode(const JSTokenLocation&, PropertyListNode*); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + PropertyListNode* m_list; + }; + + class BracketAccessorNode : public ExpressionNode, public ThrowableExpressionData { + public: + BracketAccessorNode(const JSTokenLocation&, ExpressionNode* base, ExpressionNode* subscript, bool subscriptHasAssignments); + + ExpressionNode* base() const { return m_base; } + ExpressionNode* subscript() const { return m_subscript; } + + bool subscriptHasAssignments() const { return m_subscriptHasAssignments; } + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + virtual bool isLocation() const override { return true; } + virtual bool isBracketAccessorNode() const override { return true; } + + ExpressionNode* m_base; + ExpressionNode* m_subscript; + bool m_subscriptHasAssignments; + }; + + class DotAccessorNode : public ExpressionNode, public ThrowableExpressionData { + public: + DotAccessorNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&); + + ExpressionNode* base() const { return m_base; } + const Identifier& identifier() const { return m_ident; } + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + virtual bool isLocation() const override { return true; } + virtual bool isDotAccessorNode() const override { return true; } + + ExpressionNode* m_base; + const Identifier& m_ident; + }; + + class SpreadExpressionNode : public ExpressionNode, public ThrowableExpressionData { + public: + SpreadExpressionNode(const JSTokenLocation&, ExpressionNode*); + + ExpressionNode* expression() const { return m_expression; } + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + virtual bool isSpreadExpression() const override { return true; } + ExpressionNode* m_expression; + }; + + class ArgumentListNode : public ExpressionNode { + public: + ArgumentListNode(const JSTokenLocation&, ExpressionNode*); + ArgumentListNode(const JSTokenLocation&, ArgumentListNode*, ExpressionNode*); + + ArgumentListNode* m_next; + ExpressionNode* m_expr; + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class ArgumentsNode : public ParserArenaFreeable { + public: + ArgumentsNode(); + ArgumentsNode(ArgumentListNode*); + + ArgumentListNode* m_listNode; + }; + + class NewExprNode : public ExpressionNode, public ThrowableExpressionData { + public: + NewExprNode(const JSTokenLocation&, ExpressionNode*); + NewExprNode(const JSTokenLocation&, ExpressionNode*, ArgumentsNode*); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + ArgumentsNode* m_args; + }; + + class EvalFunctionCallNode : public ExpressionNode, public ThrowableExpressionData { + public: + EvalFunctionCallNode(const JSTokenLocation&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ArgumentsNode* m_args; + }; + + class FunctionCallValueNode : public ExpressionNode, public ThrowableExpressionData { + public: + FunctionCallValueNode(const JSTokenLocation&, ExpressionNode*, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + ArgumentsNode* m_args; + }; + + class FunctionCallResolveNode : public ExpressionNode, public ThrowableExpressionData { + public: + FunctionCallResolveNode(const JSTokenLocation&, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_ident; + ArgumentsNode* m_args; + }; + + class FunctionCallBracketNode : public ExpressionNode, public ThrowableSubExpressionData { + public: + FunctionCallBracketNode(const JSTokenLocation&, ExpressionNode* base, ExpressionNode* subscript, bool subscriptHasAssignments, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_base; + ExpressionNode* m_subscript; + ArgumentsNode* m_args; + bool m_subscriptHasAssignments; + }; + + class FunctionCallDotNode : public ExpressionNode, public ThrowableSubExpressionData { + public: + FunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + protected: + ExpressionNode* m_base; + const Identifier& m_ident; + ArgumentsNode* m_args; + }; + + class BytecodeIntrinsicNode : public ExpressionNode, public ThrowableExpressionData { + public: + typedef RegisterID* (BytecodeIntrinsicNode::* EmitterType)(BytecodeGenerator&, RegisterID*); + + BytecodeIntrinsicNode(const JSTokenLocation&, EmitterType, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + const Identifier& identifier() const { return m_ident; } + +#define JSC_DECLARE_BYTECODE_INTRINSIC_FUNCTIONS(name) RegisterID* emit_intrinsic_##name(BytecodeGenerator&, RegisterID*); + JSC_COMMON_BYTECODE_INTRINSICS_EACH_NAME(JSC_DECLARE_BYTECODE_INTRINSIC_FUNCTIONS) +#undef JSC_DECLARE_BYTECODE_INTRINSIC_FUNCTIONS + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + EmitterType m_emitter; + const Identifier& m_ident; + ArgumentsNode* m_args; + }; + + class CallFunctionCallDotNode : public FunctionCallDotNode { + public: + CallFunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class ApplyFunctionCallDotNode : public FunctionCallDotNode { + public: + ApplyFunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class DeleteResolveNode : public ExpressionNode, public ThrowableExpressionData { + public: + DeleteResolveNode(const JSTokenLocation&, const Identifier&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_ident; + }; + + class DeleteBracketNode : public ExpressionNode, public ThrowableExpressionData { + public: + DeleteBracketNode(const JSTokenLocation&, ExpressionNode* base, ExpressionNode* subscript, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_base; + ExpressionNode* m_subscript; + }; + + class DeleteDotNode : public ExpressionNode, public ThrowableExpressionData { + public: + DeleteDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_base; + const Identifier& m_ident; + }; + + class DeleteValueNode : public ExpressionNode { + public: + DeleteValueNode(const JSTokenLocation&, ExpressionNode*); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + }; + + class VoidNode : public ExpressionNode { + public: + VoidNode(const JSTokenLocation&, ExpressionNode*); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + }; + + class TypeOfResolveNode : public ExpressionNode { + public: + TypeOfResolveNode(const JSTokenLocation&, const Identifier&); + + const Identifier& identifier() const { return m_ident; } + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_ident; + }; + + class TypeOfValueNode : public ExpressionNode { + public: + TypeOfValueNode(const JSTokenLocation&, ExpressionNode*); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + }; + + class PrefixNode : public ExpressionNode, public ThrowablePrefixedSubExpressionData { + public: + PrefixNode(const JSTokenLocation&, ExpressionNode*, Operator, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + protected: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + virtual RegisterID* emitResolve(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBracket(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitDot(BytecodeGenerator&, RegisterID* = 0); + + ExpressionNode* m_expr; + Operator m_operator; + }; + + class PostfixNode : public PrefixNode { + public: + PostfixNode(const JSTokenLocation&, ExpressionNode*, Operator, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + virtual RegisterID* emitResolve(BytecodeGenerator&, RegisterID* = 0) override; + virtual RegisterID* emitBracket(BytecodeGenerator&, RegisterID* = 0) override; + virtual RegisterID* emitDot(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class UnaryOpNode : public ExpressionNode { + public: + UnaryOpNode(const JSTokenLocation&, ResultType, ExpressionNode*, OpcodeID); + + protected: + ExpressionNode* expr() { return m_expr; } + const ExpressionNode* expr() const { return m_expr; } + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + OpcodeID opcodeID() const { return m_opcodeID; } + + ExpressionNode* m_expr; + OpcodeID m_opcodeID; + }; + + class UnaryPlusNode : public UnaryOpNode { + public: + UnaryPlusNode(const JSTokenLocation&, ExpressionNode*); + + private: + virtual ExpressionNode* stripUnaryPlus() override { return expr(); } + }; + + class NegateNode : public UnaryOpNode { + public: + NegateNode(const JSTokenLocation&, ExpressionNode*); + }; + + class BitwiseNotNode : public ExpressionNode { + public: + BitwiseNotNode(const JSTokenLocation&, ExpressionNode*); + + protected: + ExpressionNode* expr() { return m_expr; } + const ExpressionNode* expr() const { return m_expr; } + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + }; + + class LogicalNotNode : public UnaryOpNode { + public: + LogicalNotNode(const JSTokenLocation&, ExpressionNode*); + private: + virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode) override; + }; + + class BinaryOpNode : public ExpressionNode { + public: + BinaryOpNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, OpcodeID, bool rightHasAssignments); + BinaryOpNode(const JSTokenLocation&, ResultType, ExpressionNode* expr1, ExpressionNode* expr2, OpcodeID, bool rightHasAssignments); + + RegisterID* emitStrcat(BytecodeGenerator& generator, RegisterID* destination, RegisterID* lhs = 0, ReadModifyResolveNode* emitExpressionInfoForMe = 0); + virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode) override; + + ExpressionNode* lhs() { return m_expr1; }; + ExpressionNode* rhs() { return m_expr2; }; + + private: + void tryFoldToBranch(BytecodeGenerator&, TriState& branchCondition, ExpressionNode*& branchExpression); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + protected: + OpcodeID opcodeID() const { return m_opcodeID; } + + protected: + ExpressionNode* m_expr1; + ExpressionNode* m_expr2; + private: + OpcodeID m_opcodeID; + protected: + bool m_rightHasAssignments; + }; + + class MultNode : public BinaryOpNode { + public: + MultNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class DivNode : public BinaryOpNode { + public: + DivNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class ModNode : public BinaryOpNode { + public: + ModNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class AddNode : public BinaryOpNode { + public: + AddNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + + virtual bool isAdd() const override { return true; } + }; + + class SubNode : public BinaryOpNode { + public: + SubNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + + virtual bool isSubtract() const override { return true; } + }; + + class LeftShiftNode : public BinaryOpNode { + public: + LeftShiftNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class RightShiftNode : public BinaryOpNode { + public: + RightShiftNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class UnsignedRightShiftNode : public BinaryOpNode { + public: + UnsignedRightShiftNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class LessNode : public BinaryOpNode { + public: + LessNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class GreaterNode : public BinaryOpNode { + public: + GreaterNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class LessEqNode : public BinaryOpNode { + public: + LessEqNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class GreaterEqNode : public BinaryOpNode { + public: + GreaterEqNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class ThrowableBinaryOpNode : public BinaryOpNode, public ThrowableExpressionData { + public: + ThrowableBinaryOpNode(const JSTokenLocation&, ResultType, ExpressionNode* expr1, ExpressionNode* expr2, OpcodeID, bool rightHasAssignments); + ThrowableBinaryOpNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, OpcodeID, bool rightHasAssignments); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class InstanceOfNode : public ThrowableBinaryOpNode { + public: + InstanceOfNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class InNode : public ThrowableBinaryOpNode { + public: + InNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class EqualNode : public BinaryOpNode { + public: + EqualNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class NotEqualNode : public BinaryOpNode { + public: + NotEqualNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class StrictEqualNode : public BinaryOpNode { + public: + StrictEqualNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class NotStrictEqualNode : public BinaryOpNode { + public: + NotStrictEqualNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class BitAndNode : public BinaryOpNode { + public: + BitAndNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class BitOrNode : public BinaryOpNode { + public: + BitOrNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + class BitXOrNode : public BinaryOpNode { + public: + BitXOrNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + + // m_expr1 && m_expr2, m_expr1 || m_expr2 + class LogicalOpNode : public ExpressionNode { + public: + LogicalOpNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, LogicalOperator); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode) override; + + ExpressionNode* m_expr1; + ExpressionNode* m_expr2; + LogicalOperator m_operator; + }; + + // The ternary operator, "m_logical ? m_expr1 : m_expr2" + class ConditionalNode : public ExpressionNode { + public: + ConditionalNode(const JSTokenLocation&, ExpressionNode* logical, ExpressionNode* expr1, ExpressionNode* expr2); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_logical; + ExpressionNode* m_expr1; + ExpressionNode* m_expr2; + }; + + class ReadModifyResolveNode : public ExpressionNode, public ThrowableExpressionData { + public: + ReadModifyResolveNode(const JSTokenLocation&, const Identifier&, Operator, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_ident; + ExpressionNode* m_right; + Operator m_operator; + bool m_rightHasAssignments; + }; + + class AssignResolveNode : public ExpressionNode, public ThrowableExpressionData { + public: + AssignResolveNode(const JSTokenLocation&, const Identifier&, ExpressionNode* right, AssignmentContext); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_ident; + ExpressionNode* m_right; + AssignmentContext m_assignmentContext; + }; + + class ReadModifyBracketNode : public ExpressionNode, public ThrowableSubExpressionData { + public: + ReadModifyBracketNode(const JSTokenLocation&, ExpressionNode* base, ExpressionNode* subscript, Operator, ExpressionNode* right, bool subscriptHasAssignments, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_base; + ExpressionNode* m_subscript; + ExpressionNode* m_right; + unsigned m_operator : 30; + bool m_subscriptHasAssignments : 1; + bool m_rightHasAssignments : 1; + }; + + class AssignBracketNode : public ExpressionNode, public ThrowableExpressionData { + public: + AssignBracketNode(const JSTokenLocation&, ExpressionNode* base, ExpressionNode* subscript, ExpressionNode* right, bool subscriptHasAssignments, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_base; + ExpressionNode* m_subscript; + ExpressionNode* m_right; + bool m_subscriptHasAssignments : 1; + bool m_rightHasAssignments : 1; + }; + + class AssignDotNode : public ExpressionNode, public ThrowableExpressionData { + public: + AssignDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_base; + const Identifier& m_ident; + ExpressionNode* m_right; + bool m_rightHasAssignments; + }; + + class ReadModifyDotNode : public ExpressionNode, public ThrowableSubExpressionData { + public: + ReadModifyDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, Operator, ExpressionNode* right, bool rightHasAssignments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_base; + const Identifier& m_ident; + ExpressionNode* m_right; + unsigned m_operator : 31; + bool m_rightHasAssignments : 1; + }; + + class AssignErrorNode : public ExpressionNode, public ThrowableExpressionData { + public: + AssignErrorNode(const JSTokenLocation&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class CommaNode final : public ExpressionNode { + public: + CommaNode(const JSTokenLocation&, ExpressionNode*); + + void setNext(CommaNode* next) { m_next = next; } + CommaNode* next() { return m_next; } + + private: + virtual bool isCommaNode() const override { return true; } + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + CommaNode* m_next; + }; + + class SourceElements final : public ParserArenaFreeable { + public: + SourceElements(); + + void append(StatementNode*); + + StatementNode* singleStatement() const; + StatementNode* lastStatement() const; + + void emitBytecode(BytecodeGenerator&, RegisterID* destination); + void analyzeModule(ModuleAnalyzer&); + + private: + StatementNode* m_head; + StatementNode* m_tail; + }; + + class BlockNode : public StatementNode, public VariableEnvironmentNode { + public: + BlockNode(const JSTokenLocation&, SourceElements*, VariableEnvironment&); + + StatementNode* singleStatement() const; + StatementNode* lastStatement() const; + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + virtual bool isBlock() const override { return true; } + + SourceElements* m_statements; + }; + + class EmptyStatementNode : public StatementNode { + public: + EmptyStatementNode(const JSTokenLocation&); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + virtual bool isEmptyStatement() const override { return true; } + }; + + class DebuggerStatementNode : public StatementNode { + public: + DebuggerStatementNode(const JSTokenLocation&); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class ExprStatementNode : public StatementNode { + public: + ExprStatementNode(const JSTokenLocation&, ExpressionNode*); + + ExpressionNode* expr() const { return m_expr; } + + private: + virtual bool isExprStatement() const override { return true; } + + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + }; + + class DeclarationStatement : public StatementNode { + public: + DeclarationStatement(const JSTokenLocation&, ExpressionNode*); + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + }; + + class EmptyVarExpression : public ExpressionNode { + public: + EmptyVarExpression(const JSTokenLocation&, const Identifier&); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_ident; + }; + + class EmptyLetExpression : public ExpressionNode { + public: + EmptyLetExpression(const JSTokenLocation&, const Identifier&); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_ident; + }; + + class IfElseNode : public StatementNode { + public: + IfElseNode(const JSTokenLocation&, ExpressionNode* condition, StatementNode* ifBlock, StatementNode* elseBlock); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + bool tryFoldBreakAndContinue(BytecodeGenerator&, StatementNode* ifBlock, + Label*& trueTarget, FallThroughMode&); + + ExpressionNode* m_condition; + StatementNode* m_ifBlock; + StatementNode* m_elseBlock; + }; + + class DoWhileNode : public StatementNode { + public: + DoWhileNode(const JSTokenLocation&, StatementNode*, ExpressionNode*); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + StatementNode* m_statement; + ExpressionNode* m_expr; + }; + + class WhileNode : public StatementNode { + public: + WhileNode(const JSTokenLocation&, ExpressionNode*, StatementNode*); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + StatementNode* m_statement; + }; + + class ForNode : public StatementNode, public VariableEnvironmentNode { + public: + ForNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, ExpressionNode* expr3, StatementNode*, VariableEnvironment&); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr1; + ExpressionNode* m_expr2; + ExpressionNode* m_expr3; + StatementNode* m_statement; + }; + + class DestructuringPatternNode; + + class EnumerationNode : public StatementNode, public ThrowableExpressionData, public VariableEnvironmentNode { + public: + EnumerationNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*, VariableEnvironment&); + + protected: + ExpressionNode* m_lexpr; + ExpressionNode* m_expr; + StatementNode* m_statement; + }; + + class ForInNode : public EnumerationNode { + public: + ForInNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*, VariableEnvironment&); + + private: + RegisterID* tryGetBoundLocal(BytecodeGenerator&); + void emitLoopHeader(BytecodeGenerator&, RegisterID* propertyName); + void emitMultiLoopBytecode(BytecodeGenerator&, RegisterID* dst); + + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class ForOfNode : public EnumerationNode { + public: + ForOfNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*, VariableEnvironment&); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class ContinueNode : public StatementNode, public ThrowableExpressionData { + public: + ContinueNode(const JSTokenLocation&, const Identifier&); + Label* trivialTarget(BytecodeGenerator&); + + private: + virtual bool isContinue() const override { return true; } + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_ident; + }; + + class BreakNode : public StatementNode, public ThrowableExpressionData { + public: + BreakNode(const JSTokenLocation&, const Identifier&); + Label* trivialTarget(BytecodeGenerator&); + + private: + virtual bool isBreak() const override { return true; } + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_ident; + }; + + class ReturnNode : public StatementNode, public ThrowableExpressionData { + public: + ReturnNode(const JSTokenLocation&, ExpressionNode* value); + + ExpressionNode* value() { return m_value; } + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + virtual bool isReturnNode() const override { return true; } + + ExpressionNode* m_value; + }; + + class WithNode : public StatementNode { + public: + WithNode(const JSTokenLocation&, ExpressionNode*, StatementNode*, const JSTextPosition& divot, uint32_t expressionLength); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + StatementNode* m_statement; + JSTextPosition m_divot; + uint32_t m_expressionLength; + }; + + class LabelNode : public StatementNode, public ThrowableExpressionData { + public: + LabelNode(const JSTokenLocation&, const Identifier& name, StatementNode*); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_name; + StatementNode* m_statement; + }; + + class ThrowNode : public StatementNode, public ThrowableExpressionData { + public: + ThrowNode(const JSTokenLocation&, ExpressionNode*); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + }; + + class TryNode : public StatementNode { + public: + TryNode(const JSTokenLocation&, StatementNode* tryBlock, const Identifier& exceptionIdent, StatementNode* catchBlock, VariableEnvironment& catchEnvironment, StatementNode* finallyBlock); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + StatementNode* m_tryBlock; + const Identifier& m_thrownValueIdent; + StatementNode* m_catchBlock; + StatementNode* m_finallyBlock; + VariableEnvironment m_catchEnvironment; + }; + + class ScopeNode : public StatementNode, public ParserArenaRoot, public VariableEnvironmentNode { + public: + typedef DeclarationStacks::FunctionStack FunctionStack; + + ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, bool inStrictContext); + ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, const SourceCode&, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, CodeFeatures, int numConstants); + + using ParserArenaRoot::operator new; + + const SourceCode& source() const { return m_source; } + const String& sourceURL() const { return m_source.provider()->url(); } + intptr_t sourceID() const { return m_source.providerID(); } + + int startLine() const { return m_startLineNumber; } + int startStartOffset() const { return m_startStartOffset; } + int startLineStartOffset() const { return m_startLineStartOffset; } + + void setFeatures(CodeFeatures features) { m_features = features; } + CodeFeatures features() { return m_features; } + + bool usesEval() const { return m_features & EvalFeature; } + bool usesArguments() const { return (m_features & ArgumentsFeature) && !(m_features & ShadowsArgumentsFeature); } + bool modifiesParameter() const { return m_features & ModifiedParameterFeature; } + bool modifiesArguments() const { return m_features & (EvalFeature | ModifiedArgumentsFeature); } + bool isStrictMode() const { return m_features & StrictModeFeature; } + void setUsesArguments() { m_features |= ArgumentsFeature; } + bool usesThis() const { return m_features & ThisFeature; } + bool needsActivationForMoreThanVariables() const { return m_features & (EvalFeature | WithFeature | CatchFeature); } + bool needsActivation() const { return (hasCapturedVariables()) || (m_features & (EvalFeature | WithFeature | CatchFeature)); } + bool hasCapturedVariables() const { return m_varDeclarations.hasCapturedVariables(); } + bool captures(UniquedStringImpl* uid) { return m_varDeclarations.captures(uid); } + bool captures(const Identifier& ident) { return captures(ident.impl()); } + + VariableEnvironment& varDeclarations() { return m_varDeclarations; } + FunctionStack& functionStack() { return m_functionStack; } + + int neededConstants() + { + // We may need 2 more constants than the count given by the parser, + // because of the various uses of jsUndefined() and jsNull(). + return m_numConstants + 2; + } + + StatementNode* singleStatement() const; + + void emitStatementsBytecode(BytecodeGenerator&, RegisterID* destination); + + void setClosedVariables(Vector<RefPtr<UniquedStringImpl>>&&) { } + + void analyzeModule(ModuleAnalyzer&); + + protected: + int m_startLineNumber; + unsigned m_startStartOffset; + unsigned m_startLineStartOffset; + + private: + CodeFeatures m_features; + SourceCode m_source; + VariableEnvironment m_varDeclarations; + FunctionStack m_functionStack; + int m_numConstants; + SourceElements* m_statements; + }; + + class ProgramNode : public ScopeNode { + public: + ProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, int numConstants); + + unsigned startColumn() const { return m_startColumn; } + unsigned endColumn() const { return m_endColumn; } + + static const bool scopeIsFunction = false; + + void setClosedVariables(Vector<RefPtr<UniquedStringImpl>>&&); + const Vector<RefPtr<UniquedStringImpl>>& closedVariables() const { return m_closedVariables; } + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + Vector<RefPtr<UniquedStringImpl>> m_closedVariables; + unsigned m_startColumn; + unsigned m_endColumn; + }; + + class EvalNode : public ScopeNode { + public: + EvalNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, int numConstants); + + ALWAYS_INLINE unsigned startColumn() const { return 0; } + unsigned endColumn() const { return m_endColumn; } + + static const bool scopeIsFunction = false; + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + unsigned m_endColumn; + }; + + class ModuleProgramNode : public ScopeNode { + public: + ModuleProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, int numConstants); + + unsigned startColumn() const { return m_startColumn; } + unsigned endColumn() const { return m_endColumn; } + + static const bool scopeIsFunction = false; + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + unsigned m_startColumn; + unsigned m_endColumn; + }; + + class ModuleNameNode : public Node { + public: + ModuleNameNode(const JSTokenLocation&, const Identifier& moduleName); + + const Identifier& moduleName() { return m_moduleName; } + + private: + const Identifier& m_moduleName; + }; + + class ImportSpecifierNode : public Node { + public: + ImportSpecifierNode(const JSTokenLocation&, const Identifier& importedName, const Identifier& localName); + + const Identifier& importedName() { return m_importedName; } + const Identifier& localName() { return m_localName; } + + private: + const Identifier& m_importedName; + const Identifier& m_localName; + }; + + class ImportSpecifierListNode : public ParserArenaDeletable { + public: + typedef Vector<ImportSpecifierNode*, 3> Specifiers; + + const Specifiers& specifiers() const { return m_specifiers; } + void append(ImportSpecifierNode* specifier) + { + m_specifiers.append(specifier); + } + + private: + Specifiers m_specifiers; + }; + + class ModuleDeclarationNode : public StatementNode { + public: + virtual void analyzeModule(ModuleAnalyzer&) = 0; + virtual bool isModuleDeclarationNode() const { return true; } + + protected: + ModuleDeclarationNode(const JSTokenLocation&); + }; + + class ImportDeclarationNode : public ModuleDeclarationNode { + public: + ImportDeclarationNode(const JSTokenLocation&, ImportSpecifierListNode*, ModuleNameNode*); + + ImportSpecifierListNode* specifierList() const { return m_specifierList; } + ModuleNameNode* moduleName() const { return m_moduleName; } + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + virtual void analyzeModule(ModuleAnalyzer&) override; + + ImportSpecifierListNode* m_specifierList; + ModuleNameNode* m_moduleName; + }; + + class ExportAllDeclarationNode : public ModuleDeclarationNode { + public: + ExportAllDeclarationNode(const JSTokenLocation&, ModuleNameNode*); + + ModuleNameNode* moduleName() const { return m_moduleName; } + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + virtual void analyzeModule(ModuleAnalyzer&) override; + + ModuleNameNode* m_moduleName; + }; + + class ExportDefaultDeclarationNode : public ModuleDeclarationNode { + public: + ExportDefaultDeclarationNode(const JSTokenLocation&, StatementNode*, const Identifier& localName); + + const StatementNode& declaration() const { return *m_declaration; } + const Identifier& localName() const { return m_localName; } + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + virtual void analyzeModule(ModuleAnalyzer&) override; + StatementNode* m_declaration; + const Identifier& m_localName; + }; + + class ExportLocalDeclarationNode : public ModuleDeclarationNode { + public: + ExportLocalDeclarationNode(const JSTokenLocation&, StatementNode*); + + const StatementNode& declaration() const { return *m_declaration; } + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + virtual void analyzeModule(ModuleAnalyzer&) override; + StatementNode* m_declaration; + }; + + class ExportSpecifierNode : public Node { + public: + ExportSpecifierNode(const JSTokenLocation&, const Identifier& localName, const Identifier& exportedName); + + const Identifier& exportedName() { return m_exportedName; } + const Identifier& localName() { return m_localName; } + + private: + const Identifier& m_localName; + const Identifier& m_exportedName; + }; + + class ExportSpecifierListNode : public ParserArenaDeletable { + public: + typedef Vector<ExportSpecifierNode*, 3> Specifiers; + + const Specifiers& specifiers() const { return m_specifiers; } + void append(ExportSpecifierNode* specifier) + { + m_specifiers.append(specifier); + } + + private: + Specifiers m_specifiers; + }; + + class ExportNamedDeclarationNode : public ModuleDeclarationNode { + public: + ExportNamedDeclarationNode(const JSTokenLocation&, ExportSpecifierListNode*, ModuleNameNode*); + + ExportSpecifierListNode* specifierList() const { return m_specifierList; } + ModuleNameNode* moduleName() const { return m_moduleName; } + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + virtual void analyzeModule(ModuleAnalyzer&) override; + ExportSpecifierListNode* m_specifierList; + ModuleNameNode* m_moduleName { nullptr }; + }; + + class FunctionParameters : public ParserArenaDeletable { + public: + FunctionParameters(); + ALWAYS_INLINE unsigned size() const { return m_patterns.size(); } + ALWAYS_INLINE std::pair<DestructuringPatternNode*, ExpressionNode*> at(unsigned index) { return m_patterns[index]; } + bool hasDefaultParameterValues() const { return m_hasDefaultParameterValues; } + ALWAYS_INLINE void append(DestructuringPatternNode* pattern, ExpressionNode* defaultValue) + { + ASSERT(pattern); + m_patterns.append(std::make_pair(pattern, defaultValue)); + if (defaultValue) + m_hasDefaultParameterValues = true; + } + + private: + + Vector<std::pair<DestructuringPatternNode*, ExpressionNode*>, 3> m_patterns; + bool m_hasDefaultParameterValues { false }; + }; + + class FunctionMetadataNode final : public Node, public ParserArenaDeletable { + public: + using ParserArenaDeletable::operator new; + + FunctionMetadataNode( + ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, + unsigned startColumn, unsigned endColumn, int functionKeywordStart, + int functionNameStart, int parametersStart, bool isInStrictContext, + ConstructorKind, unsigned, SourceParseMode); + + void finishParsing(const SourceCode&, const Identifier&, FunctionMode); + + void overrideName(const Identifier& ident) { m_ident = ident; } + const Identifier& ident() { return m_ident; } + void setInferredName(const Identifier& inferredName) { ASSERT(!inferredName.isNull()); m_inferredName = inferredName; } + const Identifier& inferredName() { return m_inferredName.isEmpty() ? m_ident : m_inferredName; } + + FunctionMode functionMode() { return m_functionMode; } + + int functionNameStart() const { return m_functionNameStart; } + int functionKeywordStart() const { return m_functionKeywordStart; } + int parametersStart() const { return m_parametersStart; } + unsigned startColumn() const { return m_startColumn; } + unsigned endColumn() const { return m_endColumn; } + unsigned parameterCount() const { return m_parameterCount; } + SourceParseMode parseMode() const { return m_parseMode; } + + void setEndPosition(JSTextPosition); + + const SourceCode& source() const { return m_source; } + + int startStartOffset() const { return m_startStartOffset; } + bool isInStrictContext() const { return m_isInStrictContext; } + ConstructorKind constructorKind() { return static_cast<ConstructorKind>(m_constructorKind); } + + void setLoc(unsigned firstLine, unsigned lastLine, int startOffset, int lineStartOffset) + { + m_lastLine = lastLine; + m_position = JSTextPosition(firstLine, startOffset, lineStartOffset); + ASSERT(m_position.offset >= m_position.lineStartOffset); + } + unsigned lastLine() const { return m_lastLine; } + + protected: + Identifier m_ident; + Identifier m_inferredName; + FunctionMode m_functionMode; + unsigned m_startColumn; + unsigned m_endColumn; + int m_functionKeywordStart; + int m_functionNameStart; + int m_parametersStart; + SourceCode m_source; + int m_startStartOffset; + unsigned m_parameterCount; + int m_lastLine; + SourceParseMode m_parseMode; + unsigned m_isInStrictContext : 1; + unsigned m_constructorKind : 2; + }; + + class FunctionNode final : public ScopeNode { + public: + FunctionNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, int numConstants); + + FunctionParameters* parameters() const { return m_parameters; } + + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + void finishParsing(const Identifier&, FunctionMode); + + const Identifier& ident() { return m_ident; } + + FunctionMode functionMode() { return m_functionMode; } + + unsigned startColumn() const { return m_startColumn; } + unsigned endColumn() const { return m_endColumn; } + + static const bool scopeIsFunction = true; + + private: + Identifier m_ident; + FunctionMode m_functionMode; + FunctionParameters* m_parameters; + unsigned m_startColumn; + unsigned m_endColumn; + }; + + class FuncExprNode : public ExpressionNode { + public: + FuncExprNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&); + + FunctionMetadataNode* metadata() { return m_metadata; } + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + virtual bool isFuncExprNode() const override { return true; } + + FunctionMetadataNode* m_metadata; + }; + +#if ENABLE(ES6_CLASS_SYNTAX) + class ClassExprNode final : public ExpressionNode { + public: + ClassExprNode(const JSTokenLocation&, const Identifier&, ExpressionNode* constructorExpresssion, + ExpressionNode* parentClass, PropertyListNode* instanceMethods, PropertyListNode* staticMethods); + + const Identifier& name() { return m_name; } + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_name; + ExpressionNode* m_constructorExpression; + ExpressionNode* m_classHeritage; + PropertyListNode* m_instanceMethods; + PropertyListNode* m_staticMethods; + }; +#endif + + class DestructuringPatternNode : public ParserArenaFreeable { + public: + virtual ~DestructuringPatternNode() { } + virtual void collectBoundIdentifiers(Vector<Identifier>&) const = 0; + virtual void bindValue(BytecodeGenerator&, RegisterID* source) const = 0; + virtual void toString(StringBuilder&) const = 0; + + virtual bool isBindingNode() const { return false; } + virtual RegisterID* emitDirectBinding(BytecodeGenerator&, RegisterID*, ExpressionNode*) { return 0; } + + protected: + DestructuringPatternNode(); + }; + + class ArrayPatternNode : public DestructuringPatternNode, public ThrowableExpressionData, public ParserArenaDeletable { + public: + using ParserArenaDeletable::operator new; + + ArrayPatternNode(); + enum class BindingType { + Elision, + Element, + RestElement + }; + + void appendIndex(BindingType bindingType, const JSTokenLocation&, DestructuringPatternNode* node, ExpressionNode* defaultValue) + { + m_targetPatterns.append({ bindingType, node, defaultValue }); + } + + private: + struct Entry { + BindingType bindingType; + DestructuringPatternNode* pattern; + ExpressionNode* defaultValue; + }; + virtual void collectBoundIdentifiers(Vector<Identifier>&) const override; + virtual void bindValue(BytecodeGenerator&, RegisterID*) const override; + virtual RegisterID* emitDirectBinding(BytecodeGenerator&, RegisterID* dst, ExpressionNode*) override; + virtual void toString(StringBuilder&) const override; + + Vector<Entry> m_targetPatterns; + }; + + class ObjectPatternNode : public DestructuringPatternNode, public ParserArenaDeletable { + public: + using ParserArenaDeletable::operator new; + + ObjectPatternNode(); + void appendEntry(const JSTokenLocation&, const Identifier& identifier, bool wasString, DestructuringPatternNode* pattern, ExpressionNode* defaultValue) + { + m_targetPatterns.append(Entry{ identifier, wasString, pattern, defaultValue }); + } + + private: + virtual void collectBoundIdentifiers(Vector<Identifier>&) const override; + virtual void bindValue(BytecodeGenerator&, RegisterID*) const override; + virtual void toString(StringBuilder&) const override; + struct Entry { + const Identifier& propertyName; + bool wasString; + DestructuringPatternNode* pattern; + ExpressionNode* defaultValue; + }; + Vector<Entry> m_targetPatterns; + }; + + class BindingNode : public DestructuringPatternNode { + public: + BindingNode(const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end, AssignmentContext); + const Identifier& boundProperty() const { return m_boundProperty; } + + const JSTextPosition& divotStart() const { return m_divotStart; } + const JSTextPosition& divotEnd() const { return m_divotEnd; } + + private: + virtual void collectBoundIdentifiers(Vector<Identifier>&) const override; + virtual void bindValue(BytecodeGenerator&, RegisterID*) const override; + virtual void toString(StringBuilder&) const override; + + virtual bool isBindingNode() const override { return true; } + + JSTextPosition m_divotStart; + JSTextPosition m_divotEnd; + const Identifier& m_boundProperty; + AssignmentContext m_bindingContext; + }; + + class DestructuringAssignmentNode : public ExpressionNode { + public: + DestructuringAssignmentNode(const JSTokenLocation&, DestructuringPatternNode*, ExpressionNode*); + DestructuringPatternNode* bindings() { return m_bindings; } + + private: + virtual bool isAssignmentLocation() const override { return true; } + virtual bool isDestructuringNode() const override { return true; } + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + DestructuringPatternNode* m_bindings; + ExpressionNode* m_initializer; + }; + + class FuncDeclNode : public StatementNode { + public: + FuncDeclNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&); + + virtual bool isFuncDeclNode() const override { return true; } + FunctionMetadataNode* metadata() { return m_metadata; } + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + FunctionMetadataNode* m_metadata; + }; + +#if ENABLE(ES6_CLASS_SYNTAX) + class ClassDeclNode final : public StatementNode { + public: + ClassDeclNode(const JSTokenLocation&, ExpressionNode* classExpression); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_classDeclaration; + }; +#endif + + class CaseClauseNode : public ParserArenaFreeable { + public: + CaseClauseNode(ExpressionNode*, SourceElements* = 0); + + ExpressionNode* expr() const { return m_expr; } + + void emitBytecode(BytecodeGenerator&, RegisterID* destination); + void setStartOffset(int offset) { m_startOffset = offset; } + + private: + ExpressionNode* m_expr; + SourceElements* m_statements; + int m_startOffset; + }; + + class ClauseListNode : public ParserArenaFreeable { + public: + ClauseListNode(CaseClauseNode*); + ClauseListNode(ClauseListNode*, CaseClauseNode*); + + CaseClauseNode* getClause() const { return m_clause; } + ClauseListNode* getNext() const { return m_next; } + + private: + CaseClauseNode* m_clause; + ClauseListNode* m_next; + }; + + class CaseBlockNode : public ParserArenaFreeable { + public: + CaseBlockNode(ClauseListNode* list1, CaseClauseNode* defaultClause, ClauseListNode* list2); + + void emitBytecodeForBlock(BytecodeGenerator&, RegisterID* input, RegisterID* destination); + + private: + SwitchInfo::SwitchType tryTableSwitch(Vector<ExpressionNode*, 8>& literalVector, int32_t& min_num, int32_t& max_num); + static const size_t s_tableSwitchMinimum = 3; + ClauseListNode* m_list1; + CaseClauseNode* m_defaultClause; + ClauseListNode* m_list2; + }; + + class SwitchNode : public StatementNode, public VariableEnvironmentNode { + public: + SwitchNode(const JSTokenLocation&, ExpressionNode*, CaseBlockNode*, VariableEnvironment&); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + CaseBlockNode* m_block; + }; + + struct ElementList { + ElementNode* head; + ElementNode* tail; + }; + + struct PropertyList { + PropertyListNode* head; + PropertyListNode* tail; + }; + + struct ArgumentList { + ArgumentListNode* head; + ArgumentListNode* tail; + }; + + struct ClauseList { + ClauseListNode* head; + ClauseListNode* tail; + }; + +} // namespace JSC + +#endif // Nodes_h diff --git a/Source/JavaScriptCore/parser/NodesAnalyzeModule.cpp b/Source/JavaScriptCore/parser/NodesAnalyzeModule.cpp new file mode 100644 index 000000000..633deaece --- /dev/null +++ b/Source/JavaScriptCore/parser/NodesAnalyzeModule.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Nodes.h" +#include "NodeConstructors.h" + +#include "ModuleAnalyzer.h" +#include "ModuleRecord.h" + +namespace JSC { + +void ScopeNode::analyzeModule(ModuleAnalyzer& analyzer) +{ + m_statements->analyzeModule(analyzer); +} + +void SourceElements::analyzeModule(ModuleAnalyzer& analyzer) +{ + // In the module analyzer phase, only module declarations are included in the top-level SourceElements. + for (StatementNode* statement = m_head; statement; statement = statement->next()) { + ASSERT(statement->isModuleDeclarationNode()); + static_cast<ModuleDeclarationNode*>(statement)->analyzeModule(analyzer); + } +} + +void ImportDeclarationNode::analyzeModule(ModuleAnalyzer& analyzer) +{ + analyzer.moduleRecord().appendRequestedModule(m_moduleName->moduleName()); + for (auto* specifier : m_specifierList->specifiers()) { + analyzer.moduleRecord().addImportEntry(ModuleRecord::ImportEntry { + m_moduleName->moduleName(), + specifier->importedName(), + specifier->localName() + }); + } +} + +void ExportAllDeclarationNode::analyzeModule(ModuleAnalyzer& analyzer) +{ + analyzer.moduleRecord().appendRequestedModule(m_moduleName->moduleName()); + analyzer.moduleRecord().addStarExportEntry(m_moduleName->moduleName()); +} + +void ExportDefaultDeclarationNode::analyzeModule(ModuleAnalyzer& analyzer) +{ + analyzer.declareExportAlias(m_localName, analyzer.vm().propertyNames->defaultKeyword); +} + +void ExportLocalDeclarationNode::analyzeModule(ModuleAnalyzer&) +{ +} + +void ExportNamedDeclarationNode::analyzeModule(ModuleAnalyzer& analyzer) +{ + if (m_moduleName) + analyzer.moduleRecord().appendRequestedModule(m_moduleName->moduleName()); + + for (auto* specifier : m_specifierList->specifiers()) { + if (m_moduleName) { + // export { v } from "mod" + // + // In this case, no local variable names are imported into the current module. + // "v" indirectly points the binding in "mod". + analyzer.moduleRecord().addExportEntry(ModuleRecord::ExportEntry::createIndirect(specifier->exportedName(), specifier->localName(), m_moduleName->moduleName())); + continue; + } + + if (specifier->localName() != specifier->exportedName()) + analyzer.declareExportAlias(specifier->localName(), specifier->exportedName()); + } +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/Parser.cpp b/Source/JavaScriptCore/parser/Parser.cpp new file mode 100644 index 000000000..aee70b6a7 --- /dev/null +++ b/Source/JavaScriptCore/parser/Parser.cpp @@ -0,0 +1,3700 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "Parser.h" + +#include "ASTBuilder.h" +#include "CodeBlock.h" +#include "Debugger.h" +#include "JSCJSValueInlines.h" +#include "Lexer.h" +#include "JSCInlines.h" +#include "SourceProvider.h" +#include "VM.h" +#include <utility> +#include <wtf/HashFunctions.h> +#include <wtf/StringPrintStream.h> +#include <wtf/WTFThreadData.h> + + +#define updateErrorMessage(shouldPrintToken, ...) do {\ + propagateError(); \ + logError(shouldPrintToken, __VA_ARGS__); \ +} while (0) + +#define propagateError() do { if (hasError()) return 0; } while (0) +#define internalFailWithMessage(shouldPrintToken, ...) do { updateErrorMessage(shouldPrintToken, __VA_ARGS__); return 0; } while (0) +#define handleErrorToken() do { if (m_token.m_type == EOFTOK || m_token.m_type & ErrorTokenFlag) { failDueToUnexpectedToken(); } } while (0) +#define failWithMessage(...) do { { handleErrorToken(); updateErrorMessage(true, __VA_ARGS__); } return 0; } while (0) +#define failWithStackOverflow() do { updateErrorMessage(false, "Stack exhausted"); m_hasStackOverflow = true; return 0; } while (0) +#define failIfFalse(cond, ...) do { if (!(cond)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) +#define failIfTrue(cond, ...) do { if (cond) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) +#define failIfTrueIfStrict(cond, ...) do { if ((cond) && strictMode()) internalFailWithMessage(false, __VA_ARGS__); } while (0) +#define failIfFalseIfStrict(cond, ...) do { if ((!(cond)) && strictMode()) internalFailWithMessage(false, __VA_ARGS__); } while (0) +#define consumeOrFail(tokenType, ...) do { if (!consume(tokenType)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) +#define consumeOrFailWithFlags(tokenType, flags, ...) do { if (!consume(tokenType, flags)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) +#define matchOrFail(tokenType, ...) do { if (!match(tokenType)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) +#define failIfStackOverflow() do { if (!canRecurse()) failWithStackOverflow(); } while (0) +#define semanticFail(...) do { internalFailWithMessage(false, __VA_ARGS__); } while (0) +#define semanticFailIfTrue(cond, ...) do { if (cond) internalFailWithMessage(false, __VA_ARGS__); } while (0) +#define semanticFailIfFalse(cond, ...) do { if (!(cond)) internalFailWithMessage(false, __VA_ARGS__); } while (0) +#define regexFail(failure) do { setErrorMessage(failure); return 0; } while (0) +#define failDueToUnexpectedToken() do {\ + logError(true);\ + return 0;\ +} while (0) + +#define handleProductionOrFail(token, tokenString, operation, production) do {\ + consumeOrFail(token, "Expected '", tokenString, "' to ", operation, " a ", production);\ +} while (0) + +#define semanticFailureDueToKeyword(...) do { \ + if (strictMode() && m_token.m_type == RESERVED_IF_STRICT) \ + semanticFail("Cannot use the reserved word '", getToken(), "' as a ", __VA_ARGS__, " in strict mode"); \ + if (m_token.m_type == RESERVED || m_token.m_type == RESERVED_IF_STRICT) \ + semanticFail("Cannot use the reserved word '", getToken(), "' as a ", __VA_ARGS__); \ + if (m_token.m_type & KeywordTokenFlag) \ + semanticFail("Cannot use the keyword '", getToken(), "' as a ", __VA_ARGS__); \ +} while (0) + +using namespace std; + +namespace JSC { + +template <typename LexerType> +void Parser<LexerType>::logError(bool) +{ + if (hasError()) + return; + StringPrintStream stream; + printUnexpectedTokenText(stream); + setErrorMessage(stream.toString()); +} + +template <typename LexerType> template <typename A> +void Parser<LexerType>::logError(bool shouldPrintToken, const A& value1) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, "."); + setErrorMessage(stream.toString()); +} + +template <typename LexerType> template <typename A, typename B> +void Parser<LexerType>::logError(bool shouldPrintToken, const A& value1, const B& value2) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, value2, "."); + setErrorMessage(stream.toString()); +} + +template <typename LexerType> template <typename A, typename B, typename C> +void Parser<LexerType>::logError(bool shouldPrintToken, const A& value1, const B& value2, const C& value3) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, value2, value3, "."); + setErrorMessage(stream.toString()); +} + +template <typename LexerType> template <typename A, typename B, typename C, typename D> +void Parser<LexerType>::logError(bool shouldPrintToken, const A& value1, const B& value2, const C& value3, const D& value4) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, value2, value3, value4, "."); + setErrorMessage(stream.toString()); +} + +template <typename LexerType> template <typename A, typename B, typename C, typename D, typename E> +void Parser<LexerType>::logError(bool shouldPrintToken, const A& value1, const B& value2, const C& value3, const D& value4, const E& value5) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, value2, value3, value4, value5, "."); + setErrorMessage(stream.toString()); +} + +template <typename LexerType> template <typename A, typename B, typename C, typename D, typename E, typename F> +void Parser<LexerType>::logError(bool shouldPrintToken, const A& value1, const B& value2, const C& value3, const D& value4, const E& value5, const F& value6) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, value2, value3, value4, value5, value6, "."); + setErrorMessage(stream.toString()); +} + +template <typename LexerType> template <typename A, typename B, typename C, typename D, typename E, typename F, typename G> +void Parser<LexerType>::logError(bool shouldPrintToken, const A& value1, const B& value2, const C& value3, const D& value4, const E& value5, const F& value6, const G& value7) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, value2, value3, value4, value5, value6, value7, "."); + setErrorMessage(stream.toString()); +} + +template <typename LexerType> +Parser<LexerType>::Parser( + VM* vm, const SourceCode& source, JSParserBuiltinMode builtinMode, + JSParserStrictMode strictMode, SourceParseMode parseMode, + ConstructorKind defaultConstructorKind, ThisTDZMode thisTDZMode) + : m_vm(vm) + , m_source(&source) + , m_hasStackOverflow(false) + , m_allowsIn(true) + , m_assignmentCount(0) + , m_nonLHSCount(0) + , m_syntaxAlreadyValidated(source.provider()->isValid()) + , m_statementDepth(0) + , m_nonTrivialExpressionCount(0) + , m_lastIdentifier(0) + , m_lastFunctionName(nullptr) + , m_sourceElements(0) + , m_parsingBuiltin(builtinMode == JSParserBuiltinMode::Builtin) + , m_defaultConstructorKind(defaultConstructorKind) + , m_thisTDZMode(thisTDZMode) +{ + m_lexer = std::make_unique<LexerType>(vm, builtinMode); + m_lexer->setCode(source, &m_parserArena); + m_token.m_location.line = source.firstLine(); + m_token.m_location.startOffset = source.startOffset(); + m_token.m_location.endOffset = source.startOffset(); + m_token.m_location.lineStartOffset = source.startOffset(); + m_functionCache = vm->addSourceProviderCache(source.provider()); + ScopeRef scope = pushScope(); + if (isFunctionParseMode(parseMode)) + scope->setIsFunction(); + if (isModuleParseMode(parseMode)) + scope->setIsModule(); + if (strictMode == JSParserStrictMode::Strict) + scope->setStrictMode(); + + next(); +} + +template <typename LexerType> +Parser<LexerType>::~Parser() +{ +} + +template <typename LexerType> +String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMode parseMode) +{ + String parseError = String(); + + ASTBuilder context(const_cast<VM*>(m_vm), m_parserArena, const_cast<SourceCode*>(m_source)); + ScopeRef scope = currentScope(); + scope->setIsLexicalScope(); + + bool isArrowFunctionBodyExpression = false; + if (m_lexer->isReparsingFunction()) { + ParserFunctionInfo<ASTBuilder> functionInfo; + parseFunctionParameters(context, parseMode, functionInfo); + m_parameters = functionInfo.parameters; + +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + if (parseMode == SourceParseMode::ArrowFunctionMode && !hasError()) { + // The only way we could have an error wile reparsing is if we run out of stack space. + RELEASE_ASSERT(match(ARROWFUNCTION)); + next(); + isArrowFunctionBodyExpression = !match(OPENBRACE); + } +#endif + } + + if (!calleeName.isNull()) + scope->declareCallee(&calleeName); + + if (m_lexer->isReparsingFunction()) + m_statementDepth--; + + SourceElements* sourceElements = nullptr; + // The only way we can error this early is if we reparse a function and we run out of stack space. + if (!hasError()) { + if (isArrowFunctionBodyExpression) + sourceElements = parseArrowFunctionSingleExpressionBodySourceElements(context); +#if ENABLE(ES6_MODULES) + else if (isModuleParseMode(parseMode)) + sourceElements = parseModuleSourceElements(context, parseMode); +#endif + else + sourceElements = parseSourceElements(context, CheckForStrictMode); + } + + bool validEnding; + if (isArrowFunctionBodyExpression) { + ASSERT(m_lexer->isReparsingFunction()); + // When we reparse and stack overflow, we're not guaranteed a valid ending. If we don't run out of stack space, + // then of course this will always be valid because we already parsed for syntax errors. But we must + // be cautious in case we run out of stack space. + validEnding = isEndOfArrowFunction(); + } else + validEnding = consume(EOFTOK); + + if (!sourceElements || !validEnding) { + if (hasError()) + parseError = m_errorMessage; + else + parseError = ASCIILiteral("Parser error"); + } + + IdentifierSet capturedVariables; + bool modifiedParameter = false; + bool modifiedArguments = false; + scope->getCapturedVars(capturedVariables, modifiedParameter, modifiedArguments); + VariableEnvironment& varDeclarations = scope->declaredVariables(); + for (auto& entry : capturedVariables) + varDeclarations.markVariableAsCaptured(entry); + + CodeFeatures features = context.features(); + if (scope->strictMode()) + features |= StrictModeFeature; + if (scope->shadowsArguments()) + features |= ShadowsArgumentsFeature; + if (modifiedParameter) + features |= ModifiedParameterFeature; + if (modifiedArguments) + features |= ModifiedArgumentsFeature; + Vector<RefPtr<UniquedStringImpl>> closedVariables; + if (m_parsingBuiltin) { + IdentifierSet usedVariables; + scope->getUsedVariables(usedVariables); + // FIXME: This needs to be changed if we want to allow builtins to use lexical declarations. + for (const auto& variable : usedVariables) { + Identifier identifier = Identifier::fromUid(m_vm, variable.get()); + if (scope->hasDeclaredVariable(identifier)) + continue; + + if (scope->hasDeclaredParameter(identifier)) + continue; + + if (variable == m_vm->propertyNames->arguments.impl()) + continue; + + closedVariables.append(variable); + } + + if (!capturedVariables.isEmpty()) { + for (const auto& capturedVariable : capturedVariables) { + if (scope->hasDeclaredVariable(capturedVariable)) + continue; + + if (scope->hasDeclaredParameter(capturedVariable)) + continue; + + RELEASE_ASSERT_NOT_REACHED(); + } + } + } + didFinishParsing(sourceElements, context.funcDeclarations(), varDeclarations, features, context.numConstants(), WTF::move(closedVariables)); + + return parseError; +} + +template <typename LexerType> +void Parser<LexerType>::didFinishParsing(SourceElements* sourceElements, DeclarationStacks::FunctionStack& funcStack, + VariableEnvironment& varDeclarations, CodeFeatures features, int numConstants, const Vector<RefPtr<UniquedStringImpl>>&& closedVariables) +{ + m_sourceElements = sourceElements; + m_funcDeclarations.swap(funcStack); + m_varDeclarations.swap(varDeclarations); + m_closedVariables = closedVariables; + m_features = features; + m_numConstants = numConstants; +} + +template <typename LexerType> +bool Parser<LexerType>::allowAutomaticSemicolon() +{ + return match(CLOSEBRACE) || match(EOFTOK) || m_lexer->prevTerminator(); +} + +template <typename LexerType> +template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseSourceElements(TreeBuilder& context, SourceElementsMode mode) +{ + const unsigned lengthOfUseStrictLiteral = 12; // "use strict".length + TreeSourceElements sourceElements = context.createSourceElements(); + bool seenNonDirective = false; + const Identifier* directive = 0; + unsigned directiveLiteralLength = 0; + auto savePoint = createSavePoint(); + bool hasSetStrict = false; + + while (TreeStatement statement = parseStatementListItem(context, directive, &directiveLiteralLength)) { + if (mode == CheckForStrictMode && !seenNonDirective) { + if (directive) { + // "use strict" must be the exact literal without escape sequences or line continuation. + if (!hasSetStrict && directiveLiteralLength == lengthOfUseStrictLiteral && m_vm->propertyNames->useStrictIdentifier == *directive) { + setStrictMode(); + hasSetStrict = true; + if (!isValidStrictMode()) { + if (m_lastFunctionName) { + if (m_vm->propertyNames->arguments == *m_lastFunctionName) + semanticFail("Cannot name a function 'arguments' in strict mode"); + if (m_vm->propertyNames->eval == *m_lastFunctionName) + semanticFail("Cannot name a function 'eval' in strict mode"); + } + if (hasDeclaredVariable(m_vm->propertyNames->arguments)) + semanticFail("Cannot declare a variable named 'arguments' in strict mode"); + if (hasDeclaredVariable(m_vm->propertyNames->eval)) + semanticFail("Cannot declare a variable named 'eval' in strict mode"); + semanticFailIfFalse(isValidStrictMode(), "Invalid parameters or function name in strict mode"); + } + restoreSavePoint(savePoint); + propagateError(); + continue; + } + } else + seenNonDirective = true; + } + context.appendStatement(sourceElements, statement); + } + + propagateError(); + return sourceElements; +} + +template <typename LexerType> +template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseModuleSourceElements(TreeBuilder& context, SourceParseMode parseMode) +{ + TreeSourceElements sourceElements = context.createSourceElements(); + SyntaxChecker syntaxChecker(const_cast<VM*>(m_vm), m_lexer.get()); + + while (true) { + TreeStatement statement = 0; + if (match(IMPORT)) + statement = parseImportDeclaration(context); + else if (match(EXPORT)) + statement = parseExportDeclaration(context); + else { + const Identifier* directive = 0; + unsigned directiveLiteralLength = 0; + if (parseMode == SourceParseMode::ModuleAnalyzeMode) { + if (!parseStatementListItem(syntaxChecker, directive, &directiveLiteralLength)) + break; + continue; + } + statement = parseStatementListItem(context, directive, &directiveLiteralLength); + } + + if (!statement) + break; + context.appendStatement(sourceElements, statement); + } + + propagateError(); + + for (const auto& uid : currentScope()->moduleScopeData().exportedBindings()) { + if (currentScope()->hasDeclaredVariable(uid)) { + currentScope()->declaredVariables().markVariableAsExported(uid); + continue; + } + + if (currentScope()->hasLexicallyDeclaredVariable(uid)) { + currentScope()->lexicalVariables().markVariableAsExported(uid); + continue; + } + + semanticFail("Exported binding '", uid.get(), "' needs to refer to a top-level declared variable"); + } + + return sourceElements; +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementListItem(TreeBuilder& context, const Identifier*& directive, unsigned* directiveLiteralLength) +{ + // The grammar is documented here: + // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-statements + DepthManager statementDepth(&m_statementDepth); + m_statementDepth++; + TreeStatement result = 0; + bool shouldSetEndOffset = true; + switch (m_token.m_type) { + case CONSTTOKEN: + result = parseVariableDeclaration(context, DeclarationType::ConstDeclaration); + break; + case LET: { + bool shouldParseVariableDeclaration = true; + if (!strictMode()) { + SavePoint savePoint = createSavePoint(); + next(); + if (!match(IDENT) && !match(OPENBRACE) && !match(OPENBRACKET)) + shouldParseVariableDeclaration = false; + restoreSavePoint(savePoint); + } + if (shouldParseVariableDeclaration) + result = parseVariableDeclaration(context, DeclarationType::LetDeclaration); + else + result = parseExpressionOrLabelStatement(context); // Treat this as an IDENT. This is how ::parseStatement() handles IDENT. + + break; + } +#if ENABLE(ES6_CLASS_SYNTAX) + case CLASSTOKEN: + result = parseClassDeclaration(context); + break; +#endif + default: + m_statementDepth--; // parseStatement() increments the depth. + result = parseStatement(context, directive, directiveLiteralLength); + shouldSetEndOffset = false; + break; + } + + if (result && shouldSetEndOffset) + context.setEndOffset(result, m_lastTokenEndPosition.offset); + + return result; +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseVariableDeclaration(TreeBuilder& context, DeclarationType declarationType, ExportType exportType) +{ + ASSERT(match(VAR) || match(LET) || match(CONSTTOKEN)); + JSTokenLocation location(tokenLocation()); + int start = tokenLine(); + int end = 0; + int scratch; + TreeDestructuringPattern scratch1 = 0; + TreeExpression scratch2 = 0; + JSTextPosition scratch3; + bool scratchBool; + TreeExpression variableDecls = parseVariableDeclarationList(context, scratch, scratch1, scratch2, scratch3, scratch3, scratch3, VarDeclarationContext, declarationType, exportType, scratchBool); + propagateError(); + failIfFalse(autoSemiColon(), "Expected ';' after variable declaration"); + + return context.createDeclarationStatement(location, variableDecls, start, end); +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseDoWhileStatement(TreeBuilder& context) +{ + ASSERT(match(DO)); + int startLine = tokenLine(); + next(); + const Identifier* unused = 0; + startLoop(); + TreeStatement statement = parseStatement(context, unused); + endLoop(); + failIfFalse(statement, "Expected a statement following 'do'"); + int endLine = tokenLine(); + JSTokenLocation location(tokenLocation()); + handleProductionOrFail(WHILE, "while", "end", "do-while loop"); + handleProductionOrFail(OPENPAREN, "(", "start", "do-while loop condition"); + semanticFailIfTrue(match(CLOSEPAREN), "Must provide an expression as a do-while loop condition"); + TreeExpression expr = parseExpression(context); + failIfFalse(expr, "Unable to parse do-while loop condition"); + handleProductionOrFail(CLOSEPAREN, ")", "end", "do-while loop condition"); + if (match(SEMICOLON)) + next(); // Always performs automatic semicolon insertion. + return context.createDoWhileStatement(location, statement, expr, startLine, endLine); +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseWhileStatement(TreeBuilder& context) +{ + ASSERT(match(WHILE)); + JSTokenLocation location(tokenLocation()); + int startLine = tokenLine(); + next(); + + handleProductionOrFail(OPENPAREN, "(", "start", "while loop condition"); + semanticFailIfTrue(match(CLOSEPAREN), "Must provide an expression as a while loop condition"); + TreeExpression expr = parseExpression(context); + failIfFalse(expr, "Unable to parse while loop condition"); + int endLine = tokenLine(); + handleProductionOrFail(CLOSEPAREN, ")", "end", "while loop condition"); + + const Identifier* unused = 0; + startLoop(); + TreeStatement statement = parseStatement(context, unused); + endLoop(); + failIfFalse(statement, "Expected a statement as the body of a while loop"); + return context.createWhileStatement(location, expr, statement, startLine, endLine); +} + +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVariableDeclarationList(TreeBuilder& context, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext declarationListContext, DeclarationType declarationType, ExportType exportType, bool& forLoopConstDoesNotHaveInitializer) +{ + ASSERT(declarationType == DeclarationType::LetDeclaration || declarationType == DeclarationType::VarDeclaration || declarationType == DeclarationType::ConstDeclaration); + TreeExpression head = 0; + TreeExpression tail = 0; + const Identifier* lastIdent; + JSToken lastIdentToken; + AssignmentContext assignmentContext = assignmentContextFromDeclarationType(declarationType); + do { + lastIdent = 0; + lastPattern = TreeDestructuringPattern(0); + JSTokenLocation location(tokenLocation()); + next(); + TreeExpression node = 0; + declarations++; + bool hasInitializer = false; + if (match(IDENT) || isLETMaskedAsIDENT()) { + failIfTrue(match(LET) && (declarationType == DeclarationType::LetDeclaration || declarationType == DeclarationType::ConstDeclaration), + "Can't use 'let' as an identifier name for a LexicalDeclaration"); + JSTextPosition varStart = tokenStartPosition(); + JSTokenLocation varStartLocation(tokenLocation()); + identStart = varStart; + const Identifier* name = m_token.m_data.ident; + lastIdent = name; + lastIdentToken = m_token; + next(); + hasInitializer = match(EQUAL); + DeclarationResultMask declarationResult = declareVariable(name, declarationType); + if (declarationResult != DeclarationResult::Valid) { + failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare a variable named ", name->impl(), " in strict mode"); + if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) { + if (declarationType == DeclarationType::LetDeclaration) + internalFailWithMessage(false, "Cannot declare a let variable twice: '", name->impl(), "'"); + if (declarationType == DeclarationType::ConstDeclaration) + internalFailWithMessage(false, "Cannot declare a const variable twice: '", name->impl(), "'"); + RELEASE_ASSERT_NOT_REACHED(); + } + } + if (exportType == ExportType::Exported) { + semanticFailIfFalse(exportName(*name), "Cannot export a duplicate name '", name->impl(), "'"); + currentScope()->moduleScopeData().exportBinding(*name); + } + + if (hasInitializer) { + JSTextPosition varDivot = tokenStartPosition() + 1; + initStart = tokenStartPosition(); + next(TreeBuilder::DontBuildStrings); // consume '=' + TreeExpression initializer = parseAssignmentExpression(context); + initEnd = lastTokenEndPosition(); + lastInitializer = initializer; + failIfFalse(initializer, "Expected expression as the intializer for the variable '", name->impl(), "'"); + + node = context.createAssignResolve(location, *name, initializer, varStart, varDivot, lastTokenEndPosition(), assignmentContext); + } else { + if (declarationListContext == ForLoopContext && declarationType == DeclarationType::ConstDeclaration) + forLoopConstDoesNotHaveInitializer = true; + failIfTrue(declarationListContext != ForLoopContext && declarationType == DeclarationType::ConstDeclaration, "const declared variable '", name->impl(), "'", " must have an initializer"); + if (declarationType == DeclarationType::VarDeclaration) + node = context.createEmptyVarExpression(varStartLocation, *name); + else + node = context.createEmptyLetExpression(varStartLocation, *name); + } + } else { + lastIdent = 0; + auto pattern = parseDestructuringPattern(context, destructuringKindFromDeclarationType(declarationType), exportType, nullptr, nullptr, assignmentContext); + failIfFalse(pattern, "Cannot parse this destructuring pattern"); + hasInitializer = match(EQUAL); + failIfTrue(declarationListContext == VarDeclarationContext && !hasInitializer, "Expected an initializer in destructuring variable declaration"); + lastPattern = pattern; + if (hasInitializer) { + next(TreeBuilder::DontBuildStrings); // consume '=' + TreeExpression rhs = parseAssignmentExpression(context); + node = context.createDestructuringAssignment(location, pattern, rhs); + lastInitializer = rhs; + } + } + + if (!head) + head = node; + else if (!tail) { + head = context.createCommaExpr(location, head); + tail = context.appendToCommaExpr(location, head, head, node); + } else + tail = context.appendToCommaExpr(location, head, tail, node); + } while (match(COMMA)); + if (lastIdent) + lastPattern = context.createBindingLocation(lastIdentToken.m_location, *lastIdent, lastIdentToken.m_startPosition, lastIdentToken.m_endPosition, assignmentContext); + + return head; +} + +template <typename LexerType> +template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createBindingPattern(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier& name, int depth, JSToken token, AssignmentContext bindingContext, const Identifier** duplicateIdentifier) +{ + ASSERT(!name.isNull()); + + ASSERT(name.impl()->isAtomic() || name.impl()->isSymbol()); + + if (kind == DestructureToVariables) + failIfTrueIfStrict(declareVariable(&name) & DeclarationResult::InvalidStrictMode, "Cannot declare a variable named '", name.impl(), "' in strict mode"); + else if (kind == DestructureToLet || kind == DestructureToConst) { + DeclarationResultMask declarationResult = declareVariable(&name, kind == DestructureToLet ? DeclarationType::LetDeclaration : DeclarationType::ConstDeclaration); + if (declarationResult != DeclarationResult::Valid) { + failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot destructure to a variable named '", name.impl(), "' in strict mode"); + failIfTrue(declarationResult & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare a lexical variable twice: '", name.impl(), "'"); + } + } else if (kind == DestructureToParameters) { + if (depth) { + auto bindingResult = declareBoundParameter(&name); + if (bindingResult == Scope::StrictBindingFailed && strictMode()) { + semanticFailIfTrue(isEvalOrArguments(&name), "Cannot destructure to a parameter name '", name.impl(), "' in strict mode"); + if (m_lastFunctionName && name == *m_lastFunctionName) + semanticFail("Cannot destructure to '", name.impl(), "' as it shadows the name of a strict mode function"); + semanticFailureDueToKeyword("bound parameter name"); + if (hasDeclaredParameter(name)) + semanticFail("Cannot destructure to '", name.impl(), "' as it has already been declared"); + semanticFail("Cannot bind to a parameter named '", name.impl(), "' in strict mode"); + } + if (bindingResult == Scope::BindingFailed) { + semanticFailureDueToKeyword("bound parameter name"); + if (hasDeclaredParameter(name)) + semanticFail("Cannot destructure to '", name.impl(), "' as it has already been declared"); + semanticFail("Cannot destructure to a parameter named '", name.impl(), "'"); + } + } else { + DeclarationResultMask declarationResult = declareParameter(&name); + if ((declarationResult & DeclarationResult::InvalidStrictMode) && strictMode()) { + semanticFailIfTrue(isEvalOrArguments(&name), "Cannot destructure to a parameter name '", name.impl(), "' in strict mode"); + if (m_lastFunctionName && name == *m_lastFunctionName) + semanticFail("Cannot declare a parameter named '", name.impl(), "' as it shadows the name of a strict mode function"); + semanticFailureDueToKeyword("parameter name"); + if (hasDeclaredParameter(name)) + semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode as it has already been declared"); + semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode"); + } + if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) { + // It's not always an error to define a duplicate parameter. + // It's only an error when there are default parameter values or destructuring parameters. + // We note this value now so we can check it later. + if (duplicateIdentifier) + *duplicateIdentifier = &name; + } + } + } + + if (exportType == ExportType::Exported) { + semanticFailIfFalse(exportName(name), "Cannot export a duplicate name '", name.impl(), "'"); + currentScope()->moduleScopeData().exportBinding(name); + } + return context.createBindingLocation(token.m_location, name, token.m_startPosition, token.m_endPosition, bindingContext); +} + +template <typename LexerType> +template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseArrowFunctionSingleExpressionBodySourceElements(TreeBuilder& context) +{ + ASSERT(!match(OPENBRACE)); + + JSTokenLocation location(tokenLocation()); + JSTextPosition start = tokenStartPosition(); + + failIfStackOverflow(); + TreeExpression expr = parseAssignmentExpression(context); + failIfFalse(expr, "Cannot parse the arrow function expression"); + + context.setEndOffset(expr, m_lastTokenEndPosition.offset); + + failIfFalse(isEndOfArrowFunction(), "Expected a ';', ']', '}', ')', ',', line terminator or EOF following a arrow function statement"); + + JSTextPosition end = tokenEndPosition(); + + if (!m_lexer->prevTerminator()) + setEndOfStatement(); + + TreeSourceElements sourceElements = context.createSourceElements(); + TreeStatement body = context.createReturnStatement(location, expr, start, end); + context.setEndOffset(body, m_lastTokenEndPosition.offset); + context.appendStatement(sourceElements, body); + + return sourceElements; +} + +template <typename LexerType> +template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::tryParseDestructuringPatternExpression(TreeBuilder& context, AssignmentContext bindingContext) +{ + return parseDestructuringPattern(context, DestructureToExpressions, ExportType::NotExported, nullptr, nullptr, bindingContext); +} + +template <typename LexerType> +template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDestructuringPattern(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth) +{ + failIfStackOverflow(); + int nonLHSCount = m_nonLHSCount; + TreeDestructuringPattern pattern; + switch (m_token.m_type) { + case OPENBRACKET: { + JSTextPosition divotStart = tokenStartPosition(); + auto arrayPattern = context.createArrayPattern(m_token.m_location); + next(); + + if (hasDestructuringPattern) + *hasDestructuringPattern = true; + + bool restElementWasFound = false; + + do { + while (match(COMMA)) { + context.appendArrayPatternSkipEntry(arrayPattern, m_token.m_location); + next(); + } + propagateError(); + + if (match(CLOSEBRACKET)) + break; + + if (UNLIKELY(match(DOTDOTDOT))) { + JSTokenLocation location = m_token.m_location; + next(); + auto innerPattern = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); + if (kind == DestructureToExpressions && !innerPattern) + return 0; + failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); + + failIfTrue(kind != DestructureToExpressions && !context.isBindingNode(innerPattern), "Expected identifier for a rest element destructuring pattern"); + + context.appendArrayPatternRestEntry(arrayPattern, location, innerPattern); + restElementWasFound = true; + break; + } + + JSTokenLocation location = m_token.m_location; + auto innerPattern = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); + if (kind == DestructureToExpressions && !innerPattern) + return 0; + failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); + TreeExpression defaultValue = parseDefaultValueForDestructuringPattern(context); + context.appendArrayPatternEntry(arrayPattern, location, innerPattern, defaultValue); + } while (consume(COMMA)); + + if (kind == DestructureToExpressions && !match(CLOSEBRACKET)) + return 0; + consumeOrFail(CLOSEBRACKET, restElementWasFound ? "Expected a closing ']' following a rest element destructuring pattern" : "Expected either a closing ']' or a ',' following an element destructuring pattern"); + context.finishArrayPattern(arrayPattern, divotStart, divotStart, lastTokenEndPosition()); + pattern = arrayPattern; + break; + } + case OPENBRACE: { + auto objectPattern = context.createObjectPattern(m_token.m_location); + next(); + + if (hasDestructuringPattern) + *hasDestructuringPattern = true; + + do { + bool wasString = false; + + if (match(CLOSEBRACE)) + break; + + const Identifier* propertyName = nullptr; + TreeDestructuringPattern innerPattern = 0; + JSTokenLocation location = m_token.m_location; + if (match(IDENT) || isLETMaskedAsIDENT()) { + failIfTrue(match(LET) && (kind == DestructureToLet || kind == DestructureToConst), "Can't use 'let' as an identifier name for a LexicalDeclaration"); + propertyName = m_token.m_data.ident; + JSToken identifierToken = m_token; + next(); + if (consume(COLON)) + innerPattern = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); + else + innerPattern = createBindingPattern(context, kind, exportType, *propertyName, depth + 1, identifierToken, bindingContext, duplicateIdentifier); + } else { + JSTokenType tokenType = m_token.m_type; + switch (m_token.m_type) { + case DOUBLE: + case INTEGER: + propertyName = &m_parserArena.identifierArena().makeNumericIdentifier(const_cast<VM*>(m_vm), m_token.m_data.doubleValue); + break; + case STRING: + propertyName = m_token.m_data.ident; + wasString = true; + break; + default: + if (m_token.m_type != RESERVED && m_token.m_type != RESERVED_IF_STRICT && !(m_token.m_type & KeywordTokenFlag)) { + if (kind == DestructureToExpressions) + return 0; + failWithMessage("Expected a property name"); + } + propertyName = m_token.m_data.ident; + break; + } + next(); + if (!consume(COLON)) { + if (kind == DestructureToExpressions) + return 0; + semanticFailIfTrue(tokenType == RESERVED, "Cannot use abbreviated destructuring syntax for reserved name '", propertyName->impl(), "'"); + semanticFailIfTrue(tokenType == RESERVED_IF_STRICT, "Cannot use abbreviated destructuring syntax for reserved name '", propertyName->impl(), "' in strict mode"); + semanticFailIfTrue(tokenType & KeywordTokenFlag, "Cannot use abbreviated destructuring syntax for keyword '", propertyName->impl(), "'"); + + failWithMessage("Expected a ':' prior to a named destructuring property"); + } + innerPattern = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); + } + if (kind == DestructureToExpressions && !innerPattern) + return 0; + failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); + TreeExpression defaultValue = parseDefaultValueForDestructuringPattern(context); + ASSERT(propertyName); + context.appendObjectPatternEntry(objectPattern, location, wasString, *propertyName, innerPattern, defaultValue); + } while (consume(COMMA)); + + if (kind == DestructureToExpressions && !match(CLOSEBRACE)) + return 0; + consumeOrFail(CLOSEBRACE, "Expected either a closing '}' or an ',' after a property destructuring pattern"); + pattern = objectPattern; + break; + } + + default: { + if (!match(IDENT) && !isLETMaskedAsIDENT()) { + if (kind == DestructureToExpressions) + return 0; + semanticFailureDueToKeyword("variable name"); + failWithMessage("Expected a parameter pattern or a ')' in parameter list"); + } + failIfTrue(match(LET) && (kind == DestructureToLet || kind == DestructureToConst), "Can't use 'let' as an identifier name for a LexicalDeclaration"); + pattern = createBindingPattern(context, kind, exportType, *m_token.m_data.ident, depth, m_token, bindingContext, duplicateIdentifier); + next(); + break; + } + } + m_nonLHSCount = nonLHSCount; + return pattern; +} + +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseDefaultValueForDestructuringPattern(TreeBuilder& context) +{ + if (!match(EQUAL)) + return 0; + + next(TreeBuilder::DontBuildStrings); // consume '=' + return parseAssignmentExpression(context); +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement(TreeBuilder& context) +{ + ASSERT(match(FOR)); + JSTokenLocation location(tokenLocation()); + int startLine = tokenLine(); + next(); + handleProductionOrFail(OPENPAREN, "(", "start", "for-loop header"); + int nonLHSCount = m_nonLHSCount; + int declarations = 0; + JSTextPosition declsStart; + JSTextPosition declsEnd; + TreeExpression decls = 0; + TreeDestructuringPattern pattern = 0; + bool isVarDeclaraton = match(VAR); + bool isLetDeclaration = match(LET); + bool isConstDeclaration = match(CONSTTOKEN); + bool forLoopConstDoesNotHaveInitializer = false; + + VariableEnvironment dummySet; + VariableEnvironment* lexicalVariables = nullptr; + AutoCleanupLexicalScope lexicalScope; + + auto gatherLexicalVariablesIfNecessary = [&] { + if (isLetDeclaration || isConstDeclaration) { + ScopeRef scope = lexicalScope.scope(); + lexicalVariables = &scope->finalizeLexicalEnvironment(); + } else + lexicalVariables = &dummySet; + }; + + auto popLexicalScopeIfNecessary = [&] { + if (isLetDeclaration || isConstDeclaration) + popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); + }; + + if (isVarDeclaraton || isLetDeclaration || isConstDeclaration) { + /* + for (var/let/const IDENT in/of expression) statement + for (var/let/const varDeclarationList; expressionOpt; expressionOpt) + */ + if (isLetDeclaration || isConstDeclaration) { + ScopeRef newScope = pushScope(); + newScope->setIsLexicalScope(); + newScope->preventVarDeclarations(); + lexicalScope.setIsValid(newScope, this); + } + + TreeDestructuringPattern forInTarget = 0; + TreeExpression forInInitializer = 0; + m_allowsIn = false; + JSTextPosition initStart; + JSTextPosition initEnd; + DeclarationType declarationType; + if (isVarDeclaraton) + declarationType = DeclarationType::VarDeclaration; + else if (isLetDeclaration) + declarationType = DeclarationType::LetDeclaration; + else if (isConstDeclaration) + declarationType = DeclarationType::ConstDeclaration; + else + RELEASE_ASSERT_NOT_REACHED(); + decls = parseVariableDeclarationList(context, declarations, forInTarget, forInInitializer, declsStart, initStart, initEnd, ForLoopContext, declarationType, ExportType::NotExported, forLoopConstDoesNotHaveInitializer); + m_allowsIn = true; + propagateError(); + + // Remainder of a standard for loop is handled identically + if (match(SEMICOLON)) + goto standardForLoop; + + failIfFalse(declarations == 1, "can only declare a single variable in an enumeration"); + failIfTrueIfStrict(forInInitializer, "Cannot use initialiser syntax in a strict mode enumeration"); + + if (forInInitializer) + failIfFalse(context.isBindingNode(forInTarget), "Cannot use initialiser syntax when binding to a pattern during enumeration"); + + // Handle for-in with var declaration + JSTextPosition inLocation = tokenStartPosition(); + bool isOfEnumeration = false; + if (!consume(INTOKEN)) { + failIfFalse(match(IDENT) && *m_token.m_data.ident == m_vm->propertyNames->of, "Expected either 'in' or 'of' in enumeration syntax"); + isOfEnumeration = true; + failIfTrue(forInInitializer, "Cannot use initialiser syntax in a for-of enumeration"); + next(); + } + TreeExpression expr = parseExpression(context); + failIfFalse(expr, "Expected expression to enumerate"); + JSTextPosition exprEnd = lastTokenEndPosition(); + + int endLine = tokenLine(); + + handleProductionOrFail(CLOSEPAREN, ")", "end", (isOfEnumeration ? "for-of header" : "for-in header")); + + const Identifier* unused = 0; + startLoop(); + TreeStatement statement = parseStatement(context, unused); + endLoop(); + failIfFalse(statement, "Expected statement as body of for-", isOfEnumeration ? "of" : "in", " statement"); + gatherLexicalVariablesIfNecessary(); + TreeStatement result; + if (isOfEnumeration) + result = context.createForOfLoop(location, forInTarget, expr, statement, declsStart, inLocation, exprEnd, startLine, endLine, *lexicalVariables); + else + result = context.createForInLoop(location, forInTarget, expr, statement, declsStart, inLocation, exprEnd, startLine, endLine, *lexicalVariables); + popLexicalScopeIfNecessary(); + return result; + } + + if (!match(SEMICOLON)) { + if (match(OPENBRACE) || match(OPENBRACKET)) { + SavePoint savePoint = createSavePoint(); + declsStart = tokenStartPosition(); + pattern = tryParseDestructuringPatternExpression(context, AssignmentContext::DeclarationStatement); + declsEnd = lastTokenEndPosition(); + if (pattern && (match(INTOKEN) || (match(IDENT) && *m_token.m_data.ident == m_vm->propertyNames->of))) + goto enumerationLoop; + pattern = TreeDestructuringPattern(0); + restoreSavePoint(savePoint); + } + m_allowsIn = false; + declsStart = tokenStartPosition(); + decls = parseExpression(context); + declsEnd = lastTokenEndPosition(); + m_allowsIn = true; + failIfFalse(decls, "Cannot parse for loop declarations"); + } + + if (match(SEMICOLON)) { + standardForLoop: + // Standard for loop + next(); + TreeExpression condition = 0; + failIfTrue(forLoopConstDoesNotHaveInitializer && isConstDeclaration, "const variables in for loops must have initializers"); + + if (!match(SEMICOLON)) { + condition = parseExpression(context); + failIfFalse(condition, "Cannot parse for loop condition expression"); + } + consumeOrFail(SEMICOLON, "Expected a ';' after the for loop condition expression"); + + TreeExpression increment = 0; + if (!match(CLOSEPAREN)) { + increment = parseExpression(context); + failIfFalse(increment, "Cannot parse for loop iteration expression"); + } + int endLine = tokenLine(); + handleProductionOrFail(CLOSEPAREN, ")", "end", "for-loop header"); + const Identifier* unused = 0; + startLoop(); + TreeStatement statement = parseStatement(context, unused); + endLoop(); + failIfFalse(statement, "Expected a statement as the body of a for loop"); + gatherLexicalVariablesIfNecessary(); + TreeStatement result = context.createForLoop(location, decls, condition, increment, statement, startLine, endLine, *lexicalVariables); + popLexicalScopeIfNecessary(); + return result; + } + + // For-in and For-of loop +enumerationLoop: + failIfFalse(nonLHSCount == m_nonLHSCount, "Expected a reference on the left hand side of an enumeration statement"); + bool isOfEnumeration = false; + if (!consume(INTOKEN)) { + failIfFalse(match(IDENT) && *m_token.m_data.ident == m_vm->propertyNames->of, "Expected either 'in' or 'of' in enumeration syntax"); + isOfEnumeration = true; + next(); + } + TreeExpression expr = parseExpression(context); + failIfFalse(expr, "Cannot parse subject for-", isOfEnumeration ? "of" : "in", " statement"); + JSTextPosition exprEnd = lastTokenEndPosition(); + int endLine = tokenLine(); + + handleProductionOrFail(CLOSEPAREN, ")", "end", (isOfEnumeration ? "for-of header" : "for-in header")); + const Identifier* unused = 0; + startLoop(); + TreeStatement statement = parseStatement(context, unused); + endLoop(); + failIfFalse(statement, "Expected a statement as the body of a for-", isOfEnumeration ? "of" : "in", "loop"); + gatherLexicalVariablesIfNecessary(); + TreeStatement result; + if (pattern) { + ASSERT(!decls); + if (isOfEnumeration) + result = context.createForOfLoop(location, pattern, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables); + else + result = context.createForInLoop(location, pattern, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables); + + popLexicalScopeIfNecessary(); + return result; + } + if (isOfEnumeration) + result = context.createForOfLoop(location, decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables); + else + result = context.createForInLoop(location, decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables); + popLexicalScopeIfNecessary(); + return result; +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseBreakStatement(TreeBuilder& context) +{ + ASSERT(match(BREAK)); + JSTokenLocation location(tokenLocation()); + JSTextPosition start = tokenStartPosition(); + JSTextPosition end = tokenEndPosition(); + next(); + + if (autoSemiColon()) { + semanticFailIfFalse(breakIsValid(), "'break' is only valid inside a switch or loop statement"); + return context.createBreakStatement(location, &m_vm->propertyNames->nullIdentifier, start, end); + } + failIfFalse(match(IDENT) || isLETMaskedAsIDENT(), "Expected an identifier as the target for a break statement"); + const Identifier* ident = m_token.m_data.ident; + semanticFailIfFalse(getLabel(ident), "Cannot use the undeclared label '", ident->impl(), "'"); + end = tokenEndPosition(); + next(); + failIfFalse(autoSemiColon(), "Expected a ';' following a targeted break statement"); + return context.createBreakStatement(location, ident, start, end); +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseContinueStatement(TreeBuilder& context) +{ + ASSERT(match(CONTINUE)); + JSTokenLocation location(tokenLocation()); + JSTextPosition start = tokenStartPosition(); + JSTextPosition end = tokenEndPosition(); + next(); + + if (autoSemiColon()) { + semanticFailIfFalse(continueIsValid(), "'continue' is only valid inside a loop statement"); + return context.createContinueStatement(location, &m_vm->propertyNames->nullIdentifier, start, end); + } + failIfFalse(match(IDENT) || isLETMaskedAsIDENT(), "Expected an identifier as the target for a continue statement"); + const Identifier* ident = m_token.m_data.ident; + ScopeLabelInfo* label = getLabel(ident); + semanticFailIfFalse(label, "Cannot use the undeclared label '", ident->impl(), "'"); + semanticFailIfFalse(label->isLoop, "Cannot continue to the label '", ident->impl(), "' as it is not targeting a loop"); + end = tokenEndPosition(); + next(); + failIfFalse(autoSemiColon(), "Expected a ';' following a targeted continue statement"); + return context.createContinueStatement(location, ident, start, end); +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseReturnStatement(TreeBuilder& context) +{ + ASSERT(match(RETURN)); + JSTokenLocation location(tokenLocation()); + semanticFailIfFalse(currentScope()->isFunction(), "Return statements are only valid inside functions"); + JSTextPosition start = tokenStartPosition(); + JSTextPosition end = tokenEndPosition(); + next(); + // We do the auto semicolon check before attempting to parse expression + // as we need to ensure the a line break after the return correctly terminates + // the statement + if (match(SEMICOLON)) + end = tokenEndPosition(); + + if (autoSemiColon()) + return context.createReturnStatement(location, 0, start, end); + TreeExpression expr = parseExpression(context); + failIfFalse(expr, "Cannot parse the return expression"); + end = lastTokenEndPosition(); + if (match(SEMICOLON)) + end = tokenEndPosition(); + if (!autoSemiColon()) + failWithMessage("Expected a ';' following a return statement"); + return context.createReturnStatement(location, expr, start, end); +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseThrowStatement(TreeBuilder& context) +{ + ASSERT(match(THROW)); + JSTokenLocation location(tokenLocation()); + JSTextPosition start = tokenStartPosition(); + next(); + failIfTrue(match(SEMICOLON), "Expected expression after 'throw'"); + semanticFailIfTrue(autoSemiColon(), "Cannot have a newline after 'throw'"); + + TreeExpression expr = parseExpression(context); + failIfFalse(expr, "Cannot parse expression for throw statement"); + JSTextPosition end = lastTokenEndPosition(); + failIfFalse(autoSemiColon(), "Expected a ';' after a throw statement"); + + return context.createThrowStatement(location, expr, start, end); +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseWithStatement(TreeBuilder& context) +{ + ASSERT(match(WITH)); + JSTokenLocation location(tokenLocation()); + semanticFailIfTrue(strictMode(), "'with' statements are not valid in strict mode"); + currentScope()->setNeedsFullActivation(); + int startLine = tokenLine(); + next(); + + handleProductionOrFail(OPENPAREN, "(", "start", "subject of a 'with' statement"); + int start = tokenStart(); + TreeExpression expr = parseExpression(context); + failIfFalse(expr, "Cannot parse 'with' subject expression"); + JSTextPosition end = lastTokenEndPosition(); + int endLine = tokenLine(); + handleProductionOrFail(CLOSEPAREN, ")", "start", "subject of a 'with' statement"); + const Identifier* unused = 0; + TreeStatement statement = parseStatement(context, unused); + failIfFalse(statement, "A 'with' statement must have a body"); + + return context.createWithStatement(location, expr, statement, start, end, startLine, endLine); +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseSwitchStatement(TreeBuilder& context) +{ + ASSERT(match(SWITCH)); + JSTokenLocation location(tokenLocation()); + int startLine = tokenLine(); + next(); + handleProductionOrFail(OPENPAREN, "(", "start", "subject of a 'switch'"); + TreeExpression expr = parseExpression(context); + failIfFalse(expr, "Cannot parse switch subject expression"); + int endLine = tokenLine(); + + handleProductionOrFail(CLOSEPAREN, ")", "end", "subject of a 'switch'"); + handleProductionOrFail(OPENBRACE, "{", "start", "body of a 'switch'"); + AutoPopScopeRef lexicalScope(this, pushScope()); + lexicalScope->setIsLexicalScope(); + lexicalScope->preventVarDeclarations(); + startSwitch(); + TreeClauseList firstClauses = parseSwitchClauses(context); + propagateError(); + + TreeClause defaultClause = parseSwitchDefaultClause(context); + propagateError(); + + TreeClauseList secondClauses = parseSwitchClauses(context); + propagateError(); + endSwitch(); + handleProductionOrFail(CLOSEBRACE, "}", "end", "body of a 'switch'"); + + TreeStatement result = context.createSwitchStatement(location, expr, firstClauses, defaultClause, secondClauses, startLine, endLine, lexicalScope->finalizeLexicalEnvironment()); + popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); + return result; +} + +template <typename LexerType> +template <class TreeBuilder> TreeClauseList Parser<LexerType>::parseSwitchClauses(TreeBuilder& context) +{ + if (!match(CASE)) + return 0; + unsigned startOffset = tokenStart(); + next(); + TreeExpression condition = parseExpression(context); + failIfFalse(condition, "Cannot parse switch clause"); + consumeOrFail(COLON, "Expected a ':' after switch clause expression"); + TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode); + failIfFalse(statements, "Cannot parse the body of a switch clause"); + TreeClause clause = context.createClause(condition, statements); + context.setStartOffset(clause, startOffset); + TreeClauseList clauseList = context.createClauseList(clause); + TreeClauseList tail = clauseList; + + while (match(CASE)) { + startOffset = tokenStart(); + next(); + TreeExpression condition = parseExpression(context); + failIfFalse(condition, "Cannot parse switch case expression"); + consumeOrFail(COLON, "Expected a ':' after switch clause expression"); + TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode); + failIfFalse(statements, "Cannot parse the body of a switch clause"); + clause = context.createClause(condition, statements); + context.setStartOffset(clause, startOffset); + tail = context.createClauseList(tail, clause); + } + return clauseList; +} + +template <typename LexerType> +template <class TreeBuilder> TreeClause Parser<LexerType>::parseSwitchDefaultClause(TreeBuilder& context) +{ + if (!match(DEFAULT)) + return 0; + unsigned startOffset = tokenStart(); + next(); + consumeOrFail(COLON, "Expected a ':' after switch default clause"); + TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode); + failIfFalse(statements, "Cannot parse the body of a switch default clause"); + TreeClause result = context.createClause(0, statements); + context.setStartOffset(result, startOffset); + return result; +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseTryStatement(TreeBuilder& context) +{ + ASSERT(match(TRY)); + JSTokenLocation location(tokenLocation()); + TreeStatement tryBlock = 0; + const Identifier* ident = &m_vm->propertyNames->nullIdentifier; + TreeStatement catchBlock = 0; + TreeStatement finallyBlock = 0; + int firstLine = tokenLine(); + next(); + matchOrFail(OPENBRACE, "Expected a block statement as body of a try statement"); + + tryBlock = parseBlockStatement(context); + failIfFalse(tryBlock, "Cannot parse the body of try block"); + int lastLine = m_lastTokenEndPosition.line; + VariableEnvironment catchEnvironment; + if (match(CATCH)) { + next(); + + handleProductionOrFail(OPENPAREN, "(", "start", "'catch' target"); + if (!(match(IDENT) || isLETMaskedAsIDENT())) { + semanticFailureDueToKeyword("catch variable name"); + failWithMessage("Expected identifier name as catch target"); + } + ident = m_token.m_data.ident; + next(); + AutoPopScopeRef catchScope(this, pushScope()); + catchScope->setIsLexicalScope(); + catchScope->preventVarDeclarations(); + failIfTrueIfStrict(catchScope->declareLexicalVariable(ident, false) & DeclarationResult::InvalidStrictMode, "Cannot declare a catch variable named '", ident->impl(), "' in strict mode"); + handleProductionOrFail(CLOSEPAREN, ")", "end", "'catch' target"); + matchOrFail(OPENBRACE, "Expected exception handler to be a block statement"); + catchBlock = parseBlockStatement(context); + failIfFalse(catchBlock, "Unable to parse 'catch' block"); + catchEnvironment = catchScope->finalizeLexicalEnvironment(); + RELEASE_ASSERT(catchEnvironment.size() == 1 && catchEnvironment.contains(ident->impl())); + popScope(catchScope, TreeBuilder::NeedsFreeVariableInfo); + } + + if (match(FINALLY)) { + next(); + matchOrFail(OPENBRACE, "Expected block statement for finally body"); + finallyBlock = parseBlockStatement(context); + failIfFalse(finallyBlock, "Cannot parse finally body"); + } + failIfFalse(catchBlock || finallyBlock, "Try statements must have at least a catch or finally block"); + return context.createTryStatement(location, tryBlock, ident, catchBlock, finallyBlock, firstLine, lastLine, catchEnvironment); +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseDebuggerStatement(TreeBuilder& context) +{ + ASSERT(match(DEBUGGER)); + JSTokenLocation location(tokenLocation()); + int startLine = tokenLine(); + int endLine = startLine; + next(); + if (match(SEMICOLON)) + startLine = tokenLine(); + failIfFalse(autoSemiColon(), "Debugger keyword must be followed by a ';'"); + return context.createDebugger(location, startLine, endLine); +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseBlockStatement(TreeBuilder& context) +{ + ASSERT(match(OPENBRACE)); + + // We should treat the first block statement of the function (the body of the function) as the lexical + // scope of the function itself, and not the lexical scope of a 'block' statement within the function. + AutoCleanupLexicalScope lexicalScope; + bool shouldPushLexicalScope = m_statementDepth > 0; + if (shouldPushLexicalScope) { + ScopeRef newScope = pushScope(); + newScope->setIsLexicalScope(); + newScope->preventVarDeclarations(); + lexicalScope.setIsValid(newScope, this); + } + JSTokenLocation location(tokenLocation()); + int startOffset = m_token.m_data.offset; + int start = tokenLine(); + VariableEnvironment emptyEnvironment; + next(); + if (match(CLOSEBRACE)) { + int endOffset = m_token.m_data.offset; + next(); + TreeStatement result = context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line, shouldPushLexicalScope ? currentScope()->finalizeLexicalEnvironment() : emptyEnvironment); + context.setStartOffset(result, startOffset); + context.setEndOffset(result, endOffset); + if (shouldPushLexicalScope) + popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); + return result; + } + TreeSourceElements subtree = parseSourceElements(context, DontCheckForStrictMode); + failIfFalse(subtree, "Cannot parse the body of the block statement"); + matchOrFail(CLOSEBRACE, "Expected a closing '}' at the end of a block statement"); + int endOffset = m_token.m_data.offset; + next(); + TreeStatement result = context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line, shouldPushLexicalScope ? currentScope()->finalizeLexicalEnvironment() : emptyEnvironment); + context.setStartOffset(result, startOffset); + context.setEndOffset(result, endOffset); + if (shouldPushLexicalScope) + popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); + + return result; +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(TreeBuilder& context, const Identifier*& directive, unsigned* directiveLiteralLength) +{ + DepthManager statementDepth(&m_statementDepth); + m_statementDepth++; + directive = 0; + int nonTrivialExpressionCount = 0; + failIfStackOverflow(); + TreeStatement result = 0; + bool shouldSetEndOffset = true; + + switch (m_token.m_type) { + case OPENBRACE: + result = parseBlockStatement(context); + shouldSetEndOffset = false; + break; + case VAR: + result = parseVariableDeclaration(context, DeclarationType::VarDeclaration); + break; + case FUNCTION: + failIfFalseIfStrict(m_statementDepth == 1, "Strict mode does not allow function declarations in a lexically nested statement"); + result = parseFunctionDeclaration(context); + break; + case SEMICOLON: { + JSTokenLocation location(tokenLocation()); + next(); + result = context.createEmptyStatement(location); + break; + } + case IF: + result = parseIfStatement(context); + break; + case DO: + result = parseDoWhileStatement(context); + break; + case WHILE: + result = parseWhileStatement(context); + break; + case FOR: + result = parseForStatement(context); + break; + case CONTINUE: + result = parseContinueStatement(context); + break; + case BREAK: + result = parseBreakStatement(context); + break; + case RETURN: + result = parseReturnStatement(context); + break; + case WITH: + result = parseWithStatement(context); + break; + case SWITCH: + result = parseSwitchStatement(context); + break; + case THROW: + result = parseThrowStatement(context); + break; + case TRY: + result = parseTryStatement(context); + break; + case DEBUGGER: + result = parseDebuggerStatement(context); + break; + case EOFTOK: + case CASE: + case CLOSEBRACE: + case DEFAULT: + // These tokens imply the end of a set of source elements + return 0; + case IDENT: + result = parseExpressionOrLabelStatement(context); + break; + case STRING: + directive = m_token.m_data.ident; + if (directiveLiteralLength) + *directiveLiteralLength = m_token.m_location.endOffset - m_token.m_location.startOffset; + nonTrivialExpressionCount = m_nonTrivialExpressionCount; + FALLTHROUGH; + default: + TreeStatement exprStatement = parseExpressionStatement(context); + if (directive && nonTrivialExpressionCount != m_nonTrivialExpressionCount) + directive = 0; + result = exprStatement; + break; + } + + if (result && shouldSetEndOffset) + context.setEndOffset(result, m_lastTokenEndPosition.offset); + return result; +} + +template <typename LexerType> +template <class TreeBuilder> bool Parser<LexerType>::parseFormalParameters(TreeBuilder& context, TreeFormalParameterList list, unsigned& parameterCount) +{ +#define failFromDuplicate() \ + if (duplicateParameter) {\ + semanticFailIfTrue(defaultValue, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in function with default parameter values");\ + semanticFailIfTrue(hasDestructuringPattern, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in function with destructuring parameters");\ + } + + const Identifier* duplicateParameter = nullptr; + bool hasDestructuringPattern = false; + auto parameter = parseDestructuringPattern(context, DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern); + failIfFalse(parameter, "Cannot parse parameter pattern"); + auto defaultValue = parseDefaultValueForDestructuringPattern(context); + propagateError(); + failFromDuplicate(); + context.appendParameter(list, parameter, defaultValue); + parameterCount++; + while (consume(COMMA)) { + parameter = parseDestructuringPattern(context, DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern); + failIfFalse(parameter, "Cannot parse parameter pattern"); + defaultValue = parseDefaultValueForDestructuringPattern(context); + propagateError(); + failFromDuplicate(); + context.appendParameter(list, parameter, defaultValue); + parameterCount++; + } + return true; +#undef failFromDuplicate +} + +template <typename LexerType> +template <class TreeBuilder> TreeFunctionBody Parser<LexerType>::parseFunctionBody( + TreeBuilder& context, const JSTokenLocation& startLocation, int startColumn, int functionKeywordStart, int functionNameStart, int parametersStart, + ConstructorKind constructorKind, FunctionBodyType bodyType, unsigned parameterCount, SourceParseMode parseMode) +{ + if (bodyType == StandardFunctionBodyBlock || bodyType == ArrowFunctionBodyBlock) { + next(); + if (match(CLOSEBRACE)) { + unsigned endColumn = tokenColumn(); + return context.createFunctionMetadata(startLocation, tokenLocation(), startColumn, endColumn, functionKeywordStart, functionNameStart, parametersStart, strictMode(), constructorKind, parameterCount, parseMode); + } + } + + DepthManager statementDepth(&m_statementDepth); + m_statementDepth = 0; + SyntaxChecker syntaxChecker(const_cast<VM*>(m_vm), m_lexer.get()); + if (bodyType == ArrowFunctionBodyExpression) + failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(syntaxChecker), "Cannot parse body of this arrow function"); + else + failIfFalse(parseSourceElements(syntaxChecker, CheckForStrictMode), bodyType == StandardFunctionBodyBlock ? "Cannot parse body of this function" : "Cannot parse body of this arrow function"); + unsigned endColumn = tokenColumn(); + return context.createFunctionMetadata(startLocation, tokenLocation(), startColumn, endColumn, functionKeywordStart, functionNameStart, parametersStart, strictMode(), constructorKind, parameterCount, parseMode); +} + +static const char* stringForFunctionMode(SourceParseMode mode) +{ + switch (mode) { + case SourceParseMode::GetterMode: + return "getter"; + case SourceParseMode::SetterMode: + return "setter"; + case SourceParseMode::NormalFunctionMode: + return "function"; + case SourceParseMode::MethodMode: + return "method"; + case SourceParseMode::ArrowFunctionMode: + return "arrow function"; + case SourceParseMode::ProgramMode: + case SourceParseMode::ModuleAnalyzeMode: + case SourceParseMode::ModuleEvaluateMode: + RELEASE_ASSERT_NOT_REACHED(); + return ""; + } + RELEASE_ASSERT_NOT_REACHED(); + return nullptr; +} + +template <typename LexerType> template <class TreeBuilder> int Parser<LexerType>::parseFunctionParameters(TreeBuilder& context, SourceParseMode mode, ParserFunctionInfo<TreeBuilder>& functionInfo) +{ + RELEASE_ASSERT(mode != SourceParseMode::ProgramMode && mode != SourceParseMode::ModuleAnalyzeMode && mode != SourceParseMode::ModuleEvaluateMode); + int parametersStart = m_token.m_location.startOffset; + TreeFormalParameterList parameterList = context.createFormalParameterList(); + functionInfo.parameters = parameterList; + functionInfo.startOffset = parametersStart; + + if (mode == SourceParseMode::ArrowFunctionMode) { + if (!match(IDENT) && !match(OPENPAREN)) { + semanticFailureDueToKeyword(stringForFunctionMode(mode), " name"); + failWithMessage("Expected an arrow function input parameter"); + } else { + if (match(OPENPAREN)) { + next(); + + if (match(CLOSEPAREN)) + functionInfo.parameterCount = 0; + else + failIfFalse(parseFormalParameters(context, parameterList, functionInfo.parameterCount), "Cannot parse parameters for this ", stringForFunctionMode(mode)); + + consumeOrFail(CLOSEPAREN, "Expected a ')' or a ',' after a parameter declaration"); + } else { + functionInfo.parameterCount = 1; + auto parameter = parseDestructuringPattern(context, DestructureToParameters, ExportType::NotExported); + failIfFalse(parameter, "Cannot parse parameter pattern"); + context.appendParameter(parameterList, parameter, 0); + } + } + + return parametersStart; + } + + if (!consume(OPENPAREN)) { + semanticFailureDueToKeyword(stringForFunctionMode(mode), " name"); + failWithMessage("Expected an opening '(' before a ", stringForFunctionMode(mode), "'s parameter list"); + } + + if (mode == SourceParseMode::GetterMode) { + consumeOrFail(CLOSEPAREN, "getter functions must have no parameters"); + functionInfo.parameterCount = 0; + } else if (mode == SourceParseMode::SetterMode) { + failIfTrue(match(CLOSEPAREN), "setter functions must have one parameter"); + const Identifier* duplicateParameter = nullptr; + auto parameter = parseDestructuringPattern(context, DestructureToParameters, ExportType::NotExported, &duplicateParameter); + failIfFalse(parameter, "setter functions must have one parameter"); + auto defaultValue = parseDefaultValueForDestructuringPattern(context); + propagateError(); + semanticFailIfTrue(duplicateParameter && defaultValue, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in function with default parameter values"); + context.appendParameter(parameterList, parameter, defaultValue); + functionInfo.parameterCount = 1; + failIfTrue(match(COMMA), "setter functions must have one parameter"); + consumeOrFail(CLOSEPAREN, "Expected a ')' after a parameter declaration"); + } else { + if (match(CLOSEPAREN)) + functionInfo.parameterCount = 0; + else + failIfFalse(parseFormalParameters(context, parameterList, functionInfo.parameterCount), "Cannot parse parameters for this ", stringForFunctionMode(mode)); + consumeOrFail(CLOSEPAREN, "Expected a ')' or a ',' after a parameter declaration"); + } + + return parametersStart; +} + +template <typename LexerType> +template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuilder& context, FunctionRequirements requirements, SourceParseMode mode, bool nameIsInContainingScope, ConstructorKind constructorKind, SuperBinding expectedSuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>& functionInfo, FunctionParseType parseType) +{ + RELEASE_ASSERT(isFunctionParseMode(mode)); + + AutoPopScopeRef functionScope(this, pushScope()); + functionScope->setIsFunction(); + int functionNameStart = m_token.m_location.startOffset; + const Identifier* lastFunctionName = m_lastFunctionName; + m_lastFunctionName = nullptr; + int parametersStart; + JSTokenLocation startLocation; + int startColumn; + FunctionBodyType functionBodyType; + + switch (parseType) { + case StandardFunctionParseType: { + RELEASE_ASSERT(mode != SourceParseMode::ArrowFunctionMode); + if (match(IDENT) || isLETMaskedAsIDENT()) { + functionInfo.name = m_token.m_data.ident; + m_lastFunctionName = functionInfo.name; + next(); + if (!nameIsInContainingScope) + failIfTrueIfStrict(functionScope->declareVariable(functionInfo.name) & DeclarationResult::InvalidStrictMode, "'", functionInfo.name->impl(), "' is not a valid ", stringForFunctionMode(mode), " name in strict mode"); + } else if (requirements == FunctionNeedsName) { + if (match(OPENPAREN) && mode == SourceParseMode::NormalFunctionMode) + semanticFail("Function statements must have a name"); + semanticFailureDueToKeyword(stringForFunctionMode(mode), " name"); + failDueToUnexpectedToken(); + return false; + } + + startLocation = tokenLocation(); + functionInfo.startLine = tokenLine(); + startColumn = tokenColumn(); + + parametersStart = parseFunctionParameters(context, mode, functionInfo); + propagateError(); + + matchOrFail(OPENBRACE, "Expected an opening '{' at the start of a ", stringForFunctionMode(mode), " body"); + + // BytecodeGenerator emits code to throw TypeError when a class constructor is "call"ed. + // Set ConstructorKind to None for non-constructor methods of classes. + + if (m_defaultConstructorKind != ConstructorKind::None) { + constructorKind = m_defaultConstructorKind; + expectedSuperBinding = m_defaultConstructorKind == ConstructorKind::Derived ? SuperBinding::Needed : SuperBinding::NotNeeded; + } + + functionBodyType = StandardFunctionBodyBlock; + + break; + } +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + case ArrowFunctionParseType: { + RELEASE_ASSERT(mode == SourceParseMode::ArrowFunctionMode); + + startLocation = tokenLocation(); + functionInfo.startLine = tokenLine(); + startColumn = tokenColumn(); + + parametersStart = parseFunctionParameters(context, mode, functionInfo); + propagateError(); + + matchOrFail(ARROWFUNCTION, "Expected a '=>' after arrow function parameter declaration"); + + if (m_lexer->prevTerminator()) + failDueToUnexpectedToken(); + + ASSERT(constructorKind == ConstructorKind::None); + + // Check if arrow body start with {. If it true it mean that arrow function is Fat arrow function + // and we need use common approach to parse function body + next(); + functionBodyType = match(OPENBRACE) ? ArrowFunctionBodyBlock : ArrowFunctionBodyExpression; + + break; + } +#else + default: + RELEASE_ASSERT_NOT_REACHED(); +#endif + } + + bool isClassConstructor = constructorKind != ConstructorKind::None; + + functionInfo.bodyStartColumn = startColumn; + + // If we know about this function already, we can use the cached info and skip the parser to the end of the function. + if (const SourceProviderCacheItem* cachedInfo = TreeBuilder::CanUseFunctionCache ? findCachedFunctionInfo(functionInfo.startOffset) : 0) { + // If we're in a strict context, the cached function info must say it was strict too. + ASSERT(!strictMode() || cachedInfo->strictMode); + JSTokenLocation endLocation; + + endLocation.line = cachedInfo->lastTockenLine; + endLocation.startOffset = cachedInfo->lastTockenStartOffset; + endLocation.lineStartOffset = cachedInfo->lastTockenLineStartOffset; + + bool endColumnIsOnStartLine = (endLocation.line == functionInfo.startLine); + ASSERT(endLocation.startOffset >= endLocation.lineStartOffset); + unsigned bodyEndColumn = endColumnIsOnStartLine ? + endLocation.startOffset - m_token.m_data.lineStartOffset : + endLocation.startOffset - endLocation.lineStartOffset; + unsigned currentLineStartOffset = m_token.m_location.lineStartOffset; + + functionInfo.body = context.createFunctionMetadata( + startLocation, endLocation, functionInfo.bodyStartColumn, bodyEndColumn, + functionKeywordStart, functionNameStart, parametersStart, + cachedInfo->strictMode, constructorKind, cachedInfo->parameterCount, mode); + + functionScope->restoreFromSourceProviderCache(cachedInfo); + popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo); + + m_token = cachedInfo->endFunctionToken(); + + if (endColumnIsOnStartLine) + m_token.m_location.lineStartOffset = currentLineStartOffset; + + m_lexer->setOffset(m_token.m_location.endOffset, m_token.m_location.lineStartOffset); + m_lexer->setLineNumber(m_token.m_location.line); + functionInfo.endOffset = cachedInfo->endFunctionOffset; + + if (parseType == ArrowFunctionParseType) + functionBodyType = cachedInfo->isBodyArrowExpression ? ArrowFunctionBodyExpression : ArrowFunctionBodyBlock; + else + functionBodyType = StandardFunctionBodyBlock; + + switch (functionBodyType) { + case ArrowFunctionBodyExpression: + next(); + context.setEndOffset(functionInfo.body, m_lexer->currentOffset()); + break; + case ArrowFunctionBodyBlock: + case StandardFunctionBodyBlock: + context.setEndOffset(functionInfo.body, m_lexer->currentOffset()); + next(); + break; + } + functionInfo.endLine = m_lastTokenEndPosition.line; + return true; + } + + m_lastFunctionName = lastFunctionName; + ParserState oldState = saveState(); + + functionInfo.body = parseFunctionBody(context, startLocation, startColumn, functionKeywordStart, functionNameStart, parametersStart, constructorKind, functionBodyType, functionInfo.parameterCount, mode); + + restoreState(oldState); + failIfFalse(functionInfo.body, "Cannot parse the body of this ", stringForFunctionMode(mode)); + context.setEndOffset(functionInfo.body, m_lexer->currentOffset()); + if (functionScope->strictMode() && functionInfo.name) { + RELEASE_ASSERT(mode == SourceParseMode::NormalFunctionMode || mode == SourceParseMode::MethodMode || mode == SourceParseMode::ArrowFunctionMode); + semanticFailIfTrue(m_vm->propertyNames->arguments == *functionInfo.name, "'", functionInfo.name->impl(), "' is not a valid function name in strict mode"); + semanticFailIfTrue(m_vm->propertyNames->eval == *functionInfo.name, "'", functionInfo.name->impl(), "' is not a valid function name in strict mode"); + } + if (functionScope->hasDirectSuper()) { + semanticFailIfTrue(!isClassConstructor, "Cannot call super() outside of a class constructor"); + semanticFailIfTrue(constructorKind != ConstructorKind::Derived, "Cannot call super() in a base class constructor"); + } + if (functionScope->needsSuperBinding()) + semanticFailIfTrue(expectedSuperBinding == SuperBinding::NotNeeded, "super can only be used in a method of a derived class"); + + JSTokenLocation location = JSTokenLocation(m_token.m_location); + functionInfo.endOffset = m_token.m_data.offset; + + if (functionBodyType == ArrowFunctionBodyExpression) { + location = locationBeforeLastToken(); + functionInfo.endOffset = location.endOffset; + } + + // Cache the tokenizer state and the function scope the first time the function is parsed. + // Any future reparsing can then skip the function. + static const int minimumFunctionLengthToCache = 16; + std::unique_ptr<SourceProviderCacheItem> newInfo; + int functionLength = functionInfo.endOffset - functionInfo.startOffset; + if (TreeBuilder::CanUseFunctionCache && m_functionCache && functionLength > minimumFunctionLengthToCache) { + SourceProviderCacheItemCreationParameters parameters; + parameters.endFunctionOffset = functionInfo.endOffset; + parameters.functionNameStart = functionNameStart; + parameters.lastTockenLine = location.line; + parameters.lastTockenStartOffset = location.startOffset; + parameters.lastTockenEndOffset = location.endOffset; + parameters.lastTockenLineStartOffset = location.lineStartOffset; + parameters.parameterCount = functionInfo.parameterCount; + if (functionBodyType == ArrowFunctionBodyExpression) { + parameters.isBodyArrowExpression = true; + parameters.tokenType = m_token.m_type; + } + functionScope->fillParametersForSourceProviderCache(parameters); + newInfo = SourceProviderCacheItem::create(parameters); + } + + popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo); + + if (functionBodyType == ArrowFunctionBodyExpression) + failIfFalse(isEndOfArrowFunction(), "Expected the closing ';' ',' ']' ')' '}', line terminator or EOF after arrow function"); + else { + matchOrFail(CLOSEBRACE, "Expected a closing '}' after a ", stringForFunctionMode(mode), " body"); + next(); + } + + if (newInfo) + m_functionCache->add(functionInfo.startOffset, WTF::move(newInfo)); + + functionInfo.endLine = m_lastTokenEndPosition.line; + return true; +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context, ExportType exportType) +{ + ASSERT(match(FUNCTION)); + JSTokenLocation location(tokenLocation()); + unsigned functionKeywordStart = tokenStart(); + next(); + ParserFunctionInfo<TreeBuilder> functionInfo; + failIfFalse((parseFunctionInfo(context, FunctionNeedsName, SourceParseMode::NormalFunctionMode, true, ConstructorKind::None, SuperBinding::NotNeeded, + functionKeywordStart, functionInfo, StandardFunctionParseType)), "Cannot parse this function"); + failIfFalse(functionInfo.name, "Function statements must have a name"); + failIfTrueIfStrict(declareVariable(functionInfo.name) & DeclarationResult::InvalidStrictMode, "Cannot declare a function named '", functionInfo.name->impl(), "' in strict mode"); + if (exportType == ExportType::Exported) { + semanticFailIfFalse(exportName(*functionInfo.name), "Cannot export a duplicate function name: '", functionInfo.name->impl(), "'"); + currentScope()->moduleScopeData().exportBinding(*functionInfo.name); + } + return context.createFuncDeclStatement(location, functionInfo); +} + +#if ENABLE(ES6_CLASS_SYNTAX) +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseClassDeclaration(TreeBuilder& context, ExportType exportType) +{ + ASSERT(match(CLASSTOKEN)); + JSTokenLocation location(tokenLocation()); + JSTextPosition classStart = tokenStartPosition(); + unsigned classStartLine = tokenLine(); + + ParserClassInfo<TreeBuilder> info; + TreeClassExpression classExpr = parseClass(context, FunctionNeedsName, info); + failIfFalse(classExpr, "Failed to parse class"); + + DeclarationResultMask declarationResult = declareVariable(info.className, DeclarationType::LetDeclaration); + if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) + internalFailWithMessage(false, "Cannot declare a class twice: '", info.className->impl(), "'"); + if (exportType == ExportType::Exported) { + semanticFailIfFalse(exportName(*info.className), "Cannot export a duplicate class name: '", info.className->impl(), "'"); + currentScope()->moduleScopeData().exportBinding(*info.className); + } + + JSTextPosition classEnd = lastTokenEndPosition(); + unsigned classEndLine = tokenLine(); + + return context.createClassDeclStatement(location, classExpr, classStart, classEnd, classStartLine, classEndLine); +} + +template <typename LexerType> +template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(TreeBuilder& context, FunctionRequirements requirements, ParserClassInfo<TreeBuilder>& info) +{ + ASSERT(match(CLASSTOKEN)); + JSTokenLocation location(tokenLocation()); + next(); + + AutoPopScopeRef classScope(this, pushScope()); + classScope->setStrictMode(); + + const Identifier* className = nullptr; + if (match(IDENT)) { + className = m_token.m_data.ident; + info.className = className; + next(); + failIfTrue(classScope->declareVariable(className) & DeclarationResult::InvalidStrictMode, "'", className->impl(), "' is not a valid class name"); + } else if (requirements == FunctionNeedsName) { + if (match(OPENBRACE)) + semanticFail("Class statements must have a name"); + semanticFailureDueToKeyword("class name"); + failDueToUnexpectedToken(); + } else + className = &m_vm->propertyNames->nullIdentifier; + ASSERT(className); + + TreeExpression parentClass = 0; + if (consume(EXTENDS)) { + parentClass = parseMemberExpression(context); + failIfFalse(parentClass, "Cannot parse the parent class name"); + } + const ConstructorKind constructorKind = parentClass ? ConstructorKind::Derived : ConstructorKind::Base; + + consumeOrFail(OPENBRACE, "Expected opening '{' at the start of a class body"); + + TreeExpression constructor = 0; + TreePropertyList staticMethods = 0; + TreePropertyList instanceMethods = 0; + TreePropertyList instanceMethodsTail = 0; + TreePropertyList staticMethodsTail = 0; + while (!match(CLOSEBRACE)) { + if (match(SEMICOLON)) { + next(); + continue; + } + + JSTokenLocation methodLocation(tokenLocation()); + unsigned methodStart = tokenStart(); + + // For backwards compatibility, "static" is a non-reserved keyword in non-strict mode. + bool isStaticMethod = match(RESERVED_IF_STRICT) && *m_token.m_data.ident == m_vm->propertyNames->staticKeyword; + if (isStaticMethod) + next(); + + // FIXME: Figure out a way to share more code with parseProperty. + const CommonIdentifiers& propertyNames = *m_vm->propertyNames; + const Identifier* ident = nullptr; + bool isGetter = false; + bool isSetter = false; + switch (m_token.m_type) { + case STRING: + ident = m_token.m_data.ident; + ASSERT(ident); + next(); + break; + case IDENT: + ident = m_token.m_data.ident; + ASSERT(ident); + next(); + if (match(IDENT) || match(STRING) || match(DOUBLE) || match(INTEGER)) { + isGetter = *ident == propertyNames.get; + isSetter = *ident == propertyNames.set; + } + break; + case DOUBLE: + case INTEGER: + ident = &m_parserArena.identifierArena().makeNumericIdentifier(const_cast<VM*>(m_vm), m_token.m_data.doubleValue); + ASSERT(ident); + next(); + break; + default: + failDueToUnexpectedToken(); + } + + TreeProperty property; + const bool alwaysStrictInsideClass = true; + if (isGetter || isSetter) { + property = parseGetterSetter(context, alwaysStrictInsideClass, isGetter ? PropertyNode::Getter : PropertyNode::Setter, methodStart, + ConstructorKind::None, SuperBinding::Needed); + failIfFalse(property, "Cannot parse this method"); + } else { + ParserFunctionInfo<TreeBuilder> methodInfo; + bool isConstructor = !isStaticMethod && *ident == propertyNames.constructor; + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SourceParseMode::MethodMode, false, isConstructor ? constructorKind : ConstructorKind::None, SuperBinding::Needed, methodStart, methodInfo, StandardFunctionParseType)), "Cannot parse this method"); + failIfTrue(!ident || (declareVariable(ident) & DeclarationResult::InvalidStrictMode), "Cannot declare a method named '", methodInfo.name->impl(), "'"); + methodInfo.name = isConstructor ? className : ident; + + TreeExpression method = context.createFunctionExpr(methodLocation, methodInfo); + if (isConstructor) { + semanticFailIfTrue(constructor, "Cannot declare multiple constructors in a single class"); + constructor = method; + continue; + } + + // FIXME: Syntax error when super() is called + semanticFailIfTrue(isStaticMethod && methodInfo.name && *methodInfo.name == propertyNames.prototype, + "Cannot declare a static method named 'prototype'"); + property = context.createProperty(methodInfo.name, method, PropertyNode::Constant, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed); + } + + TreePropertyList& tail = isStaticMethod ? staticMethodsTail : instanceMethodsTail; + if (tail) + tail = context.createPropertyList(methodLocation, property, tail); + else { + tail = context.createPropertyList(methodLocation, property); + if (isStaticMethod) + staticMethods = tail; + else + instanceMethods = tail; + } + } + + popScope(classScope, TreeBuilder::NeedsFreeVariableInfo); + consumeOrFail(CLOSEBRACE, "Expected a closing '}' after a class body"); + + return context.createClassExpr(location, *className, constructor, parentClass, instanceMethods, staticMethods); +} +#endif + +struct LabelInfo { + LabelInfo(const Identifier* ident, const JSTextPosition& start, const JSTextPosition& end) + : m_ident(ident) + , m_start(start) + , m_end(end) + { + } + + const Identifier* m_ident; + JSTextPosition m_start; + JSTextPosition m_end; +}; + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrLabelStatement(TreeBuilder& context) +{ + + /* Expression and Label statements are ambiguous at LL(1), so we have a + * special case that looks for a colon as the next character in the input. + */ + Vector<LabelInfo> labels; + JSTokenLocation location; + do { + JSTextPosition start = tokenStartPosition(); + location = tokenLocation(); + if (!nextTokenIsColon()) { + // If we hit this path we're making a expression statement, which + // by definition can't make use of continue/break so we can just + // ignore any labels we might have accumulated. + TreeExpression expression = parseExpression(context); + failIfFalse(expression, "Cannot parse expression statement"); + if (!autoSemiColon()) + failDueToUnexpectedToken(); + return context.createExprStatement(location, expression, start, m_lastTokenEndPosition.line); + } + const Identifier* ident = m_token.m_data.ident; + JSTextPosition end = tokenEndPosition(); + next(); + consumeOrFail(COLON, "Labels must be followed by a ':'"); + if (!m_syntaxAlreadyValidated) { + // This is O(N^2) over the current list of consecutive labels, but I + // have never seen more than one label in a row in the real world. + for (size_t i = 0; i < labels.size(); i++) + failIfTrue(ident->impl() == labels[i].m_ident->impl(), "Attempted to redeclare the label '", ident->impl(), "'"); + failIfTrue(getLabel(ident), "Cannot find scope for the label '", ident->impl(), "'"); + labels.append(LabelInfo(ident, start, end)); + } + } while (match(IDENT) || isLETMaskedAsIDENT()); + bool isLoop = false; + switch (m_token.m_type) { + case FOR: + case WHILE: + case DO: + isLoop = true; + break; + + default: + break; + } + const Identifier* unused = 0; + ScopeRef labelScope = currentScope(); + if (!m_syntaxAlreadyValidated) { + for (size_t i = 0; i < labels.size(); i++) + pushLabel(labels[i].m_ident, isLoop); + } + TreeStatement statement = parseStatement(context, unused); + if (!m_syntaxAlreadyValidated) { + for (size_t i = 0; i < labels.size(); i++) + popLabel(labelScope); + } + failIfFalse(statement, "Cannot parse statement"); + for (size_t i = 0; i < labels.size(); i++) { + const LabelInfo& info = labels[labels.size() - i - 1]; + statement = context.createLabelStatement(location, info.m_ident, statement, info.m_start, info.m_end); + } + return statement; +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionStatement(TreeBuilder& context) +{ + switch (m_token.m_type) { + // Consult: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-expression-statement + // The ES6 spec mandates that we should fail from FUNCTION token here. We handle this case + // in parseStatement() which is the only caller of parseExpressionStatement(). + // We actually allow FUNCTION in situations where it should not be allowed unless we're in strict mode. + case CLASSTOKEN: + failWithMessage("'class' declaration is not directly within a block statement"); + break; + default: + // FIXME: when implementing 'let' we should fail when we see the token sequence "let [". + // https://bugs.webkit.org/show_bug.cgi?id=142944 + break; + } + JSTextPosition start = tokenStartPosition(); + JSTokenLocation location(tokenLocation()); + TreeExpression expression = parseExpression(context); + failIfFalse(expression, "Cannot parse expression statement"); + failIfFalse(autoSemiColon(), "Parse error"); + return context.createExprStatement(location, expression, start, m_lastTokenEndPosition.line); +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseIfStatement(TreeBuilder& context) +{ + ASSERT(match(IF)); + JSTokenLocation ifLocation(tokenLocation()); + int start = tokenLine(); + next(); + handleProductionOrFail(OPENPAREN, "(", "start", "'if' condition"); + + TreeExpression condition = parseExpression(context); + failIfFalse(condition, "Expected a expression as the condition for an if statement"); + int end = tokenLine(); + handleProductionOrFail(CLOSEPAREN, ")", "end", "'if' condition"); + + const Identifier* unused = 0; + TreeStatement trueBlock = parseStatement(context, unused); + failIfFalse(trueBlock, "Expected a statement as the body of an if block"); + + if (!match(ELSE)) + return context.createIfStatement(ifLocation, condition, trueBlock, 0, start, end); + + Vector<TreeExpression> exprStack; + Vector<std::pair<int, int>> posStack; + Vector<JSTokenLocation> tokenLocationStack; + Vector<TreeStatement> statementStack; + bool trailingElse = false; + do { + JSTokenLocation tempLocation = tokenLocation(); + next(); + if (!match(IF)) { + const Identifier* unused = 0; + TreeStatement block = parseStatement(context, unused); + failIfFalse(block, "Expected a statement as the body of an else block"); + statementStack.append(block); + trailingElse = true; + break; + } + int innerStart = tokenLine(); + next(); + + handleProductionOrFail(OPENPAREN, "(", "start", "'if' condition"); + + TreeExpression innerCondition = parseExpression(context); + failIfFalse(innerCondition, "Expected a expression as the condition for an if statement"); + int innerEnd = tokenLine(); + handleProductionOrFail(CLOSEPAREN, ")", "end", "'if' condition"); + const Identifier* unused = 0; + TreeStatement innerTrueBlock = parseStatement(context, unused); + failIfFalse(innerTrueBlock, "Expected a statement as the body of an if block"); + tokenLocationStack.append(tempLocation); + exprStack.append(innerCondition); + posStack.append(std::make_pair(innerStart, innerEnd)); + statementStack.append(innerTrueBlock); + } while (match(ELSE)); + + if (!trailingElse) { + TreeExpression condition = exprStack.last(); + exprStack.removeLast(); + TreeStatement trueBlock = statementStack.last(); + statementStack.removeLast(); + std::pair<int, int> pos = posStack.last(); + posStack.removeLast(); + JSTokenLocation elseLocation = tokenLocationStack.last(); + tokenLocationStack.removeLast(); + TreeStatement ifStatement = context.createIfStatement(elseLocation, condition, trueBlock, 0, pos.first, pos.second); + context.setEndOffset(ifStatement, context.endOffset(trueBlock)); + statementStack.append(ifStatement); + } + + while (!exprStack.isEmpty()) { + TreeExpression condition = exprStack.last(); + exprStack.removeLast(); + TreeStatement falseBlock = statementStack.last(); + statementStack.removeLast(); + TreeStatement trueBlock = statementStack.last(); + statementStack.removeLast(); + std::pair<int, int> pos = posStack.last(); + posStack.removeLast(); + JSTokenLocation elseLocation = tokenLocationStack.last(); + tokenLocationStack.removeLast(); + TreeStatement ifStatement = context.createIfStatement(elseLocation, condition, trueBlock, falseBlock, pos.first, pos.second); + context.setEndOffset(ifStatement, context.endOffset(falseBlock)); + statementStack.append(ifStatement); + } + + return context.createIfStatement(ifLocation, condition, trueBlock, statementStack.last(), start, end); +} + +template <typename LexerType> +template <class TreeBuilder> typename TreeBuilder::ModuleName Parser<LexerType>::parseModuleName(TreeBuilder& context) +{ + // ModuleName (ModuleSpecifier in the spec) represents the module name imported by the script. + // http://www.ecma-international.org/ecma-262/6.0/#sec-imports + // http://www.ecma-international.org/ecma-262/6.0/#sec-exports + JSTokenLocation specifierLocation(tokenLocation()); + failIfFalse(match(STRING), "Imported modules names must be string literals"); + const Identifier* moduleName = m_token.m_data.ident; + next(); + return context.createModuleName(specifierLocation, *moduleName); +} + +template <typename LexerType> +template <class TreeBuilder> typename TreeBuilder::ImportSpecifier Parser<LexerType>::parseImportClauseItem(TreeBuilder& context, ImportSpecifierType specifierType) +{ + // Produced node is the item of the ImportClause. + // That is the ImportSpecifier, ImportedDefaultBinding or NameSpaceImport. + // http://www.ecma-international.org/ecma-262/6.0/#sec-imports + JSTokenLocation specifierLocation(tokenLocation()); + JSToken localNameToken; + const Identifier* importedName = nullptr; + const Identifier* localName = nullptr; + + switch (specifierType) { + case ImportSpecifierType::NamespaceImport: { + // NameSpaceImport : + // * as ImportedBinding + // e.g. + // * as namespace + ASSERT(match(TIMES)); + importedName = &m_vm->propertyNames->timesIdentifier; + next(); + + failIfFalse(matchContextualKeyword(m_vm->propertyNames->as), "Expected 'as' before imported binding name"); + next(); + + matchOrFail(IDENT, "Expected a variable name for the import declaration"); + localNameToken = m_token; + localName = m_token.m_data.ident; + next(); + break; + } + + case ImportSpecifierType::NamedImport: { + // ImportSpecifier : + // ImportedBinding + // IdentifierName as ImportedBinding + // e.g. + // A + // A as B + ASSERT(matchIdentifierOrKeyword()); + localNameToken = m_token; + localName = m_token.m_data.ident; + importedName = localName; + next(); + + if (matchContextualKeyword(m_vm->propertyNames->as)) { + next(); + matchOrFail(IDENT, "Expected a variable name for the import declaration"); + localNameToken = m_token; + localName = m_token.m_data.ident; + next(); + } + break; + } + + case ImportSpecifierType::DefaultImport: { + // ImportedDefaultBinding : + // ImportedBinding + ASSERT(match(IDENT)); + localNameToken = m_token; + localName = m_token.m_data.ident; + importedName = &m_vm->propertyNames->defaultKeyword; + next(); + break; + } + } + + semanticFailIfTrue(localNameToken.m_type & KeywordTokenFlag, "Cannot use keyword as imported binding name"); + DeclarationResultMask declarationResult = declareVariable(localName, DeclarationType::ConstDeclaration, DeclarationImportType::Imported); + if (declarationResult != DeclarationResult::Valid) { + failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare an imported binding named ", localName->impl(), " in strict mode"); + if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) + internalFailWithMessage(false, "Cannot declare an imported binding name twice: '", localName->impl(), "'"); + } + + return context.createImportSpecifier(specifierLocation, *importedName, *localName); +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseImportDeclaration(TreeBuilder& context) +{ + // http://www.ecma-international.org/ecma-262/6.0/#sec-imports + ASSERT(match(IMPORT)); + JSTokenLocation importLocation(tokenLocation()); + next(); + + auto specifierList = context.createImportSpecifierList(); + + if (match(STRING)) { + // import ModuleSpecifier ; + auto moduleName = parseModuleName(context); + failIfFalse(moduleName, "Cannot parse the module name"); + failIfFalse(autoSemiColon(), "Expected a ';' following a targeted import declaration"); + return context.createImportDeclaration(importLocation, specifierList, moduleName); + } + + bool isFinishedParsingImport = false; + if (match(IDENT)) { + // ImportedDefaultBinding : + // ImportedBinding + auto specifier = parseImportClauseItem(context, ImportSpecifierType::DefaultImport); + failIfFalse(specifier, "Cannot parse the default import"); + context.appendImportSpecifier(specifierList, specifier); + if (match(COMMA)) + next(); + else + isFinishedParsingImport = true; + } + + if (!isFinishedParsingImport) { + if (match(TIMES)) { + // import NameSpaceImport FromClause ; + auto specifier = parseImportClauseItem(context, ImportSpecifierType::NamespaceImport); + failIfFalse(specifier, "Cannot parse the namespace import"); + context.appendImportSpecifier(specifierList, specifier); + } else if (match(OPENBRACE)) { + // NamedImports : + // { } + // { ImportsList } + // { ImportsList , } + next(); + + while (!match(CLOSEBRACE)) { + failIfFalse(matchIdentifierOrKeyword(), "Expected an imported name for the import declaration"); + auto specifier = parseImportClauseItem(context, ImportSpecifierType::NamedImport); + failIfFalse(specifier, "Cannot parse the named import"); + context.appendImportSpecifier(specifierList, specifier); + if (!consume(COMMA)) + break; + } + handleProductionOrFail(CLOSEBRACE, "}", "end", "import list"); + } else + failWithMessage("Expected namespace import or import list"); + } + + // FromClause : + // from ModuleSpecifier + + failIfFalse(matchContextualKeyword(m_vm->propertyNames->from), "Expected 'from' before imported module name"); + next(); + + auto moduleName = parseModuleName(context); + failIfFalse(moduleName, "Cannot parse the module name"); + failIfFalse(autoSemiColon(), "Expected a ';' following a targeted import declaration"); + + return context.createImportDeclaration(importLocation, specifierList, moduleName); +} + +template <typename LexerType> +template <class TreeBuilder> typename TreeBuilder::ExportSpecifier Parser<LexerType>::parseExportSpecifier(TreeBuilder& context, Vector<const Identifier*>& maybeLocalNames, bool& hasKeywordForLocalBindings) +{ + // ExportSpecifier : + // IdentifierName + // IdentifierName as IdentifierName + // http://www.ecma-international.org/ecma-262/6.0/#sec-exports + ASSERT(matchIdentifierOrKeyword()); + JSTokenLocation specifierLocation(tokenLocation()); + if (m_token.m_type & KeywordTokenFlag) + hasKeywordForLocalBindings = true; + const Identifier* localName = m_token.m_data.ident; + const Identifier* exportedName = localName; + next(); + + if (matchContextualKeyword(m_vm->propertyNames->as)) { + next(); + failIfFalse(matchIdentifierOrKeyword(), "Expected an exported name for the export declaration"); + exportedName = m_token.m_data.ident; + next(); + } + + semanticFailIfFalse(exportName(*exportedName), "Cannot export a duplicate name '", exportedName->impl(), "'"); + maybeLocalNames.append(localName); + return context.createExportSpecifier(specifierLocation, *localName, *exportedName); +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclaration(TreeBuilder& context) +{ + // http://www.ecma-international.org/ecma-262/6.0/#sec-exports + ASSERT(match(EXPORT)); + JSTokenLocation exportLocation(tokenLocation()); + next(); + + switch (m_token.m_type) { + case TIMES: { + // export * FromClause ; + next(); + + failIfFalse(matchContextualKeyword(m_vm->propertyNames->from), "Expected 'from' before exported module name"); + next(); + auto moduleName = parseModuleName(context); + failIfFalse(moduleName, "Cannot parse the 'from' clause"); + failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration"); + + return context.createExportAllDeclaration(exportLocation, moduleName); + } + + case DEFAULT: { + // export default HoistableDeclaration[Default] + // export default ClassDeclaration[Default] + // export default [lookahead not-in {function, class}] AssignmentExpression[In] ; + + next(); + + TreeStatement result = 0; + bool isFunctionOrClassDeclaration = false; + const Identifier* localName = nullptr; + SavePoint savePoint = createSavePoint(); + if (match(FUNCTION) +#if ENABLE(ES6_CLASS_SYNTAX) + || match(CLASSTOKEN) +#endif + ) { + isFunctionOrClassDeclaration = true; + next(); + // FIXME: When landing ES6 generators, we need to take care of that '*' comes. + if (match(IDENT)) + localName = m_token.m_data.ident; + restoreSavePoint(savePoint); + } + + if (localName) { + if (match(FUNCTION)) + result = parseFunctionDeclaration(context); +#if ENABLE(ES6_CLASS_SYNTAX) + else { + ASSERT(match(CLASSTOKEN)); + result = parseClassDeclaration(context); + } +#endif + } else { + // export default expr; + // + // It should be treated as the same to the following. + // + // const *default* = expr; + // export { *default* as default } + // + // In the above example, *default* is the invisible variable to the users. + // We use the private symbol to represent the name of this variable. + JSTokenLocation location(tokenLocation()); + JSTextPosition start = tokenStartPosition(); + TreeExpression expression = parseAssignmentExpression(context); + failIfFalse(expression, "Cannot parse expression"); + + DeclarationResultMask declarationResult = declareVariable(&m_vm->propertyNames->starDefaultPrivateName, DeclarationType::ConstDeclaration); + if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) + internalFailWithMessage(false, "Only one 'default' export is allowed"); + + TreeExpression assignment = context.createAssignResolve(location, m_vm->propertyNames->starDefaultPrivateName, expression, start, start, tokenEndPosition(), AssignmentContext::ConstDeclarationStatement); + result = context.createExprStatement(location, assignment, start, tokenEndPosition()); + if (!isFunctionOrClassDeclaration) + failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration"); + localName = &m_vm->propertyNames->starDefaultPrivateName; + } + failIfFalse(result, "Cannot parse the declaration"); + + semanticFailIfFalse(exportName(m_vm->propertyNames->defaultKeyword), "Only one 'default' export is allowed"); + currentScope()->moduleScopeData().exportBinding(*localName); + return context.createExportDefaultDeclaration(exportLocation, result, *localName); + } + + case OPENBRACE: { + // export ExportClause FromClause ; + // export ExportClause ; + // + // ExportClause : + // { } + // { ExportsList } + // { ExportsList , } + // + // ExportsList : + // ExportSpecifier + // ExportsList , ExportSpecifier + + next(); + + auto specifierList = context.createExportSpecifierList(); + Vector<const Identifier*> maybeLocalNames; + + bool hasKeywordForLocalBindings = false; + while (!match(CLOSEBRACE)) { + failIfFalse(matchIdentifierOrKeyword(), "Expected a variable name for the export declaration"); + auto specifier = parseExportSpecifier(context, maybeLocalNames, hasKeywordForLocalBindings); + failIfFalse(specifier, "Cannot parse the named export"); + context.appendExportSpecifier(specifierList, specifier); + if (!consume(COMMA)) + break; + } + handleProductionOrFail(CLOSEBRACE, "}", "end", "export list"); + + typename TreeBuilder::ModuleName moduleName = 0; + if (matchContextualKeyword(m_vm->propertyNames->from)) { + next(); + moduleName = parseModuleName(context); + failIfFalse(moduleName, "Cannot parse the 'from' clause"); + } + failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration"); + + if (!moduleName) { + semanticFailIfTrue(hasKeywordForLocalBindings, "Cannot use keyword as exported variable name"); + // Since this export declaration does not have module specifier part, it exports the local bindings. + // While the export declaration with module specifier does not have any effect on the current module's scope, + // the export named declaration without module specifier references the the local binding names. + // For example, + // export { A, B, C as D } from "mod" + // does not have effect on the current module's scope. But, + // export { A, B, C as D } + // will reference the current module's bindings. + for (const Identifier* localName : maybeLocalNames) { + currentScope()->useVariable(localName, m_vm->propertyNames->eval == *localName); + currentScope()->moduleScopeData().exportBinding(*localName); + } + } + + return context.createExportNamedDeclaration(exportLocation, specifierList, moduleName); + } + + default: { + // export VariableStatement + // export Declaration + TreeStatement result = 0; + switch (m_token.m_type) { + case VAR: + result = parseVariableDeclaration(context, DeclarationType::VarDeclaration, ExportType::Exported); + break; + + case CONSTTOKEN: + result = parseVariableDeclaration(context, DeclarationType::ConstDeclaration, ExportType::Exported); + break; + + case LET: + result = parseVariableDeclaration(context, DeclarationType::LetDeclaration, ExportType::Exported); + break; + + case FUNCTION: + result = parseFunctionDeclaration(context, ExportType::Exported); + break; + +#if ENABLE(ES6_CLASS_SYNTAX) + case CLASSTOKEN: + result = parseClassDeclaration(context, ExportType::Exported); + break; +#endif + + default: + failWithMessage("Expected either a declaration or a variable statement"); + break; + } + failIfFalse(result, "Cannot parse the declaration"); + return context.createExportLocalDeclaration(exportLocation, result); + } + } + + RELEASE_ASSERT_NOT_REACHED(); + return 0; +} + +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseExpression(TreeBuilder& context) +{ + failIfStackOverflow(); + JSTokenLocation location(tokenLocation()); + TreeExpression node = parseAssignmentExpression(context); + failIfFalse(node, "Cannot parse expression"); + context.setEndOffset(node, m_lastTokenEndPosition.offset); + if (!match(COMMA)) + return node; + next(); + m_nonTrivialExpressionCount++; + m_nonLHSCount++; + TreeExpression right = parseAssignmentExpression(context); + failIfFalse(right, "Cannot parse expression in a comma expression"); + context.setEndOffset(right, m_lastTokenEndPosition.offset); + typename TreeBuilder::Comma head = context.createCommaExpr(location, node); + typename TreeBuilder::Comma tail = context.appendToCommaExpr(location, head, head, right); + while (match(COMMA)) { + next(TreeBuilder::DontBuildStrings); + right = parseAssignmentExpression(context); + failIfFalse(right, "Cannot parse expression in a comma expression"); + context.setEndOffset(right, m_lastTokenEndPosition.offset); + tail = context.appendToCommaExpr(location, head, tail, right); + } + context.setEndOffset(head, m_lastTokenEndPosition.offset); + return head; +} + + +template <typename LexerType> +template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmentExpression(TreeBuilder& context) +{ + failIfStackOverflow(); + JSTextPosition start = tokenStartPosition(); + JSTokenLocation location(tokenLocation()); + int initialAssignmentCount = m_assignmentCount; + int initialNonLHSCount = m_nonLHSCount; + if (match(OPENBRACE) || match(OPENBRACKET)) { + SavePoint savePoint = createSavePoint(); + auto pattern = tryParseDestructuringPatternExpression(context, AssignmentContext::AssignmentExpression); + if (pattern && consume(EQUAL)) { + auto rhs = parseAssignmentExpression(context); + if (rhs) + return context.createDestructuringAssignment(location, pattern, rhs); + } + restoreSavePoint(savePoint); + } + +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + if (isArrowFunctionParamters()) + return parseArrowFunctionExpression(context); +#endif + + TreeExpression lhs = parseConditionalExpression(context); + failIfFalse(lhs, "Cannot parse expression"); + if (initialNonLHSCount != m_nonLHSCount) { + if (m_token.m_type >= EQUAL && m_token.m_type <= OREQUAL) + semanticFail("Left hand side of operator '", getToken(), "' must be a reference"); + + return lhs; + } + + int assignmentStack = 0; + Operator op; + bool hadAssignment = false; + while (true) { + switch (m_token.m_type) { + case EQUAL: op = OpEqual; break; + case PLUSEQUAL: op = OpPlusEq; break; + case MINUSEQUAL: op = OpMinusEq; break; + case MULTEQUAL: op = OpMultEq; break; + case DIVEQUAL: op = OpDivEq; break; + case LSHIFTEQUAL: op = OpLShift; break; + case RSHIFTEQUAL: op = OpRShift; break; + case URSHIFTEQUAL: op = OpURShift; break; + case ANDEQUAL: op = OpAndEq; break; + case XOREQUAL: op = OpXOrEq; break; + case OREQUAL: op = OpOrEq; break; + case MODEQUAL: op = OpModEq; break; + default: + goto end; + } + m_nonTrivialExpressionCount++; + hadAssignment = true; + context.assignmentStackAppend(assignmentStack, lhs, start, tokenStartPosition(), m_assignmentCount, op); + start = tokenStartPosition(); + m_assignmentCount++; + next(TreeBuilder::DontBuildStrings); + if (strictMode() && m_lastIdentifier && context.isResolve(lhs)) { + failIfTrueIfStrict(m_vm->propertyNames->eval == *m_lastIdentifier, "Cannot modify 'eval' in strict mode"); + failIfTrueIfStrict(m_vm->propertyNames->arguments == *m_lastIdentifier, "Cannot modify 'arguments' in strict mode"); + declareWrite(m_lastIdentifier); + m_lastIdentifier = 0; + } + lhs = parseAssignmentExpression(context); + failIfFalse(lhs, "Cannot parse the right hand side of an assignment expression"); + if (initialNonLHSCount != m_nonLHSCount) { + if (m_token.m_type >= EQUAL && m_token.m_type <= OREQUAL) + semanticFail("Left hand side of operator '", getToken(), "' must be a reference"); + break; + } + } +end: + if (hadAssignment) + m_nonLHSCount++; + + if (!TreeBuilder::CreatesAST) + return lhs; + + while (assignmentStack) + lhs = context.createAssignment(location, assignmentStack, lhs, initialAssignmentCount, m_assignmentCount, lastTokenEndPosition()); + + return lhs; +} + +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseConditionalExpression(TreeBuilder& context) +{ + JSTokenLocation location(tokenLocation()); + TreeExpression cond = parseBinaryExpression(context); + failIfFalse(cond, "Cannot parse expression"); + if (!match(QUESTION)) + return cond; + m_nonTrivialExpressionCount++; + m_nonLHSCount++; + next(TreeBuilder::DontBuildStrings); + TreeExpression lhs = parseAssignmentExpression(context); + failIfFalse(lhs, "Cannot parse left hand side of ternary operator"); + context.setEndOffset(lhs, m_lastTokenEndPosition.offset); + consumeOrFailWithFlags(COLON, TreeBuilder::DontBuildStrings, "Expected ':' in ternary operator"); + + TreeExpression rhs = parseAssignmentExpression(context); + failIfFalse(rhs, "Cannot parse right hand side of ternary operator"); + context.setEndOffset(rhs, m_lastTokenEndPosition.offset); + return context.createConditionalExpr(location, cond, lhs, rhs); +} + +ALWAYS_INLINE static bool isUnaryOp(JSTokenType token) +{ + return token & UnaryOpTokenFlag; +} + +template <typename LexerType> +int Parser<LexerType>::isBinaryOperator(JSTokenType token) +{ + if (m_allowsIn) + return token & (BinaryOpTokenPrecedenceMask << BinaryOpTokenAllowsInPrecedenceAdditionalShift); + return token & BinaryOpTokenPrecedenceMask; +} + +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseBinaryExpression(TreeBuilder& context) +{ + int operandStackDepth = 0; + int operatorStackDepth = 0; + typename TreeBuilder::BinaryExprContext binaryExprContext(context); + JSTokenLocation location(tokenLocation()); + while (true) { + JSTextPosition exprStart = tokenStartPosition(); + int initialAssignments = m_assignmentCount; + TreeExpression current = parseUnaryExpression(context); + failIfFalse(current, "Cannot parse expression"); + + context.appendBinaryExpressionInfo(operandStackDepth, current, exprStart, lastTokenEndPosition(), lastTokenEndPosition(), initialAssignments != m_assignmentCount); + int precedence = isBinaryOperator(m_token.m_type); + if (!precedence) + break; + m_nonTrivialExpressionCount++; + m_nonLHSCount++; + int operatorToken = m_token.m_type; + next(TreeBuilder::DontBuildStrings); + + while (operatorStackDepth && context.operatorStackHasHigherPrecedence(operatorStackDepth, precedence)) { + ASSERT(operandStackDepth > 1); + + typename TreeBuilder::BinaryOperand rhs = context.getFromOperandStack(-1); + typename TreeBuilder::BinaryOperand lhs = context.getFromOperandStack(-2); + context.shrinkOperandStackBy(operandStackDepth, 2); + context.appendBinaryOperation(location, operandStackDepth, operatorStackDepth, lhs, rhs); + context.operatorStackPop(operatorStackDepth); + } + context.operatorStackAppend(operatorStackDepth, operatorToken, precedence); + } + while (operatorStackDepth) { + ASSERT(operandStackDepth > 1); + + typename TreeBuilder::BinaryOperand rhs = context.getFromOperandStack(-1); + typename TreeBuilder::BinaryOperand lhs = context.getFromOperandStack(-2); + context.shrinkOperandStackBy(operandStackDepth, 2); + context.appendBinaryOperation(location, operandStackDepth, operatorStackDepth, lhs, rhs); + context.operatorStackPop(operatorStackDepth); + } + return context.popOperandStack(operandStackDepth); +} + +template <typename LexerType> +template <class TreeBuilder> TreeProperty Parser<LexerType>::parseProperty(TreeBuilder& context, bool complete) +{ + bool wasIdent = false; + switch (m_token.m_type) { + namedProperty: + case IDENT: + wasIdent = true; + FALLTHROUGH; + case STRING: { + const Identifier* ident = m_token.m_data.ident; + unsigned getterOrSetterStartOffset = tokenStart(); + if (complete || (wasIdent && (*ident == m_vm->propertyNames->get || *ident == m_vm->propertyNames->set))) + nextExpectIdentifier(LexerFlagsIgnoreReservedWords); + else + nextExpectIdentifier(LexerFlagsIgnoreReservedWords | TreeBuilder::DontBuildKeywords); + + if (match(COLON)) { + next(); + TreeExpression node = parseAssignmentExpression(context); + failIfFalse(node, "Cannot parse expression for property declaration"); + context.setEndOffset(node, m_lexer->currentOffset()); + return context.createProperty(ident, node, PropertyNode::Constant, PropertyNode::Unknown, complete); + } + + if (match(OPENPAREN)) { + auto method = parsePropertyMethod(context, ident); + propagateError(); + return context.createProperty(ident, method, PropertyNode::Constant, PropertyNode::KnownDirect, complete); + } + + failIfFalse(wasIdent, "Expected an identifier as property name"); + + if (match(COMMA) || match(CLOSEBRACE)) { + JSTextPosition start = tokenStartPosition(); + JSTokenLocation location(tokenLocation()); + currentScope()->useVariable(ident, m_vm->propertyNames->eval == *ident); + TreeExpression node = context.createResolve(location, ident, start); + return context.createProperty(ident, node, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Shorthand), PropertyNode::KnownDirect, complete); + } + + PropertyNode::Type type; + if (*ident == m_vm->propertyNames->get) + type = PropertyNode::Getter; + else if (*ident == m_vm->propertyNames->set) + type = PropertyNode::Setter; + else + failWithMessage("Expected a ':' following the property name '", ident->impl(), "'"); + return parseGetterSetter(context, complete, type, getterOrSetterStartOffset); + } + case DOUBLE: + case INTEGER: { + double propertyName = m_token.m_data.doubleValue; + next(); + + if (match(OPENPAREN)) { + const Identifier& ident = m_parserArena.identifierArena().makeNumericIdentifier(const_cast<VM*>(m_vm), propertyName); + auto method = parsePropertyMethod(context, &ident); + propagateError(); + return context.createProperty(&ident, method, PropertyNode::Constant, PropertyNode::Unknown, complete); + } + + consumeOrFail(COLON, "Expected ':' after property name"); + TreeExpression node = parseAssignmentExpression(context); + failIfFalse(node, "Cannot parse expression for property declaration"); + context.setEndOffset(node, m_lexer->currentOffset()); + return context.createProperty(const_cast<VM*>(m_vm), m_parserArena, propertyName, node, PropertyNode::Constant, PropertyNode::Unknown, complete); + } + case OPENBRACKET: { + next(); + auto propertyName = parseAssignmentExpression(context); + failIfFalse(propertyName, "Cannot parse computed property name"); + handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); + + if (match(OPENPAREN)) { + auto method = parsePropertyMethod(context, &m_vm->propertyNames->nullIdentifier); + propagateError(); + return context.createProperty(propertyName, method, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Computed), PropertyNode::KnownDirect, complete); + } + + consumeOrFail(COLON, "Expected ':' after property name"); + TreeExpression node = parseAssignmentExpression(context); + failIfFalse(node, "Cannot parse expression for property declaration"); + context.setEndOffset(node, m_lexer->currentOffset()); + return context.createProperty(propertyName, node, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Computed), PropertyNode::Unknown, complete); + } + default: + failIfFalse(m_token.m_type & KeywordTokenFlag, "Expected a property name"); + goto namedProperty; + } +} + +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePropertyMethod(TreeBuilder& context, const Identifier* methodName) +{ + JSTokenLocation methodLocation(tokenLocation()); + unsigned methodStart = tokenStart(); + ParserFunctionInfo<TreeBuilder> methodInfo; + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SourceParseMode::MethodMode, false, ConstructorKind::None, SuperBinding::NotNeeded, methodStart, methodInfo, StandardFunctionParseType)), "Cannot parse this method"); + methodInfo.name = methodName; + return context.createFunctionExpr(methodLocation, methodInfo); +} + +template <typename LexerType> +template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(TreeBuilder& context, bool strict, PropertyNode::Type type, unsigned getterOrSetterStartOffset, + ConstructorKind constructorKind, SuperBinding superBinding) +{ + const Identifier* stringPropertyName = 0; + double numericPropertyName = 0; + if (m_token.m_type == IDENT || m_token.m_type == STRING || isLETMaskedAsIDENT()) { + stringPropertyName = m_token.m_data.ident; + semanticFailIfTrue(superBinding == SuperBinding::Needed && *stringPropertyName == m_vm->propertyNames->prototype, + "Cannot declare a static method named 'prototype'"); + semanticFailIfTrue(superBinding == SuperBinding::Needed && *stringPropertyName == m_vm->propertyNames->constructor, + "Cannot declare a getter or setter named 'constructor'"); + } else if (m_token.m_type == DOUBLE || m_token.m_type == INTEGER) + numericPropertyName = m_token.m_data.doubleValue; + else + failDueToUnexpectedToken(); + JSTokenLocation location(tokenLocation()); + next(); + ParserFunctionInfo<TreeBuilder> info; + if (type & PropertyNode::Getter) { + failIfFalse(match(OPENPAREN), "Expected a parameter list for getter definition"); + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SourceParseMode::GetterMode, false, constructorKind, superBinding, + getterOrSetterStartOffset, info, StandardFunctionParseType)), "Cannot parse getter definition"); + } else { + failIfFalse(match(OPENPAREN), "Expected a parameter list for setter definition"); + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SourceParseMode::SetterMode, false, constructorKind, superBinding, + getterOrSetterStartOffset, info, StandardFunctionParseType)), "Cannot parse setter definition"); + } + if (stringPropertyName) + return context.createGetterOrSetterProperty(location, type, strict, stringPropertyName, info, superBinding); + return context.createGetterOrSetterProperty(const_cast<VM*>(m_vm), m_parserArena, location, type, strict, numericPropertyName, info, superBinding); +} + +template <typename LexerType> +template <class TreeBuilder> bool Parser<LexerType>::shouldCheckPropertyForUnderscoreProtoDuplicate(TreeBuilder& context, const TreeProperty& property) +{ + if (m_syntaxAlreadyValidated) + return false; + + if (!context.getName(property)) + return false; + + // A Constant property that is not a Computed or Shorthand Constant property. + return context.getType(property) == PropertyNode::Constant; +} + +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseObjectLiteral(TreeBuilder& context) +{ + auto savePoint = createSavePoint(); + consumeOrFailWithFlags(OPENBRACE, TreeBuilder::DontBuildStrings, "Expected opening '{' at the start of an object literal"); + + int oldNonLHSCount = m_nonLHSCount; + + JSTokenLocation location(tokenLocation()); + if (match(CLOSEBRACE)) { + next(); + return context.createObjectLiteral(location); + } + + TreeProperty property = parseProperty(context, false); + failIfFalse(property, "Cannot parse object literal property"); + + if (!m_syntaxAlreadyValidated && context.getType(property) & (PropertyNode::Getter | PropertyNode::Setter)) { + restoreSavePoint(savePoint); + return parseStrictObjectLiteral(context); + } + + bool seenUnderscoreProto = false; + if (shouldCheckPropertyForUnderscoreProtoDuplicate(context, property)) + seenUnderscoreProto = *context.getName(property) == m_vm->propertyNames->underscoreProto; + + TreePropertyList propertyList = context.createPropertyList(location, property); + TreePropertyList tail = propertyList; + while (match(COMMA)) { + next(TreeBuilder::DontBuildStrings); + if (match(CLOSEBRACE)) + break; + JSTokenLocation propertyLocation(tokenLocation()); + property = parseProperty(context, false); + failIfFalse(property, "Cannot parse object literal property"); + if (!m_syntaxAlreadyValidated && context.getType(property) & (PropertyNode::Getter | PropertyNode::Setter)) { + restoreSavePoint(savePoint); + return parseStrictObjectLiteral(context); + } + if (shouldCheckPropertyForUnderscoreProtoDuplicate(context, property)) { + if (*context.getName(property) == m_vm->propertyNames->underscoreProto) { + semanticFailIfTrue(seenUnderscoreProto, "Attempted to redefine __proto__ property"); + seenUnderscoreProto = true; + } + } + tail = context.createPropertyList(propertyLocation, property, tail); + } + + location = tokenLocation(); + handleProductionOrFail(CLOSEBRACE, "}", "end", "object literal"); + + m_nonLHSCount = oldNonLHSCount; + + return context.createObjectLiteral(location, propertyList); +} + +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseStrictObjectLiteral(TreeBuilder& context) +{ + consumeOrFail(OPENBRACE, "Expected opening '{' at the start of an object literal"); + + int oldNonLHSCount = m_nonLHSCount; + + JSTokenLocation location(tokenLocation()); + if (match(CLOSEBRACE)) { + next(); + return context.createObjectLiteral(location); + } + + TreeProperty property = parseProperty(context, true); + failIfFalse(property, "Cannot parse object literal property"); + + bool seenUnderscoreProto = false; + if (shouldCheckPropertyForUnderscoreProtoDuplicate(context, property)) + seenUnderscoreProto = *context.getName(property) == m_vm->propertyNames->underscoreProto; + + TreePropertyList propertyList = context.createPropertyList(location, property); + TreePropertyList tail = propertyList; + while (match(COMMA)) { + next(); + if (match(CLOSEBRACE)) + break; + JSTokenLocation propertyLocation(tokenLocation()); + property = parseProperty(context, true); + failIfFalse(property, "Cannot parse object literal property"); + if (shouldCheckPropertyForUnderscoreProtoDuplicate(context, property)) { + if (*context.getName(property) == m_vm->propertyNames->underscoreProto) { + semanticFailIfTrue(seenUnderscoreProto, "Attempted to redefine __proto__ property"); + seenUnderscoreProto = true; + } + } + tail = context.createPropertyList(propertyLocation, property, tail); + } + + location = tokenLocation(); + handleProductionOrFail(CLOSEBRACE, "}", "end", "object literal"); + + m_nonLHSCount = oldNonLHSCount; + + return context.createObjectLiteral(location, propertyList); +} + +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArrayLiteral(TreeBuilder& context) +{ + consumeOrFailWithFlags(OPENBRACKET, TreeBuilder::DontBuildStrings, "Expected an opening '[' at the beginning of an array literal"); + + int oldNonLHSCount = m_nonLHSCount; + + int elisions = 0; + while (match(COMMA)) { + next(TreeBuilder::DontBuildStrings); + elisions++; + } + if (match(CLOSEBRACKET)) { + JSTokenLocation location(tokenLocation()); + next(TreeBuilder::DontBuildStrings); + return context.createArray(location, elisions); + } + + TreeExpression elem; + if (UNLIKELY(match(DOTDOTDOT))) { + auto spreadLocation = m_token.m_location; + auto start = m_token.m_startPosition; + auto divot = m_token.m_endPosition; + next(); + auto spreadExpr = parseAssignmentExpression(context); + failIfFalse(spreadExpr, "Cannot parse subject of a spread operation"); + elem = context.createSpreadExpression(spreadLocation, spreadExpr, start, divot, m_lastTokenEndPosition); + } else + elem = parseAssignmentExpression(context); + failIfFalse(elem, "Cannot parse array literal element"); + typename TreeBuilder::ElementList elementList = context.createElementList(elisions, elem); + typename TreeBuilder::ElementList tail = elementList; + elisions = 0; + while (match(COMMA)) { + next(TreeBuilder::DontBuildStrings); + elisions = 0; + + while (match(COMMA)) { + next(); + elisions++; + } + + if (match(CLOSEBRACKET)) { + JSTokenLocation location(tokenLocation()); + next(TreeBuilder::DontBuildStrings); + return context.createArray(location, elisions, elementList); + } + if (UNLIKELY(match(DOTDOTDOT))) { + auto spreadLocation = m_token.m_location; + auto start = m_token.m_startPosition; + auto divot = m_token.m_endPosition; + next(); + TreeExpression elem = parseAssignmentExpression(context); + failIfFalse(elem, "Cannot parse subject of a spread operation"); + auto spread = context.createSpreadExpression(spreadLocation, elem, start, divot, m_lastTokenEndPosition); + tail = context.createElementList(tail, elisions, spread); + continue; + } + TreeExpression elem = parseAssignmentExpression(context); + failIfFalse(elem, "Cannot parse array literal element"); + tail = context.createElementList(tail, elisions, elem); + } + + JSTokenLocation location(tokenLocation()); + if (!consume(CLOSEBRACKET)) { + failIfFalse(match(DOTDOTDOT), "Expected either a closing ']' or a ',' following an array element"); + semanticFail("The '...' operator should come before a target expression"); + } + + m_nonLHSCount = oldNonLHSCount; + + return context.createArray(location, elementList); +} + +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) +template <typename LexerType> +template <class TreeBuilder> typename TreeBuilder::TemplateString Parser<LexerType>::parseTemplateString(TreeBuilder& context, bool isTemplateHead, typename LexerType::RawStringsBuildMode rawStringsBuildMode, bool& elementIsTail) +{ + if (!isTemplateHead) { + matchOrFail(CLOSEBRACE, "Expected a closing '}' following an expression in template literal"); + // Re-scan the token to recognize it as Template Element. + m_token.m_type = m_lexer->scanTrailingTemplateString(&m_token, rawStringsBuildMode); + } + matchOrFail(TEMPLATE, "Expected an template element"); + const Identifier* cooked = m_token.m_data.cooked; + const Identifier* raw = m_token.m_data.raw; + elementIsTail = m_token.m_data.isTail; + JSTokenLocation location(tokenLocation()); + next(); + return context.createTemplateString(location, *cooked, *raw); +} + +template <typename LexerType> +template <class TreeBuilder> typename TreeBuilder::TemplateLiteral Parser<LexerType>::parseTemplateLiteral(TreeBuilder& context, typename LexerType::RawStringsBuildMode rawStringsBuildMode) +{ + JSTokenLocation location(tokenLocation()); + bool elementIsTail = false; + + auto headTemplateString = parseTemplateString(context, true, rawStringsBuildMode, elementIsTail); + failIfFalse(headTemplateString, "Cannot parse head template element"); + + typename TreeBuilder::TemplateStringList templateStringList = context.createTemplateStringList(headTemplateString); + typename TreeBuilder::TemplateStringList templateStringTail = templateStringList; + + if (elementIsTail) + return context.createTemplateLiteral(location, templateStringList); + + failIfTrue(match(CLOSEBRACE), "Template literal expression cannot be empty"); + TreeExpression expression = parseExpression(context); + failIfFalse(expression, "Cannot parse expression in template literal"); + + typename TreeBuilder::TemplateExpressionList templateExpressionList = context.createTemplateExpressionList(expression); + typename TreeBuilder::TemplateExpressionList templateExpressionTail = templateExpressionList; + + auto templateString = parseTemplateString(context, false, rawStringsBuildMode, elementIsTail); + failIfFalse(templateString, "Cannot parse template element"); + templateStringTail = context.createTemplateStringList(templateStringTail, templateString); + + while (!elementIsTail) { + failIfTrue(match(CLOSEBRACE), "Template literal expression cannot be empty"); + TreeExpression expression = parseExpression(context); + failIfFalse(expression, "Cannot parse expression in template literal"); + + templateExpressionTail = context.createTemplateExpressionList(templateExpressionTail, expression); + + auto templateString = parseTemplateString(context, false, rawStringsBuildMode, elementIsTail); + failIfFalse(templateString, "Cannot parse template element"); + templateStringTail = context.createTemplateStringList(templateStringTail, templateString); + } + + return context.createTemplateLiteral(location, templateStringList, templateExpressionList); +} +#endif + +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpression(TreeBuilder& context) +{ + failIfStackOverflow(); + switch (m_token.m_type) { + case FUNCTION: { + JSTokenLocation location(tokenLocation()); + unsigned functionKeywordStart = tokenStart(); + next(); + ParserFunctionInfo<TreeBuilder> info; + info.name = &m_vm->propertyNames->nullIdentifier; + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SourceParseMode::NormalFunctionMode, false, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, info, StandardFunctionParseType)), "Cannot parse function expression"); + return context.createFunctionExpr(location, info); + } +#if ENABLE(ES6_CLASS_SYNTAX) + case CLASSTOKEN: { + ParserClassInfo<TreeBuilder> info; + return parseClass(context, FunctionNoRequirements, info); + } +#endif + case OPENBRACE: + if (strictMode()) + return parseStrictObjectLiteral(context); + return parseObjectLiteral(context); + case OPENBRACKET: + return parseArrayLiteral(context); + case OPENPAREN: { + next(); + int oldNonLHSCount = m_nonLHSCount; + TreeExpression result = parseExpression(context); + m_nonLHSCount = oldNonLHSCount; + handleProductionOrFail(CLOSEPAREN, ")", "end", "compound expression"); + return result; + } + case THISTOKEN: { + JSTokenLocation location(tokenLocation()); + next(); + return context.createThisExpr(location, m_thisTDZMode); + } + case IDENT: { + identifierExpression: + JSTextPosition start = tokenStartPosition(); + const Identifier* ident = m_token.m_data.ident; + JSTokenLocation location(tokenLocation()); + next(); + currentScope()->useVariable(ident, m_vm->propertyNames->eval == *ident); + m_lastIdentifier = ident; + return context.createResolve(location, ident, start); + } + case STRING: { + const Identifier* ident = m_token.m_data.ident; + JSTokenLocation location(tokenLocation()); + next(); + return context.createString(location, ident); + } + case DOUBLE: { + double d = m_token.m_data.doubleValue; + JSTokenLocation location(tokenLocation()); + next(); + return context.createDoubleExpr(location, d); + } + case INTEGER: { + double d = m_token.m_data.doubleValue; + JSTokenLocation location(tokenLocation()); + next(); + return context.createIntegerExpr(location, d); + } + case NULLTOKEN: { + JSTokenLocation location(tokenLocation()); + next(); + return context.createNull(location); + } + case TRUETOKEN: { + JSTokenLocation location(tokenLocation()); + next(); + return context.createBoolean(location, true); + } + case FALSETOKEN: { + JSTokenLocation location(tokenLocation()); + next(); + return context.createBoolean(location, false); + } + case DIVEQUAL: + case DIVIDE: { + /* regexp */ + const Identifier* pattern; + const Identifier* flags; + if (match(DIVEQUAL)) + failIfFalse(m_lexer->scanRegExp(pattern, flags, '='), "Invalid regular expression"); + else + failIfFalse(m_lexer->scanRegExp(pattern, flags), "Invalid regular expression"); + + JSTextPosition start = tokenStartPosition(); + JSTokenLocation location(tokenLocation()); + next(); + TreeExpression re = context.createRegExp(location, *pattern, *flags, start); + if (!re) { + const char* yarrErrorMsg = Yarr::checkSyntax(pattern->string()); + regexFail(yarrErrorMsg); + } + return re; + } +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + case TEMPLATE: + return parseTemplateLiteral(context, LexerType::RawStringsBuildMode::DontBuildRawStrings); +#endif + case LET: + if (!strictMode()) + goto identifierExpression; + FALLTHROUGH; + default: + failDueToUnexpectedToken(); + } +} + +template <typename LexerType> +template <class TreeBuilder> TreeArguments Parser<LexerType>::parseArguments(TreeBuilder& context, SpreadMode mode) +{ + consumeOrFailWithFlags(OPENPAREN, TreeBuilder::DontBuildStrings, "Expected opening '(' at start of argument list"); + JSTokenLocation location(tokenLocation()); + if (match(CLOSEPAREN)) { + next(TreeBuilder::DontBuildStrings); + return context.createArguments(); + } + if (match(DOTDOTDOT) && mode == AllowSpread) { + JSTokenLocation spreadLocation(tokenLocation()); + auto start = m_token.m_startPosition; + auto divot = m_token.m_endPosition; + next(); + auto spreadExpr = parseAssignmentExpression(context); + auto end = m_lastTokenEndPosition; + if (!spreadExpr) + failWithMessage("Cannot parse spread expression"); + if (!consume(CLOSEPAREN)) { + if (match(COMMA)) + semanticFail("Spread operator may only be applied to the last argument passed to a function"); + handleProductionOrFail(CLOSEPAREN, ")", "end", "argument list"); + } + auto spread = context.createSpreadExpression(spreadLocation, spreadExpr, start, divot, end); + TreeArgumentsList argList = context.createArgumentsList(location, spread); + return context.createArguments(argList); + } + TreeExpression firstArg = parseAssignmentExpression(context); + failIfFalse(firstArg, "Cannot parse function argument"); + + TreeArgumentsList argList = context.createArgumentsList(location, firstArg); + TreeArgumentsList tail = argList; + while (match(COMMA)) { + JSTokenLocation argumentLocation(tokenLocation()); + next(TreeBuilder::DontBuildStrings); + TreeExpression arg = parseAssignmentExpression(context); + failIfFalse(arg, "Cannot parse function argument"); + tail = context.createArgumentsList(argumentLocation, tail, arg); + } + semanticFailIfTrue(match(DOTDOTDOT), "The '...' operator should come before the target expression"); + handleProductionOrFail(CLOSEPAREN, ")", "end", "argument list"); + return context.createArguments(argList); +} + +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpression(TreeBuilder& context) +{ + TreeExpression base = 0; + JSTextPosition expressionStart = tokenStartPosition(); + int newCount = 0; + JSTokenLocation startLocation = tokenLocation(); + JSTokenLocation location; + while (match(NEW)) { + next(); + newCount++; + } + +#if ENABLE(ES6_CLASS_SYNTAX) + bool baseIsSuper = match(SUPER); + semanticFailIfTrue(baseIsSuper && newCount, "Cannot use new with super"); +#else + bool baseIsSuper = false; +#endif + + bool baseIsNewTarget = false; + if (newCount && match(DOT)) { + next(); + if (match(IDENT)) { + const Identifier* ident = m_token.m_data.ident; + if (m_vm->propertyNames->target == *ident) { + semanticFailIfFalse(currentScope()->isFunction(), "new.target is only valid inside functions"); + baseIsNewTarget = true; + base = context.createNewTargetExpr(location); + newCount--; + next(); + } else + failWithMessage("\"new.\" can only followed with target"); + } else + failDueToUnexpectedToken(); + } + + if (baseIsSuper) { + semanticFailIfFalse(currentScope()->isFunction(), "super is only valid inside functions"); + base = context.createSuperExpr(location); + next(); + currentScope()->setNeedsSuperBinding(); + } else if (!baseIsNewTarget) + base = parsePrimaryExpression(context); + + failIfFalse(base, "Cannot parse base expression"); + while (true) { + location = tokenLocation(); + switch (m_token.m_type) { + case OPENBRACKET: { + m_nonTrivialExpressionCount++; + JSTextPosition expressionEnd = lastTokenEndPosition(); + next(); + int nonLHSCount = m_nonLHSCount; + int initialAssignments = m_assignmentCount; + TreeExpression property = parseExpression(context); + failIfFalse(property, "Cannot parse subscript expression"); + base = context.createBracketAccess(location, base, property, initialAssignments != m_assignmentCount, expressionStart, expressionEnd, tokenEndPosition()); + handleProductionOrFail(CLOSEBRACKET, "]", "end", "subscript expression"); + m_nonLHSCount = nonLHSCount; + break; + } + case OPENPAREN: { + m_nonTrivialExpressionCount++; + int nonLHSCount = m_nonLHSCount; + if (newCount) { + newCount--; + JSTextPosition expressionEnd = lastTokenEndPosition(); + TreeArguments arguments = parseArguments(context, AllowSpread); + failIfFalse(arguments, "Cannot parse call arguments"); + base = context.createNewExpr(location, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition()); + } else { + JSTextPosition expressionEnd = lastTokenEndPosition(); + TreeArguments arguments = parseArguments(context, AllowSpread); + failIfFalse(arguments, "Cannot parse call arguments"); + if (baseIsSuper) + currentScope()->setHasDirectSuper(); + base = context.makeFunctionCallNode(startLocation, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition()); + } + m_nonLHSCount = nonLHSCount; + break; + } + case DOT: { + m_nonTrivialExpressionCount++; + JSTextPosition expressionEnd = lastTokenEndPosition(); + nextExpectIdentifier(LexerFlagsIgnoreReservedWords | TreeBuilder::DontBuildKeywords); + matchOrFail(IDENT, "Expected a property name after '.'"); + base = context.createDotAccess(location, base, m_token.m_data.ident, expressionStart, expressionEnd, tokenEndPosition()); + next(); + break; + } +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + case TEMPLATE: { + semanticFailIfTrue(baseIsSuper, "Cannot use super as tag for tagged templates"); + JSTextPosition expressionEnd = lastTokenEndPosition(); + int nonLHSCount = m_nonLHSCount; + typename TreeBuilder::TemplateLiteral templateLiteral = parseTemplateLiteral(context, LexerType::RawStringsBuildMode::BuildRawStrings); + failIfFalse(templateLiteral, "Cannot parse template literal"); + base = context.createTaggedTemplate(location, base, templateLiteral, expressionStart, expressionEnd, lastTokenEndPosition()); + m_nonLHSCount = nonLHSCount; + break; + } +#endif + default: + goto endMemberExpression; + } + baseIsSuper = false; + } +endMemberExpression: + semanticFailIfTrue(baseIsSuper, "Cannot reference super"); + while (newCount--) + base = context.createNewExpr(location, base, expressionStart, lastTokenEndPosition()); + return base; +} + +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArrowFunctionExpression(TreeBuilder& context) +{ + JSTokenLocation location; + + unsigned functionKeywordStart = tokenStart(); + location = tokenLocation(); + ParserFunctionInfo<TreeBuilder> info; + info.name = &m_vm->propertyNames->nullIdentifier; + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SourceParseMode::ArrowFunctionMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, info, ArrowFunctionParseType)), "Cannot parse arrow function expression"); + + return context.createArrowFunctionExpr(location, info); +} + +static const char* operatorString(bool prefix, unsigned tok) +{ + switch (tok) { + case MINUSMINUS: + case AUTOMINUSMINUS: + return prefix ? "prefix-decrement" : "decrement"; + + case PLUSPLUS: + case AUTOPLUSPLUS: + return prefix ? "prefix-increment" : "increment"; + + case EXCLAMATION: + return "logical-not"; + + case TILDE: + return "bitwise-not"; + + case TYPEOF: + return "typeof"; + + case VOIDTOKEN: + return "void"; + + case DELETETOKEN: + return "delete"; + } + RELEASE_ASSERT_NOT_REACHED(); + return "error"; +} + +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpression(TreeBuilder& context) +{ + typename TreeBuilder::UnaryExprContext unaryExprContext(context); + AllowInOverride allowInOverride(this); + int tokenStackDepth = 0; + bool modifiesExpr = false; + bool requiresLExpr = false; + unsigned lastOperator = 0; + while (isUnaryOp(m_token.m_type)) { + if (strictMode()) { + switch (m_token.m_type) { + case PLUSPLUS: + case MINUSMINUS: + case AUTOPLUSPLUS: + case AUTOMINUSMINUS: + semanticFailIfTrue(requiresLExpr, "The ", operatorString(true, lastOperator), " operator requires a reference expression"); + modifiesExpr = true; + requiresLExpr = true; + break; + case DELETETOKEN: + semanticFailIfTrue(requiresLExpr, "The ", operatorString(true, lastOperator), " operator requires a reference expression"); + requiresLExpr = true; + break; + default: + semanticFailIfTrue(requiresLExpr, "The ", operatorString(true, lastOperator), " operator requires a reference expression"); + break; + } + } + lastOperator = m_token.m_type; + m_nonLHSCount++; + context.appendUnaryToken(tokenStackDepth, m_token.m_type, tokenStartPosition()); + next(); + m_nonTrivialExpressionCount++; + } + JSTextPosition subExprStart = tokenStartPosition(); + ASSERT(subExprStart.offset >= subExprStart.lineStartOffset); + JSTokenLocation location(tokenLocation()); + TreeExpression expr = parseMemberExpression(context); + if (!expr) { + if (lastOperator) + failWithMessage("Cannot parse subexpression of ", operatorString(true, lastOperator), "operator"); + failWithMessage("Cannot parse member expression"); + } + bool isEvalOrArguments = false; + if (strictMode() && !m_syntaxAlreadyValidated) { + if (context.isResolve(expr)) + isEvalOrArguments = *m_lastIdentifier == m_vm->propertyNames->eval || *m_lastIdentifier == m_vm->propertyNames->arguments; + } + failIfTrueIfStrict(isEvalOrArguments && modifiesExpr, "Cannot modify '", m_lastIdentifier->impl(), "' in strict mode"); + switch (m_token.m_type) { + case PLUSPLUS: + m_nonTrivialExpressionCount++; + m_nonLHSCount++; + expr = context.makePostfixNode(location, expr, OpPlusPlus, subExprStart, lastTokenEndPosition(), tokenEndPosition()); + m_assignmentCount++; + failIfTrueIfStrict(isEvalOrArguments, "Cannot modify '", m_lastIdentifier->impl(), "' in strict mode"); + semanticFailIfTrue(requiresLExpr, "The ", operatorString(false, lastOperator), " operator requires a reference expression"); + lastOperator = PLUSPLUS; + next(); + break; + case MINUSMINUS: + m_nonTrivialExpressionCount++; + m_nonLHSCount++; + expr = context.makePostfixNode(location, expr, OpMinusMinus, subExprStart, lastTokenEndPosition(), tokenEndPosition()); + m_assignmentCount++; + failIfTrueIfStrict(isEvalOrArguments, "'", m_lastIdentifier->impl(), "' cannot be modified in strict mode"); + semanticFailIfTrue(requiresLExpr, "The ", operatorString(false, lastOperator), " operator requires a reference expression"); + lastOperator = PLUSPLUS; + next(); + break; + default: + break; + } + + JSTextPosition end = lastTokenEndPosition(); + + if (!TreeBuilder::CreatesAST && (m_syntaxAlreadyValidated || !strictMode())) + return expr; + + location = tokenLocation(); + location.line = m_lexer->lastLineNumber(); + while (tokenStackDepth) { + switch (context.unaryTokenStackLastType(tokenStackDepth)) { + case EXCLAMATION: + expr = context.createLogicalNot(location, expr); + break; + case TILDE: + expr = context.makeBitwiseNotNode(location, expr); + break; + case MINUS: + expr = context.makeNegateNode(location, expr); + break; + case PLUS: + expr = context.createUnaryPlus(location, expr); + break; + case PLUSPLUS: + case AUTOPLUSPLUS: + expr = context.makePrefixNode(location, expr, OpPlusPlus, context.unaryTokenStackLastStart(tokenStackDepth), subExprStart + 1, end); + m_assignmentCount++; + break; + case MINUSMINUS: + case AUTOMINUSMINUS: + expr = context.makePrefixNode(location, expr, OpMinusMinus, context.unaryTokenStackLastStart(tokenStackDepth), subExprStart + 1, end); + m_assignmentCount++; + break; + case TYPEOF: + expr = context.makeTypeOfNode(location, expr); + break; + case VOIDTOKEN: + expr = context.createVoid(location, expr); + break; + case DELETETOKEN: + failIfTrueIfStrict(context.isResolve(expr), "Cannot delete unqualified property '", m_lastIdentifier->impl(), "' in strict mode"); + expr = context.makeDeleteNode(location, expr, context.unaryTokenStackLastStart(tokenStackDepth), end, end); + break; + default: + // If we get here something has gone horribly horribly wrong + CRASH(); + } + subExprStart = context.unaryTokenStackLastStart(tokenStackDepth); + context.unaryTokenStackRemoveLast(tokenStackDepth); + } + return expr; +} + + +template <typename LexerType> void Parser<LexerType>::printUnexpectedTokenText(WTF::PrintStream& out) +{ + switch (m_token.m_type) { + case EOFTOK: + out.print("Unexpected end of script"); + return; + case UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK: + case UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK: + out.print("Incomplete unicode escape in identifier: '", getToken(), "'"); + return; + case UNTERMINATED_MULTILINE_COMMENT_ERRORTOK: + out.print("Unterminated multiline comment"); + return; + case UNTERMINATED_NUMERIC_LITERAL_ERRORTOK: + out.print("Unterminated numeric literal '", getToken(), "'"); + return; + case UNTERMINATED_STRING_LITERAL_ERRORTOK: + out.print("Unterminated string literal '", getToken(), "'"); + return; + case INVALID_IDENTIFIER_ESCAPE_ERRORTOK: + out.print("Invalid escape in identifier: '", getToken(), "'"); + return; + case INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK: + out.print("Invalid unicode escape in identifier: '", getToken(), "'"); + return; + case INVALID_NUMERIC_LITERAL_ERRORTOK: + out.print("Invalid numeric literal: '", getToken(), "'"); + return; + case INVALID_OCTAL_NUMBER_ERRORTOK: + out.print("Invalid use of octal: '", getToken(), "'"); + return; + case INVALID_STRING_LITERAL_ERRORTOK: + out.print("Invalid string literal: '", getToken(), "'"); + return; + case ERRORTOK: + out.print("Unrecognized token '", getToken(), "'"); + return; + case STRING: + out.print("Unexpected string literal ", getToken()); + return; + case INTEGER: + case DOUBLE: + out.print("Unexpected number '", getToken(), "'"); + return; + + case RESERVED_IF_STRICT: + out.print("Unexpected use of reserved word '", getToken(), "' in strict mode"); + return; + + case RESERVED: + out.print("Unexpected use of reserved word '", getToken(), "'"); + return; + + case INVALID_PRIVATE_NAME_ERRORTOK: + out.print("Invalid private name '", getToken(), "'"); + return; + + case IDENT: + out.print("Unexpected identifier '", getToken(), "'"); + return; + + default: + break; + } + + if (m_token.m_type & KeywordTokenFlag) { + out.print("Unexpected keyword '", getToken(), "'"); + return; + } + + out.print("Unexpected token '", getToken(), "'"); +} + +// Instantiate the two flavors of Parser we need instead of putting most of this file in Parser.h +template class Parser<Lexer<LChar>>; +template class Parser<Lexer<UChar>>; + +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/Parser.h b/Source/JavaScriptCore/parser/Parser.h new file mode 100644 index 000000000..c2e6a6504 --- /dev/null +++ b/Source/JavaScriptCore/parser/Parser.h @@ -0,0 +1,1385 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2011, 2013 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef Parser_h +#define Parser_h + +#include "Debugger.h" +#include "ExceptionHelpers.h" +#include "Executable.h" +#include "JSGlobalObject.h" +#include "Lexer.h" +#include "Nodes.h" +#include "ParserArena.h" +#include "ParserError.h" +#include "ParserFunctionInfo.h" +#include "ParserTokens.h" +#include "SourceProvider.h" +#include "SourceProviderCache.h" +#include "SourceProviderCacheItem.h" +#include "VariableEnvironment.h" +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> +#include <wtf/RefPtr.h> +namespace JSC { +struct Scope; +} + +namespace WTF { +template <> struct VectorTraits<JSC::Scope> : SimpleClassVectorTraits { + static const bool canInitializeWithMemset = false; // Not all Scope data members initialize to 0. +}; +} + +namespace JSC { + +class ExecState; +class FunctionMetadataNode; +class FunctionParameters; +class Identifier; +class VM; +class ProgramNode; +class SourceCode; + +// Macros to make the more common TreeBuilder types a little less verbose +#define TreeStatement typename TreeBuilder::Statement +#define TreeExpression typename TreeBuilder::Expression +#define TreeFormalParameterList typename TreeBuilder::FormalParameterList +#define TreeSourceElements typename TreeBuilder::SourceElements +#define TreeClause typename TreeBuilder::Clause +#define TreeClauseList typename TreeBuilder::ClauseList +#define TreeArguments typename TreeBuilder::Arguments +#define TreeArgumentsList typename TreeBuilder::ArgumentsList +#define TreeFunctionBody typename TreeBuilder::FunctionBody +#if ENABLE(ES6_CLASS_SYNTAX) +#define TreeClassExpression typename TreeBuilder::ClassExpression +#endif +#define TreeProperty typename TreeBuilder::Property +#define TreePropertyList typename TreeBuilder::PropertyList +#define TreeDestructuringPattern typename TreeBuilder::DestructuringPattern + +COMPILE_ASSERT(LastUntaggedToken < 64, LessThan64UntaggedTokens); + +enum SourceElementsMode { CheckForStrictMode, DontCheckForStrictMode }; +enum FunctionParseType { StandardFunctionParseType, ArrowFunctionParseType }; +enum FunctionBodyType { ArrowFunctionBodyExpression, ArrowFunctionBodyBlock, StandardFunctionBodyBlock }; +enum FunctionRequirements { FunctionNoRequirements, FunctionNeedsName }; + +enum DestructuringKind { + DestructureToVariables, + DestructureToLet, + DestructureToConst, + DestructureToParameters, + DestructureToExpressions +}; + +enum class DeclarationType { + VarDeclaration, + LetDeclaration, + ConstDeclaration +}; + +enum class DeclarationImportType { + Imported, + NotImported +}; + +enum DeclarationResult { + Valid = 0, + InvalidStrictMode = 1 << 0, + InvalidDuplicateDeclaration = 1 << 1 +}; + +typedef uint8_t DeclarationResultMask; + + +template <typename T> inline bool isEvalNode() { return false; } +template <> inline bool isEvalNode<EvalNode>() { return true; } + +struct ScopeLabelInfo { + UniquedStringImpl* uid; + bool isLoop; +}; + +ALWAYS_INLINE static bool isArguments(const VM* vm, const Identifier* ident) +{ + return vm->propertyNames->arguments == *ident; +} +ALWAYS_INLINE static bool isEval(const VM* vm, const Identifier* ident) +{ + return vm->propertyNames->eval == *ident; +} +ALWAYS_INLINE static bool isEvalOrArgumentsIdentifier(const VM* vm, const Identifier* ident) +{ + return isEval(vm, ident) || isArguments(vm, ident); +} +ALWAYS_INLINE static bool isIdentifierOrKeyword(const JSToken& token) +{ + return token.m_type == IDENT || token.m_type & KeywordTokenFlag; +} + +class ModuleScopeData : public RefCounted<ModuleScopeData> { +public: + static Ref<ModuleScopeData> create() { return adoptRef(*new ModuleScopeData); } + + const IdentifierSet& exportedBindings() const { return m_exportedBindings; } + + bool exportName(const Identifier& exportedName) + { + return m_exportedNames.add(exportedName.impl()).isNewEntry; + } + + void exportBinding(const Identifier& localName) + { + m_exportedBindings.add(localName.impl()); + } + +private: + IdentifierSet m_exportedNames { }; + IdentifierSet m_exportedBindings { }; +}; + +struct Scope { + Scope(const VM* vm, bool isFunction, bool strictMode) + : m_vm(vm) + , m_shadowsArguments(false) + , m_usesEval(false) + , m_needsFullActivation(false) + , m_hasDirectSuper(false) + , m_needsSuperBinding(false) + , m_allowsVarDeclarations(true) + , m_allowsLexicalDeclarations(true) + , m_strictMode(strictMode) + , m_isFunction(isFunction) + , m_isLexicalScope(false) + , m_isFunctionBoundary(false) + , m_isValidStrictMode(true) + , m_loopDepth(0) + , m_switchDepth(0) + { + } + + Scope(const Scope& rhs) + : m_vm(rhs.m_vm) + , m_shadowsArguments(rhs.m_shadowsArguments) + , m_usesEval(rhs.m_usesEval) + , m_needsFullActivation(rhs.m_needsFullActivation) + , m_hasDirectSuper(rhs.m_hasDirectSuper) + , m_needsSuperBinding(rhs.m_needsSuperBinding) + , m_allowsVarDeclarations(rhs.m_allowsVarDeclarations) + , m_allowsLexicalDeclarations(rhs.m_allowsLexicalDeclarations) + , m_strictMode(rhs.m_strictMode) + , m_isFunction(rhs.m_isFunction) + , m_isLexicalScope(rhs.m_isLexicalScope) + , m_isFunctionBoundary(rhs.m_isFunctionBoundary) + , m_isValidStrictMode(rhs.m_isValidStrictMode) + , m_loopDepth(rhs.m_loopDepth) + , m_switchDepth(rhs.m_switchDepth) + , m_moduleScopeData(rhs.m_moduleScopeData) + { + if (rhs.m_labels) { + m_labels = std::make_unique<LabelStack>(); + + typedef LabelStack::const_iterator iterator; + iterator end = rhs.m_labels->end(); + for (iterator it = rhs.m_labels->begin(); it != end; ++it) + m_labels->append(ScopeLabelInfo { it->uid, it->isLoop }); + } + } + + void startSwitch() { m_switchDepth++; } + void endSwitch() { m_switchDepth--; } + void startLoop() { m_loopDepth++; } + void endLoop() { ASSERT(m_loopDepth); m_loopDepth--; } + bool inLoop() { return !!m_loopDepth; } + bool breakIsValid() { return m_loopDepth || m_switchDepth; } + bool continueIsValid() { return m_loopDepth; } + + void pushLabel(const Identifier* label, bool isLoop) + { + if (!m_labels) + m_labels = std::make_unique<LabelStack>(); + m_labels->append(ScopeLabelInfo { label->impl(), isLoop }); + } + + void popLabel() + { + ASSERT(m_labels); + ASSERT(m_labels->size()); + m_labels->removeLast(); + } + + ScopeLabelInfo* getLabel(const Identifier* label) + { + if (!m_labels) + return 0; + for (int i = m_labels->size(); i > 0; i--) { + if (m_labels->at(i - 1).uid == label->impl()) + return &m_labels->at(i - 1); + } + return 0; + } + + void setIsFunction() + { + m_isFunction = true; + m_isFunctionBoundary = true; + setIsLexicalScope(); + } + + void setIsModule() + { + m_moduleScopeData = ModuleScopeData::create(); + } + + bool isFunction() const { return m_isFunction; } + bool isFunctionBoundary() const { return m_isFunctionBoundary; } + + void setIsLexicalScope() + { + m_isLexicalScope = true; + m_allowsLexicalDeclarations = true; + } + bool isLexicalScope() { return m_isLexicalScope; } + + VariableEnvironment& declaredVariables() { return m_declaredVariables; } + VariableEnvironment& lexicalVariables() { return m_lexicalVariables; } + VariableEnvironment& finalizeLexicalEnvironment() + { + if (m_usesEval || m_needsFullActivation) + m_lexicalVariables.markAllVariablesAsCaptured(); + else + computeLexicallyCapturedVariablesAndPurgeCandidates(); + + return m_lexicalVariables; + } + + ModuleScopeData& moduleScopeData() const + { + ASSERT(m_moduleScopeData); + return *m_moduleScopeData; + } + + void computeLexicallyCapturedVariablesAndPurgeCandidates() + { + // Because variables may be defined at any time in the range of a lexical scope, we must + // track lexical variables that might be captured. Then, when we're preparing to pop the top + // lexical scope off the stack, we should find which variables are truly captured, and which + // variable still may be captured in a parent scope. + if (m_lexicalVariables.size() && m_closedVariableCandidates.size()) { + auto end = m_closedVariableCandidates.end(); + for (auto iter = m_closedVariableCandidates.begin(); iter != end; ++iter) + m_lexicalVariables.markVariableAsCapturedIfDefined(iter->get()); + } + + // We can now purge values from the captured candidates because they're captured in this scope. + { + for (auto entry : m_lexicalVariables) { + if (entry.value.isCaptured()) + m_closedVariableCandidates.remove(entry.key); + } + } + } + + void declareCallee(const Identifier* ident) + { + auto addResult = m_declaredVariables.add(ident->impl()); + // We want to track if callee is captured, but we don't want to act like it's a 'var' + // because that would cause the BytecodeGenerator to emit bad code. + addResult.iterator->value.clearIsVar(); + } + + DeclarationResultMask declareVariable(const Identifier* ident) + { + ASSERT(m_allowsVarDeclarations); + DeclarationResultMask result = DeclarationResult::Valid; + bool isValidStrictMode = !isEvalOrArgumentsIdentifier(m_vm, ident); + m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode; + auto addResult = m_declaredVariables.add(ident->impl()); + addResult.iterator->value.setIsVar(); + if (!isValidStrictMode) + result |= DeclarationResult::InvalidStrictMode; + return result; + } + + DeclarationResultMask declareLexicalVariable(const Identifier* ident, bool isConstant, DeclarationImportType importType = DeclarationImportType::NotImported) + { + ASSERT(m_allowsLexicalDeclarations); + DeclarationResultMask result = DeclarationResult::Valid; + bool isValidStrictMode = !isEvalOrArgumentsIdentifier(m_vm, ident); + m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode; + auto addResult = m_lexicalVariables.add(ident->impl()); + if (isConstant) + addResult.iterator->value.setIsConst(); + else + addResult.iterator->value.setIsLet(); + + if (importType == DeclarationImportType::Imported) + addResult.iterator->value.setIsImported(); + + if (!addResult.isNewEntry) + result |= DeclarationResult::InvalidDuplicateDeclaration; + if (!isValidStrictMode) + result |= DeclarationResult::InvalidStrictMode; + + return result; + } + + bool hasDeclaredVariable(const Identifier& ident) + { + return hasDeclaredVariable(ident.impl()); + } + + bool hasDeclaredVariable(const RefPtr<UniquedStringImpl>& ident) + { + return m_declaredVariables.contains(ident.get()); + } + + bool hasLexicallyDeclaredVariable(const RefPtr<UniquedStringImpl>& ident) const + { + return m_lexicalVariables.contains(ident.get()); + } + + ALWAYS_INLINE bool hasDeclaredParameter(const Identifier& ident) + { + return hasDeclaredParameter(ident.impl()); + } + + bool hasDeclaredParameter(const RefPtr<UniquedStringImpl>& ident) + { + return m_declaredParameters.contains(ident) || m_declaredVariables.contains(ident.get()); + } + + void declareWrite(const Identifier* ident) + { + ASSERT(m_strictMode); + m_writtenVariables.add(ident->impl()); + } + + void preventAllVariableDeclarations() + { + m_allowsVarDeclarations = false; + m_allowsLexicalDeclarations = false; + } + void preventVarDeclarations() { m_allowsVarDeclarations = false; } + bool allowsVarDeclarations() const { return m_allowsVarDeclarations; } + bool allowsLexicalDeclarations() const { return m_allowsLexicalDeclarations; } + + DeclarationResultMask declareParameter(const Identifier* ident) + { + ASSERT(m_allowsVarDeclarations); + DeclarationResultMask result = DeclarationResult::Valid; + bool isArgumentsIdent = isArguments(m_vm, ident); + auto addResult = m_declaredVariables.add(ident->impl()); + addResult.iterator->value.clearIsVar(); + bool isValidStrictMode = addResult.isNewEntry && m_vm->propertyNames->eval != *ident && !isArgumentsIdent; + m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode; + m_declaredParameters.add(ident->impl()); + if (!isValidStrictMode) + result |= DeclarationResult::InvalidStrictMode; + if (isArgumentsIdent) + m_shadowsArguments = true; + if (!addResult.isNewEntry) + result |= DeclarationResult::InvalidDuplicateDeclaration; + + return result; + } + + enum BindingResult { + BindingFailed, + StrictBindingFailed, + BindingSucceeded + }; + BindingResult declareBoundParameter(const Identifier* ident) + { + bool isArgumentsIdent = isArguments(m_vm, ident); + auto addResult = m_declaredVariables.add(ident->impl()); + addResult.iterator->value.setIsVar(); // Treat destructuring parameters as "var"s. + bool isValidStrictMode = addResult.isNewEntry && !isEval(m_vm, ident) && !isArgumentsIdent; + m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode; + + if (isArgumentsIdent) + m_shadowsArguments = true; + if (!addResult.isNewEntry) + return BindingFailed; + return isValidStrictMode ? BindingSucceeded : StrictBindingFailed; + } + + void getUsedVariables(IdentifierSet& usedVariables) + { + usedVariables.swap(m_usedVariables); + } + + void useVariable(const Identifier* ident, bool isEval) + { + m_usesEval |= isEval; + m_usedVariables.add(ident->impl()); + } + + void setNeedsFullActivation() { m_needsFullActivation = true; } + bool needsFullActivation() const { return m_needsFullActivation; } + +#if ENABLE(ES6_CLASS_SYNTAX) + bool hasDirectSuper() { return m_hasDirectSuper; } +#else + bool hasDirectSuper() { return false; } +#endif + void setHasDirectSuper() { m_hasDirectSuper = true; } + +#if ENABLE(ES6_CLASS_SYNTAX) + bool needsSuperBinding() { return m_needsSuperBinding; } +#else + bool needsSuperBinding() { return false; } +#endif + void setNeedsSuperBinding() { m_needsSuperBinding = true; } + + void collectFreeVariables(Scope* nestedScope, bool shouldTrackClosedVariables) + { + if (nestedScope->m_usesEval) + m_usesEval = true; + + { + IdentifierSet::iterator end = nestedScope->m_usedVariables.end(); + for (IdentifierSet::iterator ptr = nestedScope->m_usedVariables.begin(); ptr != end; ++ptr) { + if (nestedScope->m_declaredVariables.contains(*ptr) || nestedScope->m_lexicalVariables.contains(*ptr)) + continue; + m_usedVariables.add(*ptr); + // We don't want a declared variable that is used in an inner scope to be thought of as captured if + // that inner scope is both a lexical scope and not a function. Only inner functions and "catch" + // statements can cause variables to be captured. + if (shouldTrackClosedVariables && (nestedScope->m_isFunctionBoundary || !nestedScope->m_isLexicalScope)) + m_closedVariableCandidates.add(*ptr); + } + } + // Propagate closed variable candidates downwards within the same function. + // Cross function captures will be realized via m_usedVariables propagation. + if (shouldTrackClosedVariables && !nestedScope->m_isFunctionBoundary && nestedScope->m_closedVariableCandidates.size()) { + IdentifierSet::iterator end = nestedScope->m_closedVariableCandidates.end(); + IdentifierSet::iterator begin = nestedScope->m_closedVariableCandidates.begin(); + m_closedVariableCandidates.add(begin, end); + } + + if (nestedScope->m_writtenVariables.size()) { + IdentifierSet::iterator end = nestedScope->m_writtenVariables.end(); + for (IdentifierSet::iterator ptr = nestedScope->m_writtenVariables.begin(); ptr != end; ++ptr) { + if (nestedScope->m_declaredVariables.contains(*ptr) || nestedScope->m_lexicalVariables.contains(*ptr)) + continue; + m_writtenVariables.add(*ptr); + } + } + } + + void getCapturedVars(IdentifierSet& capturedVariables, bool& modifiedParameter, bool& modifiedArguments) + { + if (m_needsFullActivation || m_usesEval) { + modifiedParameter = true; + for (auto& entry : m_declaredVariables) + capturedVariables.add(entry.key); + return; + } + for (IdentifierSet::iterator ptr = m_closedVariableCandidates.begin(); ptr != m_closedVariableCandidates.end(); ++ptr) { + if (!m_declaredVariables.contains(*ptr)) + continue; + capturedVariables.add(*ptr); + } + modifiedParameter = false; + if (shadowsArguments()) + modifiedArguments = true; + if (m_declaredParameters.size()) { + IdentifierSet::iterator end = m_writtenVariables.end(); + for (IdentifierSet::iterator ptr = m_writtenVariables.begin(); ptr != end; ++ptr) { + if (*ptr == m_vm->propertyNames->arguments.impl()) + modifiedArguments = true; + if (!m_declaredParameters.contains(*ptr)) + continue; + modifiedParameter = true; + break; + } + } + } + void setStrictMode() { m_strictMode = true; } + bool strictMode() const { return m_strictMode; } + bool isValidStrictMode() const { return m_isValidStrictMode; } + bool shadowsArguments() const { return m_shadowsArguments; } + + void copyCapturedVariablesToVector(const IdentifierSet& capturedVariables, Vector<RefPtr<UniquedStringImpl>>& vector) + { + IdentifierSet::iterator end = capturedVariables.end(); + for (IdentifierSet::iterator it = capturedVariables.begin(); it != end; ++it) { + if (m_declaredVariables.contains(*it) || m_lexicalVariables.contains(*it)) + continue; + vector.append(*it); + } + } + + void fillParametersForSourceProviderCache(SourceProviderCacheItemCreationParameters& parameters) + { + ASSERT(m_isFunction); + parameters.usesEval = m_usesEval; + parameters.strictMode = m_strictMode; + parameters.needsFullActivation = m_needsFullActivation; + copyCapturedVariablesToVector(m_writtenVariables, parameters.writtenVariables); + copyCapturedVariablesToVector(m_usedVariables, parameters.usedVariables); + } + + void restoreFromSourceProviderCache(const SourceProviderCacheItem* info) + { + ASSERT(m_isFunction); + m_usesEval = info->usesEval; + m_strictMode = info->strictMode; + m_needsFullActivation = info->needsFullActivation; + for (unsigned i = 0; i < info->usedVariablesCount; ++i) + m_usedVariables.add(info->usedVariables()[i]); + for (unsigned i = 0; i < info->writtenVariablesCount; ++i) + m_writtenVariables.add(info->writtenVariables()[i]); + } + +private: + const VM* m_vm; + bool m_shadowsArguments : 1; + bool m_usesEval : 1; + bool m_needsFullActivation : 1; + bool m_hasDirectSuper : 1; + bool m_needsSuperBinding : 1; + bool m_allowsVarDeclarations : 1; + bool m_allowsLexicalDeclarations : 1; + bool m_strictMode : 1; + bool m_isFunction : 1; + bool m_isLexicalScope : 1; + bool m_isFunctionBoundary : 1; + bool m_isValidStrictMode : 1; + int m_loopDepth; + int m_switchDepth; + + typedef Vector<ScopeLabelInfo, 2> LabelStack; + std::unique_ptr<LabelStack> m_labels; + IdentifierSet m_declaredParameters; + VariableEnvironment m_declaredVariables; + VariableEnvironment m_lexicalVariables; + IdentifierSet m_usedVariables; + IdentifierSet m_closedVariableCandidates; + IdentifierSet m_writtenVariables; + RefPtr<ModuleScopeData> m_moduleScopeData { }; +}; + +typedef Vector<Scope, 10> ScopeStack; + +struct ScopeRef { + ScopeRef(ScopeStack* scopeStack, unsigned index) + : m_scopeStack(scopeStack) + , m_index(index) + { + } + Scope* operator->() { return &m_scopeStack->at(m_index); } + unsigned index() const { return m_index; } + + bool hasContainingScope() + { + return m_index && !m_scopeStack->at(m_index).isFunctionBoundary(); + } + + ScopeRef containingScope() + { + ASSERT(hasContainingScope()); + return ScopeRef(m_scopeStack, m_index - 1); + } + +private: + ScopeStack* m_scopeStack; + unsigned m_index; +}; + +template <typename LexerType> +class Parser { + WTF_MAKE_NONCOPYABLE(Parser); + WTF_MAKE_FAST_ALLOCATED; + +public: + Parser( + VM*, const SourceCode&, JSParserBuiltinMode, JSParserStrictMode, SourceParseMode, + ConstructorKind defaultConstructorKind = ConstructorKind::None, ThisTDZMode = ThisTDZMode::CheckIfNeeded); + ~Parser(); + + template <class ParsedNode> + std::unique_ptr<ParsedNode> parse(ParserError&, const Identifier&, SourceParseMode); + + JSTextPosition positionBeforeLastNewline() const { return m_lexer->positionBeforeLastNewline(); } + JSTokenLocation locationBeforeLastToken() const { return m_lexer->lastTokenLocation(); } + Vector<RefPtr<UniquedStringImpl>>&& closedVariables() { return WTF::move(m_closedVariables); } + +private: + struct AllowInOverride { + AllowInOverride(Parser* parser) + : m_parser(parser) + , m_oldAllowsIn(parser->m_allowsIn) + { + parser->m_allowsIn = true; + } + ~AllowInOverride() + { + m_parser->m_allowsIn = m_oldAllowsIn; + } + Parser* m_parser; + bool m_oldAllowsIn; + }; + + struct AutoPopScopeRef : public ScopeRef { + AutoPopScopeRef(Parser* parser, ScopeRef scope) + : ScopeRef(scope) + , m_parser(parser) + { + } + + ~AutoPopScopeRef() + { + if (m_parser) + m_parser->popScope(*this, false); + } + + void setPopped() + { + m_parser = 0; + } + + private: + Parser* m_parser; + }; + + struct AutoCleanupLexicalScope { + // We can allocate this object on the stack without actually knowing beforehand if we're + // going to create a new lexical scope. If we decide to create a new lexical scope, we + // can pass the scope into this obejct and it will take care of the cleanup for us if the parse fails. + // This is helpful if we may fail from syntax errors after creating a lexical scope conditionally. + AutoCleanupLexicalScope() + : m_scope(nullptr, UINT_MAX) + , m_parser(nullptr) + { + } + + ~AutoCleanupLexicalScope() + { + // This should only ever be called if we fail from a syntax error. Otherwise + // it's the intention that a user of this class pops this scope manually on a + // successful parse. + if (isValid()) + m_parser->popScope(*this, false); + } + + void setIsValid(ScopeRef& scope, Parser* parser) + { + RELEASE_ASSERT(scope->isLexicalScope()); + m_scope = scope; + m_parser = parser; + } + + bool isValid() const { return !!m_parser; } + + void setPopped() + { + m_parser = nullptr; + } + + ScopeRef& scope() { return m_scope; } + + private: + ScopeRef m_scope; + Parser* m_parser; + }; + + ALWAYS_INLINE DestructuringKind destructuringKindFromDeclarationType(DeclarationType type) + { + switch (type) { + case DeclarationType::VarDeclaration: + return DestructureToVariables; + case DeclarationType::LetDeclaration: + return DestructureToLet; + case DeclarationType::ConstDeclaration: + return DestructureToConst; + } + + RELEASE_ASSERT_NOT_REACHED(); + return DestructureToVariables; + } + + ALWAYS_INLINE AssignmentContext assignmentContextFromDeclarationType(DeclarationType type) + { + switch (type) { + case DeclarationType::ConstDeclaration: + return AssignmentContext::ConstDeclarationStatement; + default: + return AssignmentContext::DeclarationStatement; + } + } + + ALWAYS_INLINE bool isEvalOrArguments(const Identifier* ident) { return isEvalOrArgumentsIdentifier(m_vm, ident); } + + ScopeRef currentScope() + { + return ScopeRef(&m_scopeStack, m_scopeStack.size() - 1); + } + + ScopeRef pushScope() + { + bool isFunction = false; + bool isStrict = false; + if (!m_scopeStack.isEmpty()) { + isStrict = m_scopeStack.last().strictMode(); + isFunction = m_scopeStack.last().isFunction(); + } + m_scopeStack.append(Scope(m_vm, isFunction, isStrict)); + return currentScope(); + } + + void popScopeInternal(ScopeRef& scope, bool shouldTrackClosedVariables) + { + ASSERT_UNUSED(scope, scope.index() == m_scopeStack.size() - 1); + ASSERT(m_scopeStack.size() > 1); + m_scopeStack[m_scopeStack.size() - 2].collectFreeVariables(&m_scopeStack.last(), shouldTrackClosedVariables); + if (!m_scopeStack.last().isFunctionBoundary() && m_scopeStack.last().needsFullActivation()) + m_scopeStack[m_scopeStack.size() - 2].setNeedsFullActivation(); + m_scopeStack.removeLast(); + } + + ALWAYS_INLINE void popScope(ScopeRef& scope, bool shouldTrackClosedVariables) + { + popScopeInternal(scope, shouldTrackClosedVariables); + } + + ALWAYS_INLINE void popScope(AutoPopScopeRef& scope, bool shouldTrackClosedVariables) + { + scope.setPopped(); + popScopeInternal(scope, shouldTrackClosedVariables); + } + + ALWAYS_INLINE void popScope(AutoCleanupLexicalScope& cleanupScope, bool shouldTrackClosedVariables) + { + RELEASE_ASSERT(cleanupScope.isValid()); + ScopeRef& scope = cleanupScope.scope(); + cleanupScope.setPopped(); + popScopeInternal(scope, shouldTrackClosedVariables); + } + + DeclarationResultMask declareVariable(const Identifier* ident, DeclarationType type = DeclarationType::VarDeclaration, DeclarationImportType importType = DeclarationImportType::NotImported) + { + unsigned i = m_scopeStack.size() - 1; + ASSERT(i < m_scopeStack.size()); + + if (type == DeclarationType::VarDeclaration) { + while (!m_scopeStack[i].allowsVarDeclarations()) { + i--; + ASSERT(i < m_scopeStack.size()); + } + + return m_scopeStack[i].declareVariable(ident); + } + + ASSERT(type == DeclarationType::LetDeclaration || type == DeclarationType::ConstDeclaration); + + // Lexical variables declared at a top level scope that shadow arguments or vars are not allowed. + if (m_statementDepth == 1 && (hasDeclaredParameter(*ident) || hasDeclaredVariable(*ident))) + return DeclarationResult::InvalidDuplicateDeclaration; + + while (!m_scopeStack[i].allowsLexicalDeclarations()) { + i--; + ASSERT(i < m_scopeStack.size()); + } + + return m_scopeStack[i].declareLexicalVariable(ident, type == DeclarationType::ConstDeclaration, importType); + } + + NEVER_INLINE bool hasDeclaredVariable(const Identifier& ident) + { + unsigned i = m_scopeStack.size() - 1; + ASSERT(i < m_scopeStack.size()); + while (!m_scopeStack[i].allowsVarDeclarations()) { + i--; + ASSERT(i < m_scopeStack.size()); + } + return m_scopeStack[i].hasDeclaredVariable(ident); + } + + NEVER_INLINE bool hasDeclaredParameter(const Identifier& ident) + { + unsigned i = m_scopeStack.size() - 1; + ASSERT(i < m_scopeStack.size()); + while (!m_scopeStack[i].allowsVarDeclarations()) { + i--; + ASSERT(i < m_scopeStack.size()); + } + return m_scopeStack[i].hasDeclaredParameter(ident); + } + + void declareWrite(const Identifier* ident) + { + if (!m_syntaxAlreadyValidated || strictMode()) + m_scopeStack.last().declareWrite(ident); + } + + bool exportName(const Identifier& ident) + { + ASSERT(currentScope().index() == 0); + return currentScope()->moduleScopeData().exportName(ident); + } + + ScopeStack m_scopeStack; + + const SourceProviderCacheItem* findCachedFunctionInfo(int openBracePos) + { + return m_functionCache ? m_functionCache->get(openBracePos) : 0; + } + + Parser(); + String parseInner(const Identifier&, SourceParseMode); + + void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&, VariableEnvironment&, CodeFeatures, int, const Vector<RefPtr<UniquedStringImpl>>&&); + + // Used to determine type of error to report. + bool isFunctionMetadataNode(ScopeNode*) { return false; } + bool isFunctionMetadataNode(FunctionMetadataNode*) { return true; } + + ALWAYS_INLINE void next(unsigned lexerFlags = 0) + { + int lastLine = m_token.m_location.line; + int lastTokenEnd = m_token.m_location.endOffset; + int lastTokenLineStart = m_token.m_location.lineStartOffset; + m_lastTokenEndPosition = JSTextPosition(lastLine, lastTokenEnd, lastTokenLineStart); + m_lexer->setLastLineNumber(lastLine); + m_token.m_type = m_lexer->lex(&m_token, lexerFlags, strictMode()); + } + + ALWAYS_INLINE void nextExpectIdentifier(unsigned lexerFlags = 0) + { + int lastLine = m_token.m_location.line; + int lastTokenEnd = m_token.m_location.endOffset; + int lastTokenLineStart = m_token.m_location.lineStartOffset; + m_lastTokenEndPosition = JSTextPosition(lastLine, lastTokenEnd, lastTokenLineStart); + m_lexer->setLastLineNumber(lastLine); + m_token.m_type = m_lexer->lexExpectIdentifier(&m_token, lexerFlags, strictMode()); + } + + ALWAYS_INLINE bool nextTokenIsColon() + { + return m_lexer->nextTokenIsColon(); + } + + ALWAYS_INLINE bool consume(JSTokenType expected, unsigned flags = 0) + { + bool result = m_token.m_type == expected; + if (result) + next(flags); + return result; + } + + void printUnexpectedTokenText(WTF::PrintStream&); + ALWAYS_INLINE String getToken() { + SourceProvider* sourceProvider = m_source->provider(); + return sourceProvider->getRange(tokenStart(), tokenEndPosition().offset); + } + + ALWAYS_INLINE bool match(JSTokenType expected) + { + return m_token.m_type == expected; + } + + ALWAYS_INLINE bool matchContextualKeyword(const Identifier& identifier) + { + return m_token.m_type == IDENT && *m_token.m_data.ident == identifier; + } + + ALWAYS_INLINE bool matchIdentifierOrKeyword() + { + return isIdentifierOrKeyword(m_token); + } + + ALWAYS_INLINE bool isEndOfArrowFunction() + { + return match(SEMICOLON) || match(COMMA) || match(CLOSEPAREN) || match(CLOSEBRACE) || match(CLOSEBRACKET) || match(EOFTOK) || m_lexer->prevTerminator(); + } + + ALWAYS_INLINE bool isArrowFunctionParamters() + { +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + bool isArrowFunction = false; + + if (match(EOFTOK)) + return isArrowFunction; + + SavePoint saveArrowFunctionPoint = createSavePoint(); + + if (consume(OPENPAREN)) { + bool isArrowFunctionParamters = true; + + while (consume(IDENT)) { + if (consume(COMMA)) { + if (!match(IDENT)) { + isArrowFunctionParamters = false; + break; + } + } else + break; + } + + if (isArrowFunctionParamters) { + if (consume(CLOSEPAREN) && match(ARROWFUNCTION)) + isArrowFunction = true; + } + } else if (consume(IDENT) && match(ARROWFUNCTION)) + isArrowFunction = true; + + restoreSavePoint(saveArrowFunctionPoint); + + return isArrowFunction; +#else + return false; +#endif + } + + ALWAYS_INLINE unsigned tokenStart() + { + return m_token.m_location.startOffset; + } + + ALWAYS_INLINE const JSTextPosition& tokenStartPosition() + { + return m_token.m_startPosition; + } + + ALWAYS_INLINE int tokenLine() + { + return m_token.m_location.line; + } + + ALWAYS_INLINE int tokenColumn() + { + return tokenStart() - tokenLineStart(); + } + + ALWAYS_INLINE const JSTextPosition& tokenEndPosition() + { + return m_token.m_endPosition; + } + + ALWAYS_INLINE unsigned tokenLineStart() + { + return m_token.m_location.lineStartOffset; + } + + ALWAYS_INLINE const JSTokenLocation& tokenLocation() + { + return m_token.m_location; + } + + void setErrorMessage(const String& message) + { + m_errorMessage = message; + } + + NEVER_INLINE void logError(bool); + template <typename A> NEVER_INLINE void logError(bool, const A&); + template <typename A, typename B> NEVER_INLINE void logError(bool, const A&, const B&); + template <typename A, typename B, typename C> NEVER_INLINE void logError(bool, const A&, const B&, const C&); + template <typename A, typename B, typename C, typename D> NEVER_INLINE void logError(bool, const A&, const B&, const C&, const D&); + template <typename A, typename B, typename C, typename D, typename E> NEVER_INLINE void logError(bool, const A&, const B&, const C&, const D&, const E&); + template <typename A, typename B, typename C, typename D, typename E, typename F> NEVER_INLINE void logError(bool, const A&, const B&, const C&, const D&, const E&, const F&); + template <typename A, typename B, typename C, typename D, typename E, typename F, typename G> NEVER_INLINE void logError(bool, const A&, const B&, const C&, const D&, const E&, const F&, const G&); + + NEVER_INLINE void updateErrorWithNameAndMessage(const char* beforeMessage, const String& name, const char* afterMessage) + { + m_errorMessage = makeString(beforeMessage, " '", name, "' ", afterMessage); + } + + NEVER_INLINE void updateErrorMessage(const char* msg) + { + ASSERT(msg); + m_errorMessage = String(msg); + ASSERT(!m_errorMessage.isNull()); + } + + void startLoop() { currentScope()->startLoop(); } + void endLoop() { currentScope()->endLoop(); } + void startSwitch() { currentScope()->startSwitch(); } + void endSwitch() { currentScope()->endSwitch(); } + void setStrictMode() { currentScope()->setStrictMode(); } + bool strictMode() { return currentScope()->strictMode(); } + bool isValidStrictMode() { return currentScope()->isValidStrictMode(); } + DeclarationResultMask declareParameter(const Identifier* ident) { return currentScope()->declareParameter(ident); } + Scope::BindingResult declareBoundParameter(const Identifier* ident) { return currentScope()->declareBoundParameter(ident); } + bool breakIsValid() + { + ScopeRef current = currentScope(); + while (!current->breakIsValid()) { + if (!current.hasContainingScope()) + return false; + current = current.containingScope(); + } + return true; + } + bool continueIsValid() + { + ScopeRef current = currentScope(); + while (!current->continueIsValid()) { + if (!current.hasContainingScope()) + return false; + current = current.containingScope(); + } + return true; + } + void pushLabel(const Identifier* label, bool isLoop) { currentScope()->pushLabel(label, isLoop); } + void popLabel(ScopeRef scope) { scope->popLabel(); } + ScopeLabelInfo* getLabel(const Identifier* label) + { + ScopeRef current = currentScope(); + ScopeLabelInfo* result = 0; + while (!(result = current->getLabel(label))) { + if (!current.hasContainingScope()) + return 0; + current = current.containingScope(); + } + return result; + } + + ALWAYS_INLINE bool isLETMaskedAsIDENT() + { + return match(LET) && !strictMode(); + } + + template <class TreeBuilder> TreeSourceElements parseSourceElements(TreeBuilder&, SourceElementsMode); + template <class TreeBuilder> TreeStatement parseStatementListItem(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength); + template <class TreeBuilder> TreeStatement parseStatement(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength = 0); + enum class ExportType { Exported, NotExported }; +#if ENABLE(ES6_CLASS_SYNTAX) + template <class TreeBuilder> TreeStatement parseClassDeclaration(TreeBuilder&, ExportType = ExportType::NotExported); +#endif + template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported); + template <class TreeBuilder> TreeStatement parseVariableDeclaration(TreeBuilder&, DeclarationType, ExportType = ExportType::NotExported); + template <class TreeBuilder> TreeStatement parseDoWhileStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseWhileStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseForStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseBreakStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseContinueStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseReturnStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseThrowStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseWithStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseSwitchStatement(TreeBuilder&); + template <class TreeBuilder> TreeClauseList parseSwitchClauses(TreeBuilder&); + template <class TreeBuilder> TreeClause parseSwitchDefaultClause(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseTryStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseDebuggerStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseExpressionStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseExpressionOrLabelStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseIfStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseBlockStatement(TreeBuilder&); + template <class TreeBuilder> TreeExpression parseExpression(TreeBuilder&); + template <class TreeBuilder> TreeExpression parseAssignmentExpression(TreeBuilder&); + template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseConditionalExpression(TreeBuilder&); + template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseBinaryExpression(TreeBuilder&); + template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseUnaryExpression(TreeBuilder&); + template <class TreeBuilder> TreeExpression parseMemberExpression(TreeBuilder&); + template <class TreeBuilder> ALWAYS_INLINE TreeExpression parsePrimaryExpression(TreeBuilder&); + template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseArrayLiteral(TreeBuilder&); + template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseObjectLiteral(TreeBuilder&); + template <class TreeBuilder> NEVER_INLINE TreeExpression parseStrictObjectLiteral(TreeBuilder&); + enum SpreadMode { AllowSpread, DontAllowSpread }; + template <class TreeBuilder> ALWAYS_INLINE TreeArguments parseArguments(TreeBuilder&, SpreadMode); + template <class TreeBuilder> TreeProperty parseProperty(TreeBuilder&, bool strict); + template <class TreeBuilder> TreeExpression parsePropertyMethod(TreeBuilder& context, const Identifier* methodName); + template <class TreeBuilder> TreeProperty parseGetterSetter(TreeBuilder&, bool strict, PropertyNode::Type, unsigned getterOrSetterStartOffset, ConstructorKind = ConstructorKind::None, SuperBinding = SuperBinding::NotNeeded); + template <class TreeBuilder> ALWAYS_INLINE TreeFunctionBody parseFunctionBody(TreeBuilder&, const JSTokenLocation&, int, int functionKeywordStart, int functionNameStart, int parametersStart, ConstructorKind, FunctionBodyType, unsigned, SourceParseMode); + template <class TreeBuilder> ALWAYS_INLINE bool parseFormalParameters(TreeBuilder&, TreeFormalParameterList, unsigned&); + enum VarDeclarationListContext { ForLoopContext, VarDeclarationContext }; + template <class TreeBuilder> TreeExpression parseVariableDeclarationList(TreeBuilder&, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext, DeclarationType, ExportType, bool& forLoopConstDoesNotHaveInitializer); + template <class TreeBuilder> TreeSourceElements parseArrowFunctionSingleExpressionBodySourceElements(TreeBuilder&); + template <class TreeBuilder> TreeExpression parseArrowFunctionExpression(TreeBuilder&); + template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern createBindingPattern(TreeBuilder&, DestructuringKind, ExportType, const Identifier&, int depth, JSToken, AssignmentContext, const Identifier** duplicateIdentifier); + template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern parseDestructuringPattern(TreeBuilder&, DestructuringKind, ExportType, const Identifier** duplicateIdentifier = nullptr, bool* hasDestructuringPattern = nullptr, AssignmentContext = AssignmentContext::DeclarationStatement, int depth = 0); + template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern tryParseDestructuringPatternExpression(TreeBuilder&, AssignmentContext); + template <class TreeBuilder> NEVER_INLINE TreeExpression parseDefaultValueForDestructuringPattern(TreeBuilder&); + template <class TreeBuilder> TreeSourceElements parseModuleSourceElements(TreeBuilder&, SourceParseMode); + enum class ImportSpecifierType { NamespaceImport, NamedImport, DefaultImport }; + template <class TreeBuilder> typename TreeBuilder::ImportSpecifier parseImportClauseItem(TreeBuilder&, ImportSpecifierType); + template <class TreeBuilder> typename TreeBuilder::ModuleName parseModuleName(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseImportDeclaration(TreeBuilder&); + template <class TreeBuilder> typename TreeBuilder::ExportSpecifier parseExportSpecifier(TreeBuilder& context, Vector<const Identifier*>& maybeLocalNames, bool& hasKeywordForLocalBindings); + template <class TreeBuilder> TreeStatement parseExportDeclaration(TreeBuilder&); + + template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionRequirements, SourceParseMode, bool nameIsInContainingScope, ConstructorKind, SuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>&, FunctionParseType); + + template <class TreeBuilder> NEVER_INLINE int parseFunctionParameters(TreeBuilder&, SourceParseMode, ParserFunctionInfo<TreeBuilder>&); + +#if ENABLE(ES6_CLASS_SYNTAX) + template <class TreeBuilder> NEVER_INLINE TreeClassExpression parseClass(TreeBuilder&, FunctionRequirements, ParserClassInfo<TreeBuilder>&); +#endif + +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + template <class TreeBuilder> NEVER_INLINE typename TreeBuilder::TemplateString parseTemplateString(TreeBuilder& context, bool isTemplateHead, typename LexerType::RawStringsBuildMode, bool& elementIsTail); + template <class TreeBuilder> NEVER_INLINE typename TreeBuilder::TemplateLiteral parseTemplateLiteral(TreeBuilder&, typename LexerType::RawStringsBuildMode); +#endif + + template <class TreeBuilder> ALWAYS_INLINE bool shouldCheckPropertyForUnderscoreProtoDuplicate(TreeBuilder&, const TreeProperty&); + + ALWAYS_INLINE int isBinaryOperator(JSTokenType); + bool allowAutomaticSemicolon(); + + bool autoSemiColon() + { + if (m_token.m_type == SEMICOLON) { + next(); + return true; + } + return allowAutomaticSemicolon(); + } + + void setEndOfStatement() + { + m_lexer->setTokenPosition(&m_token); + } + + bool canRecurse() + { + return m_vm->isSafeToRecurse(); + } + + const JSTextPosition& lastTokenEndPosition() const + { + return m_lastTokenEndPosition; + } + + bool hasError() const + { + return !m_errorMessage.isNull(); + } + + struct SavePoint { + int startOffset; + unsigned oldLineStartOffset; + unsigned oldLastLineNumber; + unsigned oldLineNumber; + }; + + ALWAYS_INLINE SavePoint createSavePoint() + { + ASSERT(!hasError()); + SavePoint result; + result.startOffset = m_token.m_location.startOffset; + result.oldLineStartOffset = m_token.m_location.lineStartOffset; + result.oldLastLineNumber = m_lexer->lastLineNumber(); + result.oldLineNumber = m_lexer->lineNumber(); + return result; + } + + ALWAYS_INLINE void restoreSavePoint(const SavePoint& savePoint) + { + m_errorMessage = String(); + m_lexer->setOffset(savePoint.startOffset, savePoint.oldLineStartOffset); + next(); + m_lexer->setLastLineNumber(savePoint.oldLastLineNumber); + m_lexer->setLineNumber(savePoint.oldLineNumber); + } + + struct ParserState { + int assignmentCount; + int nonLHSCount; + int nonTrivialExpressionCount; + }; + + ALWAYS_INLINE ParserState saveState() + { + ParserState result; + result.assignmentCount = m_assignmentCount; + result.nonLHSCount = m_nonLHSCount; + result.nonTrivialExpressionCount = m_nonTrivialExpressionCount; + return result; + } + + ALWAYS_INLINE void restoreState(const ParserState& state) + { + m_assignmentCount = state.assignmentCount; + m_nonLHSCount = state.nonLHSCount; + m_nonTrivialExpressionCount = state.nonTrivialExpressionCount; + + } + + + VM* m_vm; + const SourceCode* m_source; + ParserArena m_parserArena; + std::unique_ptr<LexerType> m_lexer; + FunctionParameters* m_parameters { nullptr }; + + bool m_hasStackOverflow; + String m_errorMessage; + JSToken m_token; + bool m_allowsIn; + JSTextPosition m_lastTokenEndPosition; + int m_assignmentCount; + int m_nonLHSCount; + bool m_syntaxAlreadyValidated; + int m_statementDepth; + int m_nonTrivialExpressionCount; + const Identifier* m_lastIdentifier; + const Identifier* m_lastFunctionName; + RefPtr<SourceProviderCache> m_functionCache; + SourceElements* m_sourceElements; + bool m_parsingBuiltin; + ConstructorKind m_defaultConstructorKind; + ThisTDZMode m_thisTDZMode; + VariableEnvironment m_varDeclarations; + DeclarationStacks::FunctionStack m_funcDeclarations; + Vector<RefPtr<UniquedStringImpl>> m_closedVariables; + CodeFeatures m_features; + int m_numConstants; + + struct DepthManager { + DepthManager(int* depth) + : m_originalDepth(*depth) + , m_depth(depth) + { + } + + ~DepthManager() + { + *m_depth = m_originalDepth; + } + + private: + int m_originalDepth; + int* m_depth; + }; +}; + + +template <typename LexerType> +template <class ParsedNode> +std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const Identifier& calleeName, SourceParseMode parseMode) +{ + int errLine; + String errMsg; + + if (ParsedNode::scopeIsFunction) + m_lexer->setIsReparsingFunction(); + + m_sourceElements = 0; + + errLine = -1; + errMsg = String(); + + JSTokenLocation startLocation(tokenLocation()); + ASSERT(m_source->startColumn() > 0); + unsigned startColumn = m_source->startColumn() - 1; + + String parseError = parseInner(calleeName, parseMode); + + int lineNumber = m_lexer->lineNumber(); + bool lexError = m_lexer->sawError(); + String lexErrorMessage = lexError ? m_lexer->getErrorMessage() : String(); + ASSERT(lexErrorMessage.isNull() != lexError); + m_lexer->clear(); + + if (!parseError.isNull() || lexError) { + errLine = lineNumber; + errMsg = !lexErrorMessage.isNull() ? lexErrorMessage : parseError; + m_sourceElements = 0; + } + + std::unique_ptr<ParsedNode> result; + if (m_sourceElements) { + JSTokenLocation endLocation; + endLocation.line = m_lexer->lineNumber(); + endLocation.lineStartOffset = m_lexer->currentLineStartOffset(); + endLocation.startOffset = m_lexer->currentOffset(); + unsigned endColumn = endLocation.startOffset - endLocation.lineStartOffset; + result = std::make_unique<ParsedNode>(m_parserArena, + startLocation, + endLocation, + startColumn, + endColumn, + m_sourceElements, + m_varDeclarations, + m_funcDeclarations, + currentScope()->finalizeLexicalEnvironment(), + m_parameters, + *m_source, + m_features, + m_numConstants); + result->setLoc(m_source->firstLine(), m_lexer->lineNumber(), m_lexer->currentOffset(), m_lexer->currentLineStartOffset()); + result->setEndOffset(m_lexer->currentOffset()); + } else { + // We can never see a syntax error when reparsing a function, since we should have + // reported the error when parsing the containing program or eval code. So if we're + // parsing a function body node, we assume that what actually happened here is that + // we ran out of stack while parsing. If we see an error while parsing eval or program + // code we assume that it was a syntax error since running out of stack is much less + // likely, and we are currently unable to distinguish between the two cases. + if (isFunctionMetadataNode(static_cast<ParsedNode*>(0)) || m_hasStackOverflow) + error = ParserError(ParserError::StackOverflow, ParserError::SyntaxErrorNone, m_token); + else { + ParserError::SyntaxErrorType errorType = ParserError::SyntaxErrorIrrecoverable; + if (m_token.m_type == EOFTOK) + errorType = ParserError::SyntaxErrorRecoverable; + else if (m_token.m_type & UnterminatedErrorTokenFlag) + errorType = ParserError::SyntaxErrorUnterminatedLiteral; + + if (isEvalNode<ParsedNode>()) + error = ParserError(ParserError::EvalError, errorType, m_token, errMsg, errLine); + else + error = ParserError(ParserError::SyntaxError, errorType, m_token, errMsg, errLine); + } + } + + return result; +} + +template <class ParsedNode> +std::unique_ptr<ParsedNode> parse( + VM* vm, const SourceCode& source, + const Identifier& name, JSParserBuiltinMode builtinMode, + JSParserStrictMode strictMode, SourceParseMode parseMode, + ParserError& error, JSTextPosition* positionBeforeLastNewline = nullptr, + ConstructorKind defaultConstructorKind = ConstructorKind::None, + ThisTDZMode thisTDZMode = ThisTDZMode::CheckIfNeeded) +{ + SamplingRegion samplingRegion("Parsing"); + + ASSERT(!source.provider()->source().isNull()); + if (source.provider()->source().is8Bit()) { + Parser<Lexer<LChar>> parser(vm, source, builtinMode, strictMode, parseMode, defaultConstructorKind, thisTDZMode); + std::unique_ptr<ParsedNode> result = parser.parse<ParsedNode>(error, name, parseMode); + if (positionBeforeLastNewline) + *positionBeforeLastNewline = parser.positionBeforeLastNewline(); + if (builtinMode == JSParserBuiltinMode::Builtin) { + if (!result) + WTF::dataLog("Error compiling builtin: ", error.message(), "\n"); + RELEASE_ASSERT(result); + result->setClosedVariables(parser.closedVariables()); + } + return result; + } + ASSERT_WITH_MESSAGE(defaultConstructorKind == ConstructorKind::None, "BuiltinExecutables::createDefaultConstructor should always use a 8-bit string"); + Parser<Lexer<UChar>> parser(vm, source, builtinMode, strictMode, parseMode, defaultConstructorKind, thisTDZMode); + std::unique_ptr<ParsedNode> result = parser.parse<ParsedNode>(error, name, parseMode); + if (positionBeforeLastNewline) + *positionBeforeLastNewline = parser.positionBeforeLastNewline(); + return result; +} + +} // namespace +#endif diff --git a/Source/JavaScriptCore/parser/ParserArena.cpp b/Source/JavaScriptCore/parser/ParserArena.cpp new file mode 100644 index 000000000..a27688770 --- /dev/null +++ b/Source/JavaScriptCore/parser/ParserArena.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ParserArena.h" + +#include "Nodes.h" +#include "JSCInlines.h" + +namespace JSC { + +ParserArena::ParserArena() + : m_freeableMemory(0) + , m_freeablePoolEnd(0) +{ +} + +inline void* ParserArena::freeablePool() +{ + ASSERT(m_freeablePoolEnd); + return m_freeablePoolEnd - freeablePoolSize; +} + +inline void ParserArena::deallocateObjects() +{ + size_t size = m_deletableObjects.size(); + for (size_t i = 0; i < size; ++i) + m_deletableObjects[i]->~ParserArenaDeletable(); + + if (m_freeablePoolEnd) + fastFree(freeablePool()); + + size = m_freeablePools.size(); + for (size_t i = 0; i < size; ++i) + fastFree(m_freeablePools[i]); +} + +ParserArena::~ParserArena() +{ + deallocateObjects(); +} + +void ParserArena::allocateFreeablePool() +{ + if (m_freeablePoolEnd) + m_freeablePools.append(freeablePool()); + + char* pool = static_cast<char*>(fastMalloc(freeablePoolSize)); + m_freeableMemory = pool; + m_freeablePoolEnd = pool + freeablePoolSize; + ASSERT(freeablePool() == pool); +} + +} diff --git a/Source/JavaScriptCore/parser/ParserArena.h b/Source/JavaScriptCore/parser/ParserArena.h new file mode 100644 index 000000000..2a7d44de7 --- /dev/null +++ b/Source/JavaScriptCore/parser/ParserArena.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ParserArena_h +#define ParserArena_h + +#include "CommonIdentifiers.h" +#include "Identifier.h" +#include <array> +#include <wtf/SegmentedVector.h> + +namespace JSC { + + class ParserArenaDeletable; + + class IdentifierArena { + WTF_MAKE_FAST_ALLOCATED; + public: + IdentifierArena() + { + clear(); + } + + template <typename T> + ALWAYS_INLINE const Identifier& makeIdentifier(VM*, const T* characters, size_t length); + ALWAYS_INLINE const Identifier& makeEmptyIdentifier(VM*); + ALWAYS_INLINE const Identifier& makeIdentifierLCharFromUChar(VM*, const UChar* characters, size_t length); + + const Identifier& makeNumericIdentifier(VM*, double number); + + public: + static const int MaximumCachableCharacter = 128; + typedef SegmentedVector<Identifier, 64> IdentifierVector; + void clear() + { + m_identifiers.clear(); + for (int i = 0; i < MaximumCachableCharacter; i++) + m_shortIdentifiers[i] = 0; + for (int i = 0; i < MaximumCachableCharacter; i++) + m_recentIdentifiers[i] = 0; + } + + private: + IdentifierVector m_identifiers; + std::array<Identifier*, MaximumCachableCharacter> m_shortIdentifiers; + std::array<Identifier*, MaximumCachableCharacter> m_recentIdentifiers; + }; + + template <typename T> + ALWAYS_INLINE const Identifier& IdentifierArena::makeIdentifier(VM* vm, const T* characters, size_t length) + { + if (!length) + return vm->propertyNames->emptyIdentifier; + if (characters[0] >= MaximumCachableCharacter) { + m_identifiers.append(Identifier::fromString(vm, characters, length)); + return m_identifiers.last(); + } + if (length == 1) { + if (Identifier* ident = m_shortIdentifiers[characters[0]]) + return *ident; + m_identifiers.append(Identifier::fromString(vm, characters, length)); + m_shortIdentifiers[characters[0]] = &m_identifiers.last(); + return m_identifiers.last(); + } + Identifier* ident = m_recentIdentifiers[characters[0]]; + if (ident && Identifier::equal(ident->impl(), characters, length)) + return *ident; + m_identifiers.append(Identifier::fromString(vm, characters, length)); + m_recentIdentifiers[characters[0]] = &m_identifiers.last(); + return m_identifiers.last(); + } + + ALWAYS_INLINE const Identifier& IdentifierArena::makeEmptyIdentifier(VM* vm) + { + return vm->propertyNames->emptyIdentifier; + } + + ALWAYS_INLINE const Identifier& IdentifierArena::makeIdentifierLCharFromUChar(VM* vm, const UChar* characters, size_t length) + { + if (!length) + return vm->propertyNames->emptyIdentifier; + if (characters[0] >= MaximumCachableCharacter) { + m_identifiers.append(Identifier::createLCharFromUChar(vm, characters, length)); + return m_identifiers.last(); + } + if (length == 1) { + if (Identifier* ident = m_shortIdentifiers[characters[0]]) + return *ident; + m_identifiers.append(Identifier::fromString(vm, characters, length)); + m_shortIdentifiers[characters[0]] = &m_identifiers.last(); + return m_identifiers.last(); + } + Identifier* ident = m_recentIdentifiers[characters[0]]; + if (ident && Identifier::equal(ident->impl(), characters, length)) + return *ident; + m_identifiers.append(Identifier::createLCharFromUChar(vm, characters, length)); + m_recentIdentifiers[characters[0]] = &m_identifiers.last(); + return m_identifiers.last(); + } + + inline const Identifier& IdentifierArena::makeNumericIdentifier(VM* vm, double number) + { + m_identifiers.append(Identifier::fromString(vm, String::numberToStringECMAScript(number))); + return m_identifiers.last(); + } + + class ParserArena { + WTF_MAKE_NONCOPYABLE(ParserArena); + public: + ParserArena(); + ~ParserArena(); + + void swap(ParserArena& otherArena) + { + std::swap(m_freeableMemory, otherArena.m_freeableMemory); + std::swap(m_freeablePoolEnd, otherArena.m_freeablePoolEnd); + m_identifierArena.swap(otherArena.m_identifierArena); + m_freeablePools.swap(otherArena.m_freeablePools); + m_deletableObjects.swap(otherArena.m_deletableObjects); + } + + void* allocateFreeable(size_t size) + { + ASSERT(size); + ASSERT(size <= freeablePoolSize); + size_t alignedSize = alignSize(size); + ASSERT(alignedSize <= freeablePoolSize); + if (UNLIKELY(static_cast<size_t>(m_freeablePoolEnd - m_freeableMemory) < alignedSize)) + allocateFreeablePool(); + void* block = m_freeableMemory; + m_freeableMemory += alignedSize; + return block; + } + + void* allocateDeletable(size_t size) + { + ParserArenaDeletable* deletable = static_cast<ParserArenaDeletable*>(allocateFreeable(size)); + m_deletableObjects.append(deletable); + return deletable; + } + + IdentifierArena& identifierArena() + { + if (UNLIKELY (!m_identifierArena)) + m_identifierArena = std::make_unique<IdentifierArena>(); + return *m_identifierArena; + } + + private: + static const size_t freeablePoolSize = 8000; + + static size_t alignSize(size_t size) + { + return (size + sizeof(WTF::AllocAlignmentInteger) - 1) & ~(sizeof(WTF::AllocAlignmentInteger) - 1); + } + + void* freeablePool(); + void allocateFreeablePool(); + void deallocateObjects(); + + char* m_freeableMemory; + char* m_freeablePoolEnd; + + std::unique_ptr<IdentifierArena> m_identifierArena; + Vector<void*> m_freeablePools; + Vector<ParserArenaDeletable*> m_deletableObjects; + }; + +} + +#endif diff --git a/Source/JavaScriptCore/parser/ParserError.h b/Source/JavaScriptCore/parser/ParserError.h new file mode 100644 index 000000000..89a05ab42 --- /dev/null +++ b/Source/JavaScriptCore/parser/ParserError.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2013 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ParserError_h +#define ParserError_h + +#include "Error.h" +#include "ErrorHandlingScope.h" +#include "ExceptionHelpers.h" +#include "ParserTokens.h" +#include <wtf/text/WTFString.h> + +namespace JSC { + +class ParserError { +public: + enum SyntaxErrorType { + SyntaxErrorNone, + SyntaxErrorIrrecoverable, + SyntaxErrorUnterminatedLiteral, + SyntaxErrorRecoverable + }; + + enum ErrorType { + ErrorNone, + StackOverflow, + EvalError, + OutOfMemory, + SyntaxError + }; + + ParserError() + : m_type(ErrorNone) + , m_syntaxErrorType(SyntaxErrorNone) + { + } + + explicit ParserError(ErrorType type) + : m_type(type) + , m_syntaxErrorType(SyntaxErrorNone) + { + } + + ParserError(ErrorType type, SyntaxErrorType syntaxError, JSToken token) + : m_token(token) + , m_type(type) + , m_syntaxErrorType(syntaxError) + { + } + + ParserError(ErrorType type, SyntaxErrorType syntaxError, JSToken token, const String& msg, int line) + : m_token(token) + , m_message(msg) + , m_line(line) + , m_type(type) + , m_syntaxErrorType(syntaxError) + { + } + + bool isValid() const { return m_type != ErrorNone; } + SyntaxErrorType syntaxErrorType() const { return m_syntaxErrorType; } + const JSToken& token() const { return m_token; } + const String& message() const { return m_message; } + int line() const { return m_line; } + + JSObject* toErrorObject( + JSGlobalObject* globalObject, const SourceCode& source, + int overrideLineNumber = -1) + { + ExecState* exec = globalObject->globalExec(); + switch (m_type) { + case ErrorNone: + return nullptr; + case SyntaxError: + return addErrorInfo( + exec, + createSyntaxError(exec, m_message), + overrideLineNumber == -1 ? m_line : overrideLineNumber, source); + case EvalError: + return createSyntaxError(exec, m_message); + case StackOverflow: { + ErrorHandlingScope errorScope(globalObject->vm()); + return createStackOverflowError(exec); + } + case OutOfMemory: + return createOutOfMemoryError(exec); + } + CRASH(); + return nullptr; + } + +private: + JSToken m_token; + String m_message; + int m_line { -1 }; + ErrorType m_type; + SyntaxErrorType m_syntaxErrorType; +}; + +} // namespace JSC + +#endif // ParserError_h diff --git a/Source/JavaScriptCore/parser/ParserFunctionInfo.h b/Source/JavaScriptCore/parser/ParserFunctionInfo.h new file mode 100644 index 000000000..4c565aae6 --- /dev/null +++ b/Source/JavaScriptCore/parser/ParserFunctionInfo.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ParserFunctionInfo_h +#define ParserFunctionInfo_h + +namespace JSC { + +template <class TreeBuilder> +struct ParserFunctionInfo { + const Identifier* name = 0; + typename TreeBuilder::FormalParameterList parameters = 0; + typename TreeBuilder::FunctionBody body = 0; + unsigned parameterCount = 0; + unsigned startOffset = 0; + unsigned endOffset = 0; + int startLine = 0; + int endLine = 0; + unsigned bodyStartColumn = 0; +}; + +#if ENABLE(ES6_CLASS_SYNTAX) +template <class TreeBuilder> +struct ParserClassInfo { + const Identifier* className = 0; +}; +#endif + +} + +#endif diff --git a/Source/JavaScriptCore/parser/ParserModes.h b/Source/JavaScriptCore/parser/ParserModes.h new file mode 100644 index 000000000..a65603805 --- /dev/null +++ b/Source/JavaScriptCore/parser/ParserModes.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef ParserModes_h +#define ParserModes_h + +#include "Identifier.h" + +namespace JSC { + +enum class JSParserStrictMode { NotStrict, Strict }; +enum class JSParserBuiltinMode { NotBuiltin, Builtin }; +enum class JSParserCodeType { Program, Function, Module }; + +enum class ConstructorKind { None, Base, Derived }; +enum class SuperBinding { Needed, NotNeeded }; +enum class ThisTDZMode { AlwaysCheck, CheckIfNeeded }; + +enum ProfilerMode { ProfilerOff, ProfilerOn }; +enum DebuggerMode { DebuggerOff, DebuggerOn }; + +enum FunctionMode { FunctionExpression, FunctionDeclaration }; + +enum class SourceParseMode { + NormalFunctionMode, + GetterMode, + SetterMode, + MethodMode, + ArrowFunctionMode, + ProgramMode, + ModuleAnalyzeMode, + ModuleEvaluateMode +}; + +inline bool isFunctionParseMode(SourceParseMode parseMode) +{ + switch (parseMode) { + case SourceParseMode::NormalFunctionMode: + case SourceParseMode::GetterMode: + case SourceParseMode::SetterMode: + case SourceParseMode::MethodMode: + case SourceParseMode::ArrowFunctionMode: + return true; + + case SourceParseMode::ProgramMode: + case SourceParseMode::ModuleAnalyzeMode: + case SourceParseMode::ModuleEvaluateMode: + return false; + } + RELEASE_ASSERT_NOT_REACHED(); + return false; +} + +inline bool isModuleParseMode(SourceParseMode parseMode) +{ + switch (parseMode) { + case SourceParseMode::ModuleAnalyzeMode: + case SourceParseMode::ModuleEvaluateMode: + return true; + + case SourceParseMode::NormalFunctionMode: + case SourceParseMode::GetterMode: + case SourceParseMode::SetterMode: + case SourceParseMode::MethodMode: + case SourceParseMode::ArrowFunctionMode: + case SourceParseMode::ProgramMode: + return false; + } + RELEASE_ASSERT_NOT_REACHED(); + return false; +} + +inline bool isProgramParseMode(SourceParseMode parseMode) +{ + switch (parseMode) { + case SourceParseMode::ProgramMode: + return true; + + case SourceParseMode::NormalFunctionMode: + case SourceParseMode::GetterMode: + case SourceParseMode::SetterMode: + case SourceParseMode::MethodMode: + case SourceParseMode::ArrowFunctionMode: + case SourceParseMode::ModuleAnalyzeMode: + case SourceParseMode::ModuleEvaluateMode: + return false; + } + RELEASE_ASSERT_NOT_REACHED(); + return false; +} + +inline bool functionNameIsInScope(const Identifier& name, FunctionMode functionMode) +{ + if (name.isNull()) + return false; + + if (functionMode != FunctionExpression) + return false; + + return true; +} + +inline bool functionNameScopeIsDynamic(bool usesEval, bool isStrictMode) +{ + // If non-strict eval is in play, a function gets a separate object in the scope chain for its name. + // This enables eval to declare and then delete a name that shadows the function's name. + + if (!usesEval) + return false; + + if (isStrictMode) + return false; + + return true; +} + +typedef unsigned CodeFeatures; + +const CodeFeatures NoFeatures = 0; +const CodeFeatures EvalFeature = 1 << 0; +const CodeFeatures ArgumentsFeature = 1 << 1; +const CodeFeatures WithFeature = 1 << 2; +const CodeFeatures CatchFeature = 1 << 3; +const CodeFeatures ThisFeature = 1 << 4; +const CodeFeatures StrictModeFeature = 1 << 5; +const CodeFeatures ShadowsArgumentsFeature = 1 << 6; +const CodeFeatures ModifiedParameterFeature = 1 << 7; +const CodeFeatures ModifiedArgumentsFeature = 1 << 8; + +const CodeFeatures AllFeatures = EvalFeature | ArgumentsFeature | WithFeature | CatchFeature | ThisFeature | StrictModeFeature | ShadowsArgumentsFeature | ModifiedParameterFeature; + +} // namespace JSC + +#endif // ParserModes_h diff --git a/Source/JavaScriptCore/parser/ParserTokens.h b/Source/JavaScriptCore/parser/ParserTokens.h new file mode 100644 index 000000000..3dfdc28a8 --- /dev/null +++ b/Source/JavaScriptCore/parser/ParserTokens.h @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2010, 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ParserTokens_h +#define ParserTokens_h + +#include "ParserModes.h" +#include <limits.h> +#include <stdint.h> + +namespace JSC { + +class Identifier; + +enum { + UnaryOpTokenFlag = 64, + KeywordTokenFlag = 128, + BinaryOpTokenPrecedenceShift = 8, + BinaryOpTokenAllowsInPrecedenceAdditionalShift = 4, + BinaryOpTokenPrecedenceMask = 15 << BinaryOpTokenPrecedenceShift, + ErrorTokenFlag = 1 << (BinaryOpTokenAllowsInPrecedenceAdditionalShift + BinaryOpTokenPrecedenceShift + 7), + UnterminatedErrorTokenFlag = ErrorTokenFlag << 1 +}; + +#define BINARY_OP_PRECEDENCE(prec) (((prec) << BinaryOpTokenPrecedenceShift) | ((prec) << (BinaryOpTokenPrecedenceShift + BinaryOpTokenAllowsInPrecedenceAdditionalShift))) +#define IN_OP_PRECEDENCE(prec) ((prec) << (BinaryOpTokenPrecedenceShift + BinaryOpTokenAllowsInPrecedenceAdditionalShift)) + +enum JSTokenType { + NULLTOKEN = KeywordTokenFlag, + TRUETOKEN, + FALSETOKEN, + BREAK, + CASE, + DEFAULT, + FOR, + NEW, + VAR, + LET, + CONSTTOKEN, + CONTINUE, + FUNCTION, + RETURN, + IF, + THISTOKEN, + DO, + WHILE, + SWITCH, + WITH, + RESERVED, + RESERVED_IF_STRICT, + THROW, + TRY, + CATCH, + FINALLY, + DEBUGGER, + ELSE, + IMPORT, + EXPORT, +#if ENABLE(ES6_CLASS_SYNTAX) + CLASSTOKEN, + EXTENDS, + SUPER, +#else + CLASSTOKEN = RESERVED, + EXTENDS = RESERVED, + SUPER = RESERVED, +#endif + OPENBRACE = 0, + CLOSEBRACE, + OPENPAREN, + CLOSEPAREN, + OPENBRACKET, + CLOSEBRACKET, + COMMA, + QUESTION, + INTEGER, + DOUBLE, + IDENT, + STRING, + TEMPLATE, + SEMICOLON, + COLON, + DOT, + EOFTOK, + EQUAL, + PLUSEQUAL, + MINUSEQUAL, + MULTEQUAL, + DIVEQUAL, + LSHIFTEQUAL, + RSHIFTEQUAL, + URSHIFTEQUAL, + ANDEQUAL, + MODEQUAL, + XOREQUAL, + OREQUAL, + DOTDOTDOT, +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + ARROWFUNCTION, +#endif + LastUntaggedToken, + + // Begin tagged tokens + PLUSPLUS = 0 | UnaryOpTokenFlag, + MINUSMINUS = 1 | UnaryOpTokenFlag, + EXCLAMATION = 2 | UnaryOpTokenFlag, + TILDE = 3 | UnaryOpTokenFlag, + AUTOPLUSPLUS = 4 | UnaryOpTokenFlag, + AUTOMINUSMINUS = 5 | UnaryOpTokenFlag, + TYPEOF = 6 | UnaryOpTokenFlag | KeywordTokenFlag, + VOIDTOKEN = 7 | UnaryOpTokenFlag | KeywordTokenFlag, + DELETETOKEN = 8 | UnaryOpTokenFlag | KeywordTokenFlag, + OR = 0 | BINARY_OP_PRECEDENCE(1), + AND = 1 | BINARY_OP_PRECEDENCE(2), + BITOR = 2 | BINARY_OP_PRECEDENCE(3), + BITXOR = 3 | BINARY_OP_PRECEDENCE(4), + BITAND = 4 | BINARY_OP_PRECEDENCE(5), + EQEQ = 5 | BINARY_OP_PRECEDENCE(6), + NE = 6 | BINARY_OP_PRECEDENCE(6), + STREQ = 7 | BINARY_OP_PRECEDENCE(6), + STRNEQ = 8 | BINARY_OP_PRECEDENCE(6), + LT = 9 | BINARY_OP_PRECEDENCE(7), + GT = 10 | BINARY_OP_PRECEDENCE(7), + LE = 11 | BINARY_OP_PRECEDENCE(7), + GE = 12 | BINARY_OP_PRECEDENCE(7), + INSTANCEOF = 13 | BINARY_OP_PRECEDENCE(7) | KeywordTokenFlag, + INTOKEN = 14 | IN_OP_PRECEDENCE(7) | KeywordTokenFlag, + LSHIFT = 15 | BINARY_OP_PRECEDENCE(8), + RSHIFT = 16 | BINARY_OP_PRECEDENCE(8), + URSHIFT = 17 | BINARY_OP_PRECEDENCE(8), + PLUS = 18 | BINARY_OP_PRECEDENCE(9) | UnaryOpTokenFlag, + MINUS = 19 | BINARY_OP_PRECEDENCE(9) | UnaryOpTokenFlag, + TIMES = 20 | BINARY_OP_PRECEDENCE(10), + DIVIDE = 21 | BINARY_OP_PRECEDENCE(10), + MOD = 22 | BINARY_OP_PRECEDENCE(10), + ERRORTOK = 0 | ErrorTokenFlag, + UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK = 0 | ErrorTokenFlag | UnterminatedErrorTokenFlag, + INVALID_IDENTIFIER_ESCAPE_ERRORTOK = 1 | ErrorTokenFlag, + UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK = 2 | ErrorTokenFlag | UnterminatedErrorTokenFlag, + INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK = 3 | ErrorTokenFlag, + UNTERMINATED_MULTILINE_COMMENT_ERRORTOK = 4 | ErrorTokenFlag | UnterminatedErrorTokenFlag, + UNTERMINATED_NUMERIC_LITERAL_ERRORTOK = 5 | ErrorTokenFlag | UnterminatedErrorTokenFlag, + INVALID_OCTAL_NUMBER_ERRORTOK = 6 | ErrorTokenFlag | UnterminatedErrorTokenFlag, + INVALID_NUMERIC_LITERAL_ERRORTOK = 7 | ErrorTokenFlag, + UNTERMINATED_STRING_LITERAL_ERRORTOK = 8 | ErrorTokenFlag | UnterminatedErrorTokenFlag, + INVALID_STRING_LITERAL_ERRORTOK = 9 | ErrorTokenFlag, + INVALID_PRIVATE_NAME_ERRORTOK = 10 | ErrorTokenFlag, + INVALID_HEX_NUMBER_ERRORTOK = 11 | ErrorTokenFlag, + INVALID_BINARY_NUMBER_ERRORTOK = 12 | ErrorTokenFlag, + UNTERMINATED_TEMPLATE_LITERAL_ERRORTOK = 13 | ErrorTokenFlag | UnterminatedErrorTokenFlag, + INVALID_TEMPLATE_LITERAL_ERRORTOK = 14 | ErrorTokenFlag, +}; + +struct JSTextPosition { + JSTextPosition() : line(0), offset(0), lineStartOffset(0) { } + JSTextPosition(int _line, int _offset, int _lineStartOffset) : line(_line), offset(_offset), lineStartOffset(_lineStartOffset) { } + JSTextPosition(const JSTextPosition& other) : line(other.line), offset(other.offset), lineStartOffset(other.lineStartOffset) { } + + JSTextPosition operator+(int adjustment) const { return JSTextPosition(line, offset + adjustment, lineStartOffset); } + JSTextPosition operator+(unsigned adjustment) const { return *this + static_cast<int>(adjustment); } + JSTextPosition operator-(int adjustment) const { return *this + (- adjustment); } + JSTextPosition operator-(unsigned adjustment) const { return *this + (- static_cast<int>(adjustment)); } + + operator int() const { return offset; } + + int line; + int offset; + int lineStartOffset; +}; + +union JSTokenData { + struct { + uint32_t line; + uint32_t offset; + uint32_t lineStartOffset; + }; + double doubleValue; + const Identifier* ident; + struct { + const Identifier* cooked; + const Identifier* raw; + bool isTail; + }; +}; + +struct JSTokenLocation { + JSTokenLocation() : line(0), lineStartOffset(0), startOffset(0) { } + JSTokenLocation(const JSTokenLocation& location) + { + line = location.line; + lineStartOffset = location.lineStartOffset; + startOffset = location.startOffset; + endOffset = location.endOffset; + } + + int line; + unsigned lineStartOffset; + unsigned startOffset; + unsigned endOffset; +}; + +struct JSToken { + JSTokenType m_type; + JSTokenData m_data; + JSTokenLocation m_location; + JSTextPosition m_startPosition; + JSTextPosition m_endPosition; +}; + +} // namespace JSC + +#endif // ParserTokens_h diff --git a/Source/JavaScriptCore/parser/ResultType.h b/Source/JavaScriptCore/parser/ResultType.h new file mode 100644 index 000000000..ad86c98c7 --- /dev/null +++ b/Source/JavaScriptCore/parser/ResultType.h @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ResultType_h +#define ResultType_h + +namespace JSC { + + struct ResultType { + friend struct OperandTypes; + + typedef char Type; + static const Type TypeInt32 = 1; + + static const Type TypeMaybeNumber = 0x04; + static const Type TypeMaybeString = 0x08; + static const Type TypeMaybeNull = 0x10; + static const Type TypeMaybeBool = 0x20; + static const Type TypeMaybeOther = 0x40; + + static const Type TypeBits = TypeMaybeNumber | TypeMaybeString | TypeMaybeNull | TypeMaybeBool | TypeMaybeOther; + + explicit ResultType(Type type) + : m_type(type) + { + } + + bool isInt32() + { + return m_type & TypeInt32; + } + + bool definitelyIsNumber() + { + return (m_type & TypeBits) == TypeMaybeNumber; + } + + bool definitelyIsString() + { + return (m_type & TypeBits) == TypeMaybeString; + } + + bool definitelyIsBoolean() + { + return (m_type & TypeBits) == TypeMaybeBool; + } + + bool mightBeNumber() + { + return m_type & TypeMaybeNumber; + } + + bool isNotNumber() + { + return !mightBeNumber(); + } + + static ResultType nullType() + { + return ResultType(TypeMaybeNull); + } + + static ResultType booleanType() + { + return ResultType(TypeMaybeBool); + } + + static ResultType numberType() + { + return ResultType(TypeMaybeNumber); + } + + static ResultType numberTypeIsInt32() + { + return ResultType(TypeInt32 | TypeMaybeNumber); + } + + static ResultType stringOrNumberType() + { + return ResultType(TypeMaybeNumber | TypeMaybeString); + } + + static ResultType stringType() + { + return ResultType(TypeMaybeString); + } + + static ResultType unknownType() + { + return ResultType(TypeBits); + } + + static ResultType forAdd(ResultType op1, ResultType op2) + { + if (op1.definitelyIsNumber() && op2.definitelyIsNumber()) + return numberType(); + if (op1.definitelyIsString() || op2.definitelyIsString()) + return stringType(); + return stringOrNumberType(); + } + + // Unlike in C, a logical op produces the value of the + // last expression evaluated (and not true or false). + static ResultType forLogicalOp(ResultType op1, ResultType op2) + { + if (op1.definitelyIsBoolean() && op2.definitelyIsBoolean()) + return booleanType(); + if (op1.definitelyIsNumber() && op2.definitelyIsNumber()) + return numberType(); + if (op1.definitelyIsString() && op2.definitelyIsString()) + return stringType(); + return unknownType(); + } + + static ResultType forBitOp() + { + return numberTypeIsInt32(); + } + + private: + Type m_type; + }; + + struct OperandTypes + { + OperandTypes(ResultType first = ResultType::unknownType(), ResultType second = ResultType::unknownType()) + { + // We have to initialize one of the int to ensure that + // the entire struct is initialized. + m_u.i = 0; + m_u.rds.first = first.m_type; + m_u.rds.second = second.m_type; + } + + union { + struct { + ResultType::Type first; + ResultType::Type second; + } rds; + int i; + } m_u; + + ResultType first() + { + return ResultType(m_u.rds.first); + } + + ResultType second() + { + return ResultType(m_u.rds.second); + } + + int toInt() + { + return m_u.i; + } + static OperandTypes fromInt(int value) + { + OperandTypes types; + types.m_u.i = value; + return types; + } + }; + +} // namespace JSC + +#endif // ResultType_h diff --git a/Source/JavaScriptCore/parser/SourceCode.cpp b/Source/JavaScriptCore/parser/SourceCode.cpp new file mode 100644 index 000000000..8ef168dea --- /dev/null +++ b/Source/JavaScriptCore/parser/SourceCode.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SourceCode.h" + +#include "JSCInlines.h" +#include <wtf/text/CString.h> + +namespace JSC { + +CString SourceCode::toUTF8() const +{ + if (!m_provider) + return CString("", 0); + + return m_provider->source().impl()->utf8ForRange(m_startChar, m_endChar - m_startChar); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/parser/SourceCode.h b/Source/JavaScriptCore/parser/SourceCode.h new file mode 100644 index 000000000..7f37ecf86 --- /dev/null +++ b/Source/JavaScriptCore/parser/SourceCode.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2008, 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SourceCode_h +#define SourceCode_h + +#include "SourceProvider.h" +#include <wtf/RefPtr.h> + +namespace JSC { + + class SourceCode { + public: + SourceCode() + : m_provider(0) + , m_startChar(0) + , m_endChar(0) + , m_firstLine(0) + , m_startColumn(0) + { + } + + SourceCode(WTF::HashTableDeletedValueType) + : m_provider(WTF::HashTableDeletedValue) + { + } + + SourceCode(PassRefPtr<SourceProvider> provider) + : m_provider(provider) + , m_startChar(0) + , m_endChar(m_provider->source().length()) + , m_firstLine(1) + , m_startColumn(1) + { + } + + SourceCode(PassRefPtr<SourceProvider> provider, int firstLine, int startColumn) + : m_provider(provider) + , m_startChar(0) + , m_endChar(m_provider->source().length()) + , m_firstLine(std::max(firstLine, 1)) + , m_startColumn(std::max(startColumn, 1)) + { + } + + SourceCode(PassRefPtr<SourceProvider> provider, int start, int end, int firstLine, int startColumn) + : m_provider(provider) + , m_startChar(start) + , m_endChar(end) + , m_firstLine(std::max(firstLine, 1)) + , m_startColumn(std::max(startColumn, 1)) + { + } + + bool isHashTableDeletedValue() const { return m_provider.isHashTableDeletedValue(); } + + String toString() const + { + if (!m_provider) + return String(); + return m_provider->getRange(m_startChar, m_endChar); + } + + CString toUTF8() const; + + intptr_t providerID() const + { + if (!m_provider) + return SourceProvider::nullID; + return m_provider->asID(); + } + + bool isNull() const { return !m_provider; } + SourceProvider* provider() const { return m_provider.get(); } + int firstLine() const { return m_firstLine; } + int startColumn() const { return m_startColumn; } + int startOffset() const { return m_startChar; } + int endOffset() const { return m_endChar; } + int length() const { return m_endChar - m_startChar; } + + SourceCode subExpression(unsigned openBrace, unsigned closeBrace, int firstLine, int startColumn); + + private: + RefPtr<SourceProvider> m_provider; + int m_startChar; + int m_endChar; + int m_firstLine; + int m_startColumn; + }; + + inline SourceCode makeSource(const String& source, const String& url = String(), const TextPosition& startPosition = TextPosition::minimumPosition()) + { + return SourceCode(StringSourceProvider::create(source, url, startPosition), startPosition.m_line.oneBasedInt(), startPosition.m_column.oneBasedInt()); + } + + inline SourceCode SourceCode::subExpression(unsigned openBrace, unsigned closeBrace, int firstLine, int startColumn) + { + startColumn += 1; // Convert to base 1. + return SourceCode(provider(), openBrace, closeBrace + 1, firstLine, startColumn); + } + +} // namespace JSC + +#endif // SourceCode_h diff --git a/Source/JavaScriptCore/parser/SourceProvider.cpp b/Source/JavaScriptCore/parser/SourceProvider.cpp new file mode 100644 index 000000000..19ffb3372 --- /dev/null +++ b/Source/JavaScriptCore/parser/SourceProvider.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2013 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SourceProvider.h" + +#include "JSCInlines.h" +#include <wtf/Lock.h> +#include <wtf/StdLibExtras.h> + +namespace JSC { + +SourceProvider::SourceProvider(const String& url, const TextPosition& startPosition) + : m_url(url) + , m_startPosition(startPosition) + , m_validated(false) + , m_id(0) +{ +} + +SourceProvider::~SourceProvider() +{ +} + +static StaticLock providerIdLock; + +void SourceProvider::getID() +{ + LockHolder lock(&providerIdLock); + if (!m_id) { + static intptr_t nextProviderID = 0; + m_id = ++nextProviderID; + } +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/parser/SourceProvider.h b/Source/JavaScriptCore/parser/SourceProvider.h new file mode 100644 index 000000000..c263a1390 --- /dev/null +++ b/Source/JavaScriptCore/parser/SourceProvider.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2008, 2009, 2012, 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SourceProvider_h +#define SourceProvider_h + +#include <wtf/RefCounted.h> +#include <wtf/text/TextPosition.h> +#include <wtf/text/WTFString.h> + +namespace JSC { + + class SourceProvider : public RefCounted<SourceProvider> { + public: + static const intptr_t nullID = 1; + + JS_EXPORT_PRIVATE SourceProvider(const String& url, const TextPosition& startPosition); + + JS_EXPORT_PRIVATE virtual ~SourceProvider(); + + virtual const String& source() const = 0; + String getRange(int start, int end) const + { + return source().substringSharingImpl(start, end - start); + } + + const String& url() { return m_url; } + TextPosition startPosition() const { return m_startPosition; } + intptr_t asID() + { + if (!m_id) + getID(); + return m_id; + } + + bool isValid() const { return m_validated; } + void setValid() { m_validated = true; } + + private: + + JS_EXPORT_PRIVATE void getID(); + Vector<size_t>& lineStarts(); + + String m_url; + TextPosition m_startPosition; + bool m_validated : 1; + uintptr_t m_id : sizeof(uintptr_t) * 8 - 1; + }; + + class StringSourceProvider : public SourceProvider { + public: + static Ref<StringSourceProvider> create(const String& source, const String& url, const TextPosition& startPosition = TextPosition::minimumPosition()) + { + return adoptRef(*new StringSourceProvider(source, url, startPosition)); + } + + virtual const String& source() const override + { + return m_source; + } + + private: + StringSourceProvider(const String& source, const String& url, const TextPosition& startPosition) + : SourceProvider(url, startPosition) + , m_source(source) + { + } + + String m_source; + }; + +#if ENABLE(WEBASSEMBLY) + class WebAssemblySourceProvider : public SourceProvider { + public: + static Ref<WebAssemblySourceProvider> create(const Vector<uint8_t>& data, const String& url) + { + return adoptRef(*new WebAssemblySourceProvider(data, url)); + } + + virtual const String& source() const override + { + return m_source; + } + + const Vector<uint8_t>& data() const + { + return m_data; + } + + private: + WebAssemblySourceProvider(const Vector<uint8_t>& data, const String& url) + : SourceProvider(url, TextPosition::minimumPosition()) + , m_source("[WebAssembly source]") + , m_data(data) + { + } + + String m_source; + Vector<uint8_t> m_data; + }; +#endif + +} // namespace JSC + +#endif // SourceProvider_h diff --git a/Source/JavaScriptCore/parser/SourceProviderCache.cpp b/Source/JavaScriptCore/parser/SourceProviderCache.cpp new file mode 100644 index 000000000..d5958e8c4 --- /dev/null +++ b/Source/JavaScriptCore/parser/SourceProviderCache.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SourceProviderCache.h" + +#include "JSCInlines.h" + +namespace JSC { + +SourceProviderCache::~SourceProviderCache() +{ + clear(); +} + +void SourceProviderCache::clear() +{ + m_map.clear(); +} + +void SourceProviderCache::add(int sourcePosition, std::unique_ptr<SourceProviderCacheItem> item) +{ + m_map.add(sourcePosition, WTF::move(item)); +} + +} diff --git a/Source/JavaScriptCore/parser/SourceProviderCache.h b/Source/JavaScriptCore/parser/SourceProviderCache.h new file mode 100644 index 000000000..5070b2811 --- /dev/null +++ b/Source/JavaScriptCore/parser/SourceProviderCache.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SourceProviderCache_h +#define SourceProviderCache_h + +#include "SourceProviderCacheItem.h" +#include <wtf/HashMap.h> +#include <wtf/RefCounted.h> + +namespace JSC { + +class SourceProviderCache : public RefCounted<SourceProviderCache> { + WTF_MAKE_FAST_ALLOCATED; +public: + SourceProviderCache() { } + JS_EXPORT_PRIVATE ~SourceProviderCache(); + + JS_EXPORT_PRIVATE void clear(); + void add(int sourcePosition, std::unique_ptr<SourceProviderCacheItem>); + const SourceProviderCacheItem* get(int sourcePosition) const { return m_map.get(sourcePosition); } + +private: + HashMap<int, std::unique_ptr<SourceProviderCacheItem>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> m_map; +}; + +} + +#endif // SourceProviderCache_h diff --git a/Source/JavaScriptCore/parser/SourceProviderCacheItem.h b/Source/JavaScriptCore/parser/SourceProviderCacheItem.h new file mode 100644 index 000000000..81d221b39 --- /dev/null +++ b/Source/JavaScriptCore/parser/SourceProviderCacheItem.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SourceProviderCacheItem_h +#define SourceProviderCacheItem_h + +#include "ParserTokens.h" +#include <wtf/Vector.h> +#include <wtf/text/UniquedStringImpl.h> +#include <wtf/text/WTFString.h> + +namespace JSC { + +struct SourceProviderCacheItemCreationParameters { + unsigned functionNameStart; + unsigned lastTockenLine; + unsigned lastTockenStartOffset; + unsigned lastTockenEndOffset; + unsigned lastTockenLineStartOffset; + unsigned endFunctionOffset; + unsigned parameterCount; + bool needsFullActivation; + bool usesEval; + bool strictMode; + Vector<RefPtr<UniquedStringImpl>> usedVariables; + Vector<RefPtr<UniquedStringImpl>> writtenVariables; + bool isBodyArrowExpression { false }; + JSTokenType tokenType { CLOSEBRACE }; +}; + +#if COMPILER(MSVC) +#pragma warning(push) +#pragma warning(disable: 4200) // Disable "zero-sized array in struct/union" warning +#endif + +class SourceProviderCacheItem { + WTF_MAKE_FAST_ALLOCATED; +public: + static std::unique_ptr<SourceProviderCacheItem> create(const SourceProviderCacheItemCreationParameters&); + ~SourceProviderCacheItem(); + + JSToken endFunctionToken() const + { + JSToken token; + token.m_type = isBodyArrowExpression ? tokenType : CLOSEBRACE; + token.m_data.offset = lastTockenStartOffset; + token.m_location.startOffset = lastTockenStartOffset; + token.m_location.endOffset = lastTockenEndOffset; + token.m_location.line = lastTockenLine; + token.m_location.lineStartOffset = lastTockenLineStartOffset; + // token.m_location.sourceOffset is initialized once by the client. So, + // we do not need to set it here. + return token; + } + + unsigned functionNameStart : 31; + bool needsFullActivation : 1; + + unsigned endFunctionOffset : 31; + unsigned lastTockenLine : 31; + unsigned lastTockenStartOffset : 31; + unsigned lastTockenEndOffset: 31; + unsigned parameterCount; + + bool usesEval : 1; + + bool strictMode : 1; + + unsigned lastTockenLineStartOffset; + unsigned usedVariablesCount; + unsigned writtenVariablesCount; + + UniquedStringImpl** usedVariables() const { return const_cast<UniquedStringImpl**>(m_variables); } + UniquedStringImpl** writtenVariables() const { return const_cast<UniquedStringImpl**>(&m_variables[usedVariablesCount]); } + bool isBodyArrowExpression; + JSTokenType tokenType; + +private: + SourceProviderCacheItem(const SourceProviderCacheItemCreationParameters&); + + UniquedStringImpl* m_variables[0]; +}; + +inline SourceProviderCacheItem::~SourceProviderCacheItem() +{ + for (unsigned i = 0; i < usedVariablesCount + writtenVariablesCount; ++i) + m_variables[i]->deref(); +} + +inline std::unique_ptr<SourceProviderCacheItem> SourceProviderCacheItem::create(const SourceProviderCacheItemCreationParameters& parameters) +{ + size_t variableCount = parameters.writtenVariables.size() + parameters.usedVariables.size(); + size_t objectSize = sizeof(SourceProviderCacheItem) + sizeof(UniquedStringImpl*) * variableCount; + void* slot = fastMalloc(objectSize); + return std::unique_ptr<SourceProviderCacheItem>(new (slot) SourceProviderCacheItem(parameters)); +} + +inline SourceProviderCacheItem::SourceProviderCacheItem(const SourceProviderCacheItemCreationParameters& parameters) + : functionNameStart(parameters.functionNameStart) + , needsFullActivation(parameters.needsFullActivation) + , endFunctionOffset(parameters.endFunctionOffset) + , lastTockenLine(parameters.lastTockenLine) + , lastTockenStartOffset(parameters.lastTockenStartOffset) + , lastTockenEndOffset(parameters.lastTockenEndOffset) + , parameterCount(parameters.parameterCount) + , usesEval(parameters.usesEval) + , strictMode(parameters.strictMode) + , lastTockenLineStartOffset(parameters.lastTockenLineStartOffset) + , usedVariablesCount(parameters.usedVariables.size()) + , writtenVariablesCount(parameters.writtenVariables.size()) + , isBodyArrowExpression(parameters.isBodyArrowExpression) + , tokenType(parameters.tokenType) +{ + unsigned j = 0; + for (unsigned i = 0; i < usedVariablesCount; ++i, ++j) { + m_variables[j] = parameters.usedVariables[i].get(); + m_variables[j]->ref(); + } + for (unsigned i = 0; i < writtenVariablesCount; ++i, ++j) { + m_variables[j] = parameters.writtenVariables[i].get(); + m_variables[j]->ref(); + } +} + +#if COMPILER(MSVC) +#pragma warning(pop) +#endif + +} + +#endif // SourceProviderCacheItem_h diff --git a/Source/JavaScriptCore/parser/SyntaxChecker.h b/Source/JavaScriptCore/parser/SyntaxChecker.h new file mode 100644 index 000000000..0760c19eb --- /dev/null +++ b/Source/JavaScriptCore/parser/SyntaxChecker.h @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2010, 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SyntaxChecker_h +#define SyntaxChecker_h + +#include "Lexer.h" +#include "ParserFunctionInfo.h" +#include "YarrSyntaxChecker.h" + +namespace JSC { + +class SyntaxChecker { +public: + struct BinaryExprContext { + BinaryExprContext(SyntaxChecker& context) + : m_context(&context) + { + m_context->m_topBinaryExprs.append(m_context->m_topBinaryExpr); + m_context->m_topBinaryExpr = 0; + } + ~BinaryExprContext() + { + m_context->m_topBinaryExpr = m_context->m_topBinaryExprs.last(); + m_context->m_topBinaryExprs.removeLast(); + } + private: + SyntaxChecker* m_context; + }; + struct UnaryExprContext { + UnaryExprContext(SyntaxChecker& context) + : m_context(&context) + { + m_context->m_topUnaryTokens.append(m_context->m_topUnaryToken); + m_context->m_topUnaryToken = 0; + } + ~UnaryExprContext() + { + m_context->m_topUnaryToken = m_context->m_topUnaryTokens.last(); + m_context->m_topUnaryTokens.removeLast(); + } + private: + SyntaxChecker* m_context; + }; + + SyntaxChecker(VM* , void*) + { + } + + enum { NoneExpr = 0, + ResolveEvalExpr, ResolveExpr, IntegerExpr, DoubleExpr, StringExpr, + ThisExpr, NullExpr, BoolExpr, RegExpExpr, ObjectLiteralExpr, + FunctionExpr, ClassExpr, SuperExpr, BracketExpr, DotExpr, CallExpr, + NewExpr, PreExpr, PostExpr, UnaryExpr, BinaryExpr, + ConditionalExpr, AssignmentExpr, TypeofExpr, NewTargetExpr, + DeleteExpr, ArrayLiteralExpr, BindingDestructuring, + ArrayDestructuring, ObjectDestructuring, SourceElementsResult, + FunctionBodyResult, SpreadExpr, ArgumentsResult, + PropertyListResult, ArgumentsListResult, ElementsListResult, + StatementResult, FormalParameterListResult, ClauseResult, + ClauseListResult, CommaExpr, DestructuringAssignment, + TemplateStringResult, TemplateStringListResult, + TemplateExpressionListResult, TemplateExpr, + TaggedTemplateExpr, + ModuleNameResult, + ImportSpecifierResult, ImportSpecifierListResult, + ExportSpecifierResult, ExportSpecifierListResult + }; + typedef int ExpressionType; + + typedef ExpressionType Expression; + typedef int SourceElements; + typedef int Arguments; + typedef ExpressionType Comma; + struct Property { + ALWAYS_INLINE Property(void* = 0) + : type((PropertyNode::Type)0) + { + } + ALWAYS_INLINE Property(const Identifier* ident, PropertyNode::Type ty) + : name(ident) + , type(ty) + { + } + ALWAYS_INLINE Property(PropertyNode::Type ty) + : name(0) + , type(ty) + { + } + ALWAYS_INLINE bool operator!() { return !type; } + const Identifier* name; + PropertyNode::Type type; + }; + typedef int PropertyList; + typedef int ElementList; + typedef int ArgumentsList; +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + typedef int TemplateExpressionList; + typedef int TemplateString; + typedef int TemplateStringList; + typedef int TemplateLiteral; +#endif + typedef int FormalParameterList; + typedef int FunctionBody; +#if ENABLE(ES6_CLASS_SYNTAX) + typedef int ClassExpression; +#endif + typedef int ModuleName; + typedef int ImportSpecifier; + typedef int ImportSpecifierList; + typedef int ExportSpecifier; + typedef int ExportSpecifierList; + typedef int Statement; + typedef int ClauseList; + typedef int Clause; + typedef int BinaryOperand; + typedef int DestructuringPattern; + typedef DestructuringPattern ArrayPattern; + typedef DestructuringPattern ObjectPattern; + + static const bool CreatesAST = false; + static const bool NeedsFreeVariableInfo = false; + static const bool CanUseFunctionCache = true; + static const unsigned DontBuildKeywords = LexexFlagsDontBuildKeywords; + static const unsigned DontBuildStrings = LexerFlagsDontBuildStrings; + + int createSourceElements() { return SourceElementsResult; } + ExpressionType makeFunctionCallNode(const JSTokenLocation&, int, int, int, int, int) { return CallExpr; } + ExpressionType createCommaExpr(const JSTokenLocation&, ExpressionType expr) { return expr; } + ExpressionType appendToCommaExpr(const JSTokenLocation&, ExpressionType& head, ExpressionType, ExpressionType next) { head = next; return next; } + ExpressionType makeAssignNode(const JSTokenLocation&, ExpressionType, Operator, ExpressionType, bool, bool, int, int, int) { return AssignmentExpr; } + ExpressionType makePrefixNode(const JSTokenLocation&, ExpressionType, Operator, int, int, int) { return PreExpr; } + ExpressionType makePostfixNode(const JSTokenLocation&, ExpressionType, Operator, int, int, int) { return PostExpr; } + ExpressionType makeTypeOfNode(const JSTokenLocation&, ExpressionType) { return TypeofExpr; } + ExpressionType makeDeleteNode(const JSTokenLocation&, ExpressionType, int, int, int) { return DeleteExpr; } + ExpressionType makeNegateNode(const JSTokenLocation&, ExpressionType) { return UnaryExpr; } + ExpressionType makeBitwiseNotNode(const JSTokenLocation&, ExpressionType) { return UnaryExpr; } + ExpressionType createLogicalNot(const JSTokenLocation&, ExpressionType) { return UnaryExpr; } + ExpressionType createUnaryPlus(const JSTokenLocation&, ExpressionType) { return UnaryExpr; } + ExpressionType createVoid(const JSTokenLocation&, ExpressionType) { return UnaryExpr; } + ExpressionType createThisExpr(const JSTokenLocation&, ThisTDZMode) { return ThisExpr; } + ExpressionType createSuperExpr(const JSTokenLocation&) { return SuperExpr; } + ExpressionType createNewTargetExpr(const JSTokenLocation&) { return NewTargetExpr; } + ExpressionType createResolve(const JSTokenLocation&, const Identifier*, int) { return ResolveExpr; } + ExpressionType createObjectLiteral(const JSTokenLocation&) { return ObjectLiteralExpr; } + ExpressionType createObjectLiteral(const JSTokenLocation&, int) { return ObjectLiteralExpr; } + ExpressionType createArray(const JSTokenLocation&, int) { return ArrayLiteralExpr; } + ExpressionType createArray(const JSTokenLocation&, int, int) { return ArrayLiteralExpr; } + ExpressionType createDoubleExpr(const JSTokenLocation&, double) { return DoubleExpr; } + ExpressionType createIntegerExpr(const JSTokenLocation&, double) { return IntegerExpr; } + ExpressionType createString(const JSTokenLocation&, const Identifier*) { return StringExpr; } + ExpressionType createBoolean(const JSTokenLocation&, bool) { return BoolExpr; } + ExpressionType createNull(const JSTokenLocation&) { return NullExpr; } + ExpressionType createBracketAccess(const JSTokenLocation&, ExpressionType, ExpressionType, bool, int, int, int) { return BracketExpr; } + ExpressionType createDotAccess(const JSTokenLocation&, ExpressionType, const Identifier*, int, int, int) { return DotExpr; } + ExpressionType createRegExp(const JSTokenLocation&, const Identifier& pattern, const Identifier&, int) { return Yarr::checkSyntax(pattern.string()) ? 0 : RegExpExpr; } + ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int, int, int) { return NewExpr; } + ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int) { return NewExpr; } + ExpressionType createConditionalExpr(const JSTokenLocation&, ExpressionType, ExpressionType, ExpressionType) { return ConditionalExpr; } + ExpressionType createAssignResolve(const JSTokenLocation&, const Identifier&, ExpressionType, int, int, int, AssignmentContext) { return AssignmentExpr; } + ExpressionType createEmptyVarExpression(const JSTokenLocation&, const Identifier&) { return AssignmentExpr; } + ExpressionType createEmptyLetExpression(const JSTokenLocation&, const Identifier&) { return AssignmentExpr; } +#if ENABLE(ES6_CLASS_SYNTAX) + ClassExpression createClassExpr(const JSTokenLocation&, const Identifier&, ExpressionType, ExpressionType, PropertyList, PropertyList) { return ClassExpr; } +#endif + ExpressionType createFunctionExpr(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return FunctionExpr; } + int createFunctionMetadata(const JSTokenLocation&, const JSTokenLocation&, int, int, bool, int, int, int, ConstructorKind, unsigned, SourceParseMode) { return FunctionBodyResult; } + ExpressionType createArrowFunctionExpr(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return FunctionExpr; } + void setFunctionNameStart(int, int) { } + int createArguments() { return ArgumentsResult; } + int createArguments(int) { return ArgumentsResult; } + ExpressionType createSpreadExpression(const JSTokenLocation&, ExpressionType, int, int, int) { return SpreadExpr; } +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + TemplateString createTemplateString(const JSTokenLocation&, const Identifier&, const Identifier&) { return TemplateStringResult; } + TemplateStringList createTemplateStringList(TemplateString) { return TemplateStringListResult; } + TemplateStringList createTemplateStringList(TemplateStringList, TemplateString) { return TemplateStringListResult; } + TemplateExpressionList createTemplateExpressionList(Expression) { return TemplateExpressionListResult; } + TemplateExpressionList createTemplateExpressionList(TemplateExpressionList, Expression) { return TemplateExpressionListResult; } + TemplateLiteral createTemplateLiteral(const JSTokenLocation&, TemplateStringList) { return TemplateExpr; } + TemplateLiteral createTemplateLiteral(const JSTokenLocation&, TemplateStringList, TemplateExpressionList) { return TemplateExpr; } + ExpressionType createTaggedTemplate(const JSTokenLocation&, ExpressionType, TemplateLiteral, int, int, int) { return TaggedTemplateExpr; } +#endif + + int createArgumentsList(const JSTokenLocation&, int) { return ArgumentsListResult; } + int createArgumentsList(const JSTokenLocation&, int, int) { return ArgumentsListResult; } + Property createProperty(const Identifier* name, int, PropertyNode::Type type, PropertyNode::PutType, bool complete, SuperBinding = SuperBinding::NotNeeded) + { + if (!complete) + return Property(type); + ASSERT(name); + return Property(name, type); + } + Property createProperty(VM* vm, ParserArena& parserArena, double name, int, PropertyNode::Type type, PropertyNode::PutType, bool complete) + { + if (!complete) + return Property(type); + return Property(&parserArena.identifierArena().makeNumericIdentifier(vm, name), type); + } + Property createProperty(int, int, PropertyNode::Type type, PropertyNode::PutType, bool) + { + return Property(type); + } + int createPropertyList(const JSTokenLocation&, Property) { return PropertyListResult; } + int createPropertyList(const JSTokenLocation&, Property, int) { return PropertyListResult; } + int createElementList(int, int) { return ElementsListResult; } + int createElementList(int, int, int) { return ElementsListResult; } + int createFormalParameterList() { return FormalParameterListResult; } + void appendParameter(int, DestructuringPattern, int) { } + int createClause(int, int) { return ClauseResult; } + int createClauseList(int) { return ClauseListResult; } + int createClauseList(int, int) { return ClauseListResult; } + int createFuncDeclStatement(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return StatementResult; } +#if ENABLE(ES6_CLASS_SYNTAX) + int createClassDeclStatement(const JSTokenLocation&, ClassExpression, + const JSTextPosition&, const JSTextPosition&, int, int) { return StatementResult; } +#endif + int createBlockStatement(const JSTokenLocation&, int, int, int, VariableEnvironment&) { return StatementResult; } + int createExprStatement(const JSTokenLocation&, int, int, int) { return StatementResult; } + int createIfStatement(const JSTokenLocation&, int, int, int, int) { return StatementResult; } + int createIfStatement(const JSTokenLocation&, int, int, int, int, int) { return StatementResult; } + int createForLoop(const JSTokenLocation&, int, int, int, int, int, int, VariableEnvironment&) { return StatementResult; } + int createForInLoop(const JSTokenLocation&, int, int, int, int, int, int, int, int, VariableEnvironment&) { return StatementResult; } + int createForOfLoop(const JSTokenLocation&, int, int, int, int, int, int, int, int, VariableEnvironment&) { return StatementResult; } + int createEmptyStatement(const JSTokenLocation&) { return StatementResult; } + int createDeclarationStatement(const JSTokenLocation&, int, int, int) { return StatementResult; } + int createReturnStatement(const JSTokenLocation&, int, int, int) { return StatementResult; } + int createBreakStatement(const JSTokenLocation&, int, int) { return StatementResult; } + int createBreakStatement(const JSTokenLocation&, const Identifier*, int, int) { return StatementResult; } + int createContinueStatement(const JSTokenLocation&, int, int) { return StatementResult; } + int createContinueStatement(const JSTokenLocation&, const Identifier*, int, int) { return StatementResult; } + int createTryStatement(const JSTokenLocation&, int, const Identifier*, int, int, int, int, VariableEnvironment&) { return StatementResult; } + int createSwitchStatement(const JSTokenLocation&, int, int, int, int, int, int, VariableEnvironment&) { return StatementResult; } + int createWhileStatement(const JSTokenLocation&, int, int, int, int) { return StatementResult; } + int createWithStatement(const JSTokenLocation&, int, int, int, int, int, int) { return StatementResult; } + int createDoWhileStatement(const JSTokenLocation&, int, int, int, int) { return StatementResult; } + int createLabelStatement(const JSTokenLocation&, const Identifier*, int, int, int) { return StatementResult; } + int createThrowStatement(const JSTokenLocation&, int, int, int) { return StatementResult; } + int createDebugger(const JSTokenLocation&, int, int) { return StatementResult; } + int createConstStatement(const JSTokenLocation&, int, int, int) { return StatementResult; } + int createModuleName(const JSTokenLocation&, const Identifier&) { return ModuleNameResult; } + ImportSpecifier createImportSpecifier(const JSTokenLocation&, const Identifier&, const Identifier&) { return ImportSpecifierResult; } + ImportSpecifierList createImportSpecifierList() { return ImportSpecifierListResult; } + void appendImportSpecifier(ImportSpecifierList, ImportSpecifier) { } + int createImportDeclaration(const JSTokenLocation&, ImportSpecifierList, ModuleName) { return StatementResult; } + int createExportAllDeclaration(const JSTokenLocation&, ModuleName) { return StatementResult; } + int createExportDefaultDeclaration(const JSTokenLocation&, int, const Identifier&) { return StatementResult; } + int createExportLocalDeclaration(const JSTokenLocation&, int) { return StatementResult; } + int createExportNamedDeclaration(const JSTokenLocation&, ExportSpecifierList, ModuleName) { return StatementResult; } + ExportSpecifier createExportSpecifier(const JSTokenLocation&, const Identifier&, const Identifier&) { return ExportSpecifierResult; } + ExportSpecifierList createExportSpecifierList() { return ExportSpecifierListResult; } + void appendExportSpecifier(ExportSpecifierList, ExportSpecifier) { } + + int appendConstDecl(const JSTokenLocation&, int, const Identifier*, int) { return StatementResult; } + Property createGetterOrSetterProperty(const JSTokenLocation&, PropertyNode::Type type, bool strict, const Identifier* name, const ParserFunctionInfo<SyntaxChecker>&, SuperBinding) + { + ASSERT(name); + if (!strict) + return Property(type); + return Property(name, type); + } + Property createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation&, PropertyNode::Type type, bool strict, double name, const ParserFunctionInfo<SyntaxChecker>&, SuperBinding) + { + if (!strict) + return Property(type); + return Property(&parserArena.identifierArena().makeNumericIdentifier(vm, name), type); + } + + void appendStatement(int, int) { } + int combineCommaNodes(const JSTokenLocation&, int, int) { return CommaExpr; } + int evalCount() const { return 0; } + void appendBinaryExpressionInfo(int& operandStackDepth, int expr, int, int, int, bool) + { + if (!m_topBinaryExpr) + m_topBinaryExpr = expr; + else + m_topBinaryExpr = BinaryExpr; + operandStackDepth++; + } + + // Logic to handle datastructures used during parsing of binary expressions + void operatorStackPop(int& operatorStackDepth) { operatorStackDepth--; } + bool operatorStackHasHigherPrecedence(int&, int) { return true; } + BinaryOperand getFromOperandStack(int) { return m_topBinaryExpr; } + void shrinkOperandStackBy(int& operandStackDepth, int amount) { operandStackDepth -= amount; } + void appendBinaryOperation(const JSTokenLocation&, int& operandStackDepth, int&, BinaryOperand, BinaryOperand) { operandStackDepth++; } + void operatorStackAppend(int& operatorStackDepth, int, int) { operatorStackDepth++; } + int popOperandStack(int&) { int res = m_topBinaryExpr; m_topBinaryExpr = 0; return res; } + + void appendUnaryToken(int& stackDepth, int tok, int) { stackDepth = 1; m_topUnaryToken = tok; } + int unaryTokenStackLastType(int&) { return m_topUnaryToken; } + JSTextPosition unaryTokenStackLastStart(int&) { return JSTextPosition(0, 0, 0); } + void unaryTokenStackRemoveLast(int& stackDepth) { stackDepth = 0; } + + void assignmentStackAppend(int, int, int, int, int, Operator) { } + int createAssignment(const JSTokenLocation&, int, int, int, int, int) { RELEASE_ASSERT_NOT_REACHED(); return AssignmentExpr; } + const Identifier* getName(const Property& property) const { return property.name; } + PropertyNode::Type getType(const Property& property) const { return property.type; } + bool isResolve(ExpressionType expr) const { return expr == ResolveExpr || expr == ResolveEvalExpr; } + ExpressionType createDestructuringAssignment(const JSTokenLocation&, int, ExpressionType) + { + return DestructuringAssignment; + } + + ArrayPattern createArrayPattern(const JSTokenLocation&) + { + return ArrayDestructuring; + } + void appendArrayPatternSkipEntry(ArrayPattern, const JSTokenLocation&) + { + } + void appendArrayPatternEntry(ArrayPattern, const JSTokenLocation&, DestructuringPattern, int) + { + } + void appendArrayPatternRestEntry(ArrayPattern, const JSTokenLocation&, DestructuringPattern) + { + } + void finishArrayPattern(ArrayPattern, const JSTextPosition&, const JSTextPosition&, const JSTextPosition&) + { + } + ObjectPattern createObjectPattern(const JSTokenLocation&) + { + return ObjectDestructuring; + } + void appendObjectPatternEntry(ArrayPattern, const JSTokenLocation&, bool, const Identifier&, DestructuringPattern, int) + { + } + DestructuringPattern createBindingLocation(const JSTokenLocation&, const Identifier&, const JSTextPosition&, const JSTextPosition&, AssignmentContext) + { + return BindingDestructuring; + } + + bool isBindingNode(DestructuringPattern pattern) + { + return pattern == BindingDestructuring; + } + + void setEndOffset(int, int) { } + int endOffset(int) { return 0; } + void setStartOffset(int, int) { } + +private: + int m_topBinaryExpr; + int m_topUnaryToken; + Vector<int, 8> m_topBinaryExprs; + Vector<int, 8> m_topUnaryTokens; +}; + +} + +#endif diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.cpp b/Source/JavaScriptCore/parser/VariableEnvironment.cpp new file mode 100644 index 000000000..ed055f98a --- /dev/null +++ b/Source/JavaScriptCore/parser/VariableEnvironment.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2015 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "VariableEnvironment.h" + +namespace JSC { + +void VariableEnvironment::markVariableAsCapturedIfDefined(const RefPtr<UniquedStringImpl>& identifier) +{ + auto findResult = m_map.find(identifier); + if (findResult != m_map.end()) + findResult->value.setIsCaptured(); +} + +void VariableEnvironment::markVariableAsCaptured(const RefPtr<UniquedStringImpl>& identifier) +{ + auto findResult = m_map.find(identifier); + RELEASE_ASSERT(findResult != m_map.end()); + findResult->value.setIsCaptured(); +} + +void VariableEnvironment::markAllVariablesAsCaptured() +{ + if (m_isEverythingCaptured) + return; + + m_isEverythingCaptured = true; // For fast queries. + // We must mark every entry as captured for when we iterate through m_map and entry.isCaptured() is called. + for (auto iter = m_map.begin(), end = m_map.end(); iter != end; ++iter) + iter->value.setIsCaptured(); +} + +bool VariableEnvironment::hasCapturedVariables() const +{ + if (m_isEverythingCaptured) + return size() > 0; + for (auto entry : m_map) { + if (entry.value.isCaptured()) + return true; + } + return false; +} + +bool VariableEnvironment::captures(UniquedStringImpl* identifier) const +{ + if (m_isEverythingCaptured) + return true; + + auto findResult = m_map.find(identifier); + if (findResult == m_map.end()) + return false; + return findResult->value.isCaptured(); +} + +void VariableEnvironment::swap(VariableEnvironment& other) +{ + m_map.swap(other.m_map); + m_isEverythingCaptured = other.m_isEverythingCaptured; +} + +void VariableEnvironment::markVariableAsImported(const RefPtr<UniquedStringImpl>& identifier) +{ + auto findResult = m_map.find(identifier); + RELEASE_ASSERT(findResult != m_map.end()); + findResult->value.setIsImported(); +} + +void VariableEnvironment::markVariableAsExported(const RefPtr<UniquedStringImpl>& identifier) +{ + auto findResult = m_map.find(identifier); + RELEASE_ASSERT(findResult != m_map.end()); + findResult->value.setIsExported(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.h b/Source/JavaScriptCore/parser/VariableEnvironment.h new file mode 100644 index 000000000..5ed2a6bda --- /dev/null +++ b/Source/JavaScriptCore/parser/VariableEnvironment.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2015 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VariableEnvironment_h +#define VariableEnvironment_h + +#include "Identifier.h" +#include <wtf/HashMap.h> +#include <wtf/text/UniquedStringImpl.h> + +namespace JSC { + +struct VariableEnvironmentEntry { +public: + ALWAYS_INLINE bool isCaptured() const { return m_bits & IsCaptured; } + ALWAYS_INLINE bool isConst() const { return m_bits & IsConst; } + ALWAYS_INLINE bool isVar() const { return m_bits & IsVar; } + ALWAYS_INLINE bool isLet() const { return m_bits & IsLet; } + ALWAYS_INLINE bool isExported() const { return m_bits & IsExported; } + ALWAYS_INLINE bool isImported() const { return m_bits & IsImported; } + + ALWAYS_INLINE void setIsCaptured() { m_bits |= IsCaptured; } + ALWAYS_INLINE void setIsConst() { m_bits |= IsConst; } + ALWAYS_INLINE void setIsVar() { m_bits |= IsVar; } + ALWAYS_INLINE void setIsLet() { m_bits |= IsLet; } + ALWAYS_INLINE void setIsExported() { m_bits |= IsExported; } + ALWAYS_INLINE void setIsImported() { m_bits |= IsImported; } + + ALWAYS_INLINE void clearIsVar() { m_bits &= ~IsVar; } + +private: + enum Traits { + IsCaptured = 1 << 0, + IsConst = 1 << 1, + IsVar = 1 << 2, + IsLet = 1 << 3, + IsExported = 1 << 4, + IsImported = 1 << 5 + }; + uint8_t m_bits { 0 }; +}; + +struct VariableEnvironmentEntryHashTraits : HashTraits<VariableEnvironmentEntry> { + static const bool needsDestruction = false; +}; + +class VariableEnvironment { +private: + typedef HashMap<RefPtr<UniquedStringImpl>, VariableEnvironmentEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, VariableEnvironmentEntryHashTraits> Map; +public: + ALWAYS_INLINE Map::iterator begin() { return m_map.begin(); } + ALWAYS_INLINE Map::iterator end() { return m_map.end(); } + ALWAYS_INLINE Map::const_iterator begin() const { return m_map.begin(); } + ALWAYS_INLINE Map::const_iterator end() const { return m_map.end(); } + ALWAYS_INLINE Map::AddResult add(const RefPtr<UniquedStringImpl>& identifier) { return m_map.add(identifier, VariableEnvironmentEntry()); } + ALWAYS_INLINE Map::AddResult add(const Identifier& identifier) { return add(identifier.impl()); } + ALWAYS_INLINE unsigned size() const { return m_map.size(); } + ALWAYS_INLINE bool contains(const RefPtr<UniquedStringImpl>& identifier) const { return m_map.contains(identifier); } + ALWAYS_INLINE bool remove(const RefPtr<UniquedStringImpl>& identifier) { return m_map.remove(identifier); } + void swap(VariableEnvironment& other); + void markVariableAsCapturedIfDefined(const RefPtr<UniquedStringImpl>& identifier); + void markVariableAsCaptured(const RefPtr<UniquedStringImpl>& identifier); + void markAllVariablesAsCaptured(); + bool hasCapturedVariables() const; + bool captures(UniquedStringImpl* identifier) const; + void markVariableAsImported(const RefPtr<UniquedStringImpl>& identifier); + void markVariableAsExported(const RefPtr<UniquedStringImpl>& identifier); + +private: + Map m_map; + bool m_isEverythingCaptured { false }; +}; + +} // namespace JSC + +#endif // VariableEnvironment_h |
