diff options
Diffstat (limited to 'Source/JavaScriptCore/parser')
31 files changed, 7885 insertions, 2342 deletions
diff --git a/Source/JavaScriptCore/parser/ASTBuilder.h b/Source/JavaScriptCore/parser/ASTBuilder.h index 4281e7ccd..a488f7601 100644 --- a/Source/JavaScriptCore/parser/ASTBuilder.h +++ b/Source/JavaScriptCore/parser/ASTBuilder.h @@ -26,8 +26,11 @@ #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 { @@ -75,10 +78,10 @@ class ASTBuilder { Operator m_op; }; public: - ASTBuilder(VM* vm, SourceCode* sourceCode) + ASTBuilder(VM* vm, ParserArena& parserArena, SourceCode* sourceCode) : m_vm(vm) + , m_parserArena(parserArena) , m_sourceCode(sourceCode) - , m_scope(vm) , m_evalCount(0) { } @@ -90,8 +93,6 @@ public: UnaryExprContext(ASTBuilder&) {} }; - typedef SyntaxChecker FunctionBodyBuilder; - typedef ExpressionNode* Expression; typedef JSC::SourceElements* SourceElements; typedef ArgumentsNode* Arguments; @@ -100,14 +101,27 @@ public: typedef PropertyListNode* PropertyList; typedef ElementNode* ElementList; typedef ArgumentListNode* ArgumentsList; - typedef ParameterNode* FormalParameterList; - typedef FunctionBodyNode* FunctionBody; + typedef TemplateExpressionListNode* TemplateExpressionList; + typedef TemplateStringNode* TemplateString; + typedef TemplateStringListNode* TemplateStringList; + typedef TemplateLiteralNode* TemplateLiteral; + typedef FunctionParameters* FormalParameterList; + typedef FunctionMetadataNode* FunctionBody; + typedef ClassExprNode* ClassExpression; + 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 ConstDeclNode* ConstDeclList; typedef std::pair<ExpressionNode*, BinaryOpInfo> BinaryOperand; - + typedef DestructuringPatternNode* DestructuringPattern; + typedef ArrayPatternNode* ArrayPattern; + typedef ObjectPatternNode* ObjectPattern; + typedef BindingNode* BindingPattern; + typedef AssignmentElementNode* AssignmentElement; static const bool CreatesAST = true; static const bool NeedsFreeVariableInfo = true; static const bool CanUseFunctionCache = true; @@ -117,17 +131,12 @@ public: 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_vm) JSC::SourceElements(); } + JSC::SourceElements* createSourceElements() { return new (m_parserArena) JSC::SourceElements(); } - ParserArenaData<DeclarationStacks::VarStack>* varDeclarations() { return m_scope.m_varDeclarations; } - ParserArenaData<DeclarationStacks::FunctionStack>* funcDeclarations() { return m_scope.m_funcDeclarations; } + DeclarationStacks::FunctionStack& funcDeclarations() { return m_scope.m_funcDeclarations; } int features() const { return m_scope.m_features; } int numConstants() const { return m_scope.m_numConstants; } - void appendToComma(CommaNode* commaNode, ExpressionNode* expr) { commaNode->append(expr); } - - CommaNode* createCommaExpr(const JSTokenLocation& location, ExpressionNode* lhs, ExpressionNode* rhs) { return new (m_vm) CommaNode(location, lhs, rhs); } - 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); @@ -152,76 +161,144 @@ public: if (expr->isNumber()) return createBoolean(location, isZeroOrUnordered(static_cast<NumberNode*>(expr)->value())); - return new (m_vm) LogicalNotNode(location, expr); + return new (m_parserArena) LogicalNotNode(location, expr); } - ExpressionNode* createUnaryPlus(const JSTokenLocation& location, ExpressionNode* expr) { return new (m_vm) UnaryPlusNode(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_vm) VoidNode(location, expr); + return new (m_parserArena) VoidNode(location, expr); } - ExpressionNode* thisExpr(const JSTokenLocation& location) + ExpressionNode* createThisExpr(const JSTokenLocation& location, ThisTDZMode thisTDZMode) { usesThis(); - return new (m_vm) ThisNode(location); + 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) + ExpressionNode* createResolve(const JSTokenLocation& location, const Identifier& ident, const JSTextPosition& start, const JSTextPosition& end) { - if (m_vm->propertyNames->arguments == *ident) + if (m_vm->propertyNames->arguments == ident) usesArguments(); - return new (m_vm) ResolveNode(location, *ident, start); + + if (ident.isSymbol()) { + if (BytecodeIntrinsicNode::EmitterType emitter = m_vm->bytecodeIntrinsicRegistry().lookup(ident)) + return new (m_parserArena) BytecodeIntrinsicNode(BytecodeIntrinsicNode::Type::Constant, location, emitter, ident, nullptr, start, start, end); + } + + return new (m_parserArena) ResolveNode(location, ident, start); } - ExpressionNode* createObjectLiteral(const JSTokenLocation& location) { return new (m_vm) ObjectLiteralNode(location); } - ExpressionNode* createObjectLiteral(const JSTokenLocation& location, PropertyListNode* properties) { return new (m_vm) ObjectLiteralNode(location, properties); } + 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_vm) ArrayNode(location, elisions); + return new (m_parserArena) ArrayNode(location, elisions); } - ExpressionNode* createArray(const JSTokenLocation& location, ElementNode* elems) { return new (m_vm) ArrayNode(location, elems); } + 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_vm) ArrayNode(location, elisions, elems); + return new (m_parserArena) ArrayNode(location, elisions, elems); + } + ExpressionNode* createDoubleExpr(const JSTokenLocation& location, double d) + { + incConstants(); + return new (m_parserArena) DoubleNode(location, d); } - ExpressionNode* createNumberExpr(const JSTokenLocation& location, double d) + ExpressionNode* createIntegerExpr(const JSTokenLocation& location, double d) { incConstants(); - return new (m_vm) NumberNode(location, d); + return new (m_parserArena) IntegerNode(location, d); } ExpressionNode* createString(const JSTokenLocation& location, const Identifier* string) { incConstants(); - return new (m_vm) StringNode(location, *string); + return new (m_parserArena) StringNode(location, *string); } ExpressionNode* createBoolean(const JSTokenLocation& location, bool b) { incConstants(); - return new (m_vm) BooleanNode(location, b); + return new (m_parserArena) BooleanNode(location, b); } ExpressionNode* createNull(const JSTokenLocation& location) { incConstants(); - return new (m_vm) NullNode(location); + 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_vm) BracketAccessorNode(location, base, property, propertyHasAssignments); + 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_vm) DotAccessorNode(location, base, *property); + 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; + } + + 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; } @@ -230,7 +307,7 @@ public: { if (Yarr::checkSyntax(pattern.string())) return 0; - RegExpNode* node = new (m_vm) RegExpNode(location, pattern, flags); + 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); @@ -239,184 +316,285 @@ public: ExpressionNode* createNewExpr(const JSTokenLocation& location, ExpressionNode* expr, ArgumentsNode* arguments, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) { - NewExprNode* node = new (m_vm) NewExprNode(location, expr, arguments); + 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_vm) NewExprNode(location, expr); + 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_vm) ConditionalNode(location, condition, lhs, 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() || rhs->isArrowFuncExprNode()) + static_cast<FuncExprNode*>(rhs)->metadata()->setInferredName(ident); + AssignResolveNode* node = new (m_parserArena) AssignResolveNode(location, ident, rhs, assignmentContext); + setExceptionLocation(node, start, divot, end); + return node; + } + + YieldExprNode* createYield(const JSTokenLocation& location) + { + return new (m_parserArena) YieldExprNode(location, nullptr, /* delegate */ false); } - ExpressionNode* createAssignResolve(const JSTokenLocation& location, const Identifier& ident, ExpressionNode* rhs, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) + YieldExprNode* createYield(const JSTokenLocation& location, ExpressionNode* argument, bool delegate, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) { - if (rhs->isFuncExprNode()) - static_cast<FuncExprNode*>(rhs)->body()->setInferredName(ident); - AssignResolveNode* node = new (m_vm) AssignResolveNode(location, ident, rhs); + YieldExprNode* node = new (m_parserArena) YieldExprNode(location, argument, delegate); setExceptionLocation(node, start, divot, end); return node; } - ExpressionNode* createFunctionExpr(const JSTokenLocation& location, const Identifier* name, FunctionBodyNode* body, ParameterNode* parameters, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned startColumn) + ClassExprNode* createClassExpr(const JSTokenLocation& location, const Identifier& name, VariableEnvironment& classEnvironment, ExpressionNode* constructor, + ExpressionNode* parentClass, PropertyListNode* instanceMethods, PropertyListNode* staticMethods) + { + return new (m_parserArena) ClassExprNode(location, name, classEnvironment, constructor, parentClass, instanceMethods, staticMethods); + } + + ExpressionNode* createFunctionExpr(const JSTokenLocation& location, const ParserFunctionInfo<ASTBuilder>& functionInfo) { - FuncExprNode* result = new (m_vm) FuncExprNode(location, *name, body, m_sourceCode->subExpression(openBraceOffset, closeBraceOffset, bodyStartLine, startColumn), parameters); - body->setLoc(bodyStartLine, bodyEndLine, location.startOffset, location.lineStartOffset); + 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; } - FunctionBodyNode* createFunctionBody(const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, bool inStrictContext) + FunctionMetadataNode* createFunctionMetadata( + const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, + unsigned startColumn, unsigned endColumn, int functionKeywordStart, + int functionNameStart, int parametersStart, bool inStrictContext, + ConstructorKind constructorKind, SuperBinding superBinding, unsigned parameterCount, SourceParseMode mode, bool isArrowFunctionBodyExpression) { - return FunctionBodyNode::create(m_vm, startLocation, endLocation, startColumn, inStrictContext); + return new (m_parserArena) FunctionMetadataNode( + m_parserArena, startLocation, endLocation, startColumn, endColumn, + functionKeywordStart, functionNameStart, parametersStart, + inStrictContext, constructorKind, superBinding, parameterCount, mode, isArrowFunctionBodyExpression); } - void setFunctionStart(FunctionBodyNode* body, int functionStart) + ExpressionNode* createArrowFunctionExpr(const JSTokenLocation& location, const ParserFunctionInfo<ASTBuilder>& functionInfo) { - body->setFunctionStart(functionStart); + usesArrowFunction(); + SourceCode source = m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.body->isArrowFunctionBodyExpression() ? functionInfo.endOffset - 1 : functionInfo.endOffset, functionInfo.startLine, functionInfo.bodyStartColumn); + ArrowFuncExprNode* result = new (m_parserArena) ArrowFuncExprNode(location, *functionInfo.name, functionInfo.body, source); + functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset); + return result; } - - template <bool> PropertyNode* createGetterOrSetterProperty(const JSTokenLocation& location, PropertyNode::Type type, const Identifier* name, ParameterNode* params, FunctionBodyNode* body, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned bodyStartColumn) + + NEVER_INLINE PropertyNode* createGetterOrSetterProperty(const JSTokenLocation& location, PropertyNode::Type type, bool, + const Identifier* name, const ParserFunctionInfo<ASTBuilder>& functionInfo, SuperBinding superBinding) { ASSERT(name); - body->setLoc(bodyStartLine, bodyEndLine, location.startOffset, location.lineStartOffset); - body->setInferredName(*name); - return new (m_vm) PropertyNode(m_vm, *name, new (m_vm) FuncExprNode(location, m_vm->propertyNames->nullIdentifier, body, m_sourceCode->subExpression(openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn), params), type); + 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); } - - template <bool> PropertyNode* createGetterOrSetterProperty(VM*, const JSTokenLocation& location, PropertyNode::Type type, double name, ParameterNode* params, FunctionBodyNode* body, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned bodyStartColumn) + + NEVER_INLINE PropertyNode* createGetterOrSetterProperty(const JSTokenLocation& location, PropertyNode::Type type, bool, + ExpressionNode* name, const ParserFunctionInfo<ASTBuilder>& functionInfo, SuperBinding superBinding) + { + ASSERT(name); + functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset); + 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) { - body->setLoc(bodyStartLine, bodyEndLine, location.startOffset, location.lineStartOffset); - return new (m_vm) PropertyNode(m_vm, name, new (m_vm) FuncExprNode(location, m_vm->propertyNames->nullIdentifier, body, m_sourceCode->subExpression(openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn), params), type); + 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_vm) ArgumentsNode(); } - ArgumentsNode* createArguments(ArgumentListNode* args) { return new (m_vm) ArgumentsNode(args); } - ArgumentListNode* createArgumentsList(const JSTokenLocation& location, ExpressionNode* arg) { return new (m_vm) ArgumentListNode(location, arg); } - ArgumentListNode* createArgumentsList(const JSTokenLocation& location, ArgumentListNode* args, ExpressionNode* arg) { return new (m_vm) ArgumentListNode(location, args, arg); } + 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); } - template <bool> PropertyNode* createProperty(const Identifier* propertyName, ExpressionNode* node, PropertyNode::Type type) + 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)->body()->setInferredName(*propertyName); - return new (m_vm) PropertyNode(m_vm, *propertyName, node, type); + static_cast<FuncExprNode*>(node)->metadata()->setInferredName(*propertyName); + return new (m_parserArena) PropertyNode(*propertyName, node, type, putType, superBinding); } - template <bool> PropertyNode* createProperty(VM*, double propertyName, ExpressionNode* node, PropertyNode::Type type) { return new (m_vm) PropertyNode(m_vm, propertyName, node, type); } - PropertyListNode* createPropertyList(const JSTokenLocation& location, PropertyNode* property) { return new (m_vm) PropertyListNode(location, property); } - PropertyListNode* createPropertyList(const JSTokenLocation& location, PropertyNode* property, PropertyListNode* tail) { return new (m_vm) PropertyListNode(location, property, tail); } - - ElementNode* createElementList(int elisions, ExpressionNode* expr) { return new (m_vm) ElementNode(elisions, expr); } - ElementNode* createElementList(ElementNode* elems, int elisions, ExpressionNode* expr) { return new (m_vm) ElementNode(elems, elisions, expr); } + 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, SuperBinding superBinding = SuperBinding::NotNeeded) { return new (m_parserArena) PropertyNode(propertyName, node, type, putType, superBinding); } + 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); } - ParameterNode* createFormalParameterList(const Identifier& ident) { return new (m_vm) ParameterNode(ident); } - ParameterNode* createFormalParameterList(ParameterNode* list, const Identifier& ident) { return new (m_vm) ParameterNode(list, ident); } + 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); } + ElementNode* createElementList(ArgumentListNode* elems) + { + ElementNode* head = new (m_parserArena) ElementNode(0, elems->m_expr); + ElementNode* tail = head; + elems = elems->m_next; + while (elems) { + tail = new (m_parserArena) ElementNode(tail, 0, elems->m_expr); + elems = elems->m_next; + } + return head; + } - CaseClauseNode* createClause(ExpressionNode* expr, JSC::SourceElements* statements) { return new (m_vm) CaseClauseNode(expr, statements); } - ClauseListNode* createClauseList(CaseClauseNode* clause) { return new (m_vm) ClauseListNode(clause); } - ClauseListNode* createClauseList(ClauseListNode* tail, CaseClauseNode* clause) { return new (m_vm) ClauseListNode(tail, clause); } + FormalParameterList createFormalParameterList() { return new (m_parserArena) FunctionParameters(); } + void appendParameter(FormalParameterList list, DestructuringPattern pattern, ExpressionNode* defaultValue) + { + list->append(pattern, defaultValue); + } - void setUsesArguments(FunctionBodyNode* node) { node->setUsesArguments(); } + 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 Identifier* name, FunctionBodyNode* body, ParameterNode* parameters, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned bodyStartColumn) + StatementNode* createFuncDeclStatement(const JSTokenLocation& location, const ParserFunctionInfo<ASTBuilder>& functionInfo) { - FuncDeclNode* decl = new (m_vm) FuncDeclNode(location, *name, body, m_sourceCode->subExpression(openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn), parameters); - if (*name == m_vm->propertyNames->arguments) + 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->data.append(decl->body()); - body->setLoc(bodyStartLine, bodyEndLine, location.startOffset, location.lineStartOffset); + m_scope.m_funcDeclarations.append(decl->metadata()); + functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset); return decl; } - StatementNode* createBlockStatement(const JSTokenLocation& location, JSC::SourceElements* elements, int startLine, int endLine) + StatementNode* createClassDeclStatement(const JSTokenLocation& location, ClassExprNode* classExpression, + const JSTextPosition& classStart, const JSTextPosition& classEnd, unsigned startLine, unsigned endLine) { - BlockNode* block = new (m_vm) BlockNode(location, elements); + 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; + } + + 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_vm) ExprStatementNode(location, expr); + 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_vm) IfElseNode(location, condition, trueBlock, falseBlock); + 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) + StatementNode* createForLoop(const JSTokenLocation& location, ExpressionNode* initializer, ExpressionNode* condition, ExpressionNode* iter, StatementNode* statements, int start, int end, VariableEnvironment& lexicalVariables) { - ForNode* result = new (m_vm) ForNode(location, initializer, condition, iter, statements); + 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, const Identifier* ident, ExpressionNode* initializer, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end, const JSTextPosition& initStart, const JSTextPosition& initEnd, int startLine, int endLine) + 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_vm) ForInNode(m_vm, location, *ident, initializer, iter, statements, initStart, start, initEnd); - result->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset); - setExceptionLocation(result, start, divot + 1, end); + 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, ExpressionNode* lhs, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end) + + 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) { - ForInNode* result = new (m_vm) ForInNode(location, lhs, iter, statements); + 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(); + } + + bool isAssignmentLocation(const Expression& pattern) + { + return pattern->isAssignmentLocation(); + } + + bool isObjectLiteral(const Expression& node) + { + return node->isObjectLiteral(); + } + + bool isArrayLiteral(const Expression& node) + { + return node->isArrayLiteral(); + } + + bool isObjectOrArrayLiteral(const Expression& node) + { + return isObjectLiteral(node) || isArrayLiteral(node); + } - StatementNode* createEmptyStatement(const JSTokenLocation& location) { return new (m_vm) EmptyStatementNode(location); } + StatementNode* createEmptyStatement(const JSTokenLocation& location) { return new (m_parserArena) EmptyStatementNode(location); } - StatementNode* createVarStatement(const JSTokenLocation& location, ExpressionNode* expr, int start, int end) + StatementNode* createDeclarationStatement(const JSTokenLocation& location, ExpressionNode* expr, int start, int end) { StatementNode* result; - if (!expr) - result = new (m_vm) EmptyStatementNode(location); - else - result = new (m_vm) VarStatementNode(location, expr); + result = new (m_parserArena) DeclarationStatement(location, expr); result->setLoc(start, end, location.startOffset, location.lineStartOffset); return result; } - StatementNode* createReturnStatement(const JSTokenLocation& location, ExpressionNode* expression, const JSTextPosition& start, const JSTextPosition& end) + ExpressionNode* createEmptyVarExpression(const JSTokenLocation& location, const Identifier& identifier) { - ReturnNode* result = new (m_vm) ReturnNode(location, expression); - setExceptionLocation(result, start, end, end); - result->setLoc(start.line, end.line, start.offset, start.lineStartOffset); - return result; + return new (m_parserArena) EmptyVarExpression(location, identifier); } - StatementNode* createBreakStatement(const JSTokenLocation& location, const JSTextPosition& start, const JSTextPosition& end) + ExpressionNode* createEmptyLetExpression(const JSTokenLocation& location, const Identifier& identifier) { - BreakNode* result = new (m_vm) BreakNode(m_vm, location); - setExceptionLocation(result, start, end, end); - result->setLoc(start.line, end.line, start.offset, start.lineStartOffset); - return result; + return new (m_parserArena) EmptyLetExpression(location, identifier); } - StatementNode* createBreakStatement(const JSTokenLocation& location, const Identifier* ident, const JSTextPosition& start, const JSTextPosition& end) + StatementNode* createReturnStatement(const JSTokenLocation& location, ExpressionNode* expression, const JSTextPosition& start, const JSTextPosition& end) { - BreakNode* result = new (m_vm) BreakNode(location, *ident); + 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* createContinueStatement(const JSTokenLocation& location, const JSTextPosition& start, const JSTextPosition& end) + StatementNode* createBreakStatement(const JSTokenLocation& location, const Identifier* ident, const JSTextPosition& start, const JSTextPosition& end) { - ContinueNode* result = new (m_vm) ContinueNode(m_vm, location); + 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; @@ -424,46 +602,44 @@ public: StatementNode* createContinueStatement(const JSTokenLocation& location, const Identifier* ident, const JSTextPosition& start, const JSTextPosition& end) { - ContinueNode* result = new (m_vm) ContinueNode(location, *ident); + 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) + StatementNode* createTryStatement(const JSTokenLocation& location, StatementNode* tryBlock, DestructuringPatternNode* catchPattern, StatementNode* catchBlock, StatementNode* finallyBlock, int startLine, int endLine, VariableEnvironment& catchEnvironment) { - TryNode* result = new (m_vm) TryNode(location, tryBlock, *ident, catchBlock, finallyBlock); - if (catchBlock) - usesCatch(); + TryNode* result = new (m_parserArena) TryNode(location, tryBlock, catchPattern, catchBlock, catchEnvironment, finallyBlock); 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) + StatementNode* createSwitchStatement(const JSTokenLocation& location, ExpressionNode* expr, ClauseListNode* firstClauses, CaseClauseNode* defaultClause, ClauseListNode* secondClauses, int startLine, int endLine, VariableEnvironment& lexicalVariables) { - CaseBlockNode* cases = new (m_vm) CaseBlockNode(firstClauses, defaultClause, secondClauses); - SwitchNode* result = new (m_vm) SwitchNode(location, expr, cases); + 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_vm) WhileNode(location, expr, statement); + 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_vm) DoWhileNode(location, statement, expr); + 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_vm) LabelNode(location, *ident, statement); + LabelNode* result = new (m_parserArena) LabelNode(location, *ident, statement); setExceptionLocation(result, start, end, end); return result; } @@ -471,14 +647,14 @@ public: StatementNode* createWithStatement(const JSTokenLocation& location, ExpressionNode* expr, StatementNode* statement, unsigned start, const JSTextPosition& end, unsigned startLine, unsigned endLine) { usesWith(); - WithNode* result = new (m_vm) WithNode(location, expr, statement, end, end - start); + 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_vm) ThrowNode(location, expr); + 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; @@ -486,24 +662,69 @@ public: StatementNode* createDebugger(const JSTokenLocation& location, int startLine, int endLine) { - DebuggerStatementNode* result = new (m_vm) DebuggerStatementNode(location); + DebuggerStatementNode* result = new (m_parserArena) DebuggerStatementNode(location); result->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset); return result; } - - StatementNode* createConstStatement(const JSTokenLocation& location, ConstDeclNode* decls, int startLine, int endLine) + + ModuleNameNode* createModuleName(const JSTokenLocation& location, const Identifier& moduleName) { - ConstStatementNode* result = new (m_vm) ConstStatementNode(location, decls); - result->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset); - return result; + return new (m_parserArena) ModuleNameNode(location, moduleName); } - ConstDeclNode* appendConstDecl(const JSTokenLocation& location, ConstDeclNode* tail, const Identifier* name, ExpressionNode* initializer) + ImportSpecifierNode* createImportSpecifier(const JSTokenLocation& location, const Identifier& importedName, const Identifier& localName) { - ConstDeclNode* result = new (m_vm) ConstDeclNode(location, *name, initializer); - if (tail) - tail->m_next = result; - return result; + 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) @@ -511,22 +732,18 @@ public: elements->append(statement); } - void addVar(const Identifier* ident, int attrs) + CommaNode* createCommaExpr(const JSTokenLocation& location, ExpressionNode* node) { - if (m_vm->propertyNames->arguments == *ident) - usesArguments(); - m_scope.m_varDeclarations->data.append(std::make_pair(ident, attrs)); + return new (m_parserArena) CommaNode(location, node); } - ExpressionNode* combineCommaNodes(const JSTokenLocation& location, ExpressionNode* list, ExpressionNode* init) + CommaNode* appendToCommaExpr(const JSTokenLocation& location, ExpressionNode*, ExpressionNode* tail, ExpressionNode* next) { - if (!list) - return init; - if (list->isCommaNode()) { - static_cast<CommaNode*>(list)->append(init); - return list; - } - return new (m_vm) CommaNode(location, list, init); + ASSERT(tail->isCommaNode()); + ASSERT(next); + CommaNode* newTail = new (m_parserArena) CommaNode(location, next); + static_cast<CommaNode*>(tail)->setNext(newTail); + return newTail; } int evalCount() const { return m_evalCount; } @@ -609,23 +826,103 @@ public: assignmentStackDepth--; return result; } - - const Identifier& getName(Property property) const { return property->name(); } - PropertyNode::Type getType(Property property) const { return property->type(); } + + 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); + } + + void appendObjectPatternEntry(ObjectPattern node, const JSTokenLocation& location, ExpressionNode* propertyExpression, DestructuringPattern pattern, ExpressionNode* defaultValue) + { + node->appendEntry(location, propertyExpression, 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); + } + + RestParameterNode* createRestParameter(const Identifier& name, size_t numParametersToSkip, const JSTextPosition& start, const JSTextPosition& end) + { + return new (m_parserArena) RestParameterNode(name, numParametersToSkip, start, end); + } + + AssignmentElement createAssignmentElement(const Expression& assignmentTarget, const JSTextPosition& start, const JSTextPosition& end) + { + return new (m_parserArena) AssignmentElementNode(assignmentTarget, start, end); + } + + 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); + } + + + void propagateArgumentsUse() { usesArguments(); } + private: struct Scope { - Scope(VM* vm) - : m_varDeclarations(new (vm) ParserArenaData<DeclarationStacks::VarStack>) - , m_funcDeclarations(new (vm) ParserArenaData<DeclarationStacks::FunctionStack>) - , m_features(0) + Scope() + : m_features(0) , m_numConstants(0) { } - ParserArenaData<DeclarationStacks::VarStack>* m_varDeclarations; - ParserArenaData<DeclarationStacks::FunctionStack>* m_funcDeclarations; + DeclarationStacks::FunctionStack m_funcDeclarations; int m_features; int m_numConstants; }; @@ -638,7 +935,7 @@ private: void incConstants() { m_scope.m_numConstants++; } void usesThis() { m_scope.m_features |= ThisFeature; } - void usesCatch() { m_scope.m_features |= CatchFeature; } + void usesArrowFunction() { m_scope.m_features |= ArrowFunctionFeature; } void usesArguments() { m_scope.m_features |= ArgumentsFeature; } void usesWith() { m_scope.m_features |= WithFeature; } void usesEval() @@ -646,18 +943,35 @@ private: m_evalCount++; m_scope.m_features |= EvalFeature; } - ExpressionNode* createNumber(const JSTokenLocation& location, double d) + ExpressionNode* createIntegerLikeNumber(const JSTokenLocation& location, double d) { - return new (m_vm) NumberNode(location, 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<pair<int, int>, 10, UnsafeVectorOverflow> m_binaryOperatorStack; - Vector<pair<int, JSTextPosition>, 10, UnsafeVectorOverflow> m_unaryTokenStack; + Vector<std::pair<int, int>, 10, UnsafeVectorOverflow> m_binaryOperatorStack; + Vector<std::pair<int, JSTextPosition>, 10, UnsafeVectorOverflow> m_unaryTokenStack; int m_evalCount; }; @@ -665,44 +979,43 @@ ExpressionNode* ASTBuilder::makeTypeOfNode(const JSTokenLocation& location, Expr { if (expr->isResolveNode()) { ResolveNode* resolve = static_cast<ResolveNode*>(expr); - return new (m_vm) TypeOfResolveNode(location, resolve->identifier()); + return new (m_parserArena) TypeOfResolveNode(location, resolve->identifier()); } - return new (m_vm) TypeOfValueNode(location, expr); + 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_vm) DeleteValueNode(location, expr); + return new (m_parserArena) DeleteValueNode(location, expr); if (expr->isResolveNode()) { ResolveNode* resolve = static_cast<ResolveNode*>(expr); - return new (m_vm) DeleteResolveNode(location, resolve->identifier(), divot, start, end); + return new (m_parserArena) DeleteResolveNode(location, resolve->identifier(), divot, start, end); } if (expr->isBracketAccessorNode()) { BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(expr); - return new (m_vm) DeleteBracketNode(location, bracket->base(), bracket->subscript(), divot, start, end); + 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_vm) DeleteDotNode(location, dot->base(), dot->identifier(), divot, start, end); + return new (m_parserArena) DeleteDotNode(location, dot->base(), dot->identifier(), divot, start, end); } ExpressionNode* ASTBuilder::makeNegateNode(const JSTokenLocation& location, ExpressionNode* n) { if (n->isNumber()) { - NumberNode* numberNode = static_cast<NumberNode*>(n); - numberNode->setValue(-numberNode->value()); - return numberNode; + const NumberNode& numberNode = static_cast<const NumberNode&>(*n); + return createNumberFromUnaryOperation(location, -numberNode.value(), numberNode); } - return new (m_vm) NegateNode(location, n); + return new (m_parserArena) NegateNode(location, n); } ExpressionNode* ASTBuilder::makeBitwiseNotNode(const JSTokenLocation& location, ExpressionNode* expr) { if (expr->isNumber()) - return createNumber(location, ~toInt32(static_cast<NumberNode*>(expr)->value())); - return new (m_vm) BitwiseNotNode(location, expr); + 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) @@ -710,16 +1023,19 @@ ExpressionNode* ASTBuilder::makeMultNode(const JSTokenLocation& location, Expres expr1 = expr1->stripUnaryPlus(); expr2 = expr2->stripUnaryPlus(); - if (expr1->isNumber() && expr2->isNumber()) - return createNumber(location, static_cast<NumberNode*>(expr1)->value() * static_cast<NumberNode*>(expr2)->value()); + 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_vm) UnaryPlusNode(location, expr2); + return new (m_parserArena) UnaryPlusNode(location, expr2); if (expr2->isNumber() && static_cast<NumberNode*>(expr2)->value() == 1) - return new (m_vm) UnaryPlusNode(location, expr1); + return new (m_parserArena) UnaryPlusNode(location, expr1); - return new (m_vm) MultNode(location, expr1, expr2, rightHasAssignments); + return new (m_parserArena) MultNode(location, expr1, expr2, rightHasAssignments); } ExpressionNode* ASTBuilder::makeDivNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) @@ -727,26 +1043,39 @@ ExpressionNode* ASTBuilder::makeDivNode(const JSTokenLocation& location, Express expr1 = expr1->stripUnaryPlus(); expr2 = expr2->stripUnaryPlus(); - if (expr1->isNumber() && expr2->isNumber()) - return createNumber(location, static_cast<NumberNode*>(expr1)->value() / static_cast<NumberNode*>(expr2)->value()); - return new (m_vm) DivNode(location, expr1, expr2, rightHasAssignments); + 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()) - return createNumber(location, fmod(static_cast<NumberNode*>(expr1)->value(), static_cast<NumberNode*>(expr2)->value())); - return new (m_vm) ModNode(location, expr1, expr2, rightHasAssignments); + + 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()) - return createNumber(location, static_cast<NumberNode*>(expr1)->value() + static_cast<NumberNode*>(expr2)->value()); - return new (m_vm) AddNode(location, expr1, expr2, 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) @@ -754,94 +1083,120 @@ ExpressionNode* ASTBuilder::makeSubNode(const JSTokenLocation& location, Express expr1 = expr1->stripUnaryPlus(); expr2 = expr2->stripUnaryPlus(); - if (expr1->isNumber() && expr2->isNumber()) - return createNumber(location, static_cast<NumberNode*>(expr1)->value() - static_cast<NumberNode*>(expr2)->value()); - return new (m_vm) SubNode(location, expr1, expr2, 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) SubNode(location, expr1, expr2, rightHasAssignments); } ExpressionNode* ASTBuilder::makeLeftShiftNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) { - if (expr1->isNumber() && expr2->isNumber()) - return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) << (toUInt32(static_cast<NumberNode*>(expr2)->value()) & 0x1f)); - return new (m_vm) LeftShiftNode(location, expr1, expr2, 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()) - return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) >> (toUInt32(static_cast<NumberNode*>(expr2)->value()) & 0x1f)); - return new (m_vm) RightShiftNode(location, expr1, expr2, 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()) - return createNumber(location, toUInt32(static_cast<NumberNode*>(expr1)->value()) >> (toUInt32(static_cast<NumberNode*>(expr2)->value()) & 0x1f)); - return new (m_vm) UnsignedRightShiftNode(location, expr1, expr2, 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()) - return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) | toInt32(static_cast<NumberNode*>(expr2)->value())); - return new (m_vm) BitOrNode(location, expr1, expr2, 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()) - return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) & toInt32(static_cast<NumberNode*>(expr2)->value())); - return new (m_vm) BitAndNode(location, expr1, expr2, 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()) - return createNumber(location, toInt32(static_cast<NumberNode*>(expr1)->value()) ^ toInt32(static_cast<NumberNode*>(expr2)->value())); - return new (m_vm) BitXOrNode(location, expr1, expr2, 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->isBytecodeIntrinsicNode()) { + BytecodeIntrinsicNode* intrinsic = static_cast<BytecodeIntrinsicNode*>(func); + if (intrinsic->type() == BytecodeIntrinsicNode::Type::Constant) + return new (m_parserArena) BytecodeIntrinsicNode(BytecodeIntrinsicNode::Type::Function, location, intrinsic->emitter(), intrinsic->identifier(), args, divot, divotStart, divotEnd); + } if (!func->isLocation()) - return new (m_vm) FunctionCallValueNode(location, func, args, divot, divotStart, divotEnd); + 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_vm) EvalFunctionCallNode(location, args, divot, divotStart, divotEnd); + return new (m_parserArena) EvalFunctionCallNode(location, args, divot, divotStart, divotEnd); } - return new (m_vm) FunctionCallResolveNode(location, 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_vm) FunctionCallBracketNode(location, bracket->base(), bracket->subscript(), args, divot, divotStart, divotEnd); + 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->call) - node = new (m_vm) CallFunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd); - else if (dot->identifier() == m_vm->propertyNames->apply) - node = new (m_vm) ApplyFunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd); + 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_vm) FunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd); + 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, pair<ExpressionNode*, BinaryOpInfo> lhs, pair<ExpressionNode*, BinaryOpInfo> rhs) +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_vm) LogicalOpNode(location, lhs.first, rhs.first, OpLogicalOr); + return new (m_parserArena) LogicalOpNode(location, lhs.first, rhs.first, OpLogicalOr); case AND: - return new (m_vm) LogicalOpNode(location, lhs.first, rhs.first, OpLogicalAnd); + return new (m_parserArena) LogicalOpNode(location, lhs.first, rhs.first, OpLogicalAnd); case BITOR: return makeBitOrNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); @@ -853,37 +1208,37 @@ ExpressionNode* ASTBuilder::makeBinaryNode(const JSTokenLocation& location, int return makeBitAndNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); case EQEQ: - return new (m_vm) EqualNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + return new (m_parserArena) EqualNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); case NE: - return new (m_vm) NotEqualNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + return new (m_parserArena) NotEqualNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); case STREQ: - return new (m_vm) StrictEqualNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + return new (m_parserArena) StrictEqualNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); case STRNEQ: - return new (m_vm) NotStrictEqualNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + return new (m_parserArena) NotStrictEqualNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); case LT: - return new (m_vm) LessNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + return new (m_parserArena) LessNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); case GT: - return new (m_vm) GreaterNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + return new (m_parserArena) GreaterNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); case LE: - return new (m_vm) LessEqNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + return new (m_parserArena) LessEqNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); case GE: - return new (m_vm) GreaterEqNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + return new (m_parserArena) GreaterEqNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); case INSTANCEOF: { - InstanceOfNode* node = new (m_vm) InstanceOfNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + 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_vm) InNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + 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; } @@ -919,24 +1274,24 @@ ExpressionNode* ASTBuilder::makeBinaryNode(const JSTokenLocation& location, int 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_vm) AssignErrorNode(location, divot, start, end); + 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)->body()->setInferredName(resolve->identifier()); - AssignResolveNode* node = new (m_vm) AssignResolveNode(location, resolve->identifier(), expr); + 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_vm) ReadModifyResolveNode(location, resolve->identifier(), op, expr, exprHasAssignments, divot, start, end); + 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_vm) AssignBracketNode(location, bracket->base(), bracket->subscript(), expr, locHasAssignments, exprHasAssignments, bracket->divot(), start, end); - ReadModifyBracketNode* node = new (m_vm) ReadModifyBracketNode(location, bracket->base(), bracket->subscript(), op, expr, locHasAssignments, exprHasAssignments, divot, start, end); + 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; } @@ -944,23 +1299,23 @@ ExpressionNode* ASTBuilder::makeAssignNode(const JSTokenLocation& location, Expr DotAccessorNode* dot = static_cast<DotAccessorNode*>(loc); if (op == OpEqual) { if (expr->isFuncExprNode()) - static_cast<FuncExprNode*>(expr)->body()->setInferredName(dot->identifier()); - return new (m_vm) AssignDotNode(location, dot->base(), dot->identifier(), expr, exprHasAssignments, dot->divot(), start, end); + 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_vm) ReadModifyDotNode(location, dot->base(), dot->identifier(), op, expr, exprHasAssignments, 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_vm) PrefixNode(location, expr, op, divot, start, 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_vm) PostfixNode(location, expr, op, divot, start, end); + return new (m_parserArena) PostfixNode(location, expr, op, divot, start, end); } } diff --git a/Source/JavaScriptCore/parser/Keywords.table b/Source/JavaScriptCore/parser/Keywords.table index 527eada60..4582d00aa 100644 --- a/Source/JavaScriptCore/parser/Keywords.table +++ b/Source/JavaScriptCore/parser/Keywords.table @@ -10,13 +10,16 @@ false FALSETOKEN 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 @@ -28,30 +31,27 @@ do DO while WHILE else ELSE in INTOKEN +super SUPER switch SWITCH throw THROW try TRY typeof TYPEOF with WITH debugger DEBUGGER +yield YIELD # Reserved for future use. -class RESERVED enum RESERVED -export RESERVED -extends RESERVED -import RESERVED -super RESERVED +export EXPORT +import IMPORT # Reserved for future use in strict code. implements RESERVED_IF_STRICT interface RESERVED_IF_STRICT -let 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 index 37ab20689..b471d33df 100644 --- a/Source/JavaScriptCore/parser/Lexer.cpp +++ b/Source/JavaScriptCore/parser/Lexer.cpp @@ -25,31 +25,26 @@ #include "config.h" #include "Lexer.h" -#include "JSFunction.h" - -#include "JSGlobalObjectFunctions.h" +#include "BuiltinNames.h" #include "Identifier.h" -#include "NodeInfo.h" +#include "JSCInlines.h" +#include "JSFunctionInlines.h" +#include "JSGlobalObjectFunctions.h" +#include "KeywordLookup.h" +#include "Lexer.lut.h" #include "Nodes.h" -#include <wtf/dtoa.h> +#include "Parser.h" #include <ctype.h> #include <limits.h> #include <string.h> #include <wtf/Assertions.h> - -using namespace WTF; -using namespace Unicode; - -#include "KeywordLookup.h" -#include "Lexer.lut.h" -#include "Parser.h" +#include <wtf/dtoa.h> namespace JSC { -Keywords::Keywords(VM* vm) - : m_vm(vm) - , m_keywordTable(JSC::mainTable) +bool isLexerKeyword(const Identifier& identifier) { + return JSC::mainTable.entry(identifier); } enum CharacterType { @@ -73,6 +68,7 @@ enum CharacterType { CharacterQuestion, CharacterTilde, CharacterQuote, + CharacterBackQuote, CharacterDot, CharacterSlash, CharacterBackSlash, @@ -93,6 +89,7 @@ enum CharacterType { // Other types (only one so far) CharacterWhiteSpace, + CharacterPrivateIdentifierStart }; // 256 Latin-1 codes @@ -161,7 +158,7 @@ static const unsigned short typesOfLatin1Characters[256] = { /* 61 - = */ CharacterEqual, /* 62 - > */ CharacterGreater, /* 63 - ? */ CharacterQuestion, -/* 64 - @ */ CharacterInvalid, +/* 64 - @ */ CharacterPrivateIdentifierStart, /* 65 - A */ CharacterIdentifierStart, /* 66 - B */ CharacterIdentifierStart, /* 67 - C */ CharacterIdentifierStart, @@ -193,7 +190,7 @@ static const unsigned short typesOfLatin1Characters[256] = { /* 93 - ] */ CharacterCloseBracket, /* 94 - ^ */ CharacterXor, /* 95 - _ */ CharacterIdentifierStart, -/* 96 - ` */ CharacterInvalid, +/* 96 - ` */ CharacterBackQuote, /* 97 - a */ CharacterIdentifierStart, /* 98 - b */ CharacterIdentifierStart, /* 99 - c */ CharacterIdentifierStart, @@ -489,12 +486,20 @@ static const LChar singleCharacterEscapeValuesForASCII[128] = { }; template <typename T> -Lexer<T>::Lexer(VM* vm) - : m_isReparsing(false) +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() { @@ -505,21 +510,21 @@ String Lexer<T>::invalidCharacterMessage() const { switch (m_current) { case 0: - return "Invalid character: '\\0'"; + return ASCIILiteral("Invalid character: '\\0'"); case 10: - return "Invalid character: '\\n'"; + return ASCIILiteral("Invalid character: '\\n'"); case 11: - return "Invalid character: '\\v'"; + return ASCIILiteral("Invalid character: '\\v'"); case 13: - return "Invalid character: '\\r'"; + return ASCIILiteral("Invalid character: '\\r'"); case 35: - return "Invalid character: '#'"; + return ASCIILiteral("Invalid character: '#'"); case 64: - return "Invalid character: '@'"; + return ASCIILiteral("Invalid character: '@'"); case 96: - return "Invalid character: '`'"; + return ASCIILiteral("Invalid character: '`'"); default: - return String::format("Invalid character '\\u%04u'", static_cast<unsigned>(m_current)).impl(); + return String::format("Invalid character '\\u%04u'", static_cast<unsigned>(m_current)); } } @@ -538,10 +543,10 @@ void Lexer<T>::setCode(const SourceCode& source, ParserArena* arena) m_lineNumber = source.firstLine(); m_lastToken = -1; - const String& sourceString = source.provider()->source(); + StringView sourceString = source.provider()->source(); if (!sourceString.isNull()) - setCodeStart(sourceString.impl()); + setCodeStart(sourceString); else m_codeStart = 0; @@ -554,9 +559,12 @@ void Lexer<T>::setCode(const SourceCode& source, ParserArena* arena) m_atLineStart = true; m_lineStart = m_code; m_lexErrorMessage = String(); + m_sourceURLDirective = String(); + m_sourceMappingURLDirective = 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; @@ -598,22 +606,60 @@ ALWAYS_INLINE T Lexer<T>::peek(int offset) const return (code < m_codeEnd) ? *code : 0; } -template <typename T> -typename Lexer<T>::UnicodeHexValue Lexer<T>::parseFourDigitUnicodeHex() -{ - T char1 = peek(1); - T char2 = peek(2); - T char3 = peek(3); +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; +}; - if (UNLIKELY(!isASCIIHexDigit(m_current) || !isASCIIHexDigit(char1) || !isASCIIHexDigit(char2) || !isASCIIHexDigit(char3))) - return UnicodeHexValue((m_code + 4) >= m_codeEnd ? UnicodeHexValue::IncompleteHex : UnicodeHexValue::InvalidHex); +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; + } - int result = convertUnicode(m_current, char1, char2, char3); + 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 UnicodeHexValue(result); + return result; } template <typename T> @@ -621,6 +667,7 @@ void Lexer<T>::shiftLineTerminator() { ASSERT(isLineTerminator(m_current)); + m_positionBeforeLastNewline = currentPosition(); T prev = m_current; shift(); @@ -637,9 +684,16 @@ 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(int c) +template <typename T> +ALWAYS_INLINE void Lexer<T>::skipWhitespace() +{ + while (isWhiteSpace(m_current)) + shift(); +} + +static NEVER_INLINE bool isNonLatin1IdentStart(UChar c) { - return category(c) & (Letter_Uppercase | Letter_Lowercase | Letter_Titlecase | Letter_Modifier | Letter_Other); + return U_GET_GC_MASK(c) & U_GC_L_MASK; } static ALWAYS_INLINE bool isLatin1(LChar) @@ -652,20 +706,25 @@ 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(UChar c) +static inline bool isIdentStart(UChar32 c) { return isLatin1(c) ? isIdentStart(static_cast<LChar>(c)) : isNonLatin1IdentStart(c); } -static NEVER_INLINE bool isNonLatin1IdentPart(int c) +static NEVER_INLINE bool isNonLatin1IdentPart(UChar32 c) { - return (category(c) & (Letter_Uppercase | Letter_Lowercase | Letter_Titlecase | Letter_Modifier | Letter_Other - | Mark_NonSpacing | Mark_SpacingCombining | Number_DecimalDigit | Punctuation_Connector)) || c == 0x200C || c == 0x200D; + // 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) @@ -676,11 +735,61 @@ static ALWAYS_INLINE bool isIdentPart(LChar c) return typesOfLatin1Characters[c] <= CharacterNumber; } -static ALWAYS_INLINE bool isIdentPart(UChar c) +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) { @@ -756,7 +865,40 @@ inline void Lexer<T>::record16(int c) 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) { @@ -768,7 +910,11 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<LChar>::p return keyword == RESERVED_IF_STRICT && !strictMode ? IDENT : keyword; } } - + + bool isPrivateName = m_current == '@' && m_parsingBuiltinFunction; + if (isPrivateName) + shift(); + const LChar* identifierStart = currentSourcePtr(); unsigned identifierLineStart = currentLineStartOffset(); @@ -782,18 +928,29 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<LChar>::p const Identifier* ident = 0; - if (shouldCreateIdentifier) { + 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->lookUpPrivateName(*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))) { + if (UNLIKELY((remaining < maxTokenLength) && !(lexerFlags & LexerFlagsIgnoreReservedWords)) && !isPrivateName) { ASSERT(shouldCreateIdentifier); if (remaining < maxTokenLength) { - const HashEntry* entry = m_vm->keywords->getKeyword(*ident); + const HashTableValue* entry = JSC::mainTable.entry(*ident); ASSERT((remaining < maxTokenLength) || !entry); if (!entry) return IDENT; @@ -817,6 +974,10 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<UChar>::p return keyword == RESERVED_IF_STRICT && !strictMode ? IDENT : keyword; } } + + bool isPrivateName = m_current == '@' && m_parsingBuiltinFunction; + if (isPrivateName) + shift(); const UChar* identifierStart = currentSourcePtr(); int identifierLineStart = currentLineStartOffset(); @@ -829,6 +990,7 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<UChar>::p } if (UNLIKELY(m_current == '\\')) { + ASSERT(!isPrivateName); setOffsetFromSourcePtr(identifierStart, identifierLineStart); return parseIdentifierSlowCase<shouldCreateIdentifier>(tokenData, lexerFlags, strictMode); } @@ -840,21 +1002,32 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<UChar>::p const Identifier* ident = 0; - if (shouldCreateIdentifier) { + 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->lookUpPrivateName(*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))) { + if (UNLIKELY((remaining < maxTokenLength) && !(lexerFlags & LexerFlagsIgnoreReservedWords)) && !isPrivateName) { ASSERT(shouldCreateIdentifier); if (remaining < maxTokenLength) { - const HashEntry* entry = m_vm->keywords->getKeyword(*ident); + const HashTableValue* entry = JSC::mainTable.entry(*ident); ASSERT((remaining < maxTokenLength) || !entry); if (!entry) return IDENT; @@ -867,11 +1040,9 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<UChar>::p return IDENT; } -template <typename T> -template <bool shouldCreateIdentifier> JSTokenType Lexer<T>::parseIdentifierSlowCase(JSTokenData* tokenData, unsigned lexerFlags, bool strictMode) +template<typename CharacterType> template<bool shouldCreateIdentifier> JSTokenType Lexer<CharacterType>::parseIdentifierSlowCase(JSTokenData* tokenData, unsigned lexerFlags, bool strictMode) { - const ptrdiff_t remaining = m_codeEnd - m_code; - const T* identifierStart = currentSourcePtr(); + auto identifierStart = currentSourcePtr(); bool bufferRequired = false; while (true) { @@ -890,19 +1061,18 @@ template <bool shouldCreateIdentifier> JSTokenType Lexer<T>::parseIdentifierSlow if (UNLIKELY(m_current != 'u')) return atEnd() ? UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK : INVALID_IDENTIFIER_ESCAPE_ERRORTOK; shift(); - UnicodeHexValue character = parseFourDigitUnicodeHex(); + auto character = parseUnicodeEscape(); if (UNLIKELY(!character.isValid())) - return character.valueType() == UnicodeHexValue::IncompleteHex ? UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK : INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK; - UChar ucharacter = static_cast<UChar>(character.value()); - if (UNLIKELY(m_buffer16.size() ? !isIdentPart(ucharacter) : !isIdentStart(ucharacter))) + 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) - record16(ucharacter); + recordUnicodeCodePoint(character.value()); identifierStart = currentSourcePtr(); } int identifierLength; - const Identifier* ident = 0; + const Identifier* ident = nullptr; if (shouldCreateIdentifier) { if (!bufferRequired) { identifierLength = currentSourcePtr() - identifierStart; @@ -915,23 +1085,19 @@ template <bool shouldCreateIdentifier> JSTokenType Lexer<T>::parseIdentifierSlow tokenData->ident = ident; } else - tokenData->ident = 0; + tokenData->ident = nullptr; + + m_buffer16.shrink(0); - if (LIKELY(!bufferRequired && !(lexerFlags & LexerFlagsIgnoreReservedWords))) { + if (LIKELY(!(lexerFlags & LexerFlagsIgnoreReservedWords))) { ASSERT(shouldCreateIdentifier); - // Keywords must not be recognized if there was an \uXXXX in the identifier. - if (remaining < maxTokenLength) { - const HashEntry* entry = m_vm->keywords->getKeyword(*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; + 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; } - m_buffer16.resize(0); return IDENT; } @@ -964,7 +1130,7 @@ template <bool shouldBuildStrings> ALWAYS_INLINE typename Lexer<T>::StringParseR LChar escape = singleEscape(m_current); - // Most common escape sequences first + // Most common escape sequences first. if (escape) { if (shouldBuildStrings) record8(escape); @@ -974,7 +1140,7 @@ template <bool shouldBuildStrings> ALWAYS_INLINE typename Lexer<T>::StringParseR else if (m_current == 'x') { shift(); if (!isASCIIHexDigit(m_current) || !isASCIIHexDigit(peek(1))) { - m_lexErrorMessage = "\\x can only be followed by a hex character sequence"; + 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; @@ -985,7 +1151,7 @@ template <bool shouldBuildStrings> ALWAYS_INLINE typename Lexer<T>::StringParseR } else { setOffset(startingOffset, startingLineStartOffset); setLineNumber(startingLineNumber); - m_buffer8.resize(0); + m_buffer8.shrink(0); return parseStringSlowCase<shouldBuildStrings>(tokenData, strictMode); } stringStart = currentSourcePtr(); @@ -995,7 +1161,7 @@ template <bool shouldBuildStrings> ALWAYS_INLINE typename Lexer<T>::StringParseR if (UNLIKELY(characterRequiresParseStringSlowCase(m_current))) { setOffset(startingOffset, startingLineStartOffset); setLineNumber(startingLineNumber); - m_buffer8.resize(0); + m_buffer8.shrink(0); return parseStringSlowCase<shouldBuildStrings>(tokenData, strictMode); } @@ -1006,7 +1172,7 @@ template <bool shouldBuildStrings> ALWAYS_INLINE typename Lexer<T>::StringParseR append8(stringStart, currentSourcePtr() - stringStart); if (shouldBuildStrings) { tokenData->ident = makeIdentifier(m_buffer8.data(), m_buffer8.size()); - m_buffer8.resize(0); + m_buffer8.shrink(0); } else tokenData->ident = 0; @@ -1014,7 +1180,93 @@ template <bool shouldBuildStrings> ALWAYS_INLINE typename Lexer<T>::StringParseR } template <typename T> -template <bool shouldBuildStrings> typename Lexer<T>::StringParseResult Lexer<T>::parseStringSlowCase(JSTokenData* tokenData, bool strictMode) +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(); @@ -1036,67 +1288,10 @@ template <bool shouldBuildStrings> typename Lexer<T>::StringParseResult Lexer<T> shift(); } else if (UNLIKELY(isLineTerminator(m_current))) shiftLineTerminator(); - else if (m_current == 'x') { - shift(); - if (!isASCIIHexDigit(m_current) || !isASCIIHexDigit(peek(1))) { - m_lexErrorMessage = "\\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(); - } else if (m_current == 'u') { - shift(); - UnicodeHexValue character = parseFourDigitUnicodeHex(); - if (character.isValid()) { - if (shouldBuildStrings) - record16(character.value()); - } else if (m_current == stringQuoteCharacter) { - if (shouldBuildStrings) - record16('u'); - } else { - m_lexErrorMessage = "\\u can only be followed by a Unicode character sequence"; - return character.valueType() == UnicodeHexValue::IncompleteHex ? StringUnterminated : StringCannotBeParsed; - } - } else if (strictMode && 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 = "The only valid numeric escape in strict mode is '\\0'"; - return StringCannotBeParsed; - } - if (shouldBuildStrings) - record16(0); - } else if (!strictMode && 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'); - } - } else if (!atEnd()) { - if (shouldBuildStrings) - record16(m_current); - shift(); - } else { - m_lexErrorMessage = "Unterminated string constant"; - return StringUnterminated; + else { + StringParseResult result = parseComplexEscape<shouldBuildStrings>(EscapeParseMode::String, strictMode, stringQuoteCharacter); + if (result != StringParsedSuccessfully) + return result; } stringStart = currentSourcePtr(); @@ -1108,7 +1303,7 @@ template <bool shouldBuildStrings> typename Lexer<T>::StringParseResult Lexer<T> 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 = "Unexpected EOF"; + m_lexErrorMessage = ASCIILiteral("Unexpected EOF"); return atEnd() ? StringUnterminated : StringCannotBeParsed; } // Anything else is just a normal character @@ -1123,7 +1318,185 @@ template <bool shouldBuildStrings> typename Lexer<T>::StringParseResult Lexer<T> else tokenData->ident = 0; - m_buffer16.resize(0); + m_buffer16.shrink(0); + return StringParsedSuccessfully; +} + +// 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; } @@ -1134,9 +1507,6 @@ ALWAYS_INLINE void Lexer<T>::parseHex(double& returnValue) uint32_t hexValue = 0; int maximumDigits = 7; - // Shift out the 'x' prefix. - shift(); - do { hexValue = (hexValue << 4) + toASCIIHexValue(m_current); shift(); @@ -1168,28 +1538,67 @@ ALWAYS_INLINE void Lexer<T>::parseHex(double& returnValue) } 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; - int maximumDigits = 9; + const unsigned maximumDigits = 10; + int digit = maximumDigits - 1; // Temporary buffer for the digits. Makes easier // to reconstruct the input characters when needed. - LChar digits[10]; + LChar digits[maximumDigits]; do { octalValue = octalValue * 8 + (m_current - '0'); - digits[maximumDigits] = m_current; + digits[digit] = m_current; shift(); - --maximumDigits; - } while (isASCIIOctalDigit(m_current) && maximumDigits >= 0); + --digit; + } while (isASCIIOctalDigit(m_current) && digit >= 0); - if (!isASCIIDigit(m_current) && maximumDigits >= 0) { + if (!isASCIIDigit(m_current) && digit >= 0) { returnValue = octalValue; return true; } - for (int i = 9; i > maximumDigits; --i) + for (int i = maximumDigits - 1; i > digit; --i) record8(digits[i]); while (isASCIIOctalDigit(m_current)) { @@ -1213,24 +1622,25 @@ ALWAYS_INLINE bool Lexer<T>::parseDecimal(double& returnValue) // Since parseOctal may be executed before parseDecimal, // the m_buffer8 may hold ascii digits. if (!m_buffer8.size()) { - int maximumDigits = 9; + const unsigned maximumDigits = 10; + int digit = maximumDigits - 1; // Temporary buffer for the digits. Makes easier // to reconstruct the input characters when needed. - LChar digits[10]; + LChar digits[maximumDigits]; do { decimalValue = decimalValue * 10 + (m_current - '0'); - digits[maximumDigits] = m_current; + digits[digit] = m_current; shift(); - --maximumDigits; - } while (isASCIIDigit(m_current) && maximumDigits >= 0); + --digit; + } while (isASCIIDigit(m_current) && digit >= 0); - if (maximumDigits >= 0 && m_current != '.' && (m_current | 0x20) != 'e') { + if (digit >= 0 && m_current != '.' && (m_current | 0x20) != 'e') { returnValue = decimalValue; return true; } - for (int i = 9; i > maximumDigits; --i) + for (int i = maximumDigits - 1; i > digit; --i) record8(digits[i]); } @@ -1296,6 +1706,56 @@ ALWAYS_INLINE bool Lexer<T>::parseMultilineComment() } template <typename T> +ALWAYS_INLINE void Lexer<T>::parseCommentDirective() +{ + // sourceURL and sourceMappingURL directives. + if (!consume("source")) + return; + + if (consume("URL=")) { + m_sourceURLDirective = parseCommentDirectiveValue(); + return; + } + + if (consume("MappingURL=")) { + m_sourceMappingURLDirective = parseCommentDirectiveValue(); + return; + } +} + +template <typename T> +ALWAYS_INLINE String Lexer<T>::parseCommentDirectiveValue() +{ + skipWhitespace(); + const T* stringStart = currentSourcePtr(); + while (!isWhiteSpace(m_current) && !isLineTerminator(m_current) && m_current != '"' && m_current != '\'' && !atEnd()) + shift(); + const T* stringEnd = currentSourcePtr(); + skipWhitespace(); + + if (!isLineTerminator(m_current) && !atEnd()) + return String(); + + append8(stringStart, stringEnd - stringStart); + String result = String(m_buffer8.data(), m_buffer8.size()); + m_buffer8.shrink(0); + return result; +} + +template <typename T> +template <unsigned length> +ALWAYS_INLINE bool Lexer<T>::consume(const char (&input)[length]) +{ + unsigned lengthToCheck = length - 1; // Ignore the ending NULL byte in the string literal. + + unsigned i = 0; + for (; i < lengthToCheck && m_current == input[i]; i++) + shift(); + + return i == lengthToCheck; +} + +template <typename T> bool Lexer<T>::nextTokenIsColon() { const T* code = m_code; @@ -1306,10 +1766,22 @@ bool Lexer<T>::nextTokenIsColon() } 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()); @@ -1318,8 +1790,7 @@ JSTokenType Lexer<T>::lex(JSToken* tokenRecord, unsigned lexerFlags, bool strict m_terminator = false; start: - while (isWhiteSpace(m_current)) - shift(); + skipWhitespace(); if (atEnd()) return EOFTOK; @@ -1368,7 +1839,18 @@ start: } token = GT; break; - case CharacterEqual: + case CharacterEqual: { + if (peek(1) == '>') { + token = ARROWFUNCTION; + tokenData->line = lineNumber(); + tokenData->offset = currentOffset(); + tokenData->lineStartOffset = currentLineStartOffset(); + ASSERT(tokenData->offset >= tokenData->lineStartOffset); + shift(); + shift(); + break; + } + shift(); if (m_current == '=') { shift(); @@ -1382,6 +1864,7 @@ start: } token = EQUAL; break; + } case CharacterLess: shift(); if (m_current == '!' && peek(1) == '-' && peek(2) == '-') { @@ -1464,13 +1947,13 @@ start: shift(); if (m_current == '/') { shift(); - goto inSingleLineComment; + goto inSingleLineCommentCheckForDirectives; } if (m_current == '*') { shift(); if (parseMultilineComment()) goto start; - m_lexErrorMessage = "Multiline comment was not closed properly"; + m_lexErrorMessage = ASCIILiteral("Multiline comment was not closed properly"); token = UNTERMINATED_MULTILINE_COMMENT_ERRORTOK; goto returnError; } @@ -1582,79 +2065,160 @@ start: 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' && isASCIIHexDigit(peek(1))) { + if ((m_current | 0x20) == 'x') { + if (!isASCIIHexDigit(peek(1))) { + m_lexErrorMessage = ASCIILiteral("No hexadecimal digits after '0x'"); + token = UNTERMINATED_HEX_NUMBER_ERRORTOK; + goto returnError; + } + + // Shift out the 'x' prefix. + shift(); + parseHex(tokenData->doubleValue); - token = NUMBER; - } else { - record8('0'); - if (isASCIIOctalDigit(m_current)) { - if (parseOctal(tokenData->doubleValue)) { - if (strictMode) { - m_lexErrorMessage = "Octal escapes are forbidden in strict mode"; - token = INVALID_OCTAL_NUMBER_ERRORTOK; - goto returnError; - } - token = NUMBER; - } + if (isIdentStart(m_current)) { + m_lexErrorMessage = ASCIILiteral("No space between hexadecimal literal and identifier"); + token = UNTERMINATED_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 = UNTERMINATED_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 = UNTERMINATED_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 = UNTERMINATED_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 = UNTERMINATED_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 = UNTERMINATED_OCTAL_NUMBER_ERRORTOK; + goto returnError; + } + if (isASCIIOctalDigit(m_current)) { + if (parseOctal(tokenData->doubleValue)) { + token = tokenTypeForIntegerLikeToken(tokenData->doubleValue); } } - // Fall through into CharacterNumber + FALLTHROUGH; case CharacterNumber: - if (LIKELY(token != NUMBER)) { + 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 = "Non-number found after exponent indicator"; + 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); - } - token = NUMBER; + if (token == INTEGER) + token = tokenTypeForIntegerLikeToken(tokenData->doubleValue); + } else + token = tokenTypeForIntegerLikeToken(tokenData->doubleValue); } - // No identifiers allowed directly after numeric literal, e.g. "3in" is bad. if (UNLIKELY(isIdentStart(m_current))) { - m_lexErrorMessage = "At least one digit must occur after a decimal point"; + m_lexErrorMessage = ASCIILiteral("No identifiers allowed directly after numeric literal"); token = atEnd() ? UNTERMINATED_NUMERIC_LITERAL_ERRORTOK : INVALID_NUMERIC_LITERAL_ERRORTOK; goto returnError; } - m_buffer8.resize(0); + m_buffer8.shrink(0); break; - case CharacterQuote: - if (lexerFlags & LexerFlagsDontBuildStrings) { - StringParseResult result = parseString<false>(tokenData, strictMode); - if (UNLIKELY(result != StringParsedSuccessfully)) { - token = result == StringUnterminated ? UNTERMINATED_STRING_LITERAL_ERRORTOK : INVALID_STRING_LITERAL_ERRORTOK; - goto returnError; - } - } else { - StringParseResult result = parseString<true>(tokenData, strictMode); - if (UNLIKELY(result != StringParsedSuccessfully)) { - token = result == StringUnterminated ? UNTERMINATED_STRING_LITERAL_ERRORTOK : INVALID_STRING_LITERAL_ERRORTOK; - goto returnError; - } + 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; + } + 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; + } case CharacterIdentifierStart: ASSERT(isIdentStart(m_current)); - // Fall through into CharacterBackSlash. + FALLTHROUGH; case CharacterBackSlash: + parseIdent: if (lexerFlags & LexexFlagsDontBuildKeywords) token = parseIdentifier<false>(tokenData, lexerFlags, strictMode); else @@ -1667,13 +2231,18 @@ inNumberAfterDecimalPoint: 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 = "Internal Error"; + m_lexErrorMessage = ASCIILiteral("Internal Error"); token = ERRORTOK; goto returnError; } @@ -1681,6 +2250,15 @@ inNumberAfterDecimalPoint: m_atLineStart = false; goto returnToken; +inSingleLineCommentCheckForDirectives: + // Script comment directives like "//# sourceURL=test.js". + if (UNLIKELY((m_current == '#' || m_current == '@') && isWhiteSpace(peek(1)))) { + shift(); + shift(); + parseCommentDirective(); + } + // Fall through to complete single line comment parsing. + inSingleLineComment: while (!isLineTerminator(m_current)) { if (atEnd()) @@ -1747,7 +2325,7 @@ bool Lexer<T>::scanRegExp(const Identifier*& pattern, const Identifier*& flags, while (true) { if (isLineTerminator(m_current) || atEnd()) { - m_buffer16.resize(0); + m_buffer16.shrink(0); return false; } @@ -1781,7 +2359,7 @@ bool Lexer<T>::scanRegExp(const Identifier*& pattern, const Identifier*& flags, pattern = makeRightSizedIdentifier(m_buffer16.data(), m_buffer16.size(), charactersOredTogether); - m_buffer16.resize(0); + m_buffer16.shrink(0); charactersOredTogether = 0; while (isIdentPart(m_current)) { @@ -1791,7 +2369,7 @@ bool Lexer<T>::scanRegExp(const Identifier*& pattern, const Identifier*& flags, } flags = makeRightSizedIdentifier(m_buffer16.data(), m_buffer16.size(), charactersOredTogether); - m_buffer16.resize(0); + m_buffer16.shrink(0); return true; } @@ -1838,6 +2416,38 @@ bool Lexer<T>::skipRegExp() } 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; +} + +template <typename T> void Lexer<T>::clear() { m_arena = 0; @@ -1848,15 +2458,10 @@ void Lexer<T>::clear() Vector<UChar> newBuffer16; m_buffer16.swap(newBuffer16); - m_isReparsing = false; -} + Vector<UChar> newBufferForRawTemplateString16; + m_bufferForRawTemplateString16.swap(newBufferForRawTemplateString16); -template <typename T> -SourceCode Lexer<T>::sourceCode(int openBrace, int closeBrace, int firstLine, unsigned startColumn) -{ - ASSERT(m_source->provider()->source()[openBrace] == '{'); - ASSERT(m_source->provider()->source()[closeBrace] == '}'); - return SourceCode(m_source->provider(), openBrace, closeBrace + 1, firstLine, startColumn); + m_isReparsingFunction = false; } // Instantiate the two flavors of Lexer we need instead of putting most of this file in Lexer.h diff --git a/Source/JavaScriptCore/parser/Lexer.h b/Source/JavaScriptCore/parser/Lexer.h index 137ff5fb6..c7b63b2a6 100644 --- a/Source/JavaScriptCore/parser/Lexer.h +++ b/Source/JavaScriptCore/parser/Lexer.h @@ -30,49 +30,26 @@ #include <wtf/ASCIICType.h> #include <wtf/SegmentedVector.h> #include <wtf/Vector.h> -#include <wtf/unicode/Unicode.h> namespace JSC { -class Keywords { -public: - bool isKeyword(const Identifier& ident) const - { - return m_keywordTable.entry(m_vm, ident); - } - - const HashEntry* getKeyword(const Identifier& ident) const - { - return m_keywordTable.entry(m_vm, ident); - } - - ~Keywords() - { - m_keywordTable.deleteTable(); - } - -private: - friend class VM; - - Keywords(VM*); - - VM* m_vm; - const HashTable m_keywordTable; -}; - 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*); + Lexer(VM*, JSParserBuiltinMode); ~Lexer(); // Character manipulation functions. @@ -83,9 +60,10 @@ public: // Functions to set up parsing. void setCode(const SourceCode&, ParserArena*); - void setIsReparsing() { m_isReparsing = true; } - bool isReparsing() const { return m_isReparsing; } + 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; } @@ -95,16 +73,21 @@ public: { 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; } - SourceCode sourceCode(int openBrace, int closeBrace, int firstLine, unsigned startColumn); bool scanRegExp(const Identifier*& pattern, const Identifier*& flags, UChar patternPrefix = 0); + enum class RawStringsBuildMode { BuildRawStrings, DontBuildRawStrings }; + JSTokenType scanTrailingTemplateString(JSToken*, RawStringsBuildMode); bool skipRegExp(); // Functions for use after parsing. bool sawError() const { return m_error; } String getErrorMessage() const { return m_lexErrorMessage; } + String sourceURL() const { return m_sourceURLDirective; } + String sourceMappingURL() const { return m_sourceMappingURLDirective; } void clear(); void setOffset(int offset, int lineStartOffset) { @@ -126,8 +109,10 @@ public: { m_lineNumber = line; } - - SourceProvider* sourceProvider() const { return m_source->provider(); } + void setTerminator(bool terminator) + { + m_terminator = terminator; + } JSTokenType lexExpectIdentifier(JSToken*, unsigned, bool strictMode); @@ -136,42 +121,15 @@ private: 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; - struct UnicodeHexValue { - - enum ValueType { ValidHex, IncompleteHex, InvalidHex }; - - explicit UnicodeHexValue(int value) - : m_value(value) - { - } - explicit UnicodeHexValue(ValueType type) - : m_value(type == IncompleteHex ? -2 : -1) - { - } - - ValueType valueType() const - { - if (m_value >= 0) - return ValidHex; - return m_value == -2 ? IncompleteHex : InvalidHex; - } - bool isValid() const { return m_value >= 0; } - int value() const - { - ASSERT(m_value >= 0); - return m_value; - } - - private: - int m_value; - }; - UnicodeHexValue parseFourDigitUnicodeHex(); + + ParsedUnicodeEscapeValue parseUnicodeEscape(); void shiftLineTerminator(); ALWAYS_INLINE int offsetFromSourcePtr(const T* ptr) const { return ptr - m_codeStart; } @@ -181,7 +139,7 @@ private: 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 void setCodeStart(const StringView&); ALWAYS_INLINE const Identifier* makeIdentifier(const LChar* characters, size_t length); ALWAYS_INLINE const Identifier* makeIdentifier(const UChar* characters, size_t length); @@ -189,8 +147,11 @@ private: 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; + + ALWAYS_INLINE void skipWhitespace(); template <int shiftAmount> void internalShift(); template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType parseKeyword(JSTokenData*); @@ -203,13 +164,24 @@ private: }; 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); + template <bool shouldBuildStrings> ALWAYS_INLINE StringParseResult parseTemplateLiteral(JSTokenData*, RawStringsBuildMode); 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(); + ALWAYS_INLINE void parseCommentDirective(); + ALWAYS_INLINE String parseCommentDirectiveValue(); + + template <unsigned length> + ALWAYS_INLINE bool consume(const char (&input)[length]); + static const size_t initialReadBufferCapacity = 32; int m_lineNumber; @@ -217,6 +189,7 @@ private: Vector<LChar> m_buffer8; Vector<UChar> m_buffer16; + Vector<UChar> m_bufferForRawTemplateString16; bool m_terminator; int m_lastToken; @@ -227,16 +200,22 @@ private: const T* m_codeEnd; const T* m_codeStartPlusOffset; const T* m_lineStart; - bool m_isReparsing; + JSTextPosition m_positionBeforeLastNewline; + JSTokenLocation m_lastTockenLocation; + bool m_isReparsingFunction; bool m_atLineStart; bool m_error; String m_lexErrorMessage; + String m_sourceURLDirective; + String m_sourceMappingURLDirective; + T m_current; IdentifierArena* m_arena; VM* m_vm; + bool m_parsingBuiltinFunction; }; template <> @@ -248,7 +227,8 @@ ALWAYS_INLINE bool Lexer<LChar>::isWhiteSpace(LChar ch) template <> ALWAYS_INLINE bool Lexer<UChar>::isWhiteSpace(UChar ch) { - return (ch < 256) ? Lexer<LChar>::isWhiteSpace(static_cast<LChar>(ch)) : (WTF::Unicode::isSeparatorSpace(ch) || ch == 0xFEFF); + // 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 <> @@ -302,18 +282,24 @@ ALWAYS_INLINE const Identifier* Lexer<UChar>::makeRightSizedIdentifier(const UCh 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) +ALWAYS_INLINE void Lexer<LChar>::setCodeStart(const StringView& sourceString) { - ASSERT(sourceString->is8Bit()); - m_codeStart = sourceString->characters8(); + ASSERT(sourceString.is8Bit()); + m_codeStart = sourceString.characters8(); } template <> -ALWAYS_INLINE void Lexer<UChar>::setCodeStart(const StringImpl* sourceString) +ALWAYS_INLINE void Lexer<UChar>::setCodeStart(const StringView& sourceString) { - ASSERT(!sourceString->is8Bit()); - m_codeStart = sourceString->characters16(); + ASSERT(!sourceString.is8Bit()); + m_codeStart = sourceString.characters16(); } template <typename T> @@ -334,6 +320,12 @@ ALWAYS_INLINE const Identifier* Lexer<T>::makeLCharIdentifier(const UChar* chara 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) { @@ -369,10 +361,15 @@ ALWAYS_INLINE JSTokenType Lexer<T>::lexExpectIdentifier(JSToken* tokenRecord, un ASSERT(currentOffset() >= currentLineStartOffset()); // Create the identifier if needed - if (lexerFlags & LexexFlagsDontBuildKeywords) + 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); @@ -380,6 +377,13 @@ ALWAYS_INLINE JSTokenType Lexer<T>::lexExpectIdentifier(JSToken* tokenRecord, un 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; diff --git a/Source/JavaScriptCore/parser/ModuleAnalyzer.cpp b/Source/JavaScriptCore/parser/ModuleAnalyzer.cpp new file mode 100644 index 000000000..c1a3cc2b1 --- /dev/null +++ b/Source/JavaScriptCore/parser/ModuleAnalyzer.cpp @@ -0,0 +1,159 @@ +/* + * 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 "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSGlobalObject.h" +#include "JSModuleRecord.h" +#include "StrongInlines.h" + +namespace JSC { + + +ModuleAnalyzer::ModuleAnalyzer(ExecState* exec, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables) + : m_vm(&exec->vm()) + , m_moduleRecord(exec->vm(), JSModuleRecord::create(exec->vm(), exec->lexicalGlobalObject()->moduleRecordStructure(), moduleKey, sourceCode, declaredVariables, 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(JSModuleRecord::ExportEntry::createLocal(exportName, Identifier::fromUid(m_vm, localName.get()), variable)); + return; + } + + Optional<JSModuleRecord::ImportEntry> optionalImportEntry = moduleRecord()->tryGetImportEntry(localName.get()); + ASSERT(optionalImportEntry); + const JSModuleRecord::ImportEntry& importEntry = *optionalImportEntry; + if (variable.isImportedNamespace()) { + // Exported namespace binding. + // import * as namespace from "mod" + // export { namespace } + moduleRecord()->addExportEntry(JSModuleRecord::ExportEntry::createNamespace(exportName, importEntry.moduleRequest)); + return; + } + + // Indirectly exported binding. + // import a from "mod" + // export { a } + moduleRecord()->addExportEntry(JSModuleRecord::ExportEntry::createIndirect(exportName, importEntry.importName, importEntry.moduleRequest)); +} + + + +JSModuleRecord* 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_moduleRecord->declaredVariables()) + exportVariable(pair.key, pair.value); + + for (const auto& pair : m_moduleRecord->lexicalVariables()) + exportVariable(pair.key, pair.value); + + if (Options::dumpModuleRecord()) + m_moduleRecord->dump(); + + return m_moduleRecord.get(); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/ModuleAnalyzer.h b/Source/JavaScriptCore/parser/ModuleAnalyzer.h new file mode 100644 index 000000000..7083ca909 --- /dev/null +++ b/Source/JavaScriptCore/parser/ModuleAnalyzer.h @@ -0,0 +1,63 @@ +/* + * 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 "Nodes.h" + +namespace JSC { + +class JSModuleRecord; +class SourceCode; + +class ModuleAnalyzer { + WTF_MAKE_NONCOPYABLE(ModuleAnalyzer); +public: + ModuleAnalyzer(ExecState*, const Identifier& moduleKey, const SourceCode&, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables); + + JSModuleRecord* analyze(ModuleProgramNode&); + + VM& vm() { return *m_vm; } + + JSModuleRecord* moduleRecord() { return m_moduleRecord.get(); } + + 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; + Strong<JSModuleRecord> m_moduleRecord; + IdentifierAliasMap m_aliasMap; +}; + +} // namespace JSC + +#endif // ModuleAnalyzer_h diff --git a/Source/JavaScriptCore/parser/NodeConstructors.h b/Source/JavaScriptCore/parser/NodeConstructors.h index 4bc670f81..1de31687d 100644 --- a/Source/JavaScriptCore/parser/NodeConstructors.h +++ b/Source/JavaScriptCore/parser/NodeConstructors.h @@ -27,23 +27,24 @@ namespace JSC { - inline void* ParserArenaFreeable::operator new(size_t size, VM* vm) + inline void* ParserArenaFreeable::operator new(size_t size, ParserArena& parserArena) { - return vm->parserArena->allocateFreeable(size); + return parserArena.allocateFreeable(size); } - inline void* ParserArenaDeletable::operator new(size_t size, VM* vm) + inline void* ParserArenaDeletable::operator new(size_t size, ParserArena& parserArena) { - return vm->parserArena->allocateDeletable(size); + return parserArena.allocateDeletable(size); } - inline ParserArenaRefCounted::ParserArenaRefCounted(VM* vm) + inline ParserArenaRoot::ParserArenaRoot(ParserArena& parserArena) { - vm->parserArena->derefWithArena(adoptRef(this)); + 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); } @@ -56,6 +57,7 @@ namespace JSC { inline StatementNode::StatementNode(const JSTokenLocation& location) : Node(location) + , m_next(nullptr) , m_lastLine(-1) { } @@ -82,12 +84,72 @@ namespace JSC { { } + 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) { } + 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) + { + } + inline RegExpNode::RegExpNode(const JSTokenLocation& location, const Identifier& pattern, const Identifier& flags) : ExpressionNode(location) , m_pattern(pattern) @@ -95,12 +157,23 @@ namespace JSC { { } - inline ThisNode::ThisNode(const JSTokenLocation& location) + 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) + inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifier& ident, const JSTextPosition& start) : ExpressionNode(location) , m_ident(ident) , m_start(start) @@ -147,17 +220,22 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie { } - inline PropertyNode::PropertyNode(VM*, const Identifier& name, ExpressionNode* assign, Type type) - : m_name(name) + 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(VM* vm, double name, ExpressionNode* assign, Type type) - : m_name(vm->parserArena->identifierArena().makeNumericIdentifier(vm, name)) + inline PropertyNode::PropertyNode(ExpressionNode* name, ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding = SuperBinding::NotNeeded) + : m_name(0) + , m_expression(name) , m_assign(assign) , m_type(type) + , m_needsSuperBinding(superBinding == SuperBinding::Needed) + , m_putType(putType) { } @@ -202,6 +280,13 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie , 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) @@ -266,12 +351,13 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie { } - inline FunctionCallBracketNode::FunctionCallBracketNode(const JSTokenLocation& location, ExpressionNode* base, ExpressionNode* subscript, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) + 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) { } @@ -284,6 +370,16 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie { } + inline BytecodeIntrinsicNode::BytecodeIntrinsicNode(Type type, 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_type(type) + , 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) { @@ -542,10 +638,11 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie { } - inline AssignResolveNode::AssignResolveNode(const JSTokenLocation& location, const Identifier& ident, ExpressionNode* right) + inline AssignResolveNode::AssignResolveNode(const JSTokenLocation& location, const Identifier& ident, ExpressionNode* right, AssignmentContext assignmentContext) : ExpressionNode(location) , m_ident(ident) , m_right(right) + , m_assignmentContext(assignmentContext) { } @@ -600,20 +697,16 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie { } - inline CommaNode::CommaNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2) + inline CommaNode::CommaNode(const JSTokenLocation& location, ExpressionNode* expr) : ExpressionNode(location) - { - m_expressions.append(expr1); - m_expressions.append(expr2); - } - - inline ConstStatementNode::ConstStatementNode(const JSTokenLocation& location, ConstDeclNode* next) - : StatementNode(location) - , m_next(next) + , m_expr(expr) + , m_next(nullptr) { } inline SourceElements::SourceElements() + : m_head(nullptr) + , m_tail(nullptr) { } @@ -633,11 +726,81 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie { } - inline VarStatementNode::VarStatementNode(const JSTokenLocation& location, ExpressionNode* 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) @@ -661,8 +824,9 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie { } - inline ForNode::ForNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, ExpressionNode* expr3, StatementNode* 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) @@ -671,24 +835,12 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie ASSERT(statement); } - inline ContinueNode::ContinueNode(VM* vm, const JSTokenLocation& location) - : StatementNode(location) - , m_ident(vm->propertyNames->nullIdentifier) - { - } - inline ContinueNode::ContinueNode(const JSTokenLocation& location, const Identifier& ident) : StatementNode(location) , m_ident(ident) { } - inline BreakNode::BreakNode(VM* vm, const JSTokenLocation& location) - : StatementNode(location) - , m_ident(vm->propertyNames->nullIdentifier) - { - } - inline BreakNode::BreakNode(const JSTokenLocation& location, const Identifier& ident) : StatementNode(location) , m_ident(ident) @@ -723,40 +875,67 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie { } - inline TryNode::TryNode(const JSTokenLocation& location, StatementNode* tryBlock, const Identifier& exceptionIdent, StatementNode* catchBlock, StatementNode* finallyBlock) + inline TryNode::TryNode(const JSTokenLocation& location, StatementNode* tryBlock, DestructuringPatternNode* catchPattern, StatementNode* catchBlock, VariableEnvironment& catchEnvironment, StatementNode* finallyBlock) : StatementNode(location) + , VariableEnvironmentNode(catchEnvironment) , m_tryBlock(tryBlock) - , m_exceptionIdent(exceptionIdent) + , m_catchPattern(catchPattern) , m_catchBlock(catchBlock) , m_finallyBlock(finallyBlock) { } - inline ParameterNode::ParameterNode(const Identifier& ident) - : m_ident(ident) - , m_next(0) + inline FunctionParameters::FunctionParameters() { } - inline ParameterNode::ParameterNode(ParameterNode* l, const Identifier& ident) - : m_ident(ident) - , m_next(0) + + inline BaseFuncExprNode::BaseFuncExprNode(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 FuncExprNode::FuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* m_metadata, const SourceCode& source) + : BaseFuncExprNode(location, ident, m_metadata, source) + { + } + + 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); + } + + inline ArrowFuncExprNode::ArrowFuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* m_metadata, const SourceCode& source) + : BaseFuncExprNode(location, ident, m_metadata, source) { - l->m_next = this; } - inline FuncExprNode::FuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionBodyNode* body, const SourceCode& source, ParameterNode* parameter) + inline YieldExprNode::YieldExprNode(const JSTokenLocation& location, ExpressionNode* argument, bool delegate) : ExpressionNode(location) - , m_body(body) + , m_argument(argument) + , m_delegate(delegate) { - m_body->finishParsing(source, parameter, ident, FunctionNameIsInScope); } - inline FuncDeclNode::FuncDeclNode(const JSTokenLocation& location, const Identifier& ident, FunctionBodyNode* body, const SourceCode& source, ParameterNode* parameter) + inline ClassDeclNode::ClassDeclNode(const JSTokenLocation& location, ExpressionNode* classDeclaration) : StatementNode(location) - , m_body(body) + , m_classDeclaration(classDeclaration) + { + } + + inline ClassExprNode::ClassExprNode(const JSTokenLocation& location, const Identifier& name, VariableEnvironment& classEnvironment, ExpressionNode* constructorExpression, ExpressionNode* classHeritage, PropertyListNode* instanceMethods, PropertyListNode* staticMethods) + : ExpressionNode(location) + , VariableEnvironmentNode(classEnvironment) + , m_name(name) + , m_constructorExpression(constructorExpression) + , m_classHeritage(classHeritage) + , m_instanceMethods(instanceMethods) + , m_staticMethods(staticMethods) { - m_body->finishParsing(source, parameter, ident, FunctionNameIsNotInScope); } inline CaseClauseNode::CaseClauseNode(ExpressionNode* expr, SourceElements* statements) @@ -785,52 +964,88 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie { } - inline SwitchNode::SwitchNode(const JSTokenLocation& location, ExpressionNode* expr, CaseBlockNode* block) + inline SwitchNode::SwitchNode(const JSTokenLocation& location, ExpressionNode* expr, CaseBlockNode* block, VariableEnvironment& lexicalVariables) : StatementNode(location) + , VariableEnvironmentNode(lexicalVariables) , m_expr(expr) , m_block(block) { } - inline ConstDeclNode::ConstDeclNode(const JSTokenLocation& location, const Identifier& ident, ExpressionNode* init) - : ExpressionNode(location) - , m_ident(ident) - , m_next(0) - , m_init(init) - { - } - - inline BlockNode::BlockNode(const JSTokenLocation& location, SourceElements* statements) + inline BlockNode::BlockNode(const JSTokenLocation& location, SourceElements* statements, VariableEnvironment& lexicalVariables) : StatementNode(location) + , VariableEnvironmentNode(lexicalVariables) , m_statements(statements) { } - inline ForInNode::ForInNode(const JSTokenLocation& location, ExpressionNode* l, ExpressionNode* expr, StatementNode* statement) + inline EnumerationNode::EnumerationNode(const JSTokenLocation& location, ExpressionNode* lexpr, ExpressionNode* expr, StatementNode* statement, VariableEnvironment& lexicalVariables) : StatementNode(location) - , m_init(0) - , m_lexpr(l) + , 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 ForInNode::ForInNode(VM* vm, const JSTokenLocation& location, const Identifier& ident, ExpressionNode* in, ExpressionNode* expr, StatementNode* statement, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) - : StatementNode(location) - , m_init(0) - , m_lexpr(new (vm) ResolveNode(location, ident, divotStart)) - , m_expr(expr) - , m_statement(statement) + 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 AssignmentElementNode::AssignmentElementNode(ExpressionNode* assignmentTarget, const JSTextPosition& start, const JSTextPosition& end) + : DestructuringPatternNode() + , m_divotStart(start) + , m_divotEnd(end) + , m_assignmentTarget(assignmentTarget) + { + } + + inline RestParameterNode::RestParameterNode(const Identifier& name, unsigned numParametersToSkip, const JSTextPosition& start, const JSTextPosition& end) + : DestructuringPatternNode() + , m_name(name) + , m_numParametersToSkip(numParametersToSkip) + , m_divotStart(start) + , m_divotEnd(end) { - if (in) { - AssignResolveNode* node = new (vm) AssignResolveNode(location, ident, in); - ASSERT(divot.offset >= divot.lineStartOffset); - node->setExceptionSourceCode(divot, divotStart, divotEnd); - m_init = node; - } - // for( var foo = bar in baz ) } + 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/NodeInfo.h b/Source/JavaScriptCore/parser/NodeInfo.h deleted file mode 100644 index 4853aec42..000000000 --- a/Source/JavaScriptCore/parser/NodeInfo.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2007 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef NodeInfo_h -#define NodeInfo_h - -#include "Nodes.h" - -namespace JSC { - - template <typename T> struct NodeInfo { - T m_node; - CodeFeatures m_features; - int m_numConstants; - }; - - typedef NodeInfo<FuncDeclNode*> FuncDeclNodeInfo; - typedef NodeInfo<FuncExprNode*> FuncExprNodeInfo; - typedef NodeInfo<ExpressionNode*> ExpressionNodeInfo; - typedef NodeInfo<ArgumentsNode*> ArgumentsNodeInfo; - typedef NodeInfo<ConstDeclNode*> ConstDeclNodeInfo; - typedef NodeInfo<PropertyNode*> PropertyNodeInfo; - typedef NodeInfo<PropertyList> PropertyListInfo; - typedef NodeInfo<ElementList> ElementListInfo; - typedef NodeInfo<ArgumentList> ArgumentListInfo; - - template <typename T> struct NodeDeclarationInfo { - T m_node; - ParserArenaData<DeclarationStacks::VarStack>* m_varDeclarations; - ParserArenaData<DeclarationStacks::FunctionStack>* m_funcDeclarations; - CodeFeatures m_features; - int m_numConstants; - }; - - typedef NodeDeclarationInfo<StatementNode*> StatementNodeInfo; - typedef NodeDeclarationInfo<CaseBlockNode*> CaseBlockNodeInfo; - typedef NodeDeclarationInfo<CaseClauseNode*> CaseClauseNodeInfo; - typedef NodeDeclarationInfo<SourceElements*> SourceElementsInfo; - typedef NodeDeclarationInfo<ClauseList> ClauseListInfo; - typedef NodeDeclarationInfo<ExpressionNode*> VarDeclListInfo; - typedef NodeDeclarationInfo<ConstDeclList> ConstDeclListInfo; - typedef NodeDeclarationInfo<ParameterList> ParameterListInfo; - -} // namespace JSC - -#endif // NodeInfo_h diff --git a/Source/JavaScriptCore/parser/Nodes.cpp b/Source/JavaScriptCore/parser/Nodes.cpp index 36ded3863..255754494 100644 --- a/Source/JavaScriptCore/parser/Nodes.cpp +++ b/Source/JavaScriptCore/parser/Nodes.cpp @@ -27,16 +27,14 @@ #include "Nodes.h" #include "NodeConstructors.h" -#include "BytecodeGenerator.h" #include "CallFrame.h" #include "Debugger.h" #include "JIT.h" #include "JSFunction.h" #include "JSGlobalObject.h" -#include "JSNameScope.h" #include "LabelScope.h" #include "Lexer.h" -#include "Operations.h" +#include "JSCInlines.h" #include "Parser.h" #include "PropertyNameArray.h" #include "RegExpObject.h" @@ -65,20 +63,27 @@ void SourceElements::append(StatementNode* statement) { if (statement->isEmptyStatement()) return; - m_statements.append(statement); + + if (!m_head) { + m_head = statement; + m_tail = statement; + return; + } + + m_tail->setNext(statement); + m_tail = statement; } StatementNode* SourceElements::singleStatement() const { - size_t size = m_statements.size(); - return size == 1 ? m_statements[0] : 0; + return m_head == m_tail ? m_head : nullptr; } // ------------------------------ ScopeNode ----------------------------- -ScopeNode::ScopeNode(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, bool inStrictContext) +ScopeNode::ScopeNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, bool inStrictContext) : StatementNode(endLocation) - , ParserArenaRefCounted(vm) + , ParserArenaRoot(parserArena) , m_startLineNumber(startLocation.line) , m_startStartOffset(startLocation.startOffset) , m_startLineStartOffset(startLocation.lineStartOffset) @@ -88,9 +93,10 @@ ScopeNode::ScopeNode(VM* vm, const JSTokenLocation& startLocation, const JSToken { } -ScopeNode::ScopeNode(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, const SourceCode& source, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, IdentifierSet& capturedVariables, CodeFeatures features, int numConstants) +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) - , ParserArenaRefCounted(vm) + , ParserArenaRoot(parserArena) + , VariableEnvironmentNode(lexicalVariables) , m_startLineNumber(startLocation.line) , m_startStartOffset(startLocation.startOffset) , m_startLineStartOffset(startLocation.lineStartOffset) @@ -99,12 +105,8 @@ ScopeNode::ScopeNode(VM* vm, const JSTokenLocation& startLocation, const JSToken , m_numConstants(numConstants) , m_statements(children) { - m_arena.swap(*vm->parserArena); - if (varStack) - m_varStack.swap(*varStack); - if (funcStack) - m_functionStack.swap(*funcStack); - m_capturedVariables.swap(capturedVariables); + m_varDeclarations.swap(varEnvironment); + m_functionStack.swap(funcStack); } StatementNode* ScopeNode::singleStatement() const @@ -114,108 +116,88 @@ StatementNode* ScopeNode::singleStatement() const // ------------------------------ ProgramNode ----------------------------- -inline ProgramNode::ProgramNode(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, IdentifierSet& capturedVariables, const SourceCode& source, CodeFeatures features, int numConstants) - : ScopeNode(vm, startLocation, endLocation, source, children, varStack, funcStack, capturedVariables, features, numConstants) +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) { } -PassRefPtr<ProgramNode> ProgramNode::create(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, IdentifierSet& capturedVariables, const SourceCode& source, CodeFeatures features, int numConstants) -{ - RefPtr<ProgramNode> node = new ProgramNode(vm, startLocation, endLocation, startColumn, children, varStack, funcStack, capturedVariables, source, features, numConstants); +// ------------------------------ ModuleProgramNode ----------------------------- - ASSERT(node->m_arena.last() == node); - node->m_arena.removeLast(); - ASSERT(!node->m_arena.contains(node.get())); - - return node.release(); +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 ----------------------------- -inline EvalNode::EvalNode(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, IdentifierSet& capturedVariables, const SourceCode& source, CodeFeatures features, int numConstants) - : ScopeNode(vm, startLocation, endLocation, source, children, varStack, funcStack, capturedVariables, features, numConstants) +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) { } -PassRefPtr<EvalNode> EvalNode::create(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, IdentifierSet& capturedVariables, const SourceCode& source, CodeFeatures features, int numConstants) -{ - RefPtr<EvalNode> node = new EvalNode(vm, startLocation, endLocation, children, varStack, funcStack, capturedVariables, source, features, numConstants); - - ASSERT(node->m_arena.last() == node); - node->m_arena.removeLast(); - ASSERT(!node->m_arena.contains(node.get())); - - return node.release(); -} - -// ------------------------------ FunctionBodyNode ----------------------------- +// ------------------------------ FunctionMetadataNode ----------------------------- -PassRefPtr<FunctionParameters> FunctionParameters::create(ParameterNode* firstParameter) +FunctionMetadataNode::FunctionMetadataNode( + ParserArena&, const JSTokenLocation& startLocation, + const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, + int functionKeywordStart, int functionNameStart, int parametersStart, bool isInStrictContext, + ConstructorKind constructorKind, SuperBinding superBinding, unsigned parameterCount, SourceParseMode mode, bool isArrowFunctionBodyExpression) + : 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_superBinding(static_cast<unsigned>(superBinding)) + , m_constructorKind(static_cast<unsigned>(constructorKind)) + , m_isArrowFunctionBodyExpression(isArrowFunctionBodyExpression) { - unsigned parameterCount = 0; - for (ParameterNode* parameter = firstParameter; parameter; parameter = parameter->nextParam()) - ++parameterCount; - - size_t objectSize = sizeof(FunctionParameters) - sizeof(void*) + sizeof(StringImpl*) * parameterCount; - void* slot = fastMalloc(objectSize); - return adoptRef(new (slot) FunctionParameters(firstParameter, parameterCount)); + ASSERT(m_superBinding == static_cast<unsigned>(superBinding)); + ASSERT(m_constructorKind == static_cast<unsigned>(constructorKind)); } -FunctionParameters::FunctionParameters(ParameterNode* firstParameter, unsigned size) - : m_size(size) +void FunctionMetadataNode::finishParsing(const SourceCode& source, const Identifier& ident, enum FunctionMode functionMode) { - unsigned i = 0; - for (ParameterNode* parameter = firstParameter; parameter; parameter = parameter->nextParam()) - new (&identifiers()[i++]) Identifier(parameter->ident()); + m_source = source; + m_ident = ident; + m_functionMode = functionMode; } -FunctionParameters::~FunctionParameters() +void FunctionMetadataNode::setEndPosition(JSTextPosition position) { - for (unsigned i = 0; i < m_size; ++i) - identifiers()[i].~Identifier(); + m_lastLine = position.line; + m_endColumn = position.offset - position.lineStartOffset; } -inline FunctionBodyNode::FunctionBodyNode(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, bool inStrictContext) - : ScopeNode(vm, startLocation, endLocation, inStrictContext) - , m_startColumn(startColumn) -{ -} +// ------------------------------ FunctionNode ----------------------------- -inline FunctionBodyNode::FunctionBodyNode(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, IdentifierSet& capturedVariables, const SourceCode& sourceCode, CodeFeatures features, int numConstants) - : ScopeNode(vm, startLocation, endLocation, sourceCode, children, varStack, funcStack, capturedVariables, features, numConstants) +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 FunctionBodyNode::finishParsing(const SourceCode& source, ParameterNode* firstParameter, const Identifier& ident, FunctionNameIsInScopeToggle functionNameIsInScopeToggle) -{ - setSource(source); - finishParsing(FunctionParameters::create(firstParameter), ident, functionNameIsInScopeToggle); -} - -void FunctionBodyNode::finishParsing(PassRefPtr<FunctionParameters> parameters, const Identifier& ident, FunctionNameIsInScopeToggle functionNameIsInScopeToggle) +void FunctionNode::finishParsing(const Identifier& ident, enum FunctionMode functionMode) { ASSERT(!source().isNull()); - m_parameters = parameters; m_ident = ident; - m_functionNameIsInScopeToggle = functionNameIsInScopeToggle; -} - -FunctionBodyNode* FunctionBodyNode::create(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, bool inStrictContext) -{ - return new FunctionBodyNode(vm, startLocation, endLocation, startColumn, inStrictContext); + m_functionMode = functionMode; } -PassRefPtr<FunctionBodyNode> FunctionBodyNode::create(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, IdentifierSet& capturedVariables, const SourceCode& sourceCode, CodeFeatures features, int numConstants) +VariableEnvironmentNode::VariableEnvironmentNode(VariableEnvironment& lexicalVariables) { - RefPtr<FunctionBodyNode> node = new FunctionBodyNode(vm, startLocation, endLocation, startColumn, children, varStack, funcStack, capturedVariables, sourceCode, features, numConstants); - - ASSERT(node->m_arena.last() == node); - node->m_arena.removeLast(); - ASSERT(!node->m_arena.contains(node.get())); - - return node.release(); + m_lexicalVariables.swap(lexicalVariables); } } // namespace JSC diff --git a/Source/JavaScriptCore/parser/Nodes.h b/Source/JavaScriptCore/parser/Nodes.h index e9b88a1a0..badb6fd93 100644 --- a/Source/JavaScriptCore/parser/Nodes.h +++ b/Source/JavaScriptCore/parser/Nodes.h @@ -1,7 +1,7 @@ /* * 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 Apple Inc. All rights reserved. + * 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> @@ -34,19 +34,26 @@ #include "ResultType.h" #include "SourceCode.h" #include "SymbolTable.h" +#include "VariableEnvironment.h" #include <wtf/MathExtras.h> +#if PLATFORM(QT) +#undef emit +#endif + namespace JSC { class ArgumentListNode; class BytecodeGenerator; - class FunctionBodyNode; + class FunctionMetadataNode; + class FunctionParameters; class Label; class PropertyListNode; class ReadModifyResolveNode; class RegisterID; class JSScope; class ScopeNode; + class ModuleAnalyzer; enum Operator { OpEqual, @@ -76,12 +83,8 @@ namespace JSC { }; inline FallThroughMode invert(FallThroughMode fallThroughMode) { return static_cast<FallThroughMode>(!fallThroughMode); } - typedef HashSet<RefPtr<StringImpl>, IdentifierRepHash> IdentifierSet; - namespace DeclarationStacks { - enum VarAttrs { IsConstant = 1, HasInitializer = 2 }; - typedef Vector<std::pair<const Identifier*, unsigned> > VarStack; - typedef Vector<FunctionBodyNode*> FunctionStack; + typedef Vector<FunctionMetadataNode*> FunctionStack; } struct SwitchInfo { @@ -90,11 +93,17 @@ namespace JSC { 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, VM*); + void* operator new(size_t, ParserArena&); }; class ParserArenaDeletable { @@ -103,23 +112,20 @@ namespace JSC { // ParserArenaDeletable objects are deleted when the arena is deleted. // Clients must not call delete directly on such objects. - void* operator new(size_t, VM*); + void* operator new(size_t, ParserArena&); }; - template <typename T> - struct ParserArenaData : ParserArenaDeletable { - T data; - }; - - class ParserArenaRefCounted : public RefCounted<ParserArenaRefCounted> { + class ParserArenaRoot { + WTF_MAKE_FAST_ALLOCATED; protected: - ParserArenaRefCounted(VM*); + ParserArenaRoot(ParserArena&); public: - virtual ~ParserArenaRefCounted() - { - ASSERT(deletionHasBegun()); - } + ParserArena& parserArena() { return m_arena; } + virtual ~ParserArenaRoot() { } + + protected: + ParserArena m_arena; }; class Node : public ParserArenaFreeable { @@ -129,13 +135,17 @@ namespace JSC { public: virtual ~Node() { } - int lineNo() const { return m_position.line; } + 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 { @@ -147,19 +157,27 @@ namespace JSC { virtual bool isNumber() const { return false; } virtual bool isString() const { return false; } + virtual bool isObjectLiteral() const { return false; } + virtual bool isArrayLiteral() 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 isArrowFuncExprNode() 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 bool isBytecodeIntrinsicNode() const { return false; } virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label*, Label*, FallThroughMode); @@ -179,29 +197,48 @@ namespace JSC { virtual void emitBytecode(BytecodeGenerator&, RegisterID* destination = 0) = 0; void setLoc(unsigned firstLine, unsigned lastLine, int startOffset, int lineStartOffset); - unsigned firstLine() const { return lineNo(); } 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; } - private: + protected: + StatementNode* m_next; int m_lastLine; }; + class VariableEnvironmentNode : public ParserArenaDeletable { + 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 { return true; } - virtual bool isConstant() const { return true; } + 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); - void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode) override; }; class NullNode : public ConstantNode { @@ -209,8 +246,8 @@ namespace JSC { NullNode(const JSTokenLocation&); private: - virtual bool isNull() const { return true; } - virtual JSValue jsValue(BytecodeGenerator&) const { return jsNull(); } + virtual bool isNull() const override { return true; } + virtual JSValue jsValue(BytecodeGenerator&) const override { return jsNull(); } }; class BooleanNode : public ConstantNode { @@ -219,8 +256,8 @@ namespace JSC { bool value() { return m_value; } private: - virtual bool isBoolean() const { return true; } - virtual JSValue jsValue(BytecodeGenerator&) const { return jsBoolean(m_value); } + virtual bool isBoolean() const override { return true; } + virtual JSValue jsValue(BytecodeGenerator&) const override { return jsBoolean(m_value); } bool m_value; }; @@ -228,28 +265,44 @@ namespace JSC { class NumberNode : public ConstantNode { public: NumberNode(const JSTokenLocation&, double value); - double value() { return m_value; } - void setValue(double value) { m_value = 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 { return true; } - virtual JSValue jsValue(BytecodeGenerator&) const { return jsNumber(m_value); } + 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 { return true; } - virtual JSValue jsValue(BytecodeGenerator&) const; + virtual bool isString() const override { return true; } + virtual JSValue jsValue(BytecodeGenerator&) const override; const Identifier& m_value; }; - + class ThrowableExpressionData { public: ThrowableExpressionData() @@ -402,12 +455,80 @@ namespace JSC { uint16_t m_subexpressionLineStartOffset; }; + 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; + }; + class RegExpNode : public ExpressionNode, public ThrowableExpressionData { public: RegExpNode(const JSTokenLocation&, const Identifier& pattern, const Identifier& flags); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_pattern; const Identifier& m_flags; @@ -415,10 +536,29 @@ namespace JSC { class ThisNode : public ExpressionNode { public: - ThisNode(const JSTokenLocation&); + ThisNode(const JSTokenLocation&, ThisTDZMode); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + 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 { @@ -428,11 +568,11 @@ namespace JSC { const Identifier& identifier() const { return m_ident; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isPure(BytecodeGenerator&) const ; - virtual bool isLocation() const { return true; } - virtual bool isResolveNode() const { return true; } + 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; @@ -459,12 +599,15 @@ namespace JSC { ArrayNode(const JSTokenLocation&, ElementNode*); ArrayNode(const JSTokenLocation&, int elision, ElementNode*); - ArgumentListNode* toArgumentList(VM*, int, int) const; + virtual bool isArrayLiteral() const override { return true; } + + ArgumentListNode* toArgumentList(ParserArena&, int, int) const; + ElementNode* elements() const { ASSERT(isSimpleArray()); return m_element; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isSimpleArray() const ; + virtual bool isSimpleArray() const override; ElementNode* m_element; int m_elision; @@ -473,19 +616,27 @@ namespace JSC { class PropertyNode : public ParserArenaFreeable { public: - enum Type { Constant = 1, Getter = 2, Setter = 4 }; + enum Type { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16 }; + enum PutType { Unknown, KnownDirect }; - PropertyNode(VM*, const Identifier&, ExpressionNode*, Type); - PropertyNode(VM*, double, ExpressionNode*, Type); + PropertyNode(const Identifier&, ExpressionNode*, Type, PutType, SuperBinding); + PropertyNode(ExpressionNode* propertyName, ExpressionNode*, Type, PutType, SuperBinding); - const Identifier& name() const { return m_name; } - Type type() const { return m_type; } + 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; + const Identifier* m_name; + ExpressionNode* m_expression; ExpressionNode* m_assign; - Type m_type; + unsigned m_type : 5; + unsigned m_needsSuperBinding : 1; + unsigned m_putType : 1; }; class PropertyListNode : public ExpressionNode { @@ -493,9 +644,10 @@ namespace JSC { PropertyListNode(const JSTokenLocation&, PropertyNode*); PropertyListNode(const JSTokenLocation&, PropertyNode*, PropertyListNode*); - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); - private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitPutConstantProperty(BytecodeGenerator&, RegisterID*, PropertyNode&); + PropertyNode* m_node; PropertyListNode* m_next; }; @@ -504,9 +656,10 @@ namespace JSC { public: ObjectLiteralNode(const JSTokenLocation&); ObjectLiteralNode(const JSTokenLocation&, PropertyListNode*); + virtual bool isObjectLiteral() const override { return true; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; PropertyListNode* m_list; }; @@ -521,10 +674,10 @@ namespace JSC { bool subscriptHasAssignments() const { return m_subscriptHasAssignments; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isLocation() const { return true; } - virtual bool isBracketAccessorNode() const { return true; } + virtual bool isLocation() const override { return true; } + virtual bool isBracketAccessorNode() const override { return true; } ExpressionNode* m_base; ExpressionNode* m_subscript; @@ -539,15 +692,28 @@ namespace JSC { const Identifier& identifier() const { return m_ident; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isLocation() const { return true; } - virtual bool isDotAccessorNode() const { return true; } + 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*); @@ -557,7 +723,7 @@ namespace JSC { ExpressionNode* m_expr; private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class ArgumentsNode : public ParserArenaFreeable { @@ -574,7 +740,7 @@ namespace JSC { NewExprNode(const JSTokenLocation&, ExpressionNode*, ArgumentsNode*); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; ArgumentsNode* m_args; @@ -585,7 +751,7 @@ namespace JSC { EvalFunctionCallNode(const JSTokenLocation&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ArgumentsNode* m_args; }; @@ -595,7 +761,7 @@ namespace JSC { FunctionCallValueNode(const JSTokenLocation&, ExpressionNode*, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; ArgumentsNode* m_args; @@ -606,7 +772,7 @@ namespace JSC { FunctionCallResolveNode(const JSTokenLocation&, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_ident; ArgumentsNode* m_args; @@ -614,14 +780,15 @@ namespace JSC { class FunctionCallBracketNode : public ExpressionNode, public ThrowableSubExpressionData { public: - FunctionCallBracketNode(const JSTokenLocation&, ExpressionNode* base, ExpressionNode* subscript, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + 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); + 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 { @@ -629,7 +796,7 @@ namespace JSC { FunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; protected: ExpressionNode* m_base; @@ -637,12 +804,43 @@ namespace JSC { ArgumentsNode* m_args; }; + class BytecodeIntrinsicNode : public ExpressionNode, public ThrowableExpressionData { + public: + enum class Type { + Constant, + Function + }; + + typedef RegisterID* (BytecodeIntrinsicNode::* EmitterType)(BytecodeGenerator&, RegisterID*); + + BytecodeIntrinsicNode(Type, const JSTokenLocation&, EmitterType, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + + virtual bool isBytecodeIntrinsicNode() const override { return true; } + + Type type() const { return m_type; } + EmitterType emitter() const { return m_emitter; } + const Identifier& identifier() const { return m_ident; } + +#define JSC_DECLARE_BYTECODE_INTRINSIC_FUNCTIONS(name) RegisterID* emit_intrinsic_##name(BytecodeGenerator&, RegisterID*); + JSC_COMMON_BYTECODE_INTRINSIC_FUNCTIONS_EACH_NAME(JSC_DECLARE_BYTECODE_INTRINSIC_FUNCTIONS) + JSC_COMMON_BYTECODE_INTRINSIC_CONSTANTS_EACH_NAME(JSC_DECLARE_BYTECODE_INTRINSIC_FUNCTIONS) +#undef JSC_DECLARE_BYTECODE_INTRINSIC_FUNCTIONS + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + Type m_type; + 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); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class ApplyFunctionCallDotNode : public FunctionCallDotNode { @@ -650,7 +848,7 @@ namespace JSC { ApplyFunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class DeleteResolveNode : public ExpressionNode, public ThrowableExpressionData { @@ -658,7 +856,7 @@ namespace JSC { DeleteResolveNode(const JSTokenLocation&, const Identifier&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_ident; }; @@ -668,7 +866,7 @@ namespace JSC { DeleteBracketNode(const JSTokenLocation&, ExpressionNode* base, ExpressionNode* subscript, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_base; ExpressionNode* m_subscript; @@ -679,7 +877,7 @@ namespace JSC { DeleteDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_base; const Identifier& m_ident; @@ -690,7 +888,7 @@ namespace JSC { DeleteValueNode(const JSTokenLocation&, ExpressionNode*); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; }; @@ -700,7 +898,7 @@ namespace JSC { VoidNode(const JSTokenLocation&, ExpressionNode*); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; }; @@ -712,7 +910,7 @@ namespace JSC { const Identifier& identifier() const { return m_ident; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_ident; }; @@ -722,7 +920,7 @@ namespace JSC { TypeOfValueNode(const JSTokenLocation&, ExpressionNode*); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; }; @@ -732,7 +930,7 @@ namespace JSC { PrefixNode(const JSTokenLocation&, ExpressionNode*, Operator, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); protected: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + 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); @@ -746,10 +944,10 @@ namespace JSC { PostfixNode(const JSTokenLocation&, ExpressionNode*, Operator, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); - virtual RegisterID* emitResolve(BytecodeGenerator&, RegisterID* = 0); - virtual RegisterID* emitBracket(BytecodeGenerator&, RegisterID* = 0); - virtual RegisterID* emitDot(BytecodeGenerator&, RegisterID* = 0); + 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 { @@ -761,7 +959,7 @@ namespace JSC { const ExpressionNode* expr() const { return m_expr; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; OpcodeID opcodeID() const { return m_opcodeID; } @@ -774,7 +972,7 @@ namespace JSC { UnaryPlusNode(const JSTokenLocation&, ExpressionNode*); private: - virtual ExpressionNode* stripUnaryPlus() { return expr(); } + virtual ExpressionNode* stripUnaryPlus() override { return expr(); } }; class NegateNode : public UnaryOpNode { @@ -791,7 +989,7 @@ namespace JSC { const ExpressionNode* expr() const { return m_expr; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; }; @@ -800,7 +998,7 @@ namespace JSC { public: LogicalNotNode(const JSTokenLocation&, ExpressionNode*); private: - void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode); + virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode) override; }; class BinaryOpNode : public ExpressionNode { @@ -809,14 +1007,14 @@ namespace JSC { BinaryOpNode(const JSTokenLocation&, ResultType, ExpressionNode* expr1, ExpressionNode* expr2, OpcodeID, bool rightHasAssignments); RegisterID* emitStrcat(BytecodeGenerator& generator, RegisterID* destination, RegisterID* lhs = 0, ReadModifyResolveNode* emitExpressionInfoForMe = 0); - void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode); + 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); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; protected: OpcodeID opcodeID() const { return m_opcodeID; } @@ -849,14 +1047,14 @@ namespace JSC { public: AddNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); - virtual bool isAdd() const { return true; } + 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 { return true; } + virtual bool isSubtract() const override { return true; } }; class LeftShiftNode : public BinaryOpNode { @@ -900,7 +1098,7 @@ namespace JSC { ThrowableBinaryOpNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, OpcodeID, bool rightHasAssignments); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class InstanceOfNode : public ThrowableBinaryOpNode { @@ -908,7 +1106,7 @@ namespace JSC { InstanceOfNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class InNode : public ThrowableBinaryOpNode { @@ -921,7 +1119,7 @@ namespace JSC { EqualNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class NotEqualNode : public BinaryOpNode { @@ -934,7 +1132,7 @@ namespace JSC { StrictEqualNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class NotStrictEqualNode : public BinaryOpNode { @@ -963,8 +1161,8 @@ namespace JSC { LogicalOpNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, LogicalOperator); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); - void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode) override; ExpressionNode* m_expr1; ExpressionNode* m_expr2; @@ -977,7 +1175,7 @@ namespace JSC { ConditionalNode(const JSTokenLocation&, ExpressionNode* logical, ExpressionNode* expr1, ExpressionNode* expr2); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_logical; ExpressionNode* m_expr1; @@ -989,7 +1187,7 @@ namespace JSC { 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); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_ident; ExpressionNode* m_right; @@ -999,13 +1197,14 @@ namespace JSC { class AssignResolveNode : public ExpressionNode, public ThrowableExpressionData { public: - AssignResolveNode(const JSTokenLocation&, const Identifier&, ExpressionNode* right); + AssignResolveNode(const JSTokenLocation&, const Identifier&, ExpressionNode* right, AssignmentContext); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_ident; ExpressionNode* m_right; + AssignmentContext m_assignmentContext; }; class ReadModifyBracketNode : public ExpressionNode, public ThrowableSubExpressionData { @@ -1013,12 +1212,12 @@ namespace JSC { 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); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_base; ExpressionNode* m_subscript; ExpressionNode* m_right; - Operator m_operator : 30; + unsigned m_operator : 30; bool m_subscriptHasAssignments : 1; bool m_rightHasAssignments : 1; }; @@ -1028,7 +1227,7 @@ namespace JSC { 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); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_base; ExpressionNode* m_subscript; @@ -1042,7 +1241,7 @@ namespace JSC { 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); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_base; const Identifier& m_ident; @@ -1055,12 +1254,12 @@ namespace JSC { 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); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_base; const Identifier& m_ident; ExpressionNode* m_right; - Operator m_operator : 31; + unsigned m_operator : 31; bool m_rightHasAssignments : 1; }; @@ -1069,57 +1268,25 @@ namespace JSC { AssignErrorNode(const JSTokenLocation&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; - typedef Vector<ExpressionNode*, 8> ExpressionVector; - - class CommaNode : public ExpressionNode, public ParserArenaDeletable { + class CommaNode final : public ExpressionNode { public: - CommaNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2); - - using ParserArenaDeletable::operator new; + CommaNode(const JSTokenLocation&, ExpressionNode*); - void append(ExpressionNode* expr) { m_expressions.append(expr); } + void setNext(CommaNode* next) { m_next = next; } + CommaNode* next() { return m_next; } private: - virtual bool isCommaNode() const { return true; } - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual bool isCommaNode() const override { return true; } + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - ExpressionVector m_expressions; + ExpressionNode* m_expr; + CommaNode* m_next; }; - class ConstDeclNode : public ExpressionNode { - public: - ConstDeclNode(const JSTokenLocation&, const Identifier&, ExpressionNode*); - - bool hasInitializer() const { return m_init; } - const Identifier& ident() { return m_ident; } - - private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); - virtual RegisterID* emitCodeSingle(BytecodeGenerator&); - - const Identifier& m_ident; - - public: - ConstDeclNode* m_next; - - private: - ExpressionNode* m_init; - }; - - class ConstStatementNode : public StatementNode { - public: - ConstStatementNode(const JSTokenLocation&, ConstDeclNode* next); - - private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); - - ConstDeclNode* m_next; - }; - - class SourceElements : public ParserArenaDeletable { + class SourceElements final : public ParserArenaFreeable { public: SourceElements(); @@ -1129,22 +1296,26 @@ namespace JSC { StatementNode* lastStatement() const; void emitBytecode(BytecodeGenerator&, RegisterID* destination); + void analyzeModule(ModuleAnalyzer&); private: - Vector<StatementNode*> m_statements; + StatementNode* m_head; + StatementNode* m_tail; }; - class BlockNode : public StatementNode { + class BlockNode : public StatementNode, public VariableEnvironmentNode { public: - BlockNode(const JSTokenLocation&, SourceElements* = 0); + using ParserArenaDeletable::operator new; + + BlockNode(const JSTokenLocation&, SourceElements*, VariableEnvironment&); StatementNode* singleStatement() const; StatementNode* lastStatement() const; private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isBlock() const { return true; } + virtual bool isBlock() const override { return true; } SourceElements* m_statements; }; @@ -1154,9 +1325,9 @@ namespace JSC { EmptyStatementNode(const JSTokenLocation&); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isEmptyStatement() const { return true; } + virtual bool isEmptyStatement() const override { return true; } }; class DebuggerStatementNode : public StatementNode { @@ -1164,7 +1335,7 @@ namespace JSC { DebuggerStatementNode(const JSTokenLocation&); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class ExprStatementNode : public StatementNode { @@ -1174,28 +1345,48 @@ namespace JSC { ExpressionNode* expr() const { return m_expr; } private: - virtual bool isExprStatement() const { return true; } + virtual bool isExprStatement() const override { return true; } - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; }; - class VarStatementNode : public StatementNode { + class DeclarationStatement : public StatementNode { public: - VarStatementNode(const JSTokenLocation&, ExpressionNode*); + DeclarationStatement(const JSTokenLocation&, ExpressionNode*); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + 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); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; bool tryFoldBreakAndContinue(BytecodeGenerator&, StatementNode* ifBlock, Label*& trueTarget, FallThroughMode&); @@ -1209,7 +1400,7 @@ namespace JSC { DoWhileNode(const JSTokenLocation&, StatementNode*, ExpressionNode*); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; StatementNode* m_statement; ExpressionNode* m_expr; @@ -1220,61 +1411,81 @@ namespace JSC { WhileNode(const JSTokenLocation&, ExpressionNode*, StatementNode*); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; StatementNode* m_statement; }; - class ForNode : public StatementNode { + class ForNode : public StatementNode, public VariableEnvironmentNode { public: - ForNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, ExpressionNode* expr3, StatementNode*); + using ParserArenaDeletable::operator new; + + ForNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, ExpressionNode* expr3, StatementNode*, VariableEnvironment&); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr1; ExpressionNode* m_expr2; ExpressionNode* m_expr3; StatementNode* m_statement; }; - - class ForInNode : public StatementNode, public ThrowableExpressionData { + + class DestructuringPatternNode; + + class EnumerationNode : public StatementNode, public ThrowableExpressionData, public VariableEnvironmentNode { public: - ForInNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*); - ForInNode(VM*, const JSTokenLocation&, const Identifier&, ExpressionNode*, ExpressionNode*, StatementNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); - - private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + using ParserArenaDeletable::operator new; - ExpressionNode* m_init; + 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(VM*, const JSTokenLocation&); ContinueNode(const JSTokenLocation&, const Identifier&); Label* trivialTarget(BytecodeGenerator&); private: - virtual bool isContinue() const { return true; } - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + 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(VM*, const JSTokenLocation&); BreakNode(const JSTokenLocation&, const Identifier&); Label* trivialTarget(BytecodeGenerator&); private: - virtual bool isBreak() const { return true; } - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual bool isBreak() const override { return true; } + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_ident; }; @@ -1286,9 +1497,9 @@ namespace JSC { ExpressionNode* value() { return m_value; } private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isReturnNode() const { return true; } + virtual bool isReturnNode() const override { return true; } ExpressionNode* m_value; }; @@ -1298,7 +1509,7 @@ namespace JSC { WithNode(const JSTokenLocation&, ExpressionNode*, StatementNode*, const JSTextPosition& divot, uint32_t expressionLength); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; StatementNode* m_statement; @@ -1311,7 +1522,7 @@ namespace JSC { LabelNode(const JSTokenLocation&, const Identifier& name, StatementNode*); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_name; StatementNode* m_statement; @@ -1322,55 +1533,34 @@ namespace JSC { ThrowNode(const JSTokenLocation&, ExpressionNode*); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; }; - class TryNode : public StatementNode { + class TryNode : public StatementNode, public VariableEnvironmentNode { public: - TryNode(const JSTokenLocation&, StatementNode* tryBlock, const Identifier& exceptionIdent, StatementNode* catchBlock, StatementNode* finallyBlock); + using ParserArenaDeletable::operator new; + + TryNode(const JSTokenLocation&, StatementNode* tryBlock, DestructuringPatternNode* catchPattern, StatementNode* catchBlock, VariableEnvironment& catchEnvironment, StatementNode* finallyBlock); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; StatementNode* m_tryBlock; - const Identifier& m_exceptionIdent; + DestructuringPatternNode* m_catchPattern; StatementNode* m_catchBlock; StatementNode* m_finallyBlock; }; - class ParameterNode : public ParserArenaFreeable { + class ScopeNode : public StatementNode, public ParserArenaRoot, public VariableEnvironmentNode { public: - ParameterNode(const Identifier&); - ParameterNode(ParameterNode*, const Identifier&); - - const Identifier& ident() const { return m_ident; } - ParameterNode* nextParam() const { return m_next; } - - private: - const Identifier& m_ident; - ParameterNode* m_next; - }; - - class ScopeNode : public StatementNode, public ParserArenaRefCounted { - public: - typedef DeclarationStacks::VarStack VarStack; typedef DeclarationStacks::FunctionStack FunctionStack; - ScopeNode(VM*, const JSTokenLocation& start, const JSTokenLocation& end, bool inStrictContext); - ScopeNode(VM*, const JSTokenLocation& start, const JSTokenLocation& end, const SourceCode&, SourceElements*, VarStack*, FunctionStack*, IdentifierSet&, CodeFeatures, int numConstants); + 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 ParserArenaRefCounted::operator new; - - void destroyData() - { - m_arena.reset(); - m_varStack.clear(); - m_functionStack.clear(); - m_statements = 0; - m_capturedVariables.clear(); - } + using ParserArenaRoot::operator new; const SourceCode& source() const { return m_source; } const String& sourceURL() const { return m_source.provider()->url(); } @@ -1385,16 +1575,18 @@ namespace JSC { bool usesEval() const { return m_features & EvalFeature; } bool usesArguments() const { return (m_features & ArgumentsFeature) && !(m_features & ShadowsArgumentsFeature); } + bool usesArrowFunction() const { return m_features & ArrowFunctionFeature; } + 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_capturedVariables.size(); } - size_t capturedVariableCount() const { return m_capturedVariables.size(); } - bool captures(const Identifier& ident) { return m_capturedVariables.contains(ident.impl()); } + bool needsActivation() const { return (hasCapturedVariables()) || (m_features & (EvalFeature | WithFeature)); } + 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()); } - VarStack& varStack() { return m_varStack; } + VariableEnvironment& varDeclarations() { return m_varDeclarations; } FunctionStack& functionStack() { return m_functionStack; } int neededConstants() @@ -1407,11 +1599,10 @@ namespace JSC { StatementNode* singleStatement() const; void emitStatementsBytecode(BytecodeGenerator&, RegisterID* destination); + + void analyzeModule(ModuleAnalyzer&); protected: - void setSource(const SourceCode& source) { m_source = source; } - ParserArena m_arena; - int m_startLineNumber; unsigned m_startStartOffset; unsigned m_startLineStartOffset; @@ -1419,127 +1610,541 @@ namespace JSC { private: CodeFeatures m_features; SourceCode m_source; - VarStack m_varStack; + VariableEnvironment m_varDeclarations; FunctionStack m_functionStack; int m_numConstants; SourceElements* m_statements; - IdentifierSet m_capturedVariables; }; class ProgramNode : public ScopeNode { public: - static const bool isFunctionNode = false; - static PassRefPtr<ProgramNode> create(VM*, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, SourceElements*, VarStack*, FunctionStack*, IdentifierSet&, const SourceCode&, CodeFeatures, int numConstants); + ProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, int numConstants); - unsigned startColumn() { return m_startColumn; } + unsigned startColumn() const { return m_startColumn; } + unsigned endColumn() const { return m_endColumn; } static const bool scopeIsFunction = false; private: - ProgramNode(VM*, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, SourceElements*, VarStack*, FunctionStack*, IdentifierSet&, const SourceCode&, CodeFeatures, int numConstants); - - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); - + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; unsigned m_startColumn; + unsigned m_endColumn; }; class EvalNode : public ScopeNode { public: - static const bool isFunctionNode = false; - static PassRefPtr<EvalNode> create(VM*, const JSTokenLocation& start, const JSTokenLocation& end, unsigned, SourceElements*, VarStack*, FunctionStack*, IdentifierSet&, const SourceCode&, CodeFeatures, int numConstants); + EvalNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, int numConstants); - unsigned startColumn() { return 1; } + ALWAYS_INLINE unsigned startColumn() const { return 0; } + unsigned endColumn() const { return m_endColumn; } static const bool scopeIsFunction = false; private: - EvalNode(VM*, const JSTokenLocation& start, const JSTokenLocation& end, SourceElements*, VarStack*, FunctionStack*, IdentifierSet&, const SourceCode&, CodeFeatures, int numConstants); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + unsigned m_endColumn; }; - class FunctionParameters : public RefCounted<FunctionParameters> { - WTF_MAKE_FAST_ALLOCATED; + 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: - static PassRefPtr<FunctionParameters> create(ParameterNode*); - ~FunctionParameters(); + ExportLocalDeclarationNode(const JSTokenLocation&, StatementNode*); - unsigned size() const { return m_size; } - const Identifier& at(unsigned index) const { ASSERT(index < m_size); return identifiers()[index]; } + const StatementNode& declaration() const { return *m_declaration; } private: - FunctionParameters(ParameterNode*, unsigned size); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + virtual void analyzeModule(ModuleAnalyzer&) override; + StatementNode* m_declaration; + }; - Identifier* identifiers() { return reinterpret_cast<Identifier*>(&m_storage); } - const Identifier* identifiers() const { return reinterpret_cast<const Identifier*>(&m_storage); } + 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; } - unsigned m_size; - void* m_storage; + private: + const Identifier& m_localName; + const Identifier& m_exportedName; }; - class FunctionBodyNode : public ScopeNode { + class ExportSpecifierListNode : public ParserArenaDeletable { public: - static const bool isFunctionNode = true; - static FunctionBodyNode* create(VM*, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, bool isStrictMode); - static PassRefPtr<FunctionBodyNode> create(VM*, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, SourceElements*, VarStack*, FunctionStack*, IdentifierSet&, const SourceCode&, CodeFeatures, int numConstants); + typedef Vector<ExportSpecifierNode*, 3> Specifiers; - FunctionParameters* parameters() const { return m_parameters.get(); } - size_t parameterCount() const { return m_parameters->size(); } + const Specifiers& specifiers() const { return m_specifiers; } + void append(ExportSpecifierNode* specifier) + { + m_specifiers.append(specifier); + } + + private: + Specifiers m_specifiers; + }; - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + class ExportNamedDeclarationNode : public ModuleDeclarationNode { + public: + ExportNamedDeclarationNode(const JSTokenLocation&, ExportSpecifierListNode*, ModuleNameNode*); - void finishParsing(const SourceCode&, ParameterNode*, const Identifier&, FunctionNameIsInScopeToggle); - void finishParsing(PassRefPtr<FunctionParameters>, const Identifier&, FunctionNameIsInScopeToggle); + 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, SuperBinding, unsigned, SourceParseMode, bool isArrowFunctionBodyExpression); + + 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; } - bool functionNameIsInScope() { return m_functionNameIsInScopeToggle == FunctionNameIsInScope; } - FunctionNameIsInScopeToggle functionNameIsInScopeToggle() { return m_functionNameIsInScopeToggle; } + FunctionMode functionMode() { return m_functionMode; } - void setFunctionStart(int functionStart) { m_functionStart = functionStart; } - int functionStart() const { return m_functionStart; } + 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; } - static const bool scopeIsFunction = true; + void setEndPosition(JSTextPosition); - private: - FunctionBodyNode(VM*, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, bool inStrictContext); - FunctionBodyNode(VM*, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, SourceElements*, VarStack*, FunctionStack*, IdentifierSet&, const SourceCode&, CodeFeatures, int numConstants); + const SourceCode& source() const { return m_source; } + + int startStartOffset() const { return m_startStartOffset; } + bool isInStrictContext() const { return m_isInStrictContext; } + SuperBinding superBinding() { return static_cast<SuperBinding>(m_superBinding); } + ConstructorKind constructorKind() { return static_cast<ConstructorKind>(m_constructorKind); } + bool isArrowFunctionBodyExpression() const { return m_isArrowFunctionBodyExpression; } + 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; - FunctionNameIsInScopeToggle m_functionNameIsInScopeToggle; - RefPtr<FunctionParameters> m_parameters; - int m_functionStart; + 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_superBinding : 1; + unsigned m_constructorKind : 2; + unsigned m_isArrowFunctionBodyExpression : 1; + }; + + 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 BaseFuncExprNode : public ExpressionNode { + public: + BaseFuncExprNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&); + + FunctionMetadataNode* metadata() { return m_metadata; } + + protected: + FunctionMetadataNode* m_metadata; + }; + + + class FuncExprNode : public BaseFuncExprNode { + public: + FuncExprNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + virtual bool isFuncExprNode() const override { return true; } + }; + + class ArrowFuncExprNode : public BaseFuncExprNode { + public: + ArrowFuncExprNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&); + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + virtual bool isArrowFuncExprNode() const override { return true; } + }; + + class YieldExprNode final : public ExpressionNode, public ThrowableExpressionData { + public: + YieldExprNode(const JSTokenLocation&, ExpressionNode* argument, bool delegate); + + ExpressionNode* argument() const { return m_argument; } + bool delegate() const { return m_delegate; } + + private: + virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_argument; + bool m_delegate; + }; + + class ClassExprNode final : public ExpressionNode, public VariableEnvironmentNode { + public: + using ParserArenaDeletable::operator new; + + ClassExprNode(const JSTokenLocation&, const Identifier&, VariableEnvironment& classEnvironment, 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; }; - class FuncExprNode : public ExpressionNode { + class DestructuringPatternNode : public ParserArenaFreeable { public: - FuncExprNode(const JSTokenLocation&, const Identifier&, FunctionBodyNode*, const SourceCode&, ParameterNode* = 0); + 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 bool isRestParameter() const { return false; } + virtual RegisterID* emitDirectBinding(BytecodeGenerator&, RegisterID*, ExpressionNode*) { return 0; } + + protected: + DestructuringPatternNode(); + }; - FunctionBodyNode* body() { return m_body; } + 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: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + 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; - virtual bool isFuncExprNode() const { return true; } + 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, nullptr, wasString, pattern, defaultValue }); + } - FunctionBodyNode* m_body; + void appendEntry(const JSTokenLocation&, ExpressionNode* propertyExpression, DestructuringPatternNode* pattern, ExpressionNode* defaultValue) + { + m_targetPatterns.append(Entry{ Identifier(), propertyExpression, false, 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; + ExpressionNode* propertyExpression; + 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 RestParameterNode : public DestructuringPatternNode { + public: + RestParameterNode(const Identifier& boundProperty, unsigned numParametersToSkip, const JSTextPosition& start, const JSTextPosition& end); + + bool isRestParameter() const override { return true; } + + void emit(BytecodeGenerator&); + + const Identifier& name() const { return m_name; } + + private: + virtual void collectBoundIdentifiers(Vector<Identifier>&) const override; + virtual void bindValue(BytecodeGenerator&, RegisterID*) const override; + virtual void toString(StringBuilder&) const override; + + const Identifier& m_name; + unsigned m_numParametersToSkip; + JSTextPosition m_divotStart; // "f" in "...foo" + JSTextPosition m_divotEnd; + }; + + class AssignmentElementNode : public DestructuringPatternNode { + public: + AssignmentElementNode(ExpressionNode* assignmentTarget, const JSTextPosition& start, const JSTextPosition& end); + const ExpressionNode* assignmentTarget() { return m_assignmentTarget; } + + 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; + + JSTextPosition m_divotStart; + JSTextPosition m_divotEnd; + ExpressionNode* m_assignmentTarget; + }; + + 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&, FunctionBodyNode*, const SourceCode&, ParameterNode* = 0); + FuncDeclNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&); - FunctionBodyNode* body() { return m_body; } + virtual bool isFuncDeclNode() const override { return true; } + FunctionMetadataNode* metadata() { return m_metadata; } private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - FunctionBodyNode* m_body; + FunctionMetadataNode* m_metadata; + }; + + class ClassDeclNode final : public StatementNode { + public: + ClassDeclNode(const JSTokenLocation&, ExpressionNode* classExpression); + + private: + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_classDeclaration; }; class CaseClauseNode : public ParserArenaFreeable { @@ -1549,10 +2154,12 @@ namespace JSC { 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 { @@ -1576,18 +2183,20 @@ namespace JSC { private: SwitchInfo::SwitchType tryTableSwitch(Vector<ExpressionNode*, 8>& literalVector, int32_t& min_num, int32_t& max_num); - static const size_t s_tableSwitchMinimum = 10; + static const size_t s_tableSwitchMinimum = 3; ClauseListNode* m_list1; CaseClauseNode* m_defaultClause; ClauseListNode* m_list2; }; - class SwitchNode : public StatementNode { + class SwitchNode : public StatementNode, public VariableEnvironmentNode { public: - SwitchNode(const JSTokenLocation&, ExpressionNode*, CaseBlockNode*); + using ParserArenaDeletable::operator new; + + SwitchNode(const JSTokenLocation&, ExpressionNode*, CaseBlockNode*, VariableEnvironment&); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0); + virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; CaseBlockNode* m_block; @@ -1608,16 +2217,6 @@ namespace JSC { ArgumentListNode* tail; }; - struct ConstDeclList { - ConstDeclNode* head; - ConstDeclNode* tail; - }; - - struct ParameterList { - ParameterNode* head; - ParameterNode* tail; - }; - struct ClauseList { ClauseListNode* head; ClauseListNode* tail; diff --git a/Source/JavaScriptCore/parser/NodesAnalyzeModule.cpp b/Source/JavaScriptCore/parser/NodesAnalyzeModule.cpp new file mode 100644 index 000000000..ed461da26 --- /dev/null +++ b/Source/JavaScriptCore/parser/NodesAnalyzeModule.cpp @@ -0,0 +1,97 @@ +/* + * 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 "JSCJSValueInlines.h" +#include "JSModuleRecord.h" +#include "ModuleAnalyzer.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(JSModuleRecord::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(JSModuleRecord::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 index 07bd59576..c66b74282 100644 --- a/Source/JavaScriptCore/parser/Parser.cpp +++ b/Source/JavaScriptCore/parser/Parser.cpp @@ -28,73 +28,201 @@ #include "Debugger.h" #include "JSCJSValueInlines.h" #include "Lexer.h" -#include "NodeInfo.h" +#include "JSCInlines.h" +#include "SetForScope.h" #include "SourceProvider.h" #include "VM.h" #include <utility> #include <wtf/HashFunctions.h> -#include <wtf/OwnPtr.h> +#include <wtf/StringPrintStream.h> #include <wtf/WTFThreadData.h> -#define fail() do { if (!hasError()) updateErrorMessage(); return 0; } while (0) -#define failWithToken(tok) do { if (!hasError()) updateErrorMessage(tok); return 0; } while (0) -#define failWithMessage(msg) do { if (!hasError()) updateErrorMessage(msg); return 0; } while (0) -#define failWithNameAndMessage(before, name, after) do { if (!hasError()) updateErrorWithNameAndMessage(before, name, after); return 0; } while (0) -#define failWithStackOverflow() do { updateErrorMessage("Stack exhausted"); m_hasStackOverflow = true; return 0; } while (0) -#define failIfFalse(cond) do { if (!(cond)) fail(); } while (0) -#define failIfFalseWithMessage(cond, msg) do { if (!(cond)) failWithMessage(msg); } while (0) -#define failIfFalseWithNameAndMessage(cond, before, name, msg) do { if (!(cond)) failWithNameAndMessage(before, name, msg); } while (0) -#define failIfTrue(cond) do { if ((cond)) fail(); } while (0) -#define failIfTrueWithMessage(cond, msg) do { if ((cond)) failWithMessage(msg); } while (0) -#define failIfTrueWithNameAndMessage(cond, before, name, msg) do { if ((cond)) failWithNameAndMessage(before, name, msg); } while (0) -#define failIfTrueIfStrict(cond) do { if ((cond) && strictMode()) fail(); } while (0) -#define failIfTrueIfStrictWithMessage(cond, msg) do { if ((cond) && strictMode()) failWithMessage(msg); } while (0) -#define failIfTrueIfStrictWithNameAndMessage(cond, before, name, after) do { if ((cond) && strictMode()) failWithNameAndMessage(before, name, after); } while (0) -#define failIfFalseIfStrict(cond) do { if ((!(cond)) && strictMode()) fail(); } while (0) -#define failIfFalseIfStrictWithMessage(cond, msg) do { if ((!(cond)) && strictMode()) failWithMessage(msg); } while (0) -#define failIfFalseIfStrictWithNameAndMessage(cond, before, name, after) do { if ((!(cond)) && strictMode()) failWithNameAndMessage(before, name, after); } while (0) -#define consumeOrFail(tokenType) do { if (!consume(tokenType)) failWithToken(tokenType); } while (0) -#define consumeOrFailWithFlags(tokenType, flags) do { if (!consume(tokenType, flags)) failWithToken(tokenType); } while (0) -#define matchOrFail(tokenType) do { if (!match(tokenType)) failWithToken(tokenType); } while (0) + +#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 restoreSavePointAndFail(savePoint, message) do { restoreSavePointWithError(savePoint, message); 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> -Parser<LexerType>::Parser(VM* vm, const SourceCode& source, FunctionParameters* parameters, const Identifier& name, JSParserStrictness strictness, JSParserMode parserMode) +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, SuperBinding superBinding, + ConstructorKind defaultConstructorKind, ThisTDZMode thisTDZMode) : m_vm(vm) , m_source(&source) - , m_stack(*vm, wtfThreadData().stack()) , 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_sourceElements(0) + , m_parsingBuiltin(builtinMode == JSParserBuiltinMode::Builtin) + , m_superBinding(superBinding) + , m_defaultConstructorKind(defaultConstructorKind) + , m_thisTDZMode(thisTDZMode) { - m_lexer = adoptPtr(new LexerType(vm)); - m_arena = m_vm->parserArena.get(); - m_lexer->setCode(source, m_arena); + 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()); + m_expressionErrorClassifier = nullptr; + ScopeRef scope = pushScope(); - if (parserMode == JSParseFunctionCode) - scope->setIsFunction(); - if (strictness == JSParseStrict) + scope->setSourceParseMode(parseMode); + + if (strictMode == JSParserStrictMode::Strict) scope->setStrictMode(); - if (parameters) { - for (unsigned i = 0; i < parameters->size(); i++) - scope->declareParameter(¶meters->at(i)); - } - if (!name.isNull()) - scope->declareCallee(&name); + next(); } @@ -104,16 +232,64 @@ Parser<LexerType>::~Parser() } template <typename LexerType> -String Parser<LexerType>::parseInner() +String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMode parseMode) { String parseError = String(); - - ASTBuilder context(const_cast<VM*>(m_vm), const_cast<SourceCode*>(m_source)); - if (m_lexer->isReparsing()) - m_statementDepth--; + + ASTBuilder context(const_cast<VM*>(m_vm), m_parserArena, const_cast<SourceCode*>(m_source)); ScopeRef scope = currentScope(); - SourceElements* sourceElements = parseSourceElements<CheckForStrictMode>(context); - if (!sourceElements || !consume(EOFTOK)) { + scope->setIsLexicalScope(); + SetForScope<FunctionParsePhase> functionParsePhasePoisoner(m_parserState.functionParsePhase, FunctionParsePhase::Body); + + bool isArrowFunctionBodyExpression = false; + if (m_lexer->isReparsingFunction()) { + ParserFunctionInfo<ASTBuilder> functionInfo; + if (parseMode == SourceParseMode::GeneratorBodyMode) + functionInfo.parameters = createGeneratorParameters(context); + else + parseFunctionParameters(context, parseMode, functionInfo); + m_parameters = functionInfo.parameters; + + 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); + } + } + + 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); + else if (isModuleParseMode(parseMode)) + sourceElements = parseModuleSourceElements(context, parseMode); + else { + if (parseMode == SourceParseMode::GeneratorWrapperFunctionMode) + sourceElements = parseGeneratorFunctionSourceElements(context, CheckForStrictMode); + 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 @@ -121,63 +297,143 @@ String Parser<LexerType>::parseInner() } IdentifierSet capturedVariables; - scope->getCapturedVariables(capturedVariables); + bool modifiedParameter = false; + bool modifiedArguments = false; + scope->getCapturedVars(capturedVariables, modifiedParameter, modifiedArguments); + + VariableEnvironment& varDeclarations = scope->declaredVariables(); + for (auto& entry : capturedVariables) + varDeclarations.markVariableAsCaptured(entry); + + IdentifierSet usedVariables; + scope->getUsedVariables(usedVariables); + if (parseMode == SourceParseMode::GeneratorWrapperFunctionMode) { + if (usedVariables.contains(m_vm->propertyNames->arguments.impl())) + context.propagateArgumentsUse(); + } + CodeFeatures features = context.features(); if (scope->strictMode()) features |= StrictModeFeature; if (scope->shadowsArguments()) features |= ShadowsArgumentsFeature; + if (modifiedParameter) + features |= ModifiedParameterFeature; + if (modifiedArguments) + features |= ModifiedArgumentsFeature; - didFinishParsing(sourceElements, context.varDeclarations(), context.funcDeclarations(), features, - context.numConstants(), capturedVariables); +#ifndef NDEBUG + if (m_parsingBuiltin && isProgramParseMode(parseMode)) { + VariableEnvironment& lexicalVariables = scope->lexicalVariables(); + const IdentifierSet& closedVariableCandidates = scope->closedVariableCandidates(); + const BuiltinNames& builtinNames = m_vm->propertyNames->builtinNames(); + for (const RefPtr<UniquedStringImpl>& candidate : closedVariableCandidates) { + if (!lexicalVariables.contains(candidate) && !varDeclarations.contains(candidate) && !builtinNames.isPrivateName(*candidate.get())) { + dataLog("Bad global capture in builtin: '", candidate, "'\n"); + dataLog(m_source->view()); + CRASH(); + } + } + } +#endif // NDEBUG + didFinishParsing(sourceElements, context.funcDeclarations(), varDeclarations, features, context.numConstants()); return parseError; } template <typename LexerType> -void Parser<LexerType>::didFinishParsing(SourceElements* sourceElements, ParserArenaData<DeclarationStacks::VarStack>* varStack, - ParserArenaData<DeclarationStacks::FunctionStack>* funcStack, CodeFeatures features, int numConstants, IdentifierSet& capturedVars) +void Parser<LexerType>::didFinishParsing(SourceElements* sourceElements, DeclarationStacks::FunctionStack& funcStack, + VariableEnvironment& varDeclarations, CodeFeatures features, int numConstants) { m_sourceElements = sourceElements; - m_varDeclarations = varStack; - m_funcDeclarations = funcStack; - m_capturedVariables.swap(capturedVars); + m_funcDeclarations.swap(funcStack); + m_varDeclarations.swap(varDeclarations); m_features = features; m_numConstants = numConstants; } template <typename LexerType> +bool Parser<LexerType>::isArrowFunctionParameters() +{ + bool isArrowFunction = false; + + if (match(EOFTOK)) + return false; + + bool isOpenParen = match(OPENPAREN); + bool isIdent = match(IDENT); + + if (!isOpenParen && !isIdent) + return false; + + SavePoint saveArrowFunctionPoint = createSavePoint(); + + if (isIdent) { + next(); + isArrowFunction = match(ARROWFUNCTION); + } else { + RELEASE_ASSERT(isOpenParen); + next(); + if (match(CLOSEPAREN)) { + next(); + isArrowFunction = match(ARROWFUNCTION); + } else { + SyntaxChecker syntaxChecker(const_cast<VM*>(m_vm), m_lexer.get()); + // We make fake scope, otherwise parseFormalParameters will add variable to current scope that lead to errors + AutoPopScopeRef fakeScope(this, pushScope()); + fakeScope->setSourceParseMode(SourceParseMode::ArrowFunctionMode); + + unsigned parametersCount = 0; + isArrowFunction = parseFormalParameters(syntaxChecker, syntaxChecker.createFormalParameterList(), parametersCount) && consume(CLOSEPAREN) && match(ARROWFUNCTION); + + popScope(fakeScope, syntaxChecker.NeedsFreeVariableInfo); + } + } + + restoreSavePoint(saveArrowFunctionPoint); + + return isArrowFunction; +} + +template <typename LexerType> bool Parser<LexerType>::allowAutomaticSemicolon() { return match(CLOSEBRACE) || match(EOFTOK) || m_lexer->prevTerminator(); } template <typename LexerType> -template <SourceElementsMode mode, class TreeBuilder> TreeSourceElements Parser<LexerType>::parseSourceElements(TreeBuilder& context) +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; - unsigned startOffset = m_token.m_location.startOffset; - unsigned startLineStartOffset = m_token.m_location.lineStartOffset; - unsigned oldLastLineNumber = m_lexer->lastLineNumber(); - unsigned oldLineNumber = m_lexer->lineNumber(); + auto savePoint = createSavePoint(); bool hasSetStrict = false; - while (TreeStatement statement = parseStatement(context, directive, &directiveLiteralLength)) { + + 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; - failIfFalse(isValidStrictMode()); - m_lexer->setOffset(startOffset, startLineStartOffset); - next(); - m_lexer->setLastLineNumber(oldLastLineNumber); - m_lexer->setLineNumber(oldLineNumber); - failIfTrue(hasError()); + if (!isValidStrictMode()) { + if (m_parserState.lastFunctionName) { + if (m_vm->propertyNames->arguments == *m_parserState.lastFunctionName) + semanticFail("Cannot name a function 'arguments' in strict mode"); + if (m_vm->propertyNames->eval == *m_parserState.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 @@ -186,40 +442,165 @@ template <SourceElementsMode mode, class TreeBuilder> TreeSourceElements Parser< context.appendStatement(sourceElements, statement); } - failIfTrue(hasError()); + propagateError(); return sourceElements; } template <typename LexerType> -template <class TreeBuilder> TreeStatement Parser<LexerType>::parseVarDeclaration(TreeBuilder& context) +template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseModuleSourceElements(TreeBuilder& context, SourceParseMode parseMode) { - ASSERT(match(VAR)); - JSTokenLocation location(tokenLocation()); - int start = tokenLine(); - int end = 0; - int scratch; - const Identifier* scratch1 = 0; - TreeExpression scratch2 = 0; - JSTextPosition scratch3; - TreeExpression varDecls = parseVarDeclarationList(context, scratch, scratch1, scratch2, scratch3, scratch3, scratch3); - failIfTrue(hasError()); - failIfFalse(autoSemiColon()); - - return context.createVarStatement(location, varDecls, start, end); + 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> TreeSourceElements Parser<LexerType>::parseGeneratorFunctionSourceElements(TreeBuilder& context, SourceElementsMode mode) +{ + auto sourceElements = context.createSourceElements(); + + unsigned functionKeywordStart = tokenStart(); + JSTokenLocation startLocation(tokenLocation()); + JSTextPosition start = tokenStartPosition(); + unsigned startColumn = tokenColumn(); + int functionNameStart = m_token.m_location.startOffset; + int parametersStart = m_token.m_location.startOffset; + + ParserFunctionInfo<TreeBuilder> info; + info.name = &m_vm->propertyNames->nullIdentifier; + info.parameters = createGeneratorParameters(context); + info.startOffset = parametersStart; + info.startLine = tokenLine(); + info.parameterCount = 4; // generator, state, value, resume mode + + { + AutoPopScopeRef generatorBodyScope(this, pushScope()); + generatorBodyScope->setSourceParseMode(SourceParseMode::GeneratorBodyMode); + SyntaxChecker generatorFunctionContext(const_cast<VM*>(m_vm), m_lexer.get()); + failIfFalse(parseSourceElements(generatorFunctionContext, mode), "Cannot parse the body of a generator"); + popScope(generatorBodyScope, TreeBuilder::NeedsFreeVariableInfo); + } + info.body = context.createFunctionMetadata(startLocation, tokenLocation(), startColumn, tokenColumn(), functionKeywordStart, functionNameStart, parametersStart, strictMode(), ConstructorKind::None, m_superBinding, info.parameterCount, SourceParseMode::GeneratorBodyMode, false); + + info.endLine = tokenLine(); + info.endOffset = m_token.m_data.offset; + info.bodyStartColumn = startColumn; + + auto functionExpr = context.createFunctionExpr(startLocation, info); + auto statement = context.createExprStatement(startLocation, functionExpr, start, m_lastTokenEndPosition.line); + context.appendStatement(sourceElements, statement); + + return sourceElements; } template <typename LexerType> -template <class TreeBuilder> TreeStatement Parser<LexerType>::parseConstDeclaration(TreeBuilder& context) +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementListItem(TreeBuilder& context, const Identifier*& directive, unsigned* directiveLiteralLength) { - ASSERT(match(CONSTTOKEN)); + // 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(); + // Intentionally use `match(IDENT) || match(LET) || match(YIELD)` and don't use `matchSpecIdentifier()`. + // We would like to fall into parseVariableDeclaration path even if "yield" is not treated as an Identifier. + // For example, under a generator context, matchSpecIdentifier() for "yield" returns `false`. + // But we would like to enter parseVariableDeclaration and raise an error under the context of parseVariableDeclaration + // to raise consistent errors between "var", "const" and "let". + if (!(match(IDENT) || match(LET) || match(YIELD)) && !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; - TreeConstDeclList constDecls = parseConstDeclarationList(context); - failIfTrue(hasError()); - failIfFalse(autoSemiColon()); + 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.createConstStatement(location, constDecls, start, end); + return context.createDeclarationStatement(location, variableDecls, start, end); } template <typename LexerType> @@ -232,14 +613,15 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseDoWhileStatem startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); - failIfFalse(statement); + failIfFalse(statement, "Expected a statement following 'do'"); int endLine = tokenLine(); JSTokenLocation location(tokenLocation()); - consumeOrFail(WHILE); - consumeOrFail(OPENPAREN); + 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); - consumeOrFail(CLOSEPAREN); + 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); @@ -252,82 +634,452 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseWhileStatemen JSTokenLocation location(tokenLocation()); int startLine = tokenLine(); next(); - consumeOrFail(OPENPAREN); + + handleProductionOrFail(OPENPAREN, "(", "start", "while loop condition"); + semanticFailIfTrue(match(CLOSEPAREN), "Must provide an expression as a while loop condition"); TreeExpression expr = parseExpression(context); - failIfFalse(expr); + failIfFalse(expr, "Unable to parse while loop condition"); int endLine = tokenLine(); - consumeOrFail(CLOSEPAREN); + handleProductionOrFail(CLOSEPAREN, ")", "end", "while loop condition"); + const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); - failIfFalse(statement); + 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>::parseVarDeclarationList(TreeBuilder& context, int& declarations, const Identifier*& lastIdent, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd) +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) { - TreeExpression varDecls = 0; + 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 { - declarations++; + lastIdent = 0; + lastPattern = TreeDestructuringPattern(0); JSTokenLocation location(tokenLocation()); next(); - matchOrFail(IDENT); - - JSTextPosition varStart = tokenStartPosition(); - identStart = varStart; - const Identifier* name = m_token.m_data.ident; - lastIdent = name; - next(); - bool hasInitializer = match(EQUAL); - failIfFalseIfStrictWithNameAndMessage(declareVariable(name), "Cannot declare a variable named", name->impl(), "in strict mode."); - context.addVar(name, (hasInitializer || (!m_allowsIn && match(INTOKEN))) ? DeclarationStacks::HasInitializer : 0); - if (hasInitializer) { - JSTextPosition varDivot = tokenStartPosition() + 1; - initStart = tokenStartPosition(); - next(TreeBuilder::DontBuildStrings); // consume '=' - TreeExpression initializer = parseAssignmentExpression(context); - initEnd = lastTokenEndPosition(); - lastInitializer = initializer; - failIfFalse(initializer); - - TreeExpression node = context.createAssignResolve(location, *name, initializer, varStart, varDivot, lastTokenEndPosition()); - if (!varDecls) - varDecls = node; - else - varDecls = context.combineCommaNodes(location, varDecls, node); + TreeExpression node = 0; + declarations++; + bool hasInitializer = false; + if (matchSpecIdentifier()) { + 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(), "'"); + ASSERT(declarationType == DeclarationType::VarDeclaration); + internalFailWithMessage(false, "Cannot declare a var variable that shadows a let/const/class variable: '", name->impl(), "'"); + } + } + 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 '=' + propagateError(); + 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 (node) { + 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)); - return varDecls; + if (lastIdent) + lastPattern = context.createBindingLocation(lastIdentToken.m_location, *lastIdent, lastIdentToken.m_startPosition, lastIdentToken.m_endPosition, assignmentContext); + + return head; } template <typename LexerType> -template <class TreeBuilder> TreeConstDeclList Parser<LexerType>::parseConstDeclarationList(TreeBuilder& context) +bool Parser<LexerType>::declareRestOrNormalParameter(const Identifier& name, const Identifier** duplicateIdentifier) { - failIfTrue(strictMode()); - TreeConstDeclList constDecls = 0; - TreeConstDeclList tail = 0; - do { - JSTokenLocation location(tokenLocation()); + 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_parserState.lastFunctionName && name == *m_parserState.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; + } + + return true; +} + +template <typename LexerType> +template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createBindingPattern(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier& name, JSToken token, AssignmentContext bindingContext, const Identifier** duplicateIdentifier) +{ + ASSERT(!name.isNull()); + + ASSERT(name.impl()->isAtomic() || name.impl()->isSymbol()); + + switch (kind) { + case DestructuringKind::DestructureToVariables: { + DeclarationResultMask declarationResult = declareVariable(&name); + failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare a variable named '", name.impl(), "' in strict mode"); + if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) + internalFailWithMessage(false, "Cannot declare a var variable that shadows a let/const/class variable: '", name.impl(), "'"); + break; + } + + case DestructuringKind::DestructureToLet: + case DestructuringKind::DestructureToConst: + case DestructuringKind::DestructureToCatchParameters: { + DeclarationResultMask declarationResult = declareVariable(&name, kind == DestructuringKind::DestructureToConst ? DeclarationType::ConstDeclaration : DeclarationType::LetDeclaration); + 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(), "'"); + } + break; + } + + case DestructuringKind::DestructureToParameters: { + declareRestOrNormalParameter(name, duplicateIdentifier); + propagateError(); + break; + } + + case DestructuringKind::DestructureToExpressions: { + break; + } + } + + 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> NEVER_INLINE TreeDestructuringPattern Parser<LexerType>::createAssignmentElement(TreeBuilder& context, TreeExpression& assignmentTarget, const JSTextPosition& startPosition, const JSTextPosition& endPosition) +{ + return context.createAssignmentElement(assignmentTarget, startPosition, endPosition); +} + +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, DestructuringKind::DestructureToExpressions, ExportType::NotExported, nullptr, nullptr, bindingContext); +} + +template <typename LexerType> +template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseBindingOrAssignmentElement(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth) +{ + if (kind == DestructuringKind::DestructureToExpressions) + return parseAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth); + return parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth); +} + +template <typename LexerType> +template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseAssignmentElement(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth) +{ + TreeDestructuringPattern assignmentTarget = 0; + + if (match(OPENBRACE) || match(OPENBRACKET)) { + SavePoint savePoint = createSavePoint(); + assignmentTarget = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth); + if (assignmentTarget && !match(DOT) && !match(OPENBRACKET) && !match(OPENPAREN) && !match(TEMPLATE)) + return assignmentTarget; + restoreSavePoint(savePoint); + } + + JSTextPosition startPosition = tokenStartPosition(); + auto element = parseMemberExpression(context); + + semanticFailIfFalse(element && context.isAssignmentLocation(element), "Invalid destructuring assignment target"); + + if (strictMode() && m_parserState.lastIdentifier && context.isResolve(element)) { + bool isEvalOrArguments = m_vm->propertyNames->eval == *m_parserState.lastIdentifier || m_vm->propertyNames->arguments == *m_parserState.lastIdentifier; + failIfTrueIfStrict(isEvalOrArguments, "Cannot modify '", m_parserState.lastIdentifier->impl(), "' in strict mode"); + } + + return createAssignmentElement(context, element, startPosition, lastTokenEndPosition()); +} + +static const char* destructuringKindToVariableKindName(DestructuringKind kind) +{ + switch (kind) { + case DestructuringKind::DestructureToLet: + case DestructuringKind::DestructureToConst: + return "lexical variable name"; + case DestructuringKind::DestructureToVariables: + return "variable name"; + case DestructuringKind::DestructureToParameters: + return "parameter name"; + case DestructuringKind::DestructureToCatchParameters: + return "catch parameter name"; + case DestructuringKind::DestructureToExpressions: + return "expression name"; + } + RELEASE_ASSERT_NOT_REACHED(); + return "invalid"; +} + +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_parserState.nonLHSCount; + TreeDestructuringPattern pattern; + switch (m_token.m_type) { + case OPENBRACKET: { + JSTextPosition divotStart = tokenStartPosition(); + auto arrayPattern = context.createArrayPattern(m_token.m_location); next(); - matchOrFail(IDENT); - const Identifier* name = m_token.m_data.ident; + + 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 = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); + if (kind == DestructuringKind::DestructureToExpressions && !innerPattern) + return 0; + failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); + + failIfTrue(kind != DestructuringKind::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 = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); + if (kind == DestructuringKind::DestructureToExpressions && !innerPattern) + return 0; + failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); + TreeExpression defaultValue = parseDefaultValueForDestructuringPattern(context); + context.appendArrayPatternEntry(arrayPattern, location, innerPattern, defaultValue); + } while (consume(COMMA)); + + 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(); - bool hasInitializer = match(EQUAL); - declareVariable(name); - context.addVar(name, DeclarationStacks::IsConstant | (hasInitializer ? DeclarationStacks::HasInitializer : 0)); - - TreeExpression initializer = 0; - if (hasInitializer) { - next(TreeBuilder::DontBuildStrings); // consume '=' - initializer = parseAssignmentExpression(context); - } - tail = context.appendConstDecl(location, tail, name, initializer); - if (!constDecls) - constDecls = tail; - } while (match(COMMA)); - return constDecls; + + if (hasDestructuringPattern) + *hasDestructuringPattern = true; + + do { + bool wasString = false; + + if (match(CLOSEBRACE)) + break; + + const Identifier* propertyName = nullptr; + TreeExpression propertyExpression = 0; + TreeDestructuringPattern innerPattern = 0; + JSTokenLocation location = m_token.m_location; + if (matchSpecIdentifier()) { + failIfTrue(match(LET) && (kind == DestructuringKind::DestructureToLet || kind == DestructuringKind::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 = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); + else { + if (kind == DestructuringKind::DestructureToExpressions) { + bool isEvalOrArguments = m_vm->propertyNames->eval == *propertyName || m_vm->propertyNames->arguments == *propertyName; + if (isEvalOrArguments && strictMode()) + reclassifyExpressionError(ErrorIndicatesPattern, ErrorIndicatesNothing); + failIfTrueIfStrict(isEvalOrArguments, "Cannot modify '", propertyName->impl(), "' in strict mode"); + } + innerPattern = createBindingPattern(context, kind, exportType, *propertyName, 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; + case OPENBRACKET: + next(); + propertyExpression = parseAssignmentExpression(context); + failIfFalse(propertyExpression, "Cannot parse computed property name"); + matchOrFail(CLOSEBRACKET, "Expected ']' to end end a computed property name"); + break; + default: + if (m_token.m_type != RESERVED && m_token.m_type != RESERVED_IF_STRICT && !(m_token.m_type & KeywordTokenFlag)) { + if (kind == DestructuringKind::DestructureToExpressions) + return 0; + failWithMessage("Expected a property name"); + } + propertyName = m_token.m_data.ident; + break; + } + next(); + if (!consume(COLON)) { + if (kind == DestructuringKind::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 = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); + } + if (kind == DestructuringKind::DestructureToExpressions && !innerPattern) + return 0; + failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); + TreeExpression defaultValue = parseDefaultValueForDestructuringPattern(context); + if (propertyExpression) + context.appendObjectPatternEntry(objectPattern, location, propertyExpression, innerPattern, defaultValue); + else { + ASSERT(propertyName); + context.appendObjectPatternEntry(objectPattern, location, wasString, *propertyName, innerPattern, defaultValue); + } + } while (consume(COMMA)); + + if (kind == DestructuringKind::DestructureToExpressions && !match(CLOSEBRACE)) + return 0; + consumeOrFail(CLOSEBRACE, "Expected either a closing '}' or an ',' after a property destructuring pattern"); + pattern = objectPattern; + break; + } + + default: { + if (!matchSpecIdentifier()) { + if (kind == DestructuringKind::DestructureToExpressions) + return 0; + semanticFailureDueToKeyword(destructuringKindToVariableKindName(kind)); + failWithMessage("Expected a parameter pattern or a ')' in parameter list"); + } + failIfTrue(match(LET) && (kind == DestructuringKind::DestructureToLet || kind == DestructuringKind::DestructureToConst), "Can't use 'let' as an identifier name for a LexicalDeclaration"); + pattern = createBindingPattern(context, kind, exportType, *m_token.m_data.ident, m_token, bindingContext, duplicateIdentifier); + next(); + break; + } + } + m_parserState.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> @@ -337,60 +1089,124 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement( JSTokenLocation location(tokenLocation()); int startLine = tokenLine(); next(); - consumeOrFail(OPENPAREN); - int nonLHSCount = m_nonLHSCount; + handleProductionOrFail(OPENPAREN, "(", "start", "for-loop header"); + int nonLHSCount = m_parserState.nonLHSCount; int declarations = 0; JSTextPosition declsStart; JSTextPosition declsEnd; TreeExpression decls = 0; - if (match(VAR)) { + 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 IDENT in expression) statement - for (var IDENT = expression in expression) statement - for (var varDeclarationList; expressionOpt; expressionOpt) + for (var/let/const IDENT in/of expression) statement + for (var/let/const varDeclarationList; expressionOpt; expressionOpt) */ - const Identifier* forInTarget = 0; + 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; - decls = parseVarDeclarationList(context, declarations, forInTarget, forInInitializer, declsStart, initStart, 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; - failIfTrue(hasError()); + propagateError(); // Remainder of a standard for loop is handled identically if (match(SEMICOLON)) goto standardForLoop; - failIfFalse(declarations == 1); - + 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(); - consumeOrFail(INTOKEN); - + 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); + failIfFalse(expr, "Expected expression to enumerate"); JSTextPosition exprEnd = lastTokenEndPosition(); int endLine = tokenLine(); - consumeOrFail(CLOSEPAREN); + + handleProductionOrFail(CLOSEPAREN, ")", "end", (isOfEnumeration ? "for-of header" : "for-in header")); const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); - failIfFalse(statement); - - return context.createForInLoop(location, forInTarget, forInInitializer, expr, statement, declsStart, inLocation, exprEnd, initStart, initEnd, startLine, endLine); + 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); + failIfFalse(decls, "Cannot parse for loop declarations"); } if (match(SEMICOLON)) { @@ -398,43 +1214,70 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement( // 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); + failIfFalse(condition, "Cannot parse for loop condition expression"); } - consumeOrFail(SEMICOLON); + consumeOrFail(SEMICOLON, "Expected a ';' after the for loop condition expression"); TreeExpression increment = 0; if (!match(CLOSEPAREN)) { increment = parseExpression(context); - failIfFalse(increment); + failIfFalse(increment, "Cannot parse for loop iteration expression"); } int endLine = tokenLine(); - consumeOrFail(CLOSEPAREN); + handleProductionOrFail(CLOSEPAREN, ")", "end", "for-loop header"); const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); - failIfFalse(statement); - return context.createForLoop(location, decls, condition, increment, statement, startLine, endLine); + 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 loop - failIfFalse(nonLHSCount == m_nonLHSCount); - consumeOrFail(INTOKEN); + // For-in and For-of loop +enumerationLoop: + failIfFalse(nonLHSCount == m_parserState.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); + failIfFalse(expr, "Cannot parse subject for-", isOfEnumeration ? "of" : "in", " statement"); JSTextPosition exprEnd = lastTokenEndPosition(); int endLine = tokenLine(); - consumeOrFail(CLOSEPAREN); + + handleProductionOrFail(CLOSEPAREN, ")", "end", (isOfEnumeration ? "for-of header" : "for-in header")); const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); - failIfFalse(statement); - - return context.createForInLoop(location, decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine); + 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> @@ -447,15 +1290,15 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseBreakStatemen next(); if (autoSemiColon()) { - failIfFalseWithMessage(breakIsValid(), "'break' is only valid inside a switch or loop statement"); - return context.createBreakStatement(location, start, end); + semanticFailIfFalse(breakIsValid(), "'break' is only valid inside a switch or loop statement"); + return context.createBreakStatement(location, &m_vm->propertyNames->nullIdentifier, start, end); } - matchOrFail(IDENT); + failIfFalse(matchSpecIdentifier(), "Expected an identifier as the target for a break statement"); const Identifier* ident = m_token.m_data.ident; - failIfFalseWithNameAndMessage(getLabel(ident), "Label", ident->impl(), "is not defined"); + semanticFailIfFalse(getLabel(ident), "Cannot use the undeclared label '", ident->impl(), "'"); end = tokenEndPosition(); next(); - failIfFalse(autoSemiColon()); + failIfFalse(autoSemiColon(), "Expected a ';' following a targeted break statement"); return context.createBreakStatement(location, ident, start, end); } @@ -469,17 +1312,17 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseContinueState next(); if (autoSemiColon()) { - failIfFalseWithMessage(continueIsValid(), "'continue' is only valid inside a loop statement"); - return context.createContinueStatement(location, start, end); + semanticFailIfFalse(continueIsValid(), "'continue' is only valid inside a loop statement"); + return context.createContinueStatement(location, &m_vm->propertyNames->nullIdentifier, start, end); } - matchOrFail(IDENT); + failIfFalse(matchSpecIdentifier(), "Expected an identifier as the target for a continue statement"); const Identifier* ident = m_token.m_data.ident; ScopeLabelInfo* label = getLabel(ident); - failIfFalseWithNameAndMessage(label, "Label", ident->impl(), "is not defined"); - failIfFalseWithMessage(label->m_isLoop, "'continue' is only valid inside a loop statement"); + 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()); + failIfFalse(autoSemiColon(), "Expected a ';' following a targeted continue statement"); return context.createContinueStatement(location, ident, start, end); } @@ -488,11 +1331,11 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseReturnStateme { ASSERT(match(RETURN)); JSTokenLocation location(tokenLocation()); - failIfFalse(currentScope()->isFunction()); + 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 an expression + // 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)) @@ -501,11 +1344,12 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseReturnStateme if (autoSemiColon()) return context.createReturnStatement(location, 0, start, end); TreeExpression expr = parseExpression(context); - failIfFalse(expr); + failIfFalse(expr, "Cannot parse the return expression"); end = lastTokenEndPosition(); if (match(SEMICOLON)) end = tokenEndPosition(); - failIfFalse(autoSemiColon()); + if (!autoSemiColon()) + failWithMessage("Expected a ';' following a return statement"); return context.createReturnStatement(location, expr, start, end); } @@ -516,13 +1360,13 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseThrowStatemen JSTokenLocation location(tokenLocation()); JSTextPosition start = tokenStartPosition(); next(); - - failIfTrue(autoSemiColon()); + failIfTrue(match(SEMICOLON), "Expected expression after 'throw'"); + semanticFailIfTrue(autoSemiColon(), "Cannot have a newline after 'throw'"); TreeExpression expr = parseExpression(context); - failIfFalse(expr); + failIfFalse(expr, "Cannot parse expression for throw statement"); JSTextPosition end = lastTokenEndPosition(); - failIfFalse(autoSemiColon()); + failIfFalse(autoSemiColon(), "Expected a ';' after a throw statement"); return context.createThrowStatement(location, expr, start, end); } @@ -532,20 +1376,21 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseWithStatement { ASSERT(match(WITH)); JSTokenLocation location(tokenLocation()); - failIfTrueWithMessage(strictMode(), "'with' statements are not valid in strict mode"); + semanticFailIfTrue(strictMode(), "'with' statements are not valid in strict mode"); currentScope()->setNeedsFullActivation(); int startLine = tokenLine(); next(); - consumeOrFail(OPENPAREN); + + handleProductionOrFail(OPENPAREN, "(", "start", "subject of a 'with' statement"); int start = tokenStart(); TreeExpression expr = parseExpression(context); - failIfFalse(expr); + failIfFalse(expr, "Cannot parse 'with' subject expression"); JSTextPosition end = lastTokenEndPosition(); int endLine = tokenLine(); - consumeOrFail(CLOSEPAREN); + handleProductionOrFail(CLOSEPAREN, ")", "start", "subject of a 'with' statement"); const Identifier* unused = 0; TreeStatement statement = parseStatement(context, unused); - failIfFalse(statement); + failIfFalse(statement, "A 'with' statement must have a body"); return context.createWithStatement(location, expr, statement, start, end, startLine, endLine); } @@ -557,26 +1402,31 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseSwitchStateme JSTokenLocation location(tokenLocation()); int startLine = tokenLine(); next(); - consumeOrFail(OPENPAREN); + handleProductionOrFail(OPENPAREN, "(", "start", "subject of a 'switch'"); TreeExpression expr = parseExpression(context); - failIfFalse(expr); + failIfFalse(expr, "Cannot parse switch subject expression"); int endLine = tokenLine(); - consumeOrFail(CLOSEPAREN); - consumeOrFail(OPENBRACE); + + 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); - failIfTrue(hasError()); + propagateError(); TreeClause defaultClause = parseSwitchDefaultClause(context); - failIfTrue(hasError()); + propagateError(); TreeClauseList secondClauses = parseSwitchClauses(context); - failIfTrue(hasError()); + propagateError(); endSwitch(); - consumeOrFail(CLOSEBRACE); - - return context.createSwitchStatement(location, expr, firstClauses, defaultClause, secondClauses, startLine, endLine); + 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> @@ -584,24 +1434,28 @@ template <class TreeBuilder> TreeClauseList Parser<LexerType>::parseSwitchClause { if (!match(CASE)) return 0; + unsigned startOffset = tokenStart(); next(); TreeExpression condition = parseExpression(context); - failIfFalse(condition); - consumeOrFail(COLON); - TreeSourceElements statements = parseSourceElements<DontCheckForStrictMode>(context); - failIfFalse(statements); + 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); - consumeOrFail(COLON); - TreeSourceElements statements = parseSourceElements<DontCheckForStrictMode>(context); - failIfFalse(statements); + 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; @@ -612,11 +1466,14 @@ template <class TreeBuilder> TreeClause Parser<LexerType>::parseSwitchDefaultCla { if (!match(DEFAULT)) return 0; + unsigned startOffset = tokenStart(); next(); - consumeOrFail(COLON); - TreeSourceElements statements = parseSourceElements<DontCheckForStrictMode>(context); - failIfFalse(statements); - return context.createClause(0, statements); + 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> @@ -625,42 +1482,51 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseTryStatement( ASSERT(match(TRY)); JSTokenLocation location(tokenLocation()); TreeStatement tryBlock = 0; - const Identifier* ident = &m_vm->propertyNames->nullIdentifier; + TreeDestructuringPattern catchPattern = 0; TreeStatement catchBlock = 0; TreeStatement finallyBlock = 0; int firstLine = tokenLine(); next(); - matchOrFail(OPENBRACE); + matchOrFail(OPENBRACE, "Expected a block statement as body of a try statement"); tryBlock = parseBlockStatement(context); - failIfFalse(tryBlock); + failIfFalse(tryBlock, "Cannot parse the body of try block"); int lastLine = m_lastTokenEndPosition.line; - + VariableEnvironment catchEnvironment; if (match(CATCH)) { - currentScope()->setNeedsFullActivation(); - next(); - consumeOrFail(OPENPAREN); - matchOrFail(IDENT); - ident = m_token.m_data.ident; next(); + + handleProductionOrFail(OPENPAREN, "(", "start", "'catch' target"); AutoPopScopeRef catchScope(this, pushScope()); - failIfFalseIfStrictWithNameAndMessage(declareVariable(ident), "Cannot declare a variable named", ident->impl(), "in strict mode"); - catchScope->preventNewDecls(); - consumeOrFail(CLOSEPAREN); - matchOrFail(OPENBRACE); + catchScope->setIsLexicalScope(); + catchScope->preventVarDeclarations(); + const Identifier* ident = nullptr; + if (matchSpecIdentifier()) { + ident = m_token.m_data.ident; + catchPattern = context.createBindingLocation(m_token.m_location, *ident, m_token.m_startPosition, m_token.m_endPosition, AssignmentContext::DeclarationStatement); + next(); + failIfTrueIfStrict(catchScope->declareLexicalVariable(ident, false) & DeclarationResult::InvalidStrictMode, "Cannot declare a catch variable named '", ident->impl(), "' in strict mode"); + } else { + catchPattern = parseDestructuringPattern(context, DestructuringKind::DestructureToCatchParameters, ExportType::NotExported); + failIfFalse(catchPattern, "Cannot parse this destructuring pattern"); + } + handleProductionOrFail(CLOSEPAREN, ")", "end", "'catch' target"); + matchOrFail(OPENBRACE, "Expected exception handler to be a block statement"); catchBlock = parseBlockStatement(context); - failIfFalseWithMessage(catchBlock, "'try' must have a catch or finally block"); - failIfFalse(popScope(catchScope, TreeBuilder::NeedsFreeVariableInfo)); + failIfFalse(catchBlock, "Unable to parse 'catch' block"); + catchEnvironment = catchScope->finalizeLexicalEnvironment(); + RELEASE_ASSERT(!ident || (catchEnvironment.size() == 1 && catchEnvironment.contains(ident->impl()))); + popScope(catchScope, TreeBuilder::NeedsFreeVariableInfo); } if (match(FINALLY)) { next(); - matchOrFail(OPENBRACE); + matchOrFail(OPENBRACE, "Expected block statement for finally body"); finallyBlock = parseBlockStatement(context); - failIfFalse(finallyBlock); + failIfFalse(finallyBlock, "Cannot parse finally body"); } - failIfFalse(catchBlock || finallyBlock); - return context.createTryStatement(location, tryBlock, ident, catchBlock, finallyBlock, firstLine, lastLine); + failIfFalse(catchBlock || finallyBlock, "Try statements must have at least a catch or finally block"); + return context.createTryStatement(location, tryBlock, catchPattern, catchBlock, finallyBlock, firstLine, lastLine, catchEnvironment); } template <typename LexerType> @@ -673,7 +1539,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseDebuggerState next(); if (match(SEMICOLON)) startLine = tokenLine(); - failIfFalse(autoSemiColon()); + failIfFalse(autoSemiColon(), "Debugger keyword must be followed by a ';'"); return context.createDebugger(location, startLine, endLine); } @@ -681,18 +1547,44 @@ 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(); - return context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line); + 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<DontCheckForStrictMode>(context); - failIfFalse(subtree); - matchOrFail(CLOSEBRACE); + 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(); - return context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line); + 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> @@ -703,45 +1595,63 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre directive = 0; int nonTrivialExpressionCount = 0; failIfStackOverflow(); + TreeStatement result = 0; + bool shouldSetEndOffset = true; + switch (m_token.m_type) { case OPENBRACE: - return parseBlockStatement(context); + result = parseBlockStatement(context); + shouldSetEndOffset = false; + break; case VAR: - return parseVarDeclaration(context); - case CONSTTOKEN: - return parseConstDeclaration(context); + result = parseVariableDeclaration(context, DeclarationType::VarDeclaration); + break; case FUNCTION: - failIfFalseIfStrictWithMessage(m_statementDepth == 1, "Functions cannot be declared in a nested block in strict mode"); - return parseFunctionDeclaration(context); + 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(); - return context.createEmptyStatement(location); + result = context.createEmptyStatement(location); + break; } case IF: - return parseIfStatement(context); + result = parseIfStatement(context); + break; case DO: - return parseDoWhileStatement(context); + result = parseDoWhileStatement(context); + break; case WHILE: - return parseWhileStatement(context); + result = parseWhileStatement(context); + break; case FOR: - return parseForStatement(context); + result = parseForStatement(context); + break; case CONTINUE: - return parseContinueStatement(context); + result = parseContinueStatement(context); + break; case BREAK: - return parseBreakStatement(context); + result = parseBreakStatement(context); + break; case RETURN: - return parseReturnStatement(context); + result = parseReturnStatement(context); + break; case WITH: - return parseWithStatement(context); + result = parseWithStatement(context); + break; case SWITCH: - return parseSwitchStatement(context); + result = parseSwitchStatement(context); + break; case THROW: - return parseThrowStatement(context); + result = parseThrowStatement(context); + break; case TRY: - return parseTryStatement(context); + result = parseTryStatement(context); + break; case DEBUGGER: - return parseDebuggerStatement(context); + result = parseDebuggerStatement(context); + break; case EOFTOK: case CASE: case CLOSEBRACE: @@ -749,163 +1659,681 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre // These tokens imply the end of a set of source elements return 0; case IDENT: - return parseExpressionOrLabelStatement(context); + case YIELD: + 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; + nonTrivialExpressionCount = m_parserState.nonTrivialExpressionCount; + FALLTHROUGH; default: TreeStatement exprStatement = parseExpressionStatement(context); - if (directive && nonTrivialExpressionCount != m_nonTrivialExpressionCount) + if (directive && nonTrivialExpressionCount != m_parserState.nonTrivialExpressionCount) directive = 0; - return exprStatement; + result = exprStatement; + break; } + + if (result && shouldSetEndOffset) + context.setEndOffset(result, m_lastTokenEndPosition.offset); + return result; } template <typename LexerType> -template <class TreeBuilder> TreeFormalParameterList Parser<LexerType>::parseFormalParameters(TreeBuilder& context) +template <class TreeBuilder> bool Parser<LexerType>::parseFormalParameters(TreeBuilder& context, TreeFormalParameterList list, unsigned& parameterCount) { - matchOrFail(IDENT); - failIfFalseIfStrictWithNameAndMessage(declareParameter(m_token.m_data.ident), "Cannot declare a parameter named", m_token.m_data.ident->impl(), " in strict mode"); - TreeFormalParameterList list = context.createFormalParameterList(*m_token.m_data.ident); - TreeFormalParameterList tail = list; - next(); - while (match(COMMA)) { - next(); - matchOrFail(IDENT); - const Identifier* ident = m_token.m_data.ident; - failIfFalseIfStrictWithNameAndMessage(declareParameter(ident), "Cannot declare a parameter named", ident->impl(), "in strict mode"); - next(); - tail = context.createFormalParameterList(tail, *ident); +#define failIfDuplicateIfViolation() \ + 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");\ + semanticFailIfTrue(isRestParameter, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in function with a rest parameter");\ } - return list; + + bool hasDestructuringPattern = false; + bool isRestParameter = false; + const Identifier* duplicateParameter = nullptr; + do { + TreeDestructuringPattern parameter = 0; + TreeExpression defaultValue = 0; + + if (match(DOTDOTDOT)) { + next(); + failIfFalse(matchSpecIdentifier(), "Rest parameter '...' should be followed by a variable identifier"); + declareRestOrNormalParameter(*m_token.m_data.ident, &duplicateParameter); + propagateError(); + JSTextPosition identifierStart = tokenStartPosition(); + JSTextPosition identifierEnd = tokenEndPosition(); + parameter = context.createRestParameter(*m_token.m_data.ident, parameterCount, identifierStart, identifierEnd); + next(); + failIfTrue(match(COMMA), "Rest parameter should be the last parameter in a function declaration"); // Let's have a good error message for this common case. + isRestParameter = true; + } else + parameter = parseDestructuringPattern(context, DestructuringKind::DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern); + failIfFalse(parameter, "Cannot parse parameter pattern"); + if (!isRestParameter) + defaultValue = parseDefaultValueForDestructuringPattern(context); + propagateError(); + failIfDuplicateIfViolation(); + context.appendParameter(list, parameter, defaultValue); + if (!isRestParameter) + parameterCount++; + } while (!isRestParameter && consume(COMMA)); + + return true; +#undef failIfDuplicateIfViolation } template <typename LexerType> -template <class TreeBuilder> TreeFunctionBody Parser<LexerType>::parseFunctionBody(TreeBuilder& context) +template <class TreeBuilder> TreeFunctionBody Parser<LexerType>::parseFunctionBody( + TreeBuilder& context, const JSTokenLocation& startLocation, int startColumn, int functionKeywordStart, int functionNameStart, int parametersStart, + ConstructorKind constructorKind, SuperBinding superBinding, FunctionBodyType bodyType, unsigned parameterCount, SourceParseMode parseMode) { - JSTokenLocation startLocation(tokenLocation()); - unsigned startColumn = tokenColumn(); - next(); + bool isArrowFunctionBodyExpression = bodyType == ArrowFunctionBodyExpression; + if (!isArrowFunctionBodyExpression) { + next(); + if (match(CLOSEBRACE)) { + unsigned endColumn = tokenColumn(); + return context.createFunctionMetadata(startLocation, tokenLocation(), startColumn, endColumn, functionKeywordStart, functionNameStart, parametersStart, strictMode(), constructorKind, superBinding, parameterCount, parseMode, isArrowFunctionBodyExpression); + } + } - if (match(CLOSEBRACE)) - return context.createFunctionBody(startLocation, tokenLocation(), startColumn, strictMode()); DepthManager statementDepth(&m_statementDepth); m_statementDepth = 0; - typename TreeBuilder::FunctionBodyBuilder bodyBuilder(const_cast<VM*>(m_vm), m_lexer.get()); - failIfFalse(parseSourceElements<CheckForStrictMode>(bodyBuilder)); - return context.createFunctionBody(startLocation, tokenLocation(), startColumn, strictMode()); + 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, superBinding, parameterCount, parseMode, isArrowFunctionBodyExpression); +} + +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::GeneratorBodyMode: + return "generator"; + case SourceParseMode::GeneratorWrapperFunctionMode: + return "generator function"; + 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; + SetForScope<FunctionParsePhase> functionParsePhasePoisoner(m_parserState.functionParsePhase, FunctionParsePhase::Parameters); + + 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, DestructuringKind::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, DestructuringKind::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 <FunctionRequirements requirements, bool nameIsInContainingScope, class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuilder& context, const Identifier*& name, TreeFormalParameterList& parameters, TreeFunctionBody& body, unsigned& openBraceOffset, unsigned& closeBraceOffset, int& bodyStartLine, unsigned& bodyStartColumn) +template <class TreeBuilder> typename TreeBuilder::FormalParameterList Parser<LexerType>::createGeneratorParameters(TreeBuilder& context) { + auto parameters = context.createFormalParameterList(); + + JSTokenLocation location(tokenLocation()); + JSTextPosition position = tokenStartPosition(); + + // @generator + declareParameter(&m_vm->propertyNames->generatorPrivateName); + auto generator = context.createBindingLocation(location, m_vm->propertyNames->generatorPrivateName, position, position, AssignmentContext::DeclarationStatement); + context.appendParameter(parameters, generator, 0); + + // @generatorState + declareParameter(&m_vm->propertyNames->generatorStatePrivateName); + auto generatorState = context.createBindingLocation(location, m_vm->propertyNames->generatorStatePrivateName, position, position, AssignmentContext::DeclarationStatement); + context.appendParameter(parameters, generatorState, 0); + + // @generatorValue + declareParameter(&m_vm->propertyNames->generatorValuePrivateName); + auto generatorValue = context.createBindingLocation(location, m_vm->propertyNames->generatorValuePrivateName, position, position, AssignmentContext::DeclarationStatement); + context.appendParameter(parameters, generatorValue, 0); + + // @generatorResumeMode + declareParameter(&m_vm->propertyNames->generatorResumeModePrivateName); + auto generatorResumeMode = context.createBindingLocation(location, m_vm->propertyNames->generatorResumeModePrivateName, position, position, AssignmentContext::DeclarationStatement); + context.appendParameter(parameters, generatorResumeMode, 0); + + return parameters; +} + +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, FunctionDefinitionType functionDefinitionType) +{ + RELEASE_ASSERT(isFunctionParseMode(mode)); + + bool upperScopeIsGenerator = currentScope()->isGenerator(); AutoPopScopeRef functionScope(this, pushScope()); - functionScope->setIsFunction(); - int functionStart = m_token.m_location.startOffset; - if (match(IDENT)) { - name = m_token.m_data.ident; + functionScope->setSourceParseMode(mode); + SetForScope<FunctionParsePhase> functionParsePhasePoisoner(m_parserState.functionParsePhase, FunctionParsePhase::Body); + int functionNameStart = m_token.m_location.startOffset; + const Identifier* lastFunctionName = m_parserState.lastFunctionName; + m_parserState.lastFunctionName = nullptr; + int parametersStart; + JSTokenLocation startLocation; + int startColumn; + FunctionBodyType functionBodyType; + + if (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(); - if (!nameIsInContainingScope) - failIfFalseIfStrict(functionScope->declareVariable(name)); - } else if (requirements == FunctionNeedsName) - return false; - consumeOrFail(OPENPAREN); - if (!match(CLOSEPAREN)) { - parameters = parseFormalParameters(context); - failIfFalse(parameters); - } - consumeOrFail(CLOSEPAREN); - matchOrFail(OPENBRACE); + functionBodyType = match(OPENBRACE) ? ArrowFunctionBodyBlock : ArrowFunctionBodyExpression; + } else { + // http://ecma-international.org/ecma-262/6.0/#sec-function-definitions + // FunctionExpression : + // function BindingIdentifieropt ( FormalParameters ) { FunctionBody } + // + // FunctionDeclaration[Yield, Default] : + // function BindingIdentifier[?Yield] ( FormalParameters ) { FunctionBody } + // [+Default] function ( FormalParameters ) { FunctionBody } + // + // GeneratorDeclaration[Yield, Default] : + // function * BindingIdentifier[?Yield] ( FormalParameters[Yield] ) { GeneratorBody } + // [+Default] function * ( FormalParameters[Yield] ) { GeneratorBody } + // + // GeneratorExpression : + // function * BindingIdentifier[Yield]opt ( FormalParameters[Yield] ) { GeneratorBody } + // + // The name of FunctionExpression can accept "yield" even in the context of generator. + if (functionDefinitionType == FunctionDefinitionType::Expression && mode == SourceParseMode::NormalFunctionMode) + upperScopeIsGenerator = false; + + if (matchSpecIdentifier(upperScopeIsGenerator)) { + functionInfo.name = m_token.m_data.ident; + m_parserState.lastFunctionName = functionInfo.name; + next(); + if (!nameIsInContainingScope) + failIfTrueIfStrict(functionScope->declareCallee(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. - openBraceOffset = m_token.m_data.offset; - bodyStartLine = tokenLine(); - bodyStartColumn = m_token.m_data.offset - m_token.m_data.lineStartOffset; - JSTokenLocation startLocation(tokenLocation()); + if (m_defaultConstructorKind != ConstructorKind::None) { + constructorKind = m_defaultConstructorKind; + expectedSuperBinding = m_defaultConstructorKind == ConstructorKind::Derived ? SuperBinding::Needed : SuperBinding::NotNeeded; + } + + functionBodyType = StandardFunctionBodyBlock; + } + + functionScope->setConstructorKind(constructorKind); + functionScope->setExpectedSuperBinding(expectedSuperBinding); + + 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(openBraceOffset) : 0) { + 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->closeBraceLine; - endLocation.startOffset = cachedInfo->closeBraceOffset; - endLocation.lineStartOffset = cachedInfo->closeBraceLineStartOffset; - ASSERT(endLocation.startOffset >= endLocation.lineStartOffset); + endLocation.line = cachedInfo->lastTockenLine; + endLocation.startOffset = cachedInfo->lastTockenStartOffset; + endLocation.lineStartOffset = cachedInfo->lastTockenLineStartOffset; - body = context.createFunctionBody(startLocation, endLocation, bodyStartColumn, cachedInfo->strictMode); + 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, expectedSuperBinding, cachedInfo->parameterCount, mode, functionBodyType == ArrowFunctionBodyExpression); functionScope->restoreFromSourceProviderCache(cachedInfo); - failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo)); + popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo); - closeBraceOffset = cachedInfo->closeBraceOffset; - - context.setFunctionStart(body, functionStart); - m_token = cachedInfo->closeBraceToken(); + 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 (mode == SourceParseMode::ArrowFunctionMode) + functionBodyType = cachedInfo->isBodyArrowExpression ? ArrowFunctionBodyExpression : ArrowFunctionBodyBlock; + else + functionBodyType = StandardFunctionBodyBlock; - next(); + 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; } - body = parseFunctionBody(context); - failIfFalse(body); - if (functionScope->strictMode() && name) { - failIfTrueWithNameAndMessage(m_vm->propertyNames->arguments == *name, "Function name", name->impl(), "is not valid in strict mode"); - failIfTrueWithNameAndMessage(m_vm->propertyNames->eval == *name, "Function name", name->impl(), "is not valid in strict mode"); - } - closeBraceOffset = m_token.m_data.offset; - unsigned closeBraceLine = m_token.m_data.line; - unsigned closeBraceLineStartOffset = m_token.m_data.lineStartOffset; + m_parserState.lastFunctionName = lastFunctionName; + ParserState oldState = internalSaveParserState(); + + auto performParsingFunctionBody = [&] { + return parseFunctionBody(context, startLocation, startColumn, functionKeywordStart, functionNameStart, parametersStart, constructorKind, expectedSuperBinding, functionBodyType, functionInfo.parameterCount, mode); + }; + + if (mode == SourceParseMode::GeneratorWrapperFunctionMode) { + AutoPopScopeRef generatorBodyScope(this, pushScope()); + generatorBodyScope->setSourceParseMode(SourceParseMode::GeneratorBodyMode); + functionInfo.body = performParsingFunctionBody(); + + // When a generator has a "use strict" directive, a generator function wrapping it should be strict mode. + if (generatorBodyScope->strictMode()) + functionScope->setStrictMode(); + + semanticFailIfTrue(generatorBodyScope->hasDirectSuper(), "Cannot call super() outside of a class constructor"); + if (generatorBodyScope->needsSuperBinding()) + semanticFailIfTrue(expectedSuperBinding == SuperBinding::NotNeeded, "super can only be used in a method of a derived class"); + + popScope(generatorBodyScope, TreeBuilder::NeedsFreeVariableInfo); + } else + functionInfo.body = performParsingFunctionBody(); + + restoreParserState(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 || mode == SourceParseMode::GeneratorBodyMode || mode == SourceParseMode::GeneratorWrapperFunctionMode); + 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"); + } + // It unncecessary to check of using super during reparsing one more time. Also it can lead to syntax error + // in case of arrow function becuase during reparsing we don't know that parse arrow function + // inside of the constructor or method + if (!m_lexer->isReparsingFunction()) { + if (functionScope->hasDirectSuper()) { + ConstructorKind functionConstructorKind = functionBodyType == StandardFunctionBodyBlock + ? constructorKind + : closestParentNonArrowFunctionNonLexicalScope()->constructorKind(); + semanticFailIfTrue(functionConstructorKind == ConstructorKind::None, "Cannot call super() outside of a class constructor"); + semanticFailIfTrue(functionConstructorKind != ConstructorKind::Derived, "Cannot call super() in a base class constructor"); + } + if (functionScope->needsSuperBinding()) { + SuperBinding functionSuperBinding = functionBodyType == StandardFunctionBodyBlock + ? expectedSuperBinding + : closestParentNonArrowFunctionNonLexicalScope()->expectedSuperBinding(); + semanticFailIfTrue(functionSuperBinding == 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; - OwnPtr<SourceProviderCacheItem> newInfo; - int functionLength = closeBraceOffset - openBraceOffset; + // For arrow function is 8 = x=>x + 4 symbols; + // For ordinary function is 16 = function(){} + 4 symbols + const int minimumFunctionLengthToCache = functionBodyType == StandardFunctionBodyBlock ? 16 : 8; + std::unique_ptr<SourceProviderCacheItem> newInfo; + int functionLength = functionInfo.endOffset - functionInfo.startOffset; if (TreeBuilder::CanUseFunctionCache && m_functionCache && functionLength > minimumFunctionLengthToCache) { SourceProviderCacheItemCreationParameters parameters; - parameters.functionStart = functionStart; - parameters.closeBraceLine = closeBraceLine; - parameters.closeBraceOffset = closeBraceOffset; - parameters.closeBraceLineStartOffset = closeBraceLineStartOffset; + 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); - } - context.setFunctionStart(body, functionStart); - failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo)); - matchOrFail(CLOSEBRACE); + 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(openBraceOffset, newInfo.release()); + m_functionCache->add(functionInfo.startOffset, WTFMove(newInfo)); - next(); + functionInfo.endLine = m_lastTokenEndPosition.line; return true; } template <typename LexerType> -template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context) +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context, ExportType exportType) { ASSERT(match(FUNCTION)); JSTokenLocation location(tokenLocation()); + unsigned functionKeywordStart = tokenStart(); next(); - const Identifier* name = 0; - TreeFormalParameterList parameters = 0; - TreeFunctionBody body = 0; - unsigned openBraceOffset = 0; - unsigned closeBraceOffset = 0; - int bodyStartLine = 0; - unsigned bodyStartColumn = 0; - failIfFalse((parseFunctionInfo<FunctionNeedsName, true>(context, name, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn))); - failIfFalse(name); - failIfFalseIfStrict(declareVariable(name)); - return context.createFuncDeclStatement(location, name, body, parameters, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn); + ParserFunctionInfo<TreeBuilder> functionInfo; + SourceParseMode parseMode = SourceParseMode::NormalFunctionMode; +#if ENABLE(ES6_GENERATORS) + if (consume(TIMES)) + parseMode = SourceParseMode::GeneratorWrapperFunctionMode; +#endif + failIfFalse((parseFunctionInfo(context, FunctionNeedsName, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration)), "Cannot parse this function"); + failIfFalse(functionInfo.name, "Function statements must have a name"); + + DeclarationResultMask declarationResult = declareVariable(functionInfo.name); + failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare a function named '", functionInfo.name->impl(), "' in strict mode"); + if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) + internalFailWithMessage(false, "Cannot declare a function that shadows a let/const/class variable '", 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); +} + +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->setIsLexicalScope(); + classScope->preventVarDeclarations(); + classScope->setStrictMode(); + + const Identifier* className = nullptr; + if (match(IDENT)) { + className = m_token.m_data.ident; + info.className = className; + next(); + failIfTrue(classScope->declareLexicalVariable(className, true) & 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 = false; + if (match(RESERVED_IF_STRICT) && *m_token.m_data.ident == m_vm->propertyNames->staticKeyword) { + SavePoint savePoint = createSavePoint(); + next(); + if (match(OPENPAREN)) { + // Reparse "static()" as a method named "static". + restoreSavePoint(savePoint); + } else + isStaticMethod = true; + } + + // FIXME: Figure out a way to share more code with parseProperty. + const CommonIdentifiers& propertyNames = *m_vm->propertyNames; + const Identifier* ident = &propertyNames.nullIdentifier; + TreeExpression computedPropertyName = 0; + bool isGetter = false; + bool isSetter = false; + bool isGenerator = false; +#if ENABLE(ES6_GENERATORS) + if (consume(TIMES)) + isGenerator = true; +#endif + switch (m_token.m_type) { + namedKeyword: + case STRING: + ident = m_token.m_data.ident; + ASSERT(ident); + next(); + break; + case IDENT: + ident = m_token.m_data.ident; + ASSERT(ident); + next(); + if (!isGenerator && (matchIdentifierOrKeyword() || match(STRING) || match(DOUBLE) || match(INTEGER) || match(OPENBRACKET))) { + 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; + case OPENBRACKET: + next(); + computedPropertyName = parseAssignmentExpression(context); + failIfFalse(computedPropertyName, "Cannot parse computed property name"); + handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); + break; + default: + if (m_token.m_type & KeywordTokenFlag) + goto namedKeyword; + 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; + SourceParseMode parseMode = SourceParseMode::MethodMode; + if (isGenerator) { + isConstructor = false; + parseMode = SourceParseMode::GeneratorWrapperFunctionMode; + semanticFailIfTrue(*ident == m_vm->propertyNames->prototype, "Cannot declare a generator named 'prototype'"); + semanticFailIfTrue(*ident == m_vm->propertyNames->constructor, "Cannot declare a generator named 'constructor'"); + } + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, parseMode, false, isConstructor ? constructorKind : ConstructorKind::None, SuperBinding::Needed, methodStart, methodInfo, FunctionDefinitionType::Method)), "Cannot parse this method"); + 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'"); + if (computedPropertyName) { + property = context.createProperty(computedPropertyName, method, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Computed), + PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed); + } else + 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; + } + } + + consumeOrFail(CLOSEBRACE, "Expected a closing '}' after a class body"); + + auto classExpression = context.createClassExpr(location, *className, classScope->finalizeLexicalEnvironment(), constructor, parentClass, instanceMethods, staticMethods); + popScope(classScope, TreeBuilder::NeedsFreeVariableInfo); + return classExpression; } struct LabelInfo { @@ -938,23 +2366,24 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrL // 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); - failIfFalse(autoSemiColon()); + 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); + 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()); - failIfTrue(getLabel(ident)); + 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)); + } while (matchSpecIdentifier()); bool isLoop = false; switch (m_token.m_type) { case FOR: @@ -967,6 +2396,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrL 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); @@ -974,9 +2404,9 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrL TreeStatement statement = parseStatement(context, unused); if (!m_syntaxAlreadyValidated) { for (size_t i = 0; i < labels.size(); i++) - popLabel(); + popLabel(labelScope); } - failIfFalse(statement); + 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); @@ -987,11 +2417,24 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrL 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); - failIfFalse(autoSemiColon()); + failIfFalse(expression, "Cannot parse expression statement"); + failIfFalse(autoSemiColon(), "Parse error"); return context.createExprStatement(location, expression, start, m_lastTokenEndPosition.line); } @@ -1002,23 +2445,22 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseIfStatement(T JSTokenLocation ifLocation(tokenLocation()); int start = tokenLine(); next(); - - consumeOrFail(OPENPAREN); + handleProductionOrFail(OPENPAREN, "(", "start", "'if' condition"); TreeExpression condition = parseExpression(context); - failIfFalse(condition); + failIfFalse(condition, "Expected a expression as the condition for an if statement"); int end = tokenLine(); - consumeOrFail(CLOSEPAREN); + handleProductionOrFail(CLOSEPAREN, ")", "end", "'if' condition"); const Identifier* unused = 0; TreeStatement trueBlock = parseStatement(context, unused); - failIfFalse(trueBlock); + 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<pair<int, int> > posStack; + Vector<std::pair<int, int>> posStack; Vector<JSTokenLocation> tokenLocationStack; Vector<TreeStatement> statementStack; bool trailingElse = false; @@ -1028,26 +2470,26 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseIfStatement(T if (!match(IF)) { const Identifier* unused = 0; TreeStatement block = parseStatement(context, unused); - failIfFalse(block); + failIfFalse(block, "Expected a statement as the body of an else block"); statementStack.append(block); trailingElse = true; break; } int innerStart = tokenLine(); next(); - - consumeOrFail(OPENPAREN); + + handleProductionOrFail(OPENPAREN, "(", "start", "'if' condition"); TreeExpression innerCondition = parseExpression(context); - failIfFalse(innerCondition); + failIfFalse(innerCondition, "Expected a expression as the condition for an if statement"); int innerEnd = tokenLine(); - consumeOrFail(CLOSEPAREN); + handleProductionOrFail(CLOSEPAREN, ")", "end", "'if' condition"); const Identifier* unused = 0; TreeStatement innerTrueBlock = parseStatement(context, unused); - failIfFalse(innerTrueBlock); + failIfFalse(innerTrueBlock, "Expected a statement as the body of an if block"); tokenLocationStack.append(tempLocation); exprStack.append(innerCondition); - posStack.append(make_pair(innerStart, innerEnd)); + posStack.append(std::make_pair(innerStart, innerEnd)); statementStack.append(innerTrueBlock); } while (match(ELSE)); @@ -1056,11 +2498,13 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseIfStatement(T exprStack.removeLast(); TreeStatement trueBlock = statementStack.last(); statementStack.removeLast(); - pair<int, int> pos = posStack.last(); + std::pair<int, int> pos = posStack.last(); posStack.removeLast(); JSTokenLocation elseLocation = tokenLocationStack.last(); tokenLocationStack.removeLast(); - statementStack.append(context.createIfStatement(elseLocation, condition, trueBlock, 0, pos.first, pos.second)); + TreeStatement ifStatement = context.createIfStatement(elseLocation, condition, trueBlock, 0, pos.first, pos.second); + context.setEndOffset(ifStatement, context.endOffset(trueBlock)); + statementStack.append(ifStatement); } while (!exprStack.isEmpty()) { @@ -1070,52 +2514,489 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseIfStatement(T statementStack.removeLast(); TreeStatement trueBlock = statementStack.last(); statementStack.removeLast(); - pair<int, int> pos = posStack.last(); + std::pair<int, int> pos = posStack.last(); posStack.removeLast(); JSTokenLocation elseLocation = tokenLocationStack.last(); tokenLocationStack.removeLast(); - statementStack.append(context.createIfStatement(elseLocation, condition, trueBlock, falseBlock, pos.first, pos.second)); + 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, (specifierType == ImportSpecifierType::NamespaceImport) ? DeclarationImportType::ImportedNamespace : 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(); + + bool startsWithFunction = match(FUNCTION); + if (startsWithFunction +#if ENABLE(ES6_CLASS_SYNTAX) + || match(CLASSTOKEN) +#endif + ) { + isFunctionOrClassDeclaration = true; + next(); + +#if ENABLE(ES6_GENERATORS) + // ES6 Generators + if (startsWithFunction && match(TIMES)) + next(); +#endif + 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()->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); + failIfFalse(node, "Cannot parse expression"); + context.setEndOffset(node, m_lastTokenEndPosition.offset); if (!match(COMMA)) return node; next(); - m_nonTrivialExpressionCount++; - m_nonLHSCount++; + m_parserState.nonTrivialExpressionCount++; + m_parserState.nonLHSCount++; TreeExpression right = parseAssignmentExpression(context); - failIfFalse(right); - typename TreeBuilder::Comma commaNode = context.createCommaExpr(location, node, right); + 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); - context.appendToComma(commaNode, right); + failIfFalse(right, "Cannot parse expression in a comma expression"); + context.setEndOffset(right, m_lastTokenEndPosition.offset); + tail = context.appendToCommaExpr(location, head, tail, right); } - return commaNode; + context.setEndOffset(head, m_lastTokenEndPosition.offset); + return head; +} + +template <typename LexerType> +template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmentExpressionOrPropagateErrorClass(TreeBuilder& context) +{ + ExpressionErrorClassifier classifier(this); + auto assignment = parseAssignmentExpression(context, classifier); + if (!assignment) + classifier.propagateExpressionErrorClass(); + return assignment; } template <typename LexerType> template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmentExpression(TreeBuilder& context) { + ExpressionErrorClassifier classifier(this); + return parseAssignmentExpression(context, classifier); +} + +template <typename LexerType> +template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmentExpression(TreeBuilder& context, ExpressionErrorClassifier& classifier) +{ + ASSERT(!hasError()); + failIfStackOverflow(); JSTextPosition start = tokenStartPosition(); JSTokenLocation location(tokenLocation()); - int initialAssignmentCount = m_assignmentCount; - int initialNonLHSCount = m_nonLHSCount; + int initialAssignmentCount = m_parserState.assignmentCount; + int initialNonLHSCount = m_parserState.nonLHSCount; + bool maybeAssignmentPattern = match(OPENBRACE) || match(OPENBRACKET); + SavePoint savePoint = createSavePoint(); + +#if ENABLE(ES6_GENERATORS) + if (match(YIELD) && !isYIELDMaskedAsIDENT(currentScope()->isGenerator())) + return parseYieldExpression(context); +#endif + +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + if (isArrowFunctionParameters()) + return parseArrowFunctionExpression(context); +#endif + TreeExpression lhs = parseConditionalExpression(context); - failIfFalse(lhs); - if (initialNonLHSCount != m_nonLHSCount) + + if (!lhs && (!maybeAssignmentPattern || !classifier.indicatesPossiblePattern())) + propagateError(); + + if (maybeAssignmentPattern && (!lhs || (context.isObjectOrArrayLiteral(lhs) && match(EQUAL)))) { + String expressionError = m_errorMessage; + SavePoint expressionErrorLocation = createSavePointForError(); + restoreSavePoint(savePoint); + auto pattern = tryParseDestructuringPatternExpression(context, AssignmentContext::AssignmentExpression); + if (classifier.indicatesPossiblePattern() && (!pattern || !match(EQUAL))) + restoreSavePointAndFail(expressionErrorLocation, expressionError); + failIfFalse(pattern, "Cannot parse assignment pattern"); + consumeOrFail(EQUAL, "Expected '=' following assignment pattern"); + auto rhs = parseAssignmentExpression(context); + if (!rhs) + propagateError(); + return context.createDestructuringAssignment(location, pattern, rhs); + } + + failIfFalse(lhs, "Cannot parse expression"); + if (initialNonLHSCount != m_parserState.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; @@ -1137,52 +3018,91 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen default: goto end; } - m_nonTrivialExpressionCount++; + m_parserState.nonTrivialExpressionCount++; hadAssignment = true; - context.assignmentStackAppend(assignmentStack, lhs, start, tokenStartPosition(), m_assignmentCount, op); + context.assignmentStackAppend(assignmentStack, lhs, start, tokenStartPosition(), m_parserState.assignmentCount, op); start = tokenStartPosition(); - m_assignmentCount++; + m_parserState.assignmentCount++; next(TreeBuilder::DontBuildStrings); - if (strictMode() && m_lastIdentifier && context.isResolve(lhs)) { - failIfTrueIfStrictWithMessage(m_vm->propertyNames->eval == *m_lastIdentifier, "'eval' cannot be modified in strict mode"); - failIfTrueIfStrictWithMessage(m_vm->propertyNames->arguments == *m_lastIdentifier, "'arguments' cannot be modified in strict mode"); - declareWrite(m_lastIdentifier); - m_lastIdentifier = 0; - } - lhs = parseConditionalExpression(context); - failIfFalse(lhs); - if (initialNonLHSCount != m_nonLHSCount) + if (strictMode() && m_parserState.lastIdentifier && context.isResolve(lhs)) { + failIfTrueIfStrict(m_vm->propertyNames->eval == *m_parserState.lastIdentifier, "Cannot modify 'eval' in strict mode"); + failIfTrueIfStrict(m_vm->propertyNames->arguments == *m_parserState.lastIdentifier, "Cannot modify 'arguments' in strict mode"); + declareWrite(m_parserState.lastIdentifier); + m_parserState.lastIdentifier = 0; + } + lhs = parseAssignmentExpression(context); + failIfFalse(lhs, "Cannot parse the right hand side of an assignment expression"); + if (initialNonLHSCount != m_parserState.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++; + m_parserState.nonLHSCount++; if (!TreeBuilder::CreatesAST) return lhs; while (assignmentStack) - lhs = context.createAssignment(location, assignmentStack, lhs, initialAssignmentCount, m_assignmentCount, lastTokenEndPosition()); + lhs = context.createAssignment(location, assignmentStack, lhs, initialAssignmentCount, m_parserState.assignmentCount, lastTokenEndPosition()); return lhs; } template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseYieldExpression(TreeBuilder& context) +{ + // YieldExpression[In] : + // yield + // yield [no LineTerminator here] AssignmentExpression[?In, Yield] + // yield [no LineTerminator here] * AssignmentExpression[?In, Yield] + + // http://ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions + failIfFalse(currentScope()->isGenerator(), "Cannot use yield expression out of generator"); + + // http://ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions-static-semantics-early-errors + failIfTrue(m_parserState.functionParsePhase == FunctionParsePhase::Parameters, "Cannot use yield expression within parameters"); + + JSTokenLocation location(tokenLocation()); + JSTextPosition divotStart = tokenStartPosition(); + ASSERT(match(YIELD)); + SavePoint savePoint = createSavePoint(); + next(); + if (m_lexer->prevTerminator()) + return context.createYield(location); + + bool delegate = consume(TIMES); + JSTextPosition argumentStart = tokenStartPosition(); + TreeExpression argument = parseAssignmentExpression(context); + if (!argument) { + restoreSavePoint(savePoint); + next(); + return context.createYield(location); + } + return context.createYield(location, argument, delegate, divotStart, argumentStart, lastTokenEndPosition()); +} + +template <typename LexerType> template <class TreeBuilder> TreeExpression Parser<LexerType>::parseConditionalExpression(TreeBuilder& context) { JSTokenLocation location(tokenLocation()); TreeExpression cond = parseBinaryExpression(context); - failIfFalse(cond); + failIfFalse(cond, "Cannot parse expression"); if (!match(QUESTION)) return cond; - m_nonTrivialExpressionCount++; - m_nonLHSCount++; + m_parserState.nonTrivialExpressionCount++; + m_parserState.nonLHSCount++; next(TreeBuilder::DontBuildStrings); TreeExpression lhs = parseAssignmentExpression(context); - consumeOrFailWithFlags(COLON, TreeBuilder::DontBuildStrings); + 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); + failIfFalse(rhs, "Cannot parse right hand side of ternary operator"); + context.setEndOffset(rhs, m_lastTokenEndPosition.offset); return context.createConditionalExpr(location, cond, lhs, rhs); } @@ -1202,23 +3122,22 @@ int Parser<LexerType>::isBinaryOperator(JSTokenType token) 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; + int initialAssignments = m_parserState.assignmentCount; TreeExpression current = parseUnaryExpression(context); - failIfFalse(current); + failIfFalse(current, "Cannot parse expression"); - context.appendBinaryExpressionInfo(operandStackDepth, current, exprStart, lastTokenEndPosition(), lastTokenEndPosition(), initialAssignments != m_assignmentCount); + context.appendBinaryExpressionInfo(operandStackDepth, current, exprStart, lastTokenEndPosition(), lastTokenEndPosition(), initialAssignments != m_parserState.assignmentCount); int precedence = isBinaryOperator(m_token.m_type); if (!precedence) break; - m_nonTrivialExpressionCount++; - m_nonLHSCount++; + m_parserState.nonTrivialExpressionCount++; + m_parserState.nonLHSCount++; int operatorToken = m_token.m_type; next(TreeBuilder::DontBuildStrings); @@ -1246,120 +3165,231 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseBinaryExpres } template <typename LexerType> -template <bool complete, class TreeBuilder> TreeProperty Parser<LexerType>::parseProperty(TreeBuilder& context) +template <class TreeBuilder> TreeProperty Parser<LexerType>::parseProperty(TreeBuilder& context, bool complete) { bool wasIdent = false; + bool isGenerator = false; +#if ENABLE(ES6_GENERATORS) + if (consume(TIMES)) + isGenerator = true; +#endif switch (m_token.m_type) { namedProperty: case IDENT: wasIdent = true; + FALLTHROUGH; case STRING: { const Identifier* ident = m_token.m_data.ident; - if (complete || (wasIdent && (*ident == m_vm->propertyNames->get || *ident == m_vm->propertyNames->set))) + unsigned getterOrSetterStartOffset = tokenStart(); + if (complete || (wasIdent && !isGenerator && (*ident == m_vm->propertyNames->get || *ident == m_vm->propertyNames->set))) nextExpectIdentifier(LexerFlagsIgnoreReservedWords); else nextExpectIdentifier(LexerFlagsIgnoreReservedWords | TreeBuilder::DontBuildKeywords); - - if (match(COLON)) { + + if (!isGenerator && match(COLON)) { next(); - TreeExpression node = parseAssignmentExpression(context); - failIfFalse(node); - return context.template createProperty<complete>(ident, node, PropertyNode::Constant); - } - failIfFalse(wasIdent); - const Identifier* accessorName = 0; - TreeFormalParameterList parameters = 0; - TreeFunctionBody body = 0; - unsigned openBraceOffset = 0; - unsigned closeBraceOffset = 0; - int bodyStartLine = 0; - unsigned bodyStartColumn = 0; + TreeExpression node = parseAssignmentExpressionOrPropagateErrorClass(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, isGenerator); + propagateError(); + return context.createProperty(ident, method, PropertyNode::Constant, PropertyNode::KnownDirect, complete); + } + failIfTrue(isGenerator, "Expected a parenthesis for argument list"); + + 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, lastTokenEndPosition()); + return context.createProperty(ident, node, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Shorthand), PropertyNode::KnownDirect, complete); + } + + if (match(EQUAL)) // CoverInitializedName is exclusive to BindingPattern and AssignmentPattern + classifyExpressionError(ErrorIndicatesPattern); + PropertyNode::Type type; if (*ident == m_vm->propertyNames->get) type = PropertyNode::Getter; else if (*ident == m_vm->propertyNames->set) type = PropertyNode::Setter; else - fail(); - const Identifier* stringPropertyName = 0; - double numericPropertyName = 0; - if (m_token.m_type == IDENT || m_token.m_type == STRING) - stringPropertyName = m_token.m_data.ident; - else if (m_token.m_type == NUMBER) - numericPropertyName = m_token.m_data.doubleValue; - else - fail(); - JSTokenLocation location(tokenLocation()); - next(); - failIfFalse((parseFunctionInfo<FunctionNoRequirements, false>(context, accessorName, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn))); - if (stringPropertyName) - return context.template createGetterOrSetterProperty<complete>(location, type, stringPropertyName, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn); - return context.template createGetterOrSetterProperty<complete>(const_cast<VM*>(m_vm), location, type, numericPropertyName, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn); + failWithMessage("Expected a ':' following the property name '", ident->impl(), "'"); + return parseGetterSetter(context, complete, type, getterOrSetterStartOffset); } - case NUMBER: { + case DOUBLE: + case INTEGER: { double propertyName = m_token.m_data.doubleValue; next(); - consumeOrFail(COLON); + + if (match(OPENPAREN)) { + const Identifier& ident = m_parserArena.identifierArena().makeNumericIdentifier(const_cast<VM*>(m_vm), propertyName); + auto method = parsePropertyMethod(context, &ident, isGenerator); + propagateError(); + return context.createProperty(&ident, method, PropertyNode::Constant, PropertyNode::Unknown, complete); + } + failIfTrue(isGenerator, "Expected a parenthesis for argument list"); + + consumeOrFail(COLON, "Expected ':' after property name"); TreeExpression node = parseAssignmentExpression(context); - failIfFalse(node); - return context.template createProperty<complete>(const_cast<VM*>(m_vm), propertyName, node, PropertyNode::Constant); + 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, isGenerator); + propagateError(); + return context.createProperty(propertyName, method, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Computed), PropertyNode::KnownDirect, complete); + } + failIfTrue(isGenerator, "Expected a parenthesis for argument list"); + + 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); + failIfFalse(m_token.m_type & KeywordTokenFlag, "Expected a property name"); goto namedProperty; } } template <typename LexerType> -template <class TreeBuilder> TreeExpression Parser<LexerType>::parseObjectLiteral(TreeBuilder& context) +template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePropertyMethod(TreeBuilder& context, const Identifier* methodName, bool isGenerator) { - int startOffset = m_token.m_data.offset; - unsigned oldLineStartOffset = m_lexer->currentLineStartOffset(); - unsigned oldLastLineNumber = m_lexer->lastLineNumber(); - unsigned oldLineNumber = m_lexer->lineNumber(); - consumeOrFailWithFlags(OPENBRACE, TreeBuilder::DontBuildStrings); + JSTokenLocation methodLocation(tokenLocation()); + unsigned methodStart = tokenStart(); + ParserFunctionInfo<TreeBuilder> methodInfo; + SourceParseMode parseMode = isGenerator ? SourceParseMode::GeneratorWrapperFunctionMode : SourceParseMode::MethodMode; + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, parseMode, false, ConstructorKind::None, SuperBinding::NotNeeded, methodStart, methodInfo, FunctionDefinitionType::Method)), "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; + TreeExpression computedPropertyName = 0; + JSTokenLocation location(tokenLocation()); - int oldNonLHSCount = m_nonLHSCount; - + if (matchSpecIdentifier() || match(STRING) || m_token.m_type & KeywordTokenFlag) { + 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'"); + next(); + } else if (match(DOUBLE) || match(INTEGER)) { + numericPropertyName = m_token.m_data.doubleValue; + next(); + } else if (match(OPENBRACKET)) { + next(); + computedPropertyName = parseAssignmentExpression(context); + failIfFalse(computedPropertyName, "Cannot parse computed property name"); + handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); + } else + failDueToUnexpectedToken(); + + 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, FunctionDefinitionType::Method)), "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, FunctionDefinitionType::Method)), "Cannot parse setter definition"); + } + + if (stringPropertyName) + return context.createGetterOrSetterProperty(location, type, strict, stringPropertyName, info, superBinding); + + if (computedPropertyName) + return context.createGetterOrSetterProperty(location, static_cast<PropertyNode::Type>(type | PropertyNode::Computed), strict, computedPropertyName, 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) +{ + SavePoint savePoint = createSavePoint(); + consumeOrFailWithFlags(OPENBRACE, TreeBuilder::DontBuildStrings, "Expected opening '{' at the start of an object literal"); + + int oldNonLHSCount = m_parserState.nonLHSCount; + + JSTokenLocation location(tokenLocation()); if (match(CLOSEBRACE)) { next(); return context.createObjectLiteral(location); } - TreeProperty property = parseProperty<false>(context); - failIfFalse(property); - if (!m_syntaxAlreadyValidated && context.getType(property) != PropertyNode::Constant) { - m_lexer->setOffset(startOffset, oldLineStartOffset); - next(); - m_lexer->setLastLineNumber(oldLastLineNumber); - m_lexer->setLineNumber(oldLineNumber); + 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); - // allow extra comma, see http://bugs.webkit.org/show_bug.cgi?id=5939 if (match(CLOSEBRACE)) break; JSTokenLocation propertyLocation(tokenLocation()); - property = parseProperty<false>(context); - failIfFalse(property); - if (!m_syntaxAlreadyValidated && context.getType(property) != PropertyNode::Constant) { - m_lexer->setOffset(startOffset, oldLineStartOffset); - next(); - m_lexer->setLastLineNumber(oldLastLineNumber); - m_lexer->setLineNumber(oldLineNumber); + 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(); - consumeOrFail(CLOSEBRACE); + handleProductionOrFail(CLOSEBRACE, "}", "end", "object literal"); - m_nonLHSCount = oldNonLHSCount; + m_parserState.nonLHSCount = oldNonLHSCount; return context.createObjectLiteral(location, propertyList); } @@ -1367,9 +3397,9 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseObjectLitera template <typename LexerType> template <class TreeBuilder> TreeExpression Parser<LexerType>::parseStrictObjectLiteral(TreeBuilder& context) { - consumeOrFail(OPENBRACE); + consumeOrFail(OPENBRACE, "Expected opening '{' at the start of an object literal"); - int oldNonLHSCount = m_nonLHSCount; + int oldNonLHSCount = m_parserState.nonLHSCount; JSTokenLocation location(tokenLocation()); if (match(CLOSEBRACE)) { @@ -1377,41 +3407,35 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseStrictObject return context.createObjectLiteral(location); } - TreeProperty property = parseProperty<true>(context); - failIfFalse(property); - - typedef HashMap<RefPtr<StringImpl>, unsigned, IdentifierRepHash> ObjectValidationMap; - ObjectValidationMap objectValidator; - // Add the first property - if (!m_syntaxAlreadyValidated) - objectValidator.add(context.getName(property).impl(), context.getType(property)); - + 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(); - // allow extra comma, see http://bugs.webkit.org/show_bug.cgi?id=5939 if (match(CLOSEBRACE)) break; JSTokenLocation propertyLocation(tokenLocation()); - property = parseProperty<true>(context); - failIfFalse(property); - if (!m_syntaxAlreadyValidated) { - ObjectValidationMap::AddResult propertyEntry = objectValidator.add(context.getName(property).impl(), context.getType(property)); - if (!propertyEntry.isNewEntry) { - failIfTrue(propertyEntry.iterator->value == PropertyNode::Constant); - failIfTrue(context.getType(property) == PropertyNode::Constant); - failIfTrue(context.getType(property) & propertyEntry.iterator->value); - propertyEntry.iterator->value |= context.getType(property); + 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(); - consumeOrFail(CLOSEBRACE); + handleProductionOrFail(CLOSEBRACE, "}", "end", "object literal"); - m_nonLHSCount = oldNonLHSCount; + m_parserState.nonLHSCount = oldNonLHSCount; return context.createObjectLiteral(location, propertyList); } @@ -1419,9 +3443,9 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseStrictObject template <typename LexerType> template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArrayLiteral(TreeBuilder& context) { - consumeOrFailWithFlags(OPENBRACKET, TreeBuilder::DontBuildStrings); + consumeOrFailWithFlags(OPENBRACKET, TreeBuilder::DontBuildStrings, "Expected an opening '[' at the beginning of an array literal"); - int oldNonLHSCount = m_nonLHSCount; + int oldNonLHSCount = m_parserState.nonLHSCount; int elisions = 0; while (match(COMMA)) { @@ -1434,8 +3458,18 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArrayLiteral return context.createArray(location, elisions); } - TreeExpression elem = parseAssignmentExpression(context); - failIfFalse(elem); + 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 = parseAssignmentExpressionOrPropagateErrorClass(context); + failIfFalse(spreadExpr, "Cannot parse subject of a spread operation"); + elem = context.createSpreadExpression(spreadLocation, spreadExpr, start, divot, m_lastTokenEndPosition); + } else + elem = parseAssignmentExpressionOrPropagateErrorClass(context); + failIfFalse(elem, "Cannot parse array literal element"); typename TreeBuilder::ElementList elementList = context.createElementList(elisions, elem); typename TreeBuilder::ElementList tail = elementList; elisions = 0; @@ -1453,24 +3487,122 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArrayLiteral next(TreeBuilder::DontBuildStrings); return context.createArray(location, elisions, elementList); } - TreeExpression elem = parseAssignmentExpression(context); - failIfFalse(elem); + 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 = parseAssignmentExpressionOrPropagateErrorClass(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 = parseAssignmentExpressionOrPropagateErrorClass(context); + failIfFalse(elem, "Cannot parse array literal element"); tail = context.createElementList(tail, elisions, elem); } JSTokenLocation location(tokenLocation()); - consumeOrFail(CLOSEBRACKET); + 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; + m_parserState.nonLHSCount = oldNonLHSCount; return context.createArray(location, elementList); } template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseFunctionExpression(TreeBuilder& context) +{ + ASSERT(match(FUNCTION)); + JSTokenLocation location(tokenLocation()); + unsigned functionKeywordStart = tokenStart(); + next(); + ParserFunctionInfo<TreeBuilder> functionInfo; + functionInfo.name = &m_vm->propertyNames->nullIdentifier; + SourceParseMode parseMode = SourceParseMode::NormalFunctionMode; +#if ENABLE(ES6_GENERATORS) + if (consume(TIMES)) + parseMode = SourceParseMode::GeneratorWrapperFunctionMode; +#endif + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, parseMode, false, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Expression)), "Cannot parse function expression"); + return context.createFunctionExpr(location, functionInfo); +} + +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); +} + +template <typename LexerType> template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpression(TreeBuilder& context) { failIfStackOverflow(); switch (m_token.m_type) { + case FUNCTION: + return parseFunctionExpression(context); +#if ENABLE(ES6_CLASS_SYNTAX) + case CLASSTOKEN: { + ParserClassInfo<TreeBuilder> info; + return parseClass(context, FunctionNoRequirements, info); + } +#endif case OPENBRACE: if (strictMode()) return parseStrictObjectLiteral(context); @@ -1479,26 +3611,26 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre return parseArrayLiteral(context); case OPENPAREN: { next(); - int oldNonLHSCount = m_nonLHSCount; + int oldNonLHSCount = m_parserState.nonLHSCount; TreeExpression result = parseExpression(context); - m_nonLHSCount = oldNonLHSCount; - consumeOrFail(CLOSEPAREN); - + m_parserState.nonLHSCount = oldNonLHSCount; + handleProductionOrFail(CLOSEPAREN, ")", "end", "compound expression"); return result; } case THISTOKEN: { JSTokenLocation location(tokenLocation()); next(); - return context.thisExpr(location); + 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); + m_parserState.lastIdentifier = ident; + return context.createResolve(location, *ident, start, lastTokenEndPosition()); } case STRING: { const Identifier* ident = m_token.m_data.ident; @@ -1506,11 +3638,17 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre next(); return context.createString(location, ident); } - case NUMBER: { + 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.createNumberExpr(location, d); + return context.createIntegerExpr(location, d); } case NULLTOKEN: { JSTokenLocation location(tokenLocation()); @@ -1533,9 +3671,9 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre const Identifier* pattern; const Identifier* flags; if (match(DIVEQUAL)) - failIfFalse(m_lexer->scanRegExp(pattern, flags, '=')); + failIfFalse(m_lexer->scanRegExp(pattern, flags, '='), "Invalid regular expression"); else - failIfFalse(m_lexer->scanRegExp(pattern, flags)); + failIfFalse(m_lexer->scanRegExp(pattern, flags), "Invalid regular expression"); JSTextPosition start = tokenStartPosition(); JSTokenLocation location(tokenLocation()); @@ -1543,122 +3681,252 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre TreeExpression re = context.createRegExp(location, *pattern, *flags, start); if (!re) { const char* yarrErrorMsg = Yarr::checkSyntax(pattern->string()); - failWithMessage(yarrErrorMsg); + regexFail(yarrErrorMsg); } return re; } +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + case TEMPLATE: + return parseTemplateLiteral(context, LexerType::RawStringsBuildMode::DontBuildRawStrings); +#endif + case YIELD: + if (!strictMode() && !currentScope()->isGenerator()) + goto identifierExpression; + failDueToUnexpectedToken(); + case LET: + if (!strictMode()) + goto identifierExpression; + FALLTHROUGH; default: - fail(); + failDueToUnexpectedToken(); } } template <typename LexerType> template <class TreeBuilder> TreeArguments Parser<LexerType>::parseArguments(TreeBuilder& context) { - consumeOrFailWithFlags(OPENPAREN, TreeBuilder::DontBuildStrings); + consumeOrFailWithFlags(OPENPAREN, TreeBuilder::DontBuildStrings, "Expected opening '(' at start of argument list"); JSTokenLocation location(tokenLocation()); if (match(CLOSEPAREN)) { next(TreeBuilder::DontBuildStrings); return context.createArguments(); } - TreeExpression firstArg = parseAssignmentExpression(context); - failIfFalse(firstArg); - + auto argumentsStart = m_token.m_startPosition; + auto argumentsDivot = m_token.m_endPosition; + + ArgumentType argType = ArgumentType::Normal; + TreeExpression firstArg = parseArgument(context, argType); + failIfFalse(firstArg, "Cannot parse function argument"); + semanticFailIfTrue(match(DOTDOTDOT), "The '...' operator should come before the target expression"); + + bool hasSpread = false; + if (argType == ArgumentType::Spread) + hasSpread = true; TreeArgumentsList argList = context.createArgumentsList(location, firstArg); TreeArgumentsList tail = argList; + while (match(COMMA)) { JSTokenLocation argumentLocation(tokenLocation()); next(TreeBuilder::DontBuildStrings); - TreeExpression arg = parseAssignmentExpression(context); - failIfFalse(arg); + + TreeExpression arg = parseArgument(context, argType); + propagateError(); + semanticFailIfTrue(match(DOTDOTDOT), "The '...' operator should come before the target expression"); + + if (argType == ArgumentType::Spread) + hasSpread = true; + tail = context.createArgumentsList(argumentLocation, tail, arg); } - consumeOrFail(CLOSEPAREN); + + handleProductionOrFail(CLOSEPAREN, ")", "end", "argument list"); + if (hasSpread) { + TreeExpression spreadArray = context.createSpreadExpression(location, context.createArray(location, context.createElementList(argList)), argumentsStart, argumentsDivot, m_lastTokenEndPosition); + return context.createArguments(context.createArgumentsList(location, spreadArray)); + } + return context.createArguments(argList); } template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArgument(TreeBuilder& context, ArgumentType& type) +{ + if (UNLIKELY(match(DOTDOTDOT))) { + JSTokenLocation spreadLocation(tokenLocation()); + auto start = m_token.m_startPosition; + auto divot = m_token.m_endPosition; + next(); + TreeExpression spreadExpr = parseAssignmentExpression(context); + propagateError(); + auto end = m_lastTokenEndPosition; + type = ArgumentType::Spread; + return context.createSpreadExpression(spreadLocation, spreadExpr, start, divot, end); + } + + type = ArgumentType::Normal; + return parseAssignmentExpression(context); +} + +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 (match(FUNCTION)) { - const Identifier* name = &m_vm->propertyNames->nullIdentifier; - TreeFormalParameterList parameters = 0; - TreeFunctionBody body = 0; - unsigned openBraceOffset = 0; - unsigned closeBraceOffset = 0; - int bodyStartLine = 0; - unsigned bodyStartColumn = 0; - location = tokenLocation(); + +#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(); - failIfFalse((parseFunctionInfo<FunctionNoRequirements, false>(context, name, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn))); - base = context.createFunctionExpr(location, name, body, parameters, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn); - } else + 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(); + currentFunctionScope()->setNeedsSuperBinding(); + } else if (!baseIsNewTarget) base = parsePrimaryExpression(context); - - failIfFalse(base); + + failIfFalse(base, "Cannot parse base expression"); while (true) { location = tokenLocation(); switch (m_token.m_type) { case OPENBRACKET: { - m_nonTrivialExpressionCount++; + m_parserState.nonTrivialExpressionCount++; JSTextPosition expressionEnd = lastTokenEndPosition(); next(); - int nonLHSCount = m_nonLHSCount; - int initialAssignments = m_assignmentCount; + int nonLHSCount = m_parserState.nonLHSCount; + int initialAssignments = m_parserState.assignmentCount; TreeExpression property = parseExpression(context); - failIfFalse(property); - base = context.createBracketAccess(location, base, property, initialAssignments != m_assignmentCount, expressionStart, expressionEnd, tokenEndPosition()); - consumeOrFail(CLOSEBRACKET); - m_nonLHSCount = nonLHSCount; + failIfFalse(property, "Cannot parse subscript expression"); + base = context.createBracketAccess(location, base, property, initialAssignments != m_parserState.assignmentCount, expressionStart, expressionEnd, tokenEndPosition()); + handleProductionOrFail(CLOSEBRACKET, "]", "end", "subscript expression"); + m_parserState.nonLHSCount = nonLHSCount; break; } case OPENPAREN: { - m_nonTrivialExpressionCount++; - int nonLHSCount = m_nonLHSCount; + m_parserState.nonTrivialExpressionCount++; + int nonLHSCount = m_parserState.nonLHSCount; if (newCount) { newCount--; JSTextPosition expressionEnd = lastTokenEndPosition(); TreeArguments arguments = parseArguments(context); - failIfFalse(arguments); + failIfFalse(arguments, "Cannot parse call arguments"); base = context.createNewExpr(location, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition()); } else { JSTextPosition expressionEnd = lastTokenEndPosition(); TreeArguments arguments = parseArguments(context); - failIfFalse(arguments); - base = context.makeFunctionCallNode(location, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition()); + failIfFalse(arguments, "Cannot parse call arguments"); + if (baseIsSuper) + currentFunctionScope()->setHasDirectSuper(); + base = context.makeFunctionCallNode(startLocation, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition()); } - m_nonLHSCount = nonLHSCount; + m_parserState.nonLHSCount = nonLHSCount; break; } case DOT: { - m_nonTrivialExpressionCount++; + m_parserState.nonTrivialExpressionCount++; JSTextPosition expressionEnd = lastTokenEndPosition(); nextExpectIdentifier(LexerFlagsIgnoreReservedWords | TreeBuilder::DontBuildKeywords); - matchOrFail(IDENT); + 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_parserState.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_parserState.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, FunctionDefinitionType::Expression)), "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); @@ -1666,6 +3934,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress 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) { @@ -1673,52 +3942,59 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress case MINUSMINUS: case AUTOPLUSPLUS: case AUTOMINUSMINUS: - failIfTrue(requiresLExpr); + semanticFailIfTrue(requiresLExpr, "The ", operatorString(true, lastOperator), " operator requires a reference expression"); modifiesExpr = true; requiresLExpr = true; break; case DELETETOKEN: - failIfTrue(requiresLExpr); + semanticFailIfTrue(requiresLExpr, "The ", operatorString(true, lastOperator), " operator requires a reference expression"); requiresLExpr = true; break; default: - failIfTrue(requiresLExpr); + semanticFailIfTrue(requiresLExpr, "The ", operatorString(true, lastOperator), " operator requires a reference expression"); break; } } - m_nonLHSCount++; + lastOperator = m_token.m_type; + m_parserState.nonLHSCount++; context.appendUnaryToken(tokenStackDepth, m_token.m_type, tokenStartPosition()); next(); - m_nonTrivialExpressionCount++; + m_parserState.nonTrivialExpressionCount++; } JSTextPosition subExprStart = tokenStartPosition(); ASSERT(subExprStart.offset >= subExprStart.lineStartOffset); JSTokenLocation location(tokenLocation()); TreeExpression expr = parseMemberExpression(context); - failIfFalse(expr); + 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; + isEvalOrArguments = *m_parserState.lastIdentifier == m_vm->propertyNames->eval || *m_parserState.lastIdentifier == m_vm->propertyNames->arguments; } - failIfTrueIfStrictWithNameAndMessage(isEvalOrArguments && modifiesExpr, "'", m_lastIdentifier->impl(), "' cannot be modified in strict mode"); + failIfTrueIfStrict(isEvalOrArguments && modifiesExpr, "Cannot modify '", m_parserState.lastIdentifier->impl(), "' in strict mode"); switch (m_token.m_type) { case PLUSPLUS: - m_nonTrivialExpressionCount++; - m_nonLHSCount++; + m_parserState.nonTrivialExpressionCount++; + m_parserState.nonLHSCount++; expr = context.makePostfixNode(location, expr, OpPlusPlus, subExprStart, lastTokenEndPosition(), tokenEndPosition()); - m_assignmentCount++; - failIfTrueIfStrictWithNameAndMessage(isEvalOrArguments, "'", m_lastIdentifier->impl(), "' cannot be modified in strict mode"); - failIfTrueIfStrict(requiresLExpr); + m_parserState.assignmentCount++; + failIfTrueIfStrict(isEvalOrArguments, "Cannot modify '", m_parserState.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++; + m_parserState.nonTrivialExpressionCount++; + m_parserState.nonLHSCount++; expr = context.makePostfixNode(location, expr, OpMinusMinus, subExprStart, lastTokenEndPosition(), tokenEndPosition()); - m_assignmentCount++; - failIfTrueIfStrictWithNameAndMessage(isEvalOrArguments, "'", m_lastIdentifier->impl(), "' cannot be modified in strict mode"); - failIfTrueIfStrict(requiresLExpr); + m_parserState.assignmentCount++; + failIfTrueIfStrict(isEvalOrArguments, "'", m_parserState.lastIdentifier->impl(), "' cannot be modified in strict mode"); + semanticFailIfTrue(requiresLExpr, "The ", operatorString(false, lastOperator), " operator requires a reference expression"); + lastOperator = PLUSPLUS; next(); break; default: @@ -1749,12 +4025,12 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress case PLUSPLUS: case AUTOPLUSPLUS: expr = context.makePrefixNode(location, expr, OpPlusPlus, context.unaryTokenStackLastStart(tokenStackDepth), subExprStart + 1, end); - m_assignmentCount++; + m_parserState.assignmentCount++; break; case MINUSMINUS: case AUTOMINUSMINUS: expr = context.makePrefixNode(location, expr, OpMinusMinus, context.unaryTokenStackLastStart(tokenStackDepth), subExprStart + 1, end); - m_assignmentCount++; + m_parserState.assignmentCount++; break; case TYPEOF: expr = context.makeTypeOfNode(location, expr); @@ -1763,7 +4039,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress expr = context.createVoid(location, expr); break; case DELETETOKEN: - failIfTrueIfStrictWithNameAndMessage(context.isResolve(expr), "Cannot delete unqualified property", m_lastIdentifier->impl(), "in strict mode"); + failIfTrueIfStrict(context.isResolve(expr), "Cannot delete unqualified property '", m_parserState.lastIdentifier->impl(), "' in strict mode"); expr = context.makeDeleteNode(location, expr, context.unaryTokenStackLastStart(tokenStackDepth), end, end); break; default: @@ -1776,8 +4052,82 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress return expr; } -// 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> >; +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 UNTERMINATED_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 index 87d523404..02a726a56 100644 --- a/Source/JavaScriptCore/parser/Parser.h +++ b/Source/JavaScriptCore/parser/Parser.h @@ -31,14 +31,14 @@ #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 "VMStackBounds.h" +#include "VariableEnvironment.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> -#include <wtf/OwnPtr.h> #include <wtf/RefPtr.h> namespace JSC { struct Scope; @@ -53,7 +53,7 @@ template <> struct VectorTraits<JSC::Scope> : SimpleClassVectorTraits { namespace JSC { class ExecState; -class FunctionBodyNode; +class FunctionMetadataNode; class FunctionParameters; class Identifier; class VM; @@ -67,60 +67,116 @@ class SourceCode; #define TreeSourceElements typename TreeBuilder::SourceElements #define TreeClause typename TreeBuilder::Clause #define TreeClauseList typename TreeBuilder::ClauseList -#define TreeConstDeclList typename TreeBuilder::ConstDeclList #define TreeArguments typename TreeBuilder::Arguments #define TreeArgumentsList typename TreeBuilder::ArgumentsList #define TreeFunctionBody typename TreeBuilder::FunctionBody +#define TreeClassExpression typename TreeBuilder::ClassExpression #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 FunctionBodyType { ArrowFunctionBodyExpression, ArrowFunctionBodyBlock, StandardFunctionBodyBlock }; enum FunctionRequirements { FunctionNoRequirements, FunctionNeedsName }; +enum class DestructuringKind { + DestructureToVariables, + DestructureToLet, + DestructureToConst, + DestructureToCatchParameters, + DestructureToParameters, + DestructureToExpressions +}; + +enum class DeclarationType { + VarDeclaration, + LetDeclaration, + ConstDeclaration +}; + +enum class DeclarationImportType { + Imported, + ImportedNamespace, + 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 DepthManager { - DepthManager(int* depth) - : m_originalDepth(*depth) - , m_depth(depth) - { - } +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; } - ~DepthManager() + bool exportName(const Identifier& exportedName) { - *m_depth = m_originalDepth; + return m_exportedNames.add(exportedName.impl()).isNewEntry; } -private: - int m_originalDepth; - int* m_depth; -}; - -struct ScopeLabelInfo { - ScopeLabelInfo(StringImpl* ident, bool isLoop) - : m_ident(ident) - , m_isLoop(isLoop) + void exportBinding(const Identifier& localName) { + m_exportedBindings.add(localName.impl()); } - StringImpl* m_ident; - bool m_isLoop; +private: + IdentifierSet m_exportedNames { }; + IdentifierSet m_exportedBindings { }; }; struct Scope { - Scope(const VM* vm, bool isFunction, bool strictMode) + Scope(const VM* vm, bool isFunction, bool isGenerator, bool strictMode) : m_vm(vm) , m_shadowsArguments(false) , m_usesEval(false) , m_needsFullActivation(false) - , m_allowsNewDecls(true) + , m_hasDirectSuper(false) + , m_needsSuperBinding(false) + , m_allowsVarDeclarations(true) + , m_allowsLexicalDeclarations(true) , m_strictMode(strictMode) , m_isFunction(isFunction) + , m_isGenerator(isGenerator) + , m_isArrowFunction(false) + , m_isLexicalScope(false) , m_isFunctionBoundary(false) , m_isValidStrictMode(true) + , m_hasArguments(false) + , m_constructorKind(static_cast<unsigned>(ConstructorKind::None)) + , m_expectedSuperBinding(static_cast<unsigned>(SuperBinding::NotNeeded)) , m_loopDepth(0) , m_switchDepth(0) { @@ -131,21 +187,31 @@ struct Scope { , m_shadowsArguments(rhs.m_shadowsArguments) , m_usesEval(rhs.m_usesEval) , m_needsFullActivation(rhs.m_needsFullActivation) - , m_allowsNewDecls(rhs.m_allowsNewDecls) + , 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_isGenerator(rhs.m_isGenerator) + , m_isArrowFunction(rhs.m_isArrowFunction) + , m_isLexicalScope(rhs.m_isLexicalScope) , m_isFunctionBoundary(rhs.m_isFunctionBoundary) , m_isValidStrictMode(rhs.m_isValidStrictMode) + , m_hasArguments(rhs.m_hasArguments) + , m_constructorKind(rhs.m_constructorKind) + , m_expectedSuperBinding(rhs.m_expectedSuperBinding) , m_loopDepth(rhs.m_loopDepth) , m_switchDepth(rhs.m_switchDepth) + , m_moduleScopeData(rhs.m_moduleScopeData) { if (rhs.m_labels) { - m_labels = adoptPtr(new LabelStack); + 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->m_ident, it->m_isLoop)); + m_labels->append(ScopeLabelInfo { it->uid, it->isLoop }); } } @@ -160,8 +226,8 @@ struct Scope { void pushLabel(const Identifier* label, bool isLoop) { if (!m_labels) - m_labels = adoptPtr(new LabelStack); - m_labels->append(ScopeLabelInfo(label->impl(), isLoop)); + m_labels = std::make_unique<LabelStack>(); + m_labels->append(ScopeLabelInfo { label->impl(), isLoop }); } void popLabel() @@ -176,115 +242,321 @@ struct Scope { if (!m_labels) return 0; for (int i = m_labels->size(); i > 0; i--) { - if (m_labels->at(i - 1).m_ident == label->impl()) + if (m_labels->at(i - 1).uid == label->impl()) return &m_labels->at(i - 1); } return 0; } - void setIsFunction() + void setSourceParseMode(SourceParseMode mode) { - m_isFunction = true; - m_isFunctionBoundary = true; + switch (mode) { + case SourceParseMode::GeneratorBodyMode: + setIsGenerator(); + break; + + case SourceParseMode::GeneratorWrapperFunctionMode: + setIsGeneratorFunction(); + break; + + case SourceParseMode::NormalFunctionMode: + case SourceParseMode::GetterMode: + case SourceParseMode::SetterMode: + case SourceParseMode::MethodMode: + setIsFunction(); + break; + + case SourceParseMode::ArrowFunctionMode: + setIsArrowFunction(); + break; + + case SourceParseMode::ProgramMode: + break; + + case SourceParseMode::ModuleAnalyzeMode: + case SourceParseMode::ModuleEvaluateMode: + setIsModule(); + break; + } + } + + bool isFunction() const { return m_isFunction; } + bool isFunctionBoundary() const { return m_isFunctionBoundary; } + bool isGenerator() const { return m_isGenerator; } + + bool hasArguments() const { return m_hasArguments; } + + void setIsLexicalScope() + { + m_isLexicalScope = true; + m_allowsLexicalDeclarations = true; + } + bool isLexicalScope() { return m_isLexicalScope; } + + const IdentifierSet& closedVariableCandidates() const { return m_closedVariableCandidates; } + 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; } - bool isFunction() { return m_isFunction; } - bool isFunctionBoundary() { return m_isFunctionBoundary; } - void declareCallee(const Identifier* ident) + ModuleScopeData& moduleScopeData() const { - m_declaredVariables.add(ident->string().impl()); + ASSERT(m_moduleScopeData); + return *m_moduleScopeData; } - bool declareVariable(const Identifier* ident) + void computeLexicallyCapturedVariablesAndPurgeCandidates() { - bool isValidStrictMode = m_vm->propertyNames->eval != *ident && m_vm->propertyNames->arguments != *ident; + // 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); + } + } + } + + DeclarationResultMask 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 result = DeclarationResult::Valid; + if (isEvalOrArgumentsIdentifier(m_vm, ident)) + result |= DeclarationResult::InvalidStrictMode; + return result; + } + + DeclarationResultMask declareVariable(const Identifier* ident) + { + ASSERT(m_allowsVarDeclarations); + DeclarationResultMask result = DeclarationResult::Valid; + bool isValidStrictMode = !isEvalOrArgumentsIdentifier(m_vm, ident); m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode; - m_declaredVariables.add(ident->string().impl()); - return isValidStrictMode; + auto addResult = m_declaredVariables.add(ident->impl()); + addResult.iterator->value.setIsVar(); + if (!isValidStrictMode) + result |= DeclarationResult::InvalidStrictMode; + if (m_lexicalVariables.contains(ident->impl())) + result |= DeclarationResult::InvalidDuplicateDeclaration; + 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(); + else if (importType == DeclarationImportType::ImportedNamespace) { + addResult.iterator->value.setIsImported(); + addResult.iterator->value.setIsImportedNamespace(); + } + + 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) + { + auto iter = m_declaredVariables.find(ident.get()); + if (iter == m_declaredVariables.end()) + return false; + VariableEnvironmentEntry entry = iter->value; + return entry.isVar(); // The callee isn't a "var". + } + + 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) || hasDeclaredVariable(ident); + } + void declareWrite(const Identifier* ident) { ASSERT(m_strictMode); m_writtenVariables.add(ident->impl()); } - void preventNewDecls() { m_allowsNewDecls = false; } - bool allowsNewDecls() const { return m_allowsNewDecls; } + 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; } - bool declareParameter(const Identifier* ident) + DeclarationResultMask declareParameter(const Identifier* ident) { - bool isArguments = m_vm->propertyNames->arguments == *ident; - bool isValidStrictMode = m_declaredVariables.add(ident->string().impl()).isNewEntry && m_vm->propertyNames->eval != *ident && !isArguments; + 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; - if (isArguments) + m_declaredParameters.add(ident->impl()); + if (!isValidStrictMode) + result |= DeclarationResult::InvalidStrictMode; + if (isArgumentsIdent) m_shadowsArguments = true; - return isValidStrictMode; + if (!addResult.isNewEntry) + result |= DeclarationResult::InvalidDuplicateDeclaration; + + return result; + } + + void getUsedVariables(IdentifierSet& usedVariables) + { + usedVariables.swap(m_usedVariables); } void useVariable(const Identifier* ident, bool isEval) { m_usesEval |= isEval; - m_usedVariables.add(ident->string().impl()); + m_usedVariables.add(ident->impl()); } void setNeedsFullActivation() { m_needsFullActivation = true; } + bool needsFullActivation() const { return m_needsFullActivation; } + bool isArrowFunction() { return m_isArrowFunction; } + + bool hasDirectSuper() { return m_hasDirectSuper; } + void setHasDirectSuper() { m_hasDirectSuper = true; } - bool collectFreeVariables(Scope* nestedScope, bool shouldTrackClosedVariables) + bool needsSuperBinding() { return m_needsSuperBinding; } + void setNeedsSuperBinding() { m_needsSuperBinding = true; } + + void setExpectedSuperBinding(SuperBinding superBinding) { m_expectedSuperBinding = static_cast<unsigned>(superBinding); } + SuperBinding expectedSuperBinding() const { return static_cast<SuperBinding>(m_expectedSuperBinding); } + void setConstructorKind(ConstructorKind constructorKind) { m_constructorKind = static_cast<unsigned>(constructorKind); } + ConstructorKind constructorKind() const { return static_cast<ConstructorKind>(m_constructorKind); } + + 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)) - continue; - m_usedVariables.add(*ptr); - if (shouldTrackClosedVariables) - m_closedVariables.add(*ptr); + + { + for (const RefPtr<UniquedStringImpl>& impl : nestedScope->m_usedVariables) { + if (nestedScope->m_declaredVariables.contains(impl) || nestedScope->m_lexicalVariables.contains(impl)) + continue; + + // "arguments" reference should be resolved at function boudary. + if (nestedScope->isFunctionBoundary() && nestedScope->hasArguments() && impl == m_vm->propertyNames->arguments.impl() && !nestedScope->isArrowFunction()) + continue; + + m_usedVariables.add(impl); + // 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(impl); + } + } + // 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)) + if (nestedScope->m_declaredVariables.contains(*ptr) || nestedScope->m_lexicalVariables.contains(*ptr)) continue; m_writtenVariables.add(*ptr); } } - - return true; - } - - void getUncapturedWrittenVariables(IdentifierSet& writtenVariables) - { - IdentifierSet::iterator end = m_writtenVariables.end(); - for (IdentifierSet::iterator ptr = m_writtenVariables.begin(); ptr != end; ++ptr) { - if (!m_declaredVariables.contains(*ptr)) - writtenVariables.add(*ptr); - } } - - void getCapturedVariables(IdentifierSet& capturedVariables) + + void getCapturedVars(IdentifierSet& capturedVariables, bool& modifiedParameter, bool& modifiedArguments) { if (m_needsFullActivation || m_usesEval) { - capturedVariables.swap(m_declaredVariables); + modifiedParameter = true; + for (auto& entry : m_declaredVariables) + capturedVariables.add(entry.key); return; } - for (IdentifierSet::iterator ptr = m_closedVariables.begin(); ptr != m_closedVariables.end(); ++ptr) { - if (!m_declaredVariables.contains(*ptr)) + for (IdentifierSet::iterator ptr = m_closedVariableCandidates.begin(); ptr != m_closedVariableCandidates.end(); ++ptr) { + // We refer to m_declaredVariables here directly instead of a hasDeclaredVariable because we want to mark the callee as captured. + 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<StringImpl> >& vector) + 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)) + if (m_declaredVariables.contains(*it) || m_lexicalVariables.contains(*it)) continue; vector.append(*it); } @@ -313,24 +585,69 @@ struct Scope { } private: + void setIsFunction() + { + m_isFunction = true; + m_isFunctionBoundary = true; + m_hasArguments = true; + setIsLexicalScope(); + m_isGenerator = false; + } + + void setIsGeneratorFunction() + { + setIsFunction(); + m_isGenerator = true; + } + + void setIsGenerator() + { + setIsFunction(); + m_isGenerator = true; + m_hasArguments = false; + } + + void setIsArrowFunction() + { + setIsFunction(); + m_isArrowFunction = true; + } + + void setIsModule() + { + m_moduleScopeData = ModuleScopeData::create(); + } + const VM* m_vm; bool m_shadowsArguments : 1; bool m_usesEval : 1; bool m_needsFullActivation : 1; - bool m_allowsNewDecls : 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_isGenerator : 1; + bool m_isArrowFunction : 1; + bool m_isLexicalScope : 1; bool m_isFunctionBoundary : 1; bool m_isValidStrictMode : 1; + bool m_hasArguments : 1; + unsigned m_constructorKind : 2; + unsigned m_expectedSuperBinding : 2; int m_loopDepth; int m_switchDepth; typedef Vector<ScopeLabelInfo, 2> LabelStack; - OwnPtr<LabelStack> m_labels; - IdentifierSet m_declaredVariables; + std::unique_ptr<LabelStack> m_labels; + IdentifierSet m_declaredParameters; + VariableEnvironment m_declaredVariables; + VariableEnvironment m_lexicalVariables; IdentifierSet m_usedVariables; - IdentifierSet m_closedVariables; + IdentifierSet m_closedVariableCandidates; IdentifierSet m_writtenVariables; + RefPtr<ModuleScopeData> m_moduleScopeData { }; }; typedef Vector<Scope, 10> ScopeStack; @@ -360,17 +677,27 @@ private: unsigned m_index; }; +enum class ArgumentType { + Normal, + Spread +}; + template <typename LexerType> class Parser { WTF_MAKE_NONCOPYABLE(Parser); WTF_MAKE_FAST_ALLOCATED; public: - Parser(VM*, const SourceCode&, FunctionParameters*, const Identifier&, JSParserStrictness, JSParserMode); + Parser( + VM*, const SourceCode&, JSParserBuiltinMode, JSParserStrictMode, SourceParseMode, SuperBinding, + ConstructorKind defaultConstructorKind = ConstructorKind::None, ThisTDZMode = ThisTDZMode::CheckIfNeeded); ~Parser(); template <class ParsedNode> - PassRefPtr<ParsedNode> parse(ParserError&); + std::unique_ptr<ParsedNode> parse(ParserError&, const Identifier&, SourceParseMode); + + JSTextPosition positionBeforeLastNewline() const { return m_lexer->positionBeforeLastNewline(); } + JSTokenLocation locationBeforeLastToken() const { return m_lexer->lastTokenLocation(); } private: struct AllowInOverride { @@ -410,60 +737,269 @@ 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; + }; + + enum ExpressionErrorClass { + ErrorIndicatesNothing, + ErrorIndicatesPattern + }; + + struct ExpressionErrorClassifier { + ExpressionErrorClassifier(Parser* parser) + : m_class(ErrorIndicatesNothing) + , m_previous(parser->m_expressionErrorClassifier) + , m_parser(parser) + { + m_parser->m_expressionErrorClassifier = this; + } + + ~ExpressionErrorClassifier() + { + m_parser->m_expressionErrorClassifier = m_previous; + } + + void classifyExpressionError(ExpressionErrorClass classification) + { + if (m_class != ErrorIndicatesNothing) + return; + m_class = classification; + } + + void reclassifyExpressionError(ExpressionErrorClass oldClassification, ExpressionErrorClass classification) + { + if (m_class != oldClassification) + return; + m_class = classification; + } + + void propagateExpressionErrorClass() + { + if (m_previous && m_class != ErrorIndicatesNothing) + m_previous->m_class = m_class; + } + + bool indicatesPossiblePattern() const { return m_class == ErrorIndicatesPattern; } + + private: + ExpressionErrorClass m_class; + ExpressionErrorClassifier* m_previous; + Parser* m_parser; + }; + + ALWAYS_INLINE void classifyExpressionError(ExpressionErrorClass classification) + { + if (m_expressionErrorClassifier) + m_expressionErrorClassifier->classifyExpressionError(classification); + } + + ALWAYS_INLINE void reclassifyExpressionError(ExpressionErrorClass oldClassification, ExpressionErrorClass classification) + { + if (m_expressionErrorClassifier) + m_expressionErrorClassifier->reclassifyExpressionError(oldClassification, classification); + } + + ALWAYS_INLINE DestructuringKind destructuringKindFromDeclarationType(DeclarationType type) + { + switch (type) { + case DeclarationType::VarDeclaration: + return DestructuringKind::DestructureToVariables; + case DeclarationType::LetDeclaration: + return DestructuringKind::DestructureToLet; + case DeclarationType::ConstDeclaration: + return DestructuringKind::DestructureToConst; + } + + RELEASE_ASSERT_NOT_REACHED(); + return DestructuringKind::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 currentVariableScope() + { + unsigned i = m_scopeStack.size() - 1; + ASSERT(i < m_scopeStack.size()); + while (!m_scopeStack[i].allowsVarDeclarations()) { + i--; + ASSERT(i < m_scopeStack.size()); + } + return ScopeRef(&m_scopeStack, i); + } + + ScopeRef currentFunctionScope() + { + unsigned i = m_scopeStack.size() - 1; + ASSERT(i < m_scopeStack.size()); + while (i && !m_scopeStack[i].isFunctionBoundary()) { + i--; + ASSERT(i < m_scopeStack.size()); + } + // When reaching the top level scope (it can be non function scope), we return it. + return ScopeRef(&m_scopeStack, i); + } + + ScopeRef closestParentNonArrowFunctionNonLexicalScope() + { + unsigned i = m_scopeStack.size() - 1; + ASSERT(i < m_scopeStack.size() && m_scopeStack.size()); + while (i && (!m_scopeStack[i].isFunctionBoundary() || m_scopeStack[i].isArrowFunction())) + i--; + // When reaching the top level scope (it can be non function scope), we return it. + return ScopeRef(&m_scopeStack, i); + } ScopeRef pushScope() { bool isFunction = false; bool isStrict = false; + bool isGenerator = false; if (!m_scopeStack.isEmpty()) { isStrict = m_scopeStack.last().strictMode(); isFunction = m_scopeStack.last().isFunction(); + isGenerator = m_scopeStack.last().isGenerator(); } - m_scopeStack.append(Scope(m_vm, isFunction, isStrict)); + m_scopeStack.append(Scope(m_vm, isFunction, isGenerator, isStrict)); return currentScope(); } - bool popScopeInternal(ScopeRef& scope, bool shouldTrackClosedVariables) + void popScopeInternal(ScopeRef& scope, bool shouldTrackClosedVariables) { ASSERT_UNUSED(scope, scope.index() == m_scopeStack.size() - 1); ASSERT(m_scopeStack.size() > 1); - bool result = m_scopeStack[m_scopeStack.size() - 2].collectFreeVariables(&m_scopeStack.last(), shouldTrackClosedVariables); + 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(); - return result; } - bool popScope(ScopeRef& scope, bool shouldTrackClosedVariables) + ALWAYS_INLINE void popScope(ScopeRef& scope, bool shouldTrackClosedVariables) { - return popScopeInternal(scope, shouldTrackClosedVariables); + popScopeInternal(scope, shouldTrackClosedVariables); } - bool popScope(AutoPopScopeRef& scope, bool shouldTrackClosedVariables) + ALWAYS_INLINE void popScope(AutoPopScopeRef& scope, bool shouldTrackClosedVariables) { scope.setPopped(); - return popScopeInternal(scope, shouldTrackClosedVariables); + popScopeInternal(scope, shouldTrackClosedVariables); + } + + ALWAYS_INLINE void popScope(AutoCleanupLexicalScope& cleanupScope, bool shouldTrackClosedVariables) + { + RELEASE_ASSERT(cleanupScope.isValid()); + ScopeRef& scope = cleanupScope.scope(); + cleanupScope.setPopped(); + popScopeInternal(scope, shouldTrackClosedVariables); } - bool declareVariable(const Identifier* ident) + DeclarationResultMask declareVariable(const Identifier* ident, DeclarationType type = DeclarationType::VarDeclaration, DeclarationImportType importType = DeclarationImportType::NotImported) { + if (type == DeclarationType::VarDeclaration) + return currentVariableScope()->declareVariable(ident); + unsigned i = m_scopeStack.size() - 1; ASSERT(i < m_scopeStack.size()); - while (!m_scopeStack[i].allowsNewDecls()) { + 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].declareVariable(ident); + + 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) + 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) @@ -472,14 +1008,13 @@ private: } Parser(); - String parseInner(); + String parseInner(const Identifier&, SourceParseMode); - void didFinishParsing(SourceElements*, ParserArenaData<DeclarationStacks::VarStack>*, - ParserArenaData<DeclarationStacks::FunctionStack>*, CodeFeatures, int, IdentifierSet&); + void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&, VariableEnvironment&, CodeFeatures, int); // Used to determine type of error to report. - bool isFunctionBodyNode(ScopeNode*) { return false; } - bool isFunctionBodyNode(FunctionBodyNode*) { return true; } + bool isFunctionMetadataNode(ScopeNode*) { return false; } + bool isFunctionMetadataNode(FunctionMetadataNode*) { return true; } ALWAYS_INLINE void next(unsigned lexerFlags = 0) { @@ -489,6 +1024,8 @@ private: m_lastTokenEndPosition = JSTextPosition(lastLine, lastTokenEnd, lastTokenLineStart); m_lexer->setLastLineNumber(lastLine); m_token.m_type = m_lexer->lex(&m_token, lexerFlags, strictMode()); + if (UNLIKELY(m_token.m_type == CONSTTOKEN && m_vm->shouldRewriteConstAsVar())) + m_token.m_type = VAR; } ALWAYS_INLINE void nextExpectIdentifier(unsigned lexerFlags = 0) @@ -513,8 +1050,9 @@ private: next(flags); return result; } - - ALWAYS_INLINE String getToken() { + + void printUnexpectedTokenText(WTF::PrintStream&); + ALWAYS_INLINE StringView getToken() { SourceProvider* sourceProvider = m_source->provider(); return sourceProvider->getRange(tokenStart(), tokenEndPosition().offset); } @@ -524,6 +1062,21 @@ private: 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 unsigned tokenStart() { return m_token.m_location.startOffset; @@ -559,281 +1112,23 @@ private: return m_token.m_location; } - const char* getTokenName(JSTokenType tok) - { - switch (tok) { - case NULLTOKEN: - return "null"; - case TRUETOKEN: - return "true"; - case FALSETOKEN: - return "false"; - case BREAK: - return "break"; - case CASE: - return "case"; - case DEFAULT: - return "default"; - case FOR: - return "for"; - case NEW: - return "new"; - case VAR: - return "var"; - case CONSTTOKEN: - return "const"; - case CONTINUE: - return "continue"; - case FUNCTION: - return "function"; - case IF: - return "if"; - case THISTOKEN: - return "this"; - case DO: - return "do"; - case WHILE: - return "while"; - case SWITCH: - return "switch"; - case WITH: - return "with"; - case THROW: - return "throw"; - case TRY: - return "try"; - case CATCH: - return "catch"; - case FINALLY: - return "finally"; - case DEBUGGER: - return "debugger"; - case ELSE: - return "else"; - case OPENBRACE: - return "{"; - case CLOSEBRACE: - return "}"; - case OPENPAREN: - return "("; - case CLOSEPAREN: - return ")"; - case OPENBRACKET: - return "["; - case CLOSEBRACKET: - return "]"; - case COMMA: - return ","; - case QUESTION: - return "?"; - case SEMICOLON: - return ";"; - case COLON: - return ":"; - case DOT: - return "."; - case EQUAL: - return "="; - case PLUSEQUAL: - return "+="; - case MINUSEQUAL: - return "-="; - case MULTEQUAL: - return "*="; - case DIVEQUAL: - return "/="; - case LSHIFTEQUAL: - return "<<="; - case RSHIFTEQUAL: - return ">>="; - case URSHIFTEQUAL: - return ">>>="; - case ANDEQUAL: - return "&="; - case MODEQUAL: - return "%="; - case XOREQUAL: - return "^="; - case OREQUAL: - return "|="; - case AUTOPLUSPLUS: - case PLUSPLUS: - return "++"; - case AUTOMINUSMINUS: - case MINUSMINUS: - return "--"; - case EXCLAMATION: - return "!"; - case TILDE: - return "~"; - case TYPEOF: - return "typeof"; - case VOIDTOKEN: - return "void"; - case DELETETOKEN: - return "delete"; - case OR: - return "||"; - case AND: - return "&&"; - case BITOR: - return "|"; - case BITXOR: - return "^"; - case BITAND: - return "&"; - case EQEQ: - return "=="; - case NE: - return "!="; - case STREQ: - return "==="; - case STRNEQ: - return "!=="; - case LT: - return "<"; - case GT: - return ">"; - case LE: - return "<="; - case GE: - return ">="; - case INSTANCEOF: - return "instanceof"; - case INTOKEN: - return "in"; - case LSHIFT: - return "<<"; - case RSHIFT: - return ">>"; - case URSHIFT: - return ">>>"; - case PLUS: - return "+"; - case MINUS: - return "-"; - case TIMES: - return "*"; - case DIVIDE: - return "/"; - case MOD: - return "%"; - case RETURN: - case RESERVED_IF_STRICT: - case RESERVED: - case NUMBER: - case IDENT: - case STRING: - case UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK: - case UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK: - case UNTERMINATED_MULTILINE_COMMENT_ERRORTOK: - case UNTERMINATED_NUMERIC_LITERAL_ERRORTOK: - case UNTERMINATED_STRING_LITERAL_ERRORTOK: - case INVALID_IDENTIFIER_ESCAPE_ERRORTOK: - case INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK: - case INVALID_NUMERIC_LITERAL_ERRORTOK: - case INVALID_OCTAL_NUMBER_ERRORTOK: - case INVALID_STRING_LITERAL_ERRORTOK: - case ERRORTOK: - case EOFTOK: - return 0; - case LastUntaggedToken: - break; - } - RELEASE_ASSERT_NOT_REACHED(); - return "internal error"; - } - - ALWAYS_INLINE void updateErrorMessageSpecialCase(JSTokenType expectedToken) - { - switch (expectedToken) { - case RESERVED_IF_STRICT: - m_errorMessage = "Use of reserved word '" + getToken() + "' in strict mode"; - return; - case RESERVED: - m_errorMessage = "Use of reserved word '" + getToken() + '\''; - return; - case NUMBER: - m_errorMessage = "Unexpected number '" + getToken() + '\''; - return; - case IDENT: - m_errorMessage = "Expected an identifier but found '" + getToken() + "' instead"; - return; - case STRING: - m_errorMessage = "Unexpected string " + getToken(); - return; - - case UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK: - case UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK: - m_errorMessage = "Incomplete unicode escape in identifier: '" + getToken() + '\''; - return; - case UNTERMINATED_MULTILINE_COMMENT_ERRORTOK: - m_errorMessage = "Unterminated multiline comment"; - return; - case UNTERMINATED_NUMERIC_LITERAL_ERRORTOK: - m_errorMessage = "Unterminated numeric literal '" + getToken() + '\''; - return; - case UNTERMINATED_STRING_LITERAL_ERRORTOK: - m_errorMessage = "Unterminated string literal '" + getToken() + '\''; - return; - case INVALID_IDENTIFIER_ESCAPE_ERRORTOK: - m_errorMessage = "Invalid escape in identifier: '" + getToken() + '\''; - return; - case INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK: - m_errorMessage = "Invalid unicode escape in identifier: '" + getToken() + '\''; - return; - case INVALID_NUMERIC_LITERAL_ERRORTOK: - m_errorMessage = "Invalid numeric literal: '" + getToken() + '\''; - return; - case INVALID_OCTAL_NUMBER_ERRORTOK: - m_errorMessage = "Invalid use of octal: '" + getToken() + '\''; - return; - case INVALID_STRING_LITERAL_ERRORTOK: - m_errorMessage = "Invalid string literal: '" + getToken() + '\''; - return; - case ERRORTOK: - m_errorMessage = "Unrecognized token '" + getToken() + '\''; - return; - case EOFTOK: - m_errorMessage = ASCIILiteral("Unexpected EOF"); - return; - case RETURN: - m_errorMessage = ASCIILiteral("Return statements are only valid inside functions"); - return; - default: - RELEASE_ASSERT_NOT_REACHED(); - m_errorMessage = ASCIILiteral("internal error"); - return; - } - } - - NEVER_INLINE void updateErrorMessage() + void setErrorMessage(const String& message) { - const char* name = getTokenName(m_token.m_type); - if (!name) - updateErrorMessageSpecialCase(m_token.m_type); - else - m_errorMessage = String::format("Unexpected token '%s'", name); - ASSERT(!m_errorMessage.isNull()); + m_errorMessage = message; } - NEVER_INLINE void updateErrorMessage(JSTokenType expectedToken) - { - const char* name = getTokenName(expectedToken); - if (name) - m_errorMessage = String::format("Expected token '%s'", name); - else { - if (!getTokenName(m_token.m_type)) - updateErrorMessageSpecialCase(m_token.m_type); - else - updateErrorMessageSpecialCase(expectedToken); - } - ASSERT(!m_errorMessage.isNull()); - } + 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* beforeMsg, String name, const char* afterMsg) + NEVER_INLINE void updateErrorWithNameAndMessage(const char* beforeMessage, const String& name, const char* afterMessage) { - m_errorMessage = makeString(beforeMsg, " '", name, "' ", afterMsg); + m_errorMessage = makeString(beforeMessage, " '", name, "' ", afterMessage); } NEVER_INLINE void updateErrorMessage(const char* msg) @@ -850,7 +1145,9 @@ private: void setStrictMode() { currentScope()->setStrictMode(); } bool strictMode() { return currentScope()->strictMode(); } bool isValidStrictMode() { return currentScope()->isValidStrictMode(); } - bool declareParameter(const Identifier* ident) { return currentScope()->declareParameter(ident); } + DeclarationResultMask declareParameter(const Identifier* ident) { return currentScope()->declareParameter(ident); } + bool declareRestOrNormalParameter(const Identifier&, const Identifier**); + bool breakIsValid() { ScopeRef current = currentScope(); @@ -872,7 +1169,7 @@ private: return true; } void pushLabel(const Identifier* label, bool isLoop) { currentScope()->pushLabel(label, isLoop); } - void popLabel() { currentScope()->popLabel(); } + void popLabel(ScopeRef scope) { scope->popLabel(); } ScopeLabelInfo* getLabel(const Identifier* label) { ScopeRef current = currentScope(); @@ -884,12 +1181,38 @@ private: } return result; } - - template <SourceElementsMode mode, class TreeBuilder> TreeSourceElements parseSourceElements(TreeBuilder&); + + // http://ecma-international.org/ecma-262/6.0/#sec-identifiers-static-semantics-early-errors + ALWAYS_INLINE bool isLETMaskedAsIDENT() + { + return match(LET) && !strictMode(); + } + + // http://ecma-international.org/ecma-262/6.0/#sec-identifiers-static-semantics-early-errors + ALWAYS_INLINE bool isYIELDMaskedAsIDENT(bool inGenerator) + { + return match(YIELD) && !strictMode() && !inGenerator; + } + + // http://ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions-static-semantics-early-errors + ALWAYS_INLINE bool matchSpecIdentifier(bool inGenerator) + { + return match(IDENT) || isLETMaskedAsIDENT() || isYIELDMaskedAsIDENT(inGenerator); + } + + ALWAYS_INLINE bool matchSpecIdentifier() + { + return matchSpecIdentifier(currentScope()->isGenerator()); + } + + template <class TreeBuilder> TreeSourceElements parseSourceElements(TreeBuilder&, SourceElementsMode); + template <class TreeBuilder> TreeSourceElements parseGeneratorFunctionSourceElements(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); - template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&); - template <class TreeBuilder> TreeStatement parseVarDeclaration(TreeBuilder&); - template <class TreeBuilder> TreeStatement parseConstDeclaration(TreeBuilder&); + enum class ExportType { Exported, NotExported }; + template <class TreeBuilder> TreeStatement parseClassDeclaration(TreeBuilder&, ExportType = ExportType::NotExported); + 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&); @@ -906,9 +1229,12 @@ private: template <class TreeBuilder> TreeStatement parseExpressionStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseExpressionOrLabelStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseIfStatement(TreeBuilder&); - template <class TreeBuilder> ALWAYS_INLINE TreeStatement parseBlockStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseBlockStatement(TreeBuilder&); template <class TreeBuilder> TreeExpression parseExpression(TreeBuilder&); + template <class TreeBuilder> TreeExpression parseAssignmentExpression(TreeBuilder&, ExpressionErrorClassifier&); template <class TreeBuilder> TreeExpression parseAssignmentExpression(TreeBuilder&); + template <class TreeBuilder> TreeExpression parseAssignmentExpressionOrPropagateErrorClass(TreeBuilder&); + template <class TreeBuilder> TreeExpression parseYieldExpression(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&); @@ -916,14 +1242,49 @@ private: 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> ALWAYS_INLINE TreeExpression parseStrictObjectLiteral(TreeBuilder&); + template <class TreeBuilder> NEVER_INLINE TreeExpression parseStrictObjectLiteral(TreeBuilder&); + template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseFunctionExpression(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeArguments parseArguments(TreeBuilder&); - template <bool strict, class TreeBuilder> ALWAYS_INLINE TreeProperty parseProperty(TreeBuilder&); - template <class TreeBuilder> ALWAYS_INLINE TreeFunctionBody parseFunctionBody(TreeBuilder&); - template <class TreeBuilder> ALWAYS_INLINE TreeFormalParameterList parseFormalParameters(TreeBuilder&); - template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseVarDeclarationList(TreeBuilder&, int& declarations, const Identifier*& lastIdent, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd); - template <class TreeBuilder> ALWAYS_INLINE TreeConstDeclList parseConstDeclarationList(TreeBuilder& context); - template <FunctionRequirements, bool nameIsInContainingScope, class TreeBuilder> bool parseFunctionInfo(TreeBuilder&, const Identifier*&, TreeFormalParameterList&, TreeFunctionBody&, unsigned& openBraceOffset, unsigned& closeBraceOffset, int& bodyStartLine, unsigned& bodyStartColumn); + template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseArgument(TreeBuilder&, ArgumentType&); + template <class TreeBuilder> TreeProperty parseProperty(TreeBuilder&, bool strict); + template <class TreeBuilder> TreeExpression parsePropertyMethod(TreeBuilder& context, const Identifier* methodName, bool isGenerator); + 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, SuperBinding, 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&, JSToken, AssignmentContext, const Identifier** duplicateIdentifier); + template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern createAssignmentElement(TreeBuilder&, TreeExpression&, const JSTextPosition&, const JSTextPosition&); + template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern parseBindingOrAssignmentElement(TreeBuilder& context, DestructuringKind, ExportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth); + template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern parseAssignmentElement(TreeBuilder& context, DestructuringKind, ExportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth); + 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&); + + enum class FunctionDefinitionType { Expression, Declaration, Method }; + template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionRequirements, SourceParseMode, bool nameIsInContainingScope, ConstructorKind, SuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>&, FunctionDefinitionType); + + ALWAYS_INLINE bool isArrowFunctionParameters(); + + template <class TreeBuilder> NEVER_INLINE int parseFunctionParameters(TreeBuilder&, SourceParseMode, ParserFunctionInfo<TreeBuilder>&); + template <class TreeBuilder> NEVER_INLINE typename TreeBuilder::FormalParameterList createGeneratorParameters(TreeBuilder&); + + template <class TreeBuilder> NEVER_INLINE TreeClassExpression parseClass(TreeBuilder&, FunctionRequirements, ParserClassInfo<TreeBuilder>&); + + 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); + + template <class TreeBuilder> ALWAYS_INLINE bool shouldCheckPropertyForUnderscoreProtoDuplicate(TreeBuilder&, const TreeProperty&); + ALWAYS_INLINE int isBinaryOperator(JSTokenType); bool allowAutomaticSemicolon(); @@ -936,9 +1297,14 @@ private: return allowAutomaticSemicolon(); } + void setEndOfStatement() + { + m_lexer->setTokenPosition(&m_token); + } + bool canRecurse() { - return m_stack.isSafeToRecurse(); + return m_vm->isSafeToRecurse(); } const JSTextPosition& lastTokenEndPosition() const @@ -951,30 +1317,114 @@ private: return !m_errorMessage.isNull(); } + enum class FunctionParsePhase { Parameters, Body }; + struct ParserState { + int assignmentCount { 0 }; + int nonLHSCount { 0 }; + int nonTrivialExpressionCount { 0 }; + FunctionParsePhase functionParsePhase { FunctionParsePhase::Body }; + const Identifier* lastIdentifier { nullptr }; + const Identifier* lastFunctionName { nullptr }; + }; + + // If you're using this directly, you probably should be using + // createSavePoint() instead. + ALWAYS_INLINE ParserState internalSaveParserState() + { + return m_parserState; + } + + ALWAYS_INLINE void restoreParserState(const ParserState& state) + { + m_parserState = state; + } + + struct LexerState { + int startOffset; + unsigned oldLineStartOffset; + unsigned oldLastLineNumber; + unsigned oldLineNumber; + }; + + // If you're using this directly, you probably should be using + // createSavePoint() instead. + // i.e, if you parse any kind of AssignmentExpression between + // saving/restoring, you should definitely not be using this directly. + ALWAYS_INLINE LexerState internalSaveLexerState() + { + LexerState 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 restoreLexerState(const LexerState& lexerState) + { + m_lexer->setOffset(lexerState.startOffset, lexerState.oldLineStartOffset); + next(); + m_lexer->setLastLineNumber(lexerState.oldLastLineNumber); + m_lexer->setLineNumber(lexerState.oldLineNumber); + } + + struct SavePoint { + ParserState parserState; + LexerState lexerState; + }; + + ALWAYS_INLINE SavePoint createSavePointForError() + { + SavePoint result; + result.parserState = internalSaveParserState(); + result.lexerState = internalSaveLexerState(); + return result; + } + + ALWAYS_INLINE SavePoint createSavePoint() + { + ASSERT(!hasError()); + return createSavePointForError(); + } + + ALWAYS_INLINE void restoreSavePointWithError(const SavePoint& savePoint, const String& message) + { + m_errorMessage = message; + restoreLexerState(savePoint.lexerState); + restoreParserState(savePoint.parserState); + } + + ALWAYS_INLINE void restoreSavePoint(const SavePoint& savePoint) + { + restoreSavePointWithError(savePoint, String()); + } + VM* m_vm; const SourceCode* m_source; - ParserArena* m_arena; - OwnPtr<LexerType> m_lexer; + ParserArena m_parserArena; + std::unique_ptr<LexerType> m_lexer; + FunctionParameters* m_parameters { nullptr }; + + ParserState m_parserState; - VMStackBounds m_stack; 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; RefPtr<SourceProviderCache> m_functionCache; SourceElements* m_sourceElements; - ParserArenaData<DeclarationStacks::VarStack>* m_varDeclarations; - ParserArenaData<DeclarationStacks::FunctionStack>* m_funcDeclarations; - IdentifierSet m_capturedVariables; + bool m_parsingBuiltin; + SuperBinding m_superBinding; + ConstructorKind m_defaultConstructorKind; + ThisTDZMode m_thisTDZMode; + VariableEnvironment m_varDeclarations; + DeclarationStacks::FunctionStack m_funcDeclarations; CodeFeatures m_features; int m_numConstants; + ExpressionErrorClassifier* m_expressionErrorClassifier; struct DepthManager { DepthManager(int* depth) @@ -997,13 +1447,13 @@ private: template <typename LexerType> template <class ParsedNode> -PassRefPtr<ParsedNode> Parser<LexerType>::parse(ParserError& error) +std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const Identifier& calleeName, SourceParseMode parseMode) { int errLine; String errMsg; if (ParsedNode::scopeIsFunction) - m_lexer->setIsReparsing(); + m_lexer->setIsReparsingFunction(); m_sourceElements = 0; @@ -1011,9 +1461,10 @@ PassRefPtr<ParsedNode> Parser<LexerType>::parse(ParserError& error) errMsg = String(); JSTokenLocation startLocation(tokenLocation()); - unsigned startColumn = m_source->startColumn(); + ASSERT(m_source->startColumn() > 0); + unsigned startColumn = m_source->startColumn() - 1; - String parseError = parseInner(); + String parseError = parseInner(calleeName, parseMode); int lineNumber = m_lexer->lineNumber(); bool lexError = m_lexer->sawError(); @@ -1027,24 +1478,33 @@ PassRefPtr<ParsedNode> Parser<LexerType>::parse(ParserError& error) m_sourceElements = 0; } - RefPtr<ParsedNode> result; + std::unique_ptr<ParsedNode> result; if (m_sourceElements) { JSTokenLocation endLocation; - endLocation.line = m_lexer->lastLineNumber(); + endLocation.line = m_lexer->lineNumber(); endLocation.lineStartOffset = m_lexer->currentLineStartOffset(); endLocation.startOffset = m_lexer->currentOffset(); - result = ParsedNode::create(m_vm, + unsigned endColumn = endLocation.startOffset - endLocation.lineStartOffset; + result = std::make_unique<ParsedNode>(m_parserArena, startLocation, endLocation, startColumn, + endColumn, m_sourceElements, - m_varDeclarations ? &m_varDeclarations->data : 0, - m_funcDeclarations ? &m_funcDeclarations->data : 0, - m_capturedVariables, + m_varDeclarations, + m_funcDeclarations, + currentScope()->finalizeLexicalEnvironment(), + m_parameters, *m_source, m_features, m_numConstants); - result->setLoc(m_source->firstLine(), m_lastTokenEndPosition.line, m_lexer->currentOffset(), m_lexer->currentLineStartOffset()); + result->setLoc(m_source->firstLine(), m_lexer->lineNumber(), m_lexer->currentOffset(), m_lexer->currentLineStartOffset()); + result->setEndOffset(m_lexer->currentOffset()); + + if (!isFunctionParseMode(parseMode)) { + m_source->provider()->setSourceURLDirective(m_lexer->sourceURL()); + m_source->provider()->setSourceMappingURLDirective(m_lexer->sourceMappingURL()); + } } 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 @@ -1052,14 +1512,19 @@ PassRefPtr<ParsedNode> Parser<LexerType>::parse(ParserError& error) // 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 (isFunctionBodyNode(static_cast<ParsedNode*>(0)) || m_hasStackOverflow) + 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; + else if (m_token.m_type & UnterminatedErrorTokenFlag) { + // Treat multiline capable unterminated literals as recoverable. + if (m_token.m_type == UNTERMINATED_MULTILINE_COMMENT_ERRORTOK || m_token.m_type == UNTERMINATED_TEMPLATE_LITERAL_ERRORTOK) + errorType = ParserError::SyntaxErrorRecoverable; + else + errorType = ParserError::SyntaxErrorUnterminatedLiteral; + } if (isEvalNode<ParsedNode>()) error = ParserError(ParserError::EvalError, errorType, m_token, errMsg, errLine); @@ -1068,23 +1533,38 @@ PassRefPtr<ParsedNode> Parser<LexerType>::parse(ParserError& error) } } - m_arena->reset(); - - return result.release(); + return result; } template <class ParsedNode> -PassRefPtr<ParsedNode> parse(VM* vm, const SourceCode& source, FunctionParameters* parameters, const Identifier& name, JSParserStrictness strictness, JSParserMode parserMode, ParserError& error) +std::unique_ptr<ParsedNode> parse( + VM* vm, const SourceCode& source, + const Identifier& name, JSParserBuiltinMode builtinMode, + JSParserStrictMode strictMode, SourceParseMode parseMode, SuperBinding superBinding, + 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, parameters, name, strictness, parserMode); - return parser.parse<ParsedNode>(error); + Parser<Lexer<LChar>> parser(vm, source, builtinMode, strictMode, parseMode, superBinding, 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"); + } + return result; } - Parser< Lexer<UChar> > parser(vm, source, parameters, name, strictness, parserMode); - return parser.parse<ParsedNode>(error); + ASSERT_WITH_MESSAGE(defaultConstructorKind == ConstructorKind::None, "BuiltinExecutables::createDefaultConstructor should always use a 8-bit string"); + Parser<Lexer<UChar>> parser(vm, source, builtinMode, strictMode, parseMode, superBinding, defaultConstructorKind, thisTDZMode); + std::unique_ptr<ParsedNode> result = parser.parse<ParsedNode>(error, name, parseMode); + if (positionBeforeLastNewline) + *positionBeforeLastNewline = parser.positionBeforeLastNewline(); + return result; } } // namespace diff --git a/Source/JavaScriptCore/parser/ParserArena.cpp b/Source/JavaScriptCore/parser/ParserArena.cpp index c53f30753..a27688770 100644 --- a/Source/JavaScriptCore/parser/ParserArena.cpp +++ b/Source/JavaScriptCore/parser/ParserArena.cpp @@ -27,7 +27,7 @@ #include "ParserArena.h" #include "Nodes.h" -#include <wtf/PassOwnPtr.h> +#include "JSCInlines.h" namespace JSC { @@ -62,38 +62,6 @@ ParserArena::~ParserArena() deallocateObjects(); } -bool ParserArena::contains(ParserArenaRefCounted* object) const -{ - return m_refCountedObjects.find(object) != notFound; -} - -ParserArenaRefCounted* ParserArena::last() const -{ - return m_refCountedObjects.last().get(); -} - -void ParserArena::removeLast() -{ - m_refCountedObjects.removeLast(); -} - -void ParserArena::reset() -{ - // Since this code path is used only when parsing fails, it's not bothering to reuse - // any of the memory the arena allocated. We could improve that later if we want to - // efficiently reuse the same arena. - - deallocateObjects(); - - m_freeableMemory = 0; - m_freeablePoolEnd = 0; - if (m_identifierArena) - m_identifierArena->clear(); - m_freeablePools.clear(); - m_deletableObjects.clear(); - m_refCountedObjects.clear(); -} - void ParserArena::allocateFreeablePool() { if (m_freeablePoolEnd) @@ -105,18 +73,4 @@ void ParserArena::allocateFreeablePool() ASSERT(freeablePool() == pool); } -bool ParserArena::isEmpty() const -{ - return !m_freeablePoolEnd - && (!m_identifierArena || m_identifierArena->isEmpty()) - && m_freeablePools.isEmpty() - && m_deletableObjects.isEmpty() - && m_refCountedObjects.isEmpty(); -} - -void ParserArena::derefWithArena(PassRefPtr<ParserArenaRefCounted> object) -{ - m_refCountedObjects.append(object); -} - } diff --git a/Source/JavaScriptCore/parser/ParserArena.h b/Source/JavaScriptCore/parser/ParserArena.h index 8d790c44c..2a7d44de7 100644 --- a/Source/JavaScriptCore/parser/ParserArena.h +++ b/Source/JavaScriptCore/parser/ParserArena.h @@ -26,13 +26,14 @@ #ifndef ParserArena_h #define ParserArena_h +#include "CommonIdentifiers.h" #include "Identifier.h" +#include <array> #include <wtf/SegmentedVector.h> namespace JSC { class ParserArenaDeletable; - class ParserArenaRefCounted; class IdentifierArena { WTF_MAKE_FAST_ALLOCATED; @@ -44,12 +45,11 @@ namespace JSC { 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); - bool isEmpty() const { return m_identifiers.isEmpty(); } - public: static const int MaximumCachableCharacter = 128; typedef SegmentedVector<Identifier, 64> IdentifierVector; @@ -64,42 +64,43 @@ namespace JSC { private: IdentifierVector m_identifiers; - FixedArray<Identifier*, MaximumCachableCharacter> m_shortIdentifiers; - FixedArray<Identifier*, MaximumCachableCharacter> m_recentIdentifiers; + 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 == 0) { - m_identifiers.append(Identifier(Identifier::EmptyIdentifier)); - return m_identifiers.last(); - } + if (!length) + return vm->propertyNames->emptyIdentifier; if (characters[0] >= MaximumCachableCharacter) { - m_identifiers.append(Identifier(vm, characters, length)); + 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(vm, characters, length)); + 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(vm, characters, length)); + 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 == 0) { - m_identifiers.append(Identifier(Identifier::EmptyIdentifier)); - return m_identifiers.last(); - } + if (!length) + return vm->propertyNames->emptyIdentifier; if (characters[0] >= MaximumCachableCharacter) { m_identifiers.append(Identifier::createLCharFromUChar(vm, characters, length)); return m_identifiers.last(); @@ -107,7 +108,7 @@ namespace JSC { if (length == 1) { if (Identifier* ident = m_shortIdentifiers[characters[0]]) return *ident; - m_identifiers.append(Identifier(vm, characters, length)); + m_identifiers.append(Identifier::fromString(vm, characters, length)); m_shortIdentifiers[characters[0]] = &m_identifiers.last(); return m_identifiers.last(); } @@ -121,7 +122,7 @@ namespace JSC { inline const Identifier& IdentifierArena::makeNumericIdentifier(VM* vm, double number) { - m_identifiers.append(Identifier(vm, String::numberToStringECMAScript(number))); + m_identifiers.append(Identifier::fromString(vm, String::numberToStringECMAScript(number))); return m_identifiers.last(); } @@ -138,7 +139,6 @@ namespace JSC { m_identifierArena.swap(otherArena.m_identifierArena); m_freeablePools.swap(otherArena.m_freeablePools); m_deletableObjects.swap(otherArena.m_deletableObjects); - m_refCountedObjects.swap(otherArena.m_refCountedObjects); } void* allocateFreeable(size_t size) @@ -161,18 +161,10 @@ namespace JSC { return deletable; } - void derefWithArena(PassRefPtr<ParserArenaRefCounted>); - bool contains(ParserArenaRefCounted*) const; - ParserArenaRefCounted* last() const; - void removeLast(); - - bool isEmpty() const; - JS_EXPORT_PRIVATE void reset(); - IdentifierArena& identifierArena() { if (UNLIKELY (!m_identifierArena)) - m_identifierArena = adoptPtr(new IdentifierArena); + m_identifierArena = std::make_unique<IdentifierArena>(); return *m_identifierArena; } @@ -191,10 +183,9 @@ namespace JSC { char* m_freeableMemory; char* m_freeablePoolEnd; - OwnPtr<IdentifierArena> m_identifierArena; + std::unique_ptr<IdentifierArena> m_identifierArena; Vector<void*> m_freeablePools; Vector<ParserArenaDeletable*> m_deletableObjects; - Vector<RefPtr<ParserArenaRefCounted> > m_refCountedObjects; }; } diff --git a/Source/JavaScriptCore/parser/ParserError.h b/Source/JavaScriptCore/parser/ParserError.h index baa4465d0..89a05ab42 100644 --- a/Source/JavaScriptCore/parser/ParserError.h +++ b/Source/JavaScriptCore/parser/ParserError.h @@ -27,13 +27,15 @@ #define ParserError_h #include "Error.h" +#include "ErrorHandlingScope.h" #include "ExceptionHelpers.h" #include "ParserTokens.h" #include <wtf/text/WTFString.h> namespace JSC { -struct ParserError { +class ParserError { +public: enum SyntaxErrorType { SyntaxErrorNone, SyntaxErrorIrrecoverable, @@ -49,60 +51,72 @@ struct ParserError { SyntaxError }; - ErrorType m_type; - SyntaxErrorType m_syntaxErrorType; - JSToken m_token; - String m_message; - int m_line; ParserError() : m_type(ErrorNone) , m_syntaxErrorType(SyntaxErrorNone) - , m_line(-1) { } explicit ParserError(ErrorType type) : m_type(type) , m_syntaxErrorType(SyntaxErrorNone) - , m_line(-1) { } ParserError(ErrorType type, SyntaxErrorType syntaxError, JSToken token) - : m_type(type) + : m_token(token) + , m_type(type) , m_syntaxErrorType(syntaxError) - , m_token(token) - , m_line(-1) { } - ParserError(ErrorType type, SyntaxErrorType syntaxError, JSToken token, String msg, int line) - : m_type(type) - , m_syntaxErrorType(syntaxError) - , m_token(token) + 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) { } - JSObject* toErrorObject(JSGlobalObject* globalObject, const SourceCode& source) + 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 0; + return nullptr; case SyntaxError: - return addErrorInfo(globalObject->globalExec(), createSyntaxError(globalObject, m_message), m_line, source); + return addErrorInfo( + exec, + createSyntaxError(exec, m_message), + overrideLineNumber == -1 ? m_line : overrideLineNumber, source); case EvalError: - return createSyntaxError(globalObject, m_message); - case StackOverflow: - return createStackOverflowError(globalObject); + return createSyntaxError(exec, m_message); + case StackOverflow: { + ErrorHandlingScope errorScope(globalObject->vm()); + return createStackOverflowError(exec); + } case OutOfMemory: - return createOutOfMemoryError(globalObject); + return createOutOfMemoryError(exec); } CRASH(); - return createOutOfMemoryError(globalObject); // Appease Qt bot + return nullptr; } -#undef GET_ERROR_CODE + +private: + JSToken m_token; + String m_message; + int m_line { -1 }; + ErrorType m_type; + SyntaxErrorType m_syntaxErrorType; }; } // namespace JSC diff --git a/Source/JavaScriptCore/parser/ParserFunctionInfo.h b/Source/JavaScriptCore/parser/ParserFunctionInfo.h new file mode 100644 index 000000000..4e8b65254 --- /dev/null +++ b/Source/JavaScriptCore/parser/ParserFunctionInfo.h @@ -0,0 +1,51 @@ +/* + * 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; +}; + +template <class TreeBuilder> +struct ParserClassInfo { + const Identifier* className = 0; +}; + +} + +#endif diff --git a/Source/JavaScriptCore/parser/ParserModes.h b/Source/JavaScriptCore/parser/ParserModes.h index a7383a3e1..4f6bac6c3 100644 --- a/Source/JavaScriptCore/parser/ParserModes.h +++ b/Source/JavaScriptCore/parser/ParserModes.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013, 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 @@ -27,28 +27,139 @@ #ifndef ParserModes_h #define ParserModes_h +#include "Identifier.h" + namespace JSC { -enum JSParserStrictness { JSParseNormal, JSParseStrict }; -enum JSParserMode { JSParseProgramCode, JSParseFunctionCode }; +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 FunctionNameIsInScopeToggle { FunctionNameIsNotInScope, FunctionNameIsInScope }; +enum FunctionMode { FunctionExpression, FunctionDeclaration }; + +enum class SourceParseMode : uint8_t { + NormalFunctionMode, + GeneratorBodyMode, + GeneratorWrapperFunctionMode, + GetterMode, + SetterMode, + MethodMode, + ArrowFunctionMode, + ProgramMode, + ModuleAnalyzeMode, + ModuleEvaluateMode +}; + +inline bool isFunctionParseMode(SourceParseMode parseMode) +{ + switch (parseMode) { + case SourceParseMode::NormalFunctionMode: + case SourceParseMode::GeneratorBodyMode: + case SourceParseMode::GeneratorWrapperFunctionMode: + 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::GeneratorBodyMode: + case SourceParseMode::GeneratorWrapperFunctionMode: + 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::GeneratorBodyMode: + case SourceParseMode::GeneratorWrapperFunctionMode: + 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; +typedef uint16_t 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 NoFeatures = 0; +const CodeFeatures EvalFeature = 1 << 0; +const CodeFeatures ArgumentsFeature = 1 << 1; +const CodeFeatures WithFeature = 1 << 2; +const CodeFeatures ThisFeature = 1 << 3; +const CodeFeatures StrictModeFeature = 1 << 4; +const CodeFeatures ShadowsArgumentsFeature = 1 << 5; +const CodeFeatures ModifiedParameterFeature = 1 << 6; +const CodeFeatures ModifiedArgumentsFeature = 1 << 7; +const CodeFeatures ArrowFunctionFeature = 1 << 8; +const CodeFeatures ArrowFunctionContextFeature = 1 << 9; -const CodeFeatures AllFeatures = EvalFeature | ArgumentsFeature | WithFeature | CatchFeature | ThisFeature | StrictModeFeature | ShadowsArgumentsFeature; +const CodeFeatures AllFeatures = EvalFeature | ArgumentsFeature | WithFeature | ThisFeature | StrictModeFeature | ShadowsArgumentsFeature | ModifiedParameterFeature | ArrowFunctionFeature | ArrowFunctionContextFeature; } // namespace JSC diff --git a/Source/JavaScriptCore/parser/ParserTokens.h b/Source/JavaScriptCore/parser/ParserTokens.h index 898fe62cb..aff4a3013 100644 --- a/Source/JavaScriptCore/parser/ParserTokens.h +++ b/Source/JavaScriptCore/parser/ParserTokens.h @@ -57,6 +57,7 @@ enum JSTokenType { FOR, NEW, VAR, + LET, CONSTTOKEN, CONTINUE, FUNCTION, @@ -75,6 +76,12 @@ enum JSTokenType { FINALLY, DEBUGGER, ELSE, + IMPORT, + EXPORT, + YIELD, + CLASSTOKEN, + EXTENDS, + SUPER, OPENBRACE = 0, CLOSEBRACE, OPENPAREN, @@ -83,9 +90,11 @@ enum JSTokenType { CLOSEBRACKET, COMMA, QUESTION, - NUMBER, + INTEGER, + DOUBLE, IDENT, STRING, + TEMPLATE, SEMICOLON, COLON, DOT, @@ -102,6 +111,8 @@ enum JSTokenType { MODEQUAL, XOREQUAL, OREQUAL, + DOTDOTDOT, + ARROWFUNCTION, LastUntaggedToken, // Begin tagged tokens @@ -144,10 +155,15 @@ enum JSTokenType { 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, + UNTERMINATED_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, + UNTERMINATED_HEX_NUMBER_ERRORTOK = 11 | ErrorTokenFlag | UnterminatedErrorTokenFlag, + UNTERMINATED_BINARY_NUMBER_ERRORTOK = 12 | ErrorTokenFlag | UnterminatedErrorTokenFlag, + UNTERMINATED_TEMPLATE_LITERAL_ERRORTOK = 13 | ErrorTokenFlag | UnterminatedErrorTokenFlag, + INVALID_TEMPLATE_LITERAL_ERRORTOK = 14 | ErrorTokenFlag, }; struct JSTextPosition { @@ -175,6 +191,11 @@ union JSTokenData { }; double doubleValue; const Identifier* ident; + struct { + const Identifier* cooked; + const Identifier* raw; + bool isTail; + }; }; struct JSTokenLocation { diff --git a/Source/JavaScriptCore/parser/ResultType.h b/Source/JavaScriptCore/parser/ResultType.h index ad86c98c7..a975fdd9c 100644 --- a/Source/JavaScriptCore/parser/ResultType.h +++ b/Source/JavaScriptCore/parser/ResultType.h @@ -29,6 +29,7 @@ namespace JSC { struct ResultType { + private: friend struct OperandTypes; typedef char Type; @@ -46,33 +47,34 @@ namespace JSC { : m_type(type) { } - - bool isInt32() + + public: + bool isInt32() const { return m_type & TypeInt32; } - bool definitelyIsNumber() + bool definitelyIsNumber() const { return (m_type & TypeBits) == TypeMaybeNumber; } - bool definitelyIsString() + bool definitelyIsString() const { return (m_type & TypeBits) == TypeMaybeString; } - bool definitelyIsBoolean() + bool definitelyIsBoolean() const { return (m_type & TypeBits) == TypeMaybeBool; } - bool mightBeNumber() + bool mightBeNumber() const { return m_type & TypeMaybeNumber; } - bool isNotNumber() + bool isNotNumber() const { return !mightBeNumber(); } diff --git a/Source/JavaScriptCore/parser/SourceCode.cpp b/Source/JavaScriptCore/parser/SourceCode.cpp new file mode 100644 index 000000000..c430e9793 --- /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().substring(m_startChar, m_endChar - m_startChar).utf8(); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/parser/SourceCode.h b/Source/JavaScriptCore/parser/SourceCode.h index f221f9244..73d6b54a7 100644 --- a/Source/JavaScriptCore/parser/SourceCode.h +++ b/Source/JavaScriptCore/parser/SourceCode.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * 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 @@ -10,7 +10,7 @@ * 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 Computer, Inc. ("Apple") nor the names of + * 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. * @@ -79,13 +79,21 @@ namespace JSC { bool isHashTableDeletedValue() const { return m_provider.isHashTableDeletedValue(); } - String toString() const + unsigned hash() const + { + ASSERT(m_provider); + return m_provider->hash(); + } + + StringView view() const { if (!m_provider) - return String(); + return StringView(); return m_provider->getRange(m_startChar, m_endChar); } + CString toUTF8() const; + intptr_t providerID() const { if (!m_provider) @@ -115,11 +123,9 @@ namespace JSC { { 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) { - ASSERT(provider()->source()[openBrace] == '{'); - ASSERT(provider()->source()[closeBrace] == '}'); startColumn += 1; // Convert to base 1. return SourceCode(provider(), openBrace, closeBrace + 1, firstLine, startColumn); } diff --git a/Source/JavaScriptCore/parser/SourceCodeKey.h b/Source/JavaScriptCore/parser/SourceCodeKey.h new file mode 100644 index 000000000..09f8f91e2 --- /dev/null +++ b/Source/JavaScriptCore/parser/SourceCodeKey.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2012 Apple Inc. All Rights Reserved. + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com> + * + * 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 SourceCodeKey_h +#define SourceCodeKey_h + +#include "ParserModes.h" +#include "SourceCode.h" +#include <wtf/HashTraits.h> + +namespace JSC { + +class SourceCodeKey { +public: + enum CodeType { EvalType, ProgramType, FunctionType, ModuleType }; + + SourceCodeKey() + { + } + + SourceCodeKey(const SourceCode& sourceCode, const String& name, CodeType codeType, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, ThisTDZMode thisTDZMode = ThisTDZMode::CheckIfNeeded) + : m_sourceCode(sourceCode) + , m_name(name) + , m_flags((static_cast<unsigned>(codeType) << 3) | (static_cast<unsigned>(builtinMode) << 2) | (static_cast<unsigned>(strictMode) << 1) | static_cast<unsigned>(thisTDZMode)) + , m_hash(sourceCode.hash()) + { + } + + SourceCodeKey(WTF::HashTableDeletedValueType) + : m_sourceCode(WTF::HashTableDeletedValue) + { + } + + bool isHashTableDeletedValue() const { return m_sourceCode.isHashTableDeletedValue(); } + + unsigned hash() const { return m_hash; } + + size_t length() const { return m_sourceCode.length(); } + + bool isNull() const { return m_sourceCode.isNull(); } + + // To save memory, we compute our string on demand. It's expected that source + // providers cache their strings to make this efficient. + StringView string() const { return m_sourceCode.view(); } + + bool operator==(const SourceCodeKey& other) const + { + return m_hash == other.m_hash + && length() == other.length() + && m_flags == other.m_flags + && m_name == other.m_name + && string() == other.string(); + } + +private: + SourceCode m_sourceCode; + String m_name; + unsigned m_flags; + unsigned m_hash; +}; + +struct SourceCodeKeyHash { + static unsigned hash(const SourceCodeKey& key) { return key.hash(); } + static bool equal(const SourceCodeKey& a, const SourceCodeKey& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +struct SourceCodeKeyHashTraits : SimpleClassHashTraits<SourceCodeKey> { + static const bool hasIsEmptyValueFunction = true; + static bool isEmptyValue(const SourceCodeKey& sourceCodeKey) { return sourceCodeKey.isNull(); } +}; + +} + +#endif // SourceCodeKey_h diff --git a/Source/JavaScriptCore/parser/SourceProvider.cpp b/Source/JavaScriptCore/parser/SourceProvider.cpp index 47b29aa37..475bc6dfc 100644 --- a/Source/JavaScriptCore/parser/SourceProvider.cpp +++ b/Source/JavaScriptCore/parser/SourceProvider.cpp @@ -25,8 +25,10 @@ #include "config.h" #include "SourceProvider.h" + +#include "JSCInlines.h" +#include <wtf/Lock.h> #include <wtf/StdLibExtras.h> -#include <wtf/TCSpinLock.h> namespace JSC { @@ -42,21 +44,18 @@ SourceProvider::~SourceProvider() { } -static inline size_t charPositionExtractor(const size_t* value) -{ - return *value; -} - -static TCMalloc_SpinLock providerIdLock = SPINLOCK_INITIALIZER; +static StaticLock providerIdLock; void SourceProvider::getID() { - SpinLockHolder lock(&providerIdLock); + LockHolder lock(&providerIdLock); if (!m_id) { static intptr_t nextProviderID = 0; m_id = ++nextProviderID; } } +StringSourceProvider::~StringSourceProvider() = default; + } // namespace JSC diff --git a/Source/JavaScriptCore/parser/SourceProvider.h b/Source/JavaScriptCore/parser/SourceProvider.h index 4ced9d9c7..1a9a6bd9e 100644 --- a/Source/JavaScriptCore/parser/SourceProvider.h +++ b/Source/JavaScriptCore/parser/SourceProvider.h @@ -10,7 +10,7 @@ * 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 Computer, Inc. ("Apple") nor the names of + * 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. * @@ -29,7 +29,6 @@ #ifndef SourceProvider_h #define SourceProvider_h -#include <wtf/PassOwnPtr.h> #include <wtf/RefCounted.h> #include <wtf/text/TextPosition.h> #include <wtf/text/WTFString.h> @@ -44,19 +43,20 @@ namespace JSC { JS_EXPORT_PRIVATE virtual ~SourceProvider(); - virtual const String& source() const = 0; - String getRange(int start, int end) const + virtual unsigned hash() const = 0; + virtual StringView source() const = 0; + StringView getRange(int start, int end) const { - return source().substringSharingImpl(start, end - start); + return source().substring(start, end - start); } - const String& url() { return m_url; } + const String& url() const { return m_url; } + const String& sourceURL() const { return m_sourceURLDirective; } + const String& sourceMappingURL() const { return m_sourceMappingURLDirective; } + TextPosition startPosition() const { return m_startPosition; } intptr_t asID() { - ASSERT(this); - if (!this) // Be defensive in release mode. - return nullID; if (!m_id) getID(); return m_id; @@ -66,11 +66,16 @@ namespace JSC { void setValid() { m_validated = true; } private: + template <typename T> friend class Parser; + + void setSourceURLDirective(const String& sourceURL) { m_sourceURLDirective = sourceURL; } + void setSourceMappingURLDirective(const String& sourceMappingURL) { m_sourceMappingURLDirective = sourceMappingURL; } JS_EXPORT_PRIVATE void getID(); - Vector<size_t>& lineStarts(); String m_url; + String m_sourceURLDirective; + String m_sourceMappingURLDirective; TextPosition m_startPosition; bool m_validated : 1; uintptr_t m_id : sizeof(uintptr_t) * 8 - 1; @@ -78,26 +83,69 @@ namespace JSC { class StringSourceProvider : public SourceProvider { public: - static PassRefPtr<StringSourceProvider> create(const String& source, const String& url, const TextPosition& startPosition = TextPosition::minimumPosition()) + static Ref<StringSourceProvider> create(const String& source, const String& url, const TextPosition& startPosition = TextPosition::minimumPosition()) { - return adoptRef(new StringSourceProvider(source, url, startPosition)); + return adoptRef(*new StringSourceProvider(source, url, startPosition)); } - virtual const String& source() const OVERRIDE + JS_EXPORT_PRIVATE ~StringSourceProvider() override; + + unsigned hash() const override { - return m_source; + return m_source.get().hash(); + } + + virtual StringView source() const override + { + return m_source.get(); } private: StringSourceProvider(const String& source, const String& url, const TextPosition& startPosition) : SourceProvider(url, startPosition) - , m_source(source) + , m_source(source.isNull() ? *StringImpl::empty() : *source.impl()) { } - String m_source; + Ref<StringImpl> 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)); + } + + unsigned hash() const override + { + return m_source.impl()->hash(); + } + + virtual StringView 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 index f284ee63a..ccc67272c 100644 --- a/Source/JavaScriptCore/parser/SourceProviderCache.cpp +++ b/Source/JavaScriptCore/parser/SourceProviderCache.cpp @@ -26,6 +26,8 @@ #include "config.h" #include "SourceProviderCache.h" +#include "JSCInlines.h" + namespace JSC { SourceProviderCache::~SourceProviderCache() @@ -38,9 +40,9 @@ void SourceProviderCache::clear() m_map.clear(); } -void SourceProviderCache::add(int sourcePosition, PassOwnPtr<SourceProviderCacheItem> item) +void SourceProviderCache::add(int sourcePosition, std::unique_ptr<SourceProviderCacheItem> item) { - m_map.add(sourcePosition, item); + m_map.add(sourcePosition, WTFMove(item)); } } diff --git a/Source/JavaScriptCore/parser/SourceProviderCache.h b/Source/JavaScriptCore/parser/SourceProviderCache.h index 06b6c45a2..5070b2811 100644 --- a/Source/JavaScriptCore/parser/SourceProviderCache.h +++ b/Source/JavaScriptCore/parser/SourceProviderCache.h @@ -28,8 +28,6 @@ #include "SourceProviderCacheItem.h" #include <wtf/HashMap.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> #include <wtf/RefCounted.h> namespace JSC { @@ -41,11 +39,11 @@ public: JS_EXPORT_PRIVATE ~SourceProviderCache(); JS_EXPORT_PRIVATE void clear(); - void add(int sourcePosition, PassOwnPtr<SourceProviderCacheItem>); + void add(int sourcePosition, std::unique_ptr<SourceProviderCacheItem>); const SourceProviderCacheItem* get(int sourcePosition) const { return m_map.get(sourcePosition); } private: - HashMap<int, OwnPtr<SourceProviderCacheItem> > m_map; + HashMap<int, std::unique_ptr<SourceProviderCacheItem>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> m_map; }; } diff --git a/Source/JavaScriptCore/parser/SourceProviderCacheItem.h b/Source/JavaScriptCore/parser/SourceProviderCacheItem.h index 8d35a3d27..81d221b39 100644 --- a/Source/JavaScriptCore/parser/SourceProviderCacheItem.h +++ b/Source/JavaScriptCore/parser/SourceProviderCacheItem.h @@ -27,22 +27,27 @@ #define SourceProviderCacheItem_h #include "ParserTokens.h" -#include <wtf/PassOwnPtr.h> #include <wtf/Vector.h> +#include <wtf/text/UniquedStringImpl.h> #include <wtf/text/WTFString.h> namespace JSC { struct SourceProviderCacheItemCreationParameters { - unsigned functionStart; - unsigned closeBraceLine; - unsigned closeBraceOffset; - unsigned closeBraceLineStartOffset; + unsigned functionNameStart; + unsigned lastTockenLine; + unsigned lastTockenStartOffset; + unsigned lastTockenEndOffset; + unsigned lastTockenLineStartOffset; + unsigned endFunctionOffset; + unsigned parameterCount; bool needsFullActivation; bool usesEval; bool strictMode; - Vector<RefPtr<StringImpl> > usedVariables; - Vector<RefPtr<StringImpl> > writtenVariables; + Vector<RefPtr<UniquedStringImpl>> usedVariables; + Vector<RefPtr<UniquedStringImpl>> writtenVariables; + bool isBodyArrowExpression { false }; + JSTokenType tokenType { CLOSEBRACE }; }; #if COMPILER(MSVC) @@ -53,43 +58,49 @@ struct SourceProviderCacheItemCreationParameters { class SourceProviderCacheItem { WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<SourceProviderCacheItem> create(const SourceProviderCacheItemCreationParameters&); + static std::unique_ptr<SourceProviderCacheItem> create(const SourceProviderCacheItemCreationParameters&); ~SourceProviderCacheItem(); - JSToken closeBraceToken() const + JSToken endFunctionToken() const { JSToken token; - token.m_type = CLOSEBRACE; - token.m_data.offset = closeBraceOffset; - token.m_location.startOffset = closeBraceOffset; - token.m_location.endOffset = closeBraceOffset + 1; - token.m_location.line = closeBraceLine; - token.m_location.lineStartOffset = closeBraceLineStartOffset; + 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 functionStart : 31; + unsigned functionNameStart : 31; bool needsFullActivation : 1; + + unsigned endFunctionOffset : 31; + unsigned lastTockenLine : 31; + unsigned lastTockenStartOffset : 31; + unsigned lastTockenEndOffset: 31; + unsigned parameterCount; - unsigned closeBraceLine : 31; bool usesEval : 1; - unsigned closeBraceOffset : 31; bool strictMode : 1; - unsigned closeBraceLineStartOffset; + unsigned lastTockenLineStartOffset; unsigned usedVariablesCount; unsigned writtenVariablesCount; - StringImpl** usedVariables() const { return const_cast<StringImpl**>(m_variables); } - StringImpl** writtenVariables() const { return const_cast<StringImpl**>(&m_variables[usedVariablesCount]); } + 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&); - StringImpl* m_variables[0]; + UniquedStringImpl* m_variables[0]; }; inline SourceProviderCacheItem::~SourceProviderCacheItem() @@ -98,24 +109,29 @@ inline SourceProviderCacheItem::~SourceProviderCacheItem() m_variables[i]->deref(); } -inline PassOwnPtr<SourceProviderCacheItem> SourceProviderCacheItem::create(const SourceProviderCacheItemCreationParameters& parameters) +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(StringImpl*) * variableCount; + size_t objectSize = sizeof(SourceProviderCacheItem) + sizeof(UniquedStringImpl*) * variableCount; void* slot = fastMalloc(objectSize); - return adoptPtr(new (slot) SourceProviderCacheItem(parameters)); + return std::unique_ptr<SourceProviderCacheItem>(new (slot) SourceProviderCacheItem(parameters)); } inline SourceProviderCacheItem::SourceProviderCacheItem(const SourceProviderCacheItemCreationParameters& parameters) - : functionStart(parameters.functionStart) + : functionNameStart(parameters.functionNameStart) , needsFullActivation(parameters.needsFullActivation) - , closeBraceLine(parameters.closeBraceLine) + , endFunctionOffset(parameters.endFunctionOffset) + , lastTockenLine(parameters.lastTockenLine) + , lastTockenStartOffset(parameters.lastTockenStartOffset) + , lastTockenEndOffset(parameters.lastTockenEndOffset) + , parameterCount(parameters.parameterCount) , usesEval(parameters.usesEval) - , closeBraceOffset(parameters.closeBraceOffset) , strictMode(parameters.strictMode) - , closeBraceLineStartOffset(parameters.closeBraceLineStartOffset) + , 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) { diff --git a/Source/JavaScriptCore/parser/SyntaxChecker.h b/Source/JavaScriptCore/parser/SyntaxChecker.h index 6e600ebbb..eed7b8ce3 100644 --- a/Source/JavaScriptCore/parser/SyntaxChecker.h +++ b/Source/JavaScriptCore/parser/SyntaxChecker.h @@ -27,7 +27,8 @@ #define SyntaxChecker_h #include "Lexer.h" -#include <yarr/YarrSyntaxChecker.h> +#include "ParserFunctionInfo.h" +#include "YarrSyntaxChecker.h" namespace JSC { @@ -68,14 +69,25 @@ public: { } - typedef SyntaxChecker FunctionBodyBuilder; enum { NoneExpr = 0, - ResolveEvalExpr, ResolveExpr, NumberExpr, StringExpr, + ResolveEvalExpr, ResolveExpr, IntegerExpr, DoubleExpr, StringExpr, ThisExpr, NullExpr, BoolExpr, RegExpExpr, ObjectLiteralExpr, - FunctionExpr, BracketExpr, DotExpr, CallExpr, + FunctionExpr, ClassExpr, SuperExpr, BracketExpr, DotExpr, CallExpr, NewExpr, PreExpr, PostExpr, UnaryExpr, BinaryExpr, - ConditionalExpr, AssignmentExpr, TypeofExpr, - DeleteExpr, ArrayLiteralExpr }; + ConditionalExpr, AssignmentExpr, TypeofExpr, NewTargetExpr, + DeleteExpr, ArrayLiteralExpr, BindingDestructuring, RestParameter, + ArrayDestructuring, ObjectDestructuring, SourceElementsResult, + FunctionBodyResult, SpreadExpr, ArgumentsResult, + PropertyListResult, ArgumentsListResult, ElementsListResult, + StatementResult, FormalParameterListResult, ClauseResult, + ClauseListResult, CommaExpr, DestructuringAssignment, + TemplateStringResult, TemplateStringListResult, + TemplateExpressionListResult, TemplateExpr, + TaggedTemplateExpr, YieldExpr, + ModuleNameResult, + ImportSpecifierResult, ImportSpecifierListResult, + ExportSpecifierResult, ExportSpecifierListResult + }; typedef int ExpressionType; typedef ExpressionType Expression; @@ -88,8 +100,8 @@ public: { } ALWAYS_INLINE Property(const Identifier* ident, PropertyNode::Type ty) - : name(ident) - , type(ty) + : name(ident) + , type(ty) { } ALWAYS_INLINE Property(PropertyNode::Type ty) @@ -104,24 +116,37 @@ public: typedef int PropertyList; typedef int ElementList; typedef int ArgumentsList; + typedef int TemplateExpressionList; + typedef int TemplateString; + typedef int TemplateStringList; + typedef int TemplateLiteral; typedef int FormalParameterList; typedef int FunctionBody; + typedef int ClassExpression; + 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 ConstDeclList; typedef int BinaryOperand; - + typedef int DestructuringPattern; + typedef DestructuringPattern ArrayPattern; + typedef DestructuringPattern ObjectPattern; + typedef DestructuringPattern RestPattern; + 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 1; } + int createSourceElements() { return SourceElementsResult; } ExpressionType makeFunctionCallNode(const JSTokenLocation&, int, int, int, int, int) { return CallExpr; } - void appendToComma(ExpressionType& base, ExpressionType right) { base = right; } - ExpressionType createCommaExpr(const JSTokenLocation&, ExpressionType, ExpressionType right) { return right; } + 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; } @@ -132,13 +157,16 @@ public: ExpressionType createLogicalNot(const JSTokenLocation&, ExpressionType) { return UnaryExpr; } ExpressionType createUnaryPlus(const JSTokenLocation&, ExpressionType) { return UnaryExpr; } ExpressionType createVoid(const JSTokenLocation&, ExpressionType) { return UnaryExpr; } - ExpressionType thisExpr(const JSTokenLocation&) { return ThisExpr; } - ExpressionType createResolve(const JSTokenLocation&, const Identifier*, int) { return ResolveExpr; } + 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, 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 createNumberExpr(const JSTokenLocation&, double) { return NumberExpr; } + 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; } @@ -148,79 +176,117 @@ public: 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) { return AssignmentExpr; } - ExpressionType createFunctionExpr(const JSTokenLocation&, const Identifier*, int, int, int, int, int, int, int) { return FunctionExpr; } - int createFunctionBody(const JSTokenLocation&, const JSTokenLocation&, int, bool) { return 1; } - void setFunctionStart(int, int) { } - int createArguments() { return 1; } - int createArguments(int) { return 1; } - int createArgumentsList(const JSTokenLocation&, int) { return 1; } - int createArgumentsList(const JSTokenLocation&, int, int) { return 1; } - template <bool complete> Property createProperty(const Identifier* name, int, PropertyNode::Type type) + 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; } + ExpressionType createYield(const JSTokenLocation&) { return YieldExpr; } + ExpressionType createYield(const JSTokenLocation&, ExpressionType, bool, int, int, int) { return YieldExpr; } + ClassExpression createClassExpr(const JSTokenLocation&, const Identifier&, VariableEnvironment&, ExpressionType, ExpressionType, PropertyList, PropertyList) { return ClassExpr; } + ExpressionType createFunctionExpr(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return FunctionExpr; } + int createFunctionMetadata(const JSTokenLocation&, const JSTokenLocation&, int, int, bool, int, int, int, ConstructorKind, SuperBinding, unsigned, SourceParseMode, bool) { 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; } + 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; } + + 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); } - template <bool complete> Property createProperty(VM* vm, double name, int, PropertyNode::Type type) + Property createProperty(VM* vm, ParserArena& parserArena, double name, int, PropertyNode::Type type, PropertyNode::PutType, bool complete) { if (!complete) return Property(type); - return Property(&vm->parserArena->identifierArena().makeNumericIdentifier(vm, name), type); + return Property(&parserArena.identifierArena().makeNumericIdentifier(vm, name), type); } - int createPropertyList(const JSTokenLocation&, Property) { return 1; } - int createPropertyList(const JSTokenLocation&, Property, int) { return 1; } - int createElementList(int, int) { return 1; } - int createElementList(int, int, int) { return 1; } - int createFormalParameterList(const Identifier&) { return 1; } - int createFormalParameterList(int, const Identifier&) { return 1; } - int createClause(int, int) { return 1; } - int createClauseList(int) { return 1; } - int createClauseList(int, int) { return 1; } - void setUsesArguments(int) { } - int createFuncDeclStatement(const JSTokenLocation&, const Identifier*, int, int, int, int, int, int, int) { return 1; } - int createBlockStatement(const JSTokenLocation&, int, int, int) { return 1; } - int createExprStatement(const JSTokenLocation&, int, int, int) { return 1; } - int createIfStatement(const JSTokenLocation&, int, int, int, int) { return 1; } - int createIfStatement(const JSTokenLocation&, int, int, int, int, int) { return 1; } - int createForLoop(const JSTokenLocation&, int, int, int, int, int, int) { return 1; } - int createForInLoop(const JSTokenLocation&, const Identifier*, int, int, int, int, int, int, int, int, int, int) { return 1; } - int createForInLoop(const JSTokenLocation&, int, int, int, int, int, int, int, int) { return 1; } - int createEmptyStatement(const JSTokenLocation&) { return 1; } - int createVarStatement(const JSTokenLocation&, int, int, int) { return 1; } - int createReturnStatement(const JSTokenLocation&, int, int, int) { return 1; } - int createBreakStatement(const JSTokenLocation&, int, int) { return 1; } - int createBreakStatement(const JSTokenLocation&, const Identifier*, int, int) { return 1; } - int createContinueStatement(const JSTokenLocation&, int, int) { return 1; } - int createContinueStatement(const JSTokenLocation&, const Identifier*, int, int) { return 1; } - int createTryStatement(const JSTokenLocation&, int, const Identifier*, int, int, int, int) { return 1; } - int createSwitchStatement(const JSTokenLocation&, int, int, int, int, int, int) { return 1; } - int createWhileStatement(const JSTokenLocation&, int, int, int, int) { return 1; } - int createWithStatement(const JSTokenLocation&, int, int, int, int, int, int) { return 1; } - int createDoWhileStatement(const JSTokenLocation&, int, int, int, int) { return 1; } - int createLabelStatement(const JSTokenLocation&, const Identifier*, int, int, int) { return 1; } - int createThrowStatement(const JSTokenLocation&, int, int, int) { return 1; } - int createDebugger(const JSTokenLocation&, int, int) { return 1; } - int createConstStatement(const JSTokenLocation&, int, int, int) { return 1; } - int appendConstDecl(const JSTokenLocation&, int, const Identifier*, int) { return 1; } - template <bool strict> Property createGetterOrSetterProperty(const JSTokenLocation&, PropertyNode::Type type, const Identifier* name, int, int, int, int, int, int, int) + Property createProperty(int, int, PropertyNode::Type type, PropertyNode::PutType, bool, SuperBinding = SuperBinding::NotNeeded) + { + 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 createElementList(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; } + int createClassDeclStatement(const JSTokenLocation&, ClassExpression, + const JSTextPosition&, const JSTextPosition&, int, int) { return StatementResult; } + 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, int, 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); } - template <bool strict> Property createGetterOrSetterProperty(VM* vm, const JSTokenLocation&, PropertyNode::Type type, double name, int, int, int, int, int, int, int) + Property createGetterOrSetterProperty(const JSTokenLocation&, PropertyNode::Type type, bool, int, const ParserFunctionInfo<SyntaxChecker>&, SuperBinding) + { + return Property(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(&vm->parserArena->identifierArena().makeNumericIdentifier(vm, name), type); + return Property(&parserArena.identifierArena().makeNumericIdentifier(vm, name), type); } void appendStatement(int, int) { } - void addVar(const Identifier*, bool) { } - int combineCommaNodes(const JSTokenLocation&, int, int) { return 1; } + int combineCommaNodes(const JSTokenLocation&, int, int) { return CommaExpr; } int evalCount() const { return 0; } void appendBinaryExpressionInfo(int& operandStackDepth, int expr, int, int, int, bool) { @@ -246,11 +312,86 @@ public: 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 1; } - const Identifier& getName(const Property& property) const { ASSERT(property.name); return *property.name; } + 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) + { + } + void appendObjectPatternEntry(ArrayPattern, const JSTokenLocation&, Expression, DestructuringPattern, Expression) + { + } + + DestructuringPattern createBindingLocation(const JSTokenLocation&, const Identifier&, const JSTextPosition&, const JSTextPosition&, AssignmentContext) + { + return BindingDestructuring; + } + RestPattern createRestParameter(const Identifier&, size_t, const JSTextPosition&, const JSTextPosition&) + { + return RestParameter; + } + DestructuringPattern createAssignmentElement(const Expression&, const JSTextPosition&, const JSTextPosition&) + { + return BindingDestructuring; + } + + bool isBindingNode(DestructuringPattern pattern) + { + return pattern == BindingDestructuring; + } + + bool isAssignmentLocation(ExpressionType type) + { + return type == ResolveExpr || type == DotExpr || type == BracketExpr; + } + + bool isObjectLiteral(ExpressionType type) + { + return type == ObjectLiteralExpr; + } + + bool isArrayLiteral(ExpressionType type) + { + return type == ArrayLiteralExpr; + } + + bool isObjectOrArrayLiteral(ExpressionType type) + { + return isObjectLiteral(type) || isArrayLiteral(type); + } + + void setEndOffset(int, int) { } + int endOffset(int) { return 0; } + void setStartOffset(int, int) { } + + void propagateArgumentsUse() { } + private: int m_topBinaryExpr; int m_topUnaryToken; diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.cpp b/Source/JavaScriptCore/parser/VariableEnvironment.cpp new file mode 100644 index 000000000..6dc2fbb2b --- /dev/null +++ b/Source/JavaScriptCore/parser/VariableEnvironment.cpp @@ -0,0 +1,99 @@ +/* + * 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" +#include <wtf/text/UniquedStringImpl.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..519d0121a --- /dev/null +++ b/Source/JavaScriptCore/parser/VariableEnvironment.h @@ -0,0 +1,102 @@ +/* + * 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> + +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 bool isImportedNamespace() const { return m_bits & IsImportedNamespace; } + + 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 setIsImportedNamespace() { m_bits |= IsImportedNamespace; } + + 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, + IsImportedNamespace = 1 << 6 + }; + 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); } + ALWAYS_INLINE Map::iterator find(const RefPtr<UniquedStringImpl>& identifier) { return m_map.find(identifier); } + ALWAYS_INLINE Map::const_iterator find(const RefPtr<UniquedStringImpl>& identifier) const { return m_map.find(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 |