diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/parser | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/JavaScriptCore/parser')
33 files changed, 9082 insertions, 2709 deletions
diff --git a/Source/JavaScriptCore/parser/ASTBuilder.h b/Source/JavaScriptCore/parser/ASTBuilder.h index 8ec1fd32a..b5d89dc91 100644 --- a/Source/JavaScriptCore/parser/ASTBuilder.h +++ b/Source/JavaScriptCore/parser/ASTBuilder.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2013, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,11 +23,14 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ASTBuilder_h -#define ASTBuilder_h +#pragma once +#include "BuiltinNames.h" +#include "BytecodeIntrinsicRegistry.h" +#include "MathCommon.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,9 +93,6 @@ public: UnaryExprContext(ASTBuilder&) {} }; - - typedef SyntaxChecker FunctionBodyBuilder; - typedef ExpressionNode* Expression; typedef JSC::SourceElements* SourceElements; typedef ArgumentsNode* Arguments; @@ -101,17 +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 RefPtr<DeconstructionPatternNode> DeconstructionPattern; - typedef RefPtr<ArrayPatternNode> ArrayPattern; - typedef RefPtr<ObjectPatternNode> ObjectPattern; - typedef RefPtr<BindingNode> BindingPattern; + 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; @@ -121,17 +131,11 @@ 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; } 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); @@ -139,6 +143,7 @@ public: ExpressionNode* makeDeleteNode(const JSTokenLocation&, ExpressionNode*, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end); ExpressionNode* makeNegateNode(const JSTokenLocation&, ExpressionNode*); ExpressionNode* makeBitwiseNotNode(const JSTokenLocation&, ExpressionNode*); + ExpressionNode* makePowNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); ExpressionNode* makeMultNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); ExpressionNode* makeDivNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); ExpressionNode* makeModNode(const JSTokenLocation&, ExpressionNode* left, ExpressionNode* right, bool rightHasAssignments); @@ -156,92 +161,167 @@ 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) { usesThis(); - return new (m_vm) ThisNode(location); + return new (m_parserArena) ThisNode(location); } - ExpressionNode* createResolve(const JSTokenLocation& location, const Identifier* ident, const JSTextPosition& start) + ExpressionNode* createSuperExpr(const JSTokenLocation& location) { - if (m_vm->propertyNames->arguments == *ident) + return new (m_parserArena) SuperNode(location); + } + ExpressionNode* createImportExpr(const JSTokenLocation& location, ExpressionNode* expr, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) + { + auto* node = new (m_parserArena) ImportNode(location, expr); + setExceptionLocation(node, start, divot, end); + return node; + } + ExpressionNode* createNewTargetExpr(const JSTokenLocation location) + { + usesNewTarget(); + return new (m_parserArena) NewTargetNode(location); + } + bool isNewTarget(ExpressionNode* node) { return node->isNewTarget(); } + ExpressionNode* createResolve(const JSTokenLocation& location, const Identifier& ident, const JSTextPosition& start, const JSTextPosition& end) + { + 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* createNumberExpr(const JSTokenLocation& location, double d) + ExpressionNode* createDoubleExpr(const JSTokenLocation& location, double d) { incConstants(); - return new (m_vm) NumberNode(location, d); + return new (m_parserArena) DoubleNode(location, d); + } + ExpressionNode* createIntegerExpr(const JSTokenLocation& location, double d) + { + incConstants(); + return new (m_parserArena) IntegerNode(location, d); } ExpressionNode* createString(const JSTokenLocation& location, const Identifier* string) { incConstants(); - return new (m_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); + if (base->isSuperNode()) + usesSuperProperty(); + + 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); + if (base->isSuperNode()) + usesSuperProperty(); + + 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_vm) SpreadExpressionNode(location, expression); + 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; } ExpressionNode* createRegExp(const JSTokenLocation& location, const Identifier& pattern, const Identifier& flags, const JSTextPosition& start) { - if (Yarr::checkSyntax(pattern.string())) + if (Yarr::checkSyntax(pattern.string(), flags.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); @@ -250,201 +330,338 @@ 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) + ExpressionNode* createAssignResolve(const JSTokenLocation& location, const Identifier& ident, ExpressionNode* rhs, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end, AssignmentContext assignmentContext) { - if (rhs->isFuncExprNode()) - static_cast<FuncExprNode*>(rhs)->body()->setInferredName(ident); - AssignResolveNode* node = new (m_vm) AssignResolveNode(location, ident, rhs); + if (rhs->isBaseFuncExprNode()) { + auto metadata = static_cast<BaseFuncExprNode*>(rhs)->metadata(); + metadata->setEcmaName(ident); + metadata->setInferredName(ident); + } else if (rhs->isClassExprNode()) + static_cast<ClassExprNode*>(rhs)->setEcmaName(ident); + AssignResolveNode* node = new (m_parserArena) AssignResolveNode(location, ident, rhs, assignmentContext); 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) + YieldExprNode* createYield(const JSTokenLocation& location) { - 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); + return new (m_parserArena) YieldExprNode(location, nullptr, /* delegate */ false); + } + + YieldExprNode* createYield(const JSTokenLocation& location, ExpressionNode* argument, bool delegate, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) + { + YieldExprNode* node = new (m_parserArena) YieldExprNode(location, argument, delegate); + setExceptionLocation(node, start, divot, end); + return node; + } + + AwaitExprNode* createAwait(const JSTokenLocation& location, ExpressionNode* argument, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) + { + ASSERT(argument); + AwaitExprNode* node = new (m_parserArena) AwaitExprNode(location, argument); + setExceptionLocation(node, start, divot, end); + return node; + } + + ClassExprNode* createClassExpr(const JSTokenLocation& location, const ParserClassInfo<ASTBuilder>& classInfo, VariableEnvironment& classEnvironment, ExpressionNode* constructor, + ExpressionNode* parentClass, PropertyListNode* instanceMethods, PropertyListNode* staticMethods) + { + SourceCode source = m_sourceCode->subExpression(classInfo.startOffset, classInfo.endOffset, classInfo.startLine, classInfo.startColumn); + return new (m_parserArena) ClassExprNode(location, *classInfo.className, source, classEnvironment, constructor, parentClass, instanceMethods, staticMethods); + } + + ExpressionNode* createFunctionExpr(const JSTokenLocation& location, const ParserFunctionInfo<ASTBuilder>& functionInfo) + { + FuncExprNode* result = new (m_parserArena) FuncExprNode(location, *functionInfo.name, functionInfo.body, + m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.parametersStartColumn)); + functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset); return result; } - FunctionBodyNode* createFunctionBody(const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, bool inStrictContext) + ExpressionNode* createGeneratorFunctionBody(const JSTokenLocation& location, const ParserFunctionInfo<ASTBuilder>& functionInfo, const Identifier& name) { - return FunctionBodyNode::create(m_vm, startLocation, endLocation, startColumn, endColumn, inStrictContext); + FuncExprNode* result = static_cast<FuncExprNode*>(createFunctionExpr(location, functionInfo)); + if (!name.isNull()) + result->metadata()->setInferredName(name); + return result; } - void setFunctionNameStart(FunctionBodyNode* body, int functionNameStart) + ExpressionNode* createAsyncFunctionBody(const JSTokenLocation& location, const ParserFunctionInfo<ASTBuilder>& functionInfo, SourceParseMode parseMode) { - body->setFunctionNameStart(functionNameStart); + if (parseMode == SourceParseMode::AsyncArrowFunctionBodyMode) { + SourceCode source = m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.body->isArrowFunctionBodyExpression() ? functionInfo.endOffset - 1 : functionInfo.endOffset, functionInfo.startLine, functionInfo.parametersStartColumn); + FuncExprNode* result = new (m_parserArena) FuncExprNode(location, *functionInfo.name, functionInfo.body, source); + functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset); + return result; + } + return createFunctionExpr(location, functionInfo); } - - NEVER_INLINE PropertyNode* createGetterOrSetterProperty(const JSTokenLocation& location, PropertyNode::Type type, bool, const Identifier* name, ParameterNode* params, FunctionBodyNode* body, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned bodyStartColumn) + + ExpressionNode* createMethodDefinition(const JSTokenLocation& location, const ParserFunctionInfo<ASTBuilder>& functionInfo) { - 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); + MethodDefinitionNode* result = new (m_parserArena) MethodDefinitionNode(location, *functionInfo.name, functionInfo.body, + m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.parametersStartColumn)); + functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset); + return result; } - NEVER_INLINE PropertyNode* createGetterOrSetterProperty(VM*, const JSTokenLocation& location, PropertyNode::Type type, bool, double name, ParameterNode* params, FunctionBodyNode* body, unsigned openBraceOffset, unsigned closeBraceOffset, int bodyStartLine, int bodyEndLine, unsigned bodyStartColumn) + 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 new (m_parserArena) FunctionMetadataNode( + m_parserArena, startLocation, endLocation, startColumn, endColumn, + functionKeywordStart, functionNameStart, parametersStart, + inStrictContext, constructorKind, superBinding, + parameterCount, mode, isArrowFunctionBodyExpression); + } + + ExpressionNode* createArrowFunctionExpr(const JSTokenLocation& location, const ParserFunctionInfo<ASTBuilder>& functionInfo) + { + usesArrowFunction(); + SourceCode source = m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.body->isArrowFunctionBodyExpression() ? functionInfo.endOffset - 1 : functionInfo.endOffset, functionInfo.startLine, functionInfo.parametersStartColumn); + 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; + } + + NEVER_INLINE PropertyNode* createGetterOrSetterProperty(const JSTokenLocation& location, PropertyNode::Type type, bool, + const Identifier* name, const ParserFunctionInfo<ASTBuilder>& functionInfo, bool isClassProperty) { - 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); + ASSERT(name); + functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset); + functionInfo.body->setEcmaName(*name); + functionInfo.body->setInferredName(*name); + SourceCode source = m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.parametersStartColumn); + MethodDefinitionNode* methodDef = new (m_parserArena) MethodDefinitionNode(location, m_vm->propertyNames->nullIdentifier, functionInfo.body, source); + return new (m_parserArena) PropertyNode(*name, methodDef, type, PropertyNode::Unknown, SuperBinding::Needed, isClassProperty); } - 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); } + NEVER_INLINE PropertyNode* createGetterOrSetterProperty(const JSTokenLocation& location, PropertyNode::Type type, bool, + ExpressionNode* name, const ParserFunctionInfo<ASTBuilder>& functionInfo, bool isClassProperty) + { + 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.parametersStartColumn); + MethodDefinitionNode* methodDef = new (m_parserArena) MethodDefinitionNode(location, m_vm->propertyNames->nullIdentifier, functionInfo.body, source); + return new (m_parserArena) PropertyNode(name, methodDef, type, PropertyNode::Unknown, SuperBinding::Needed, isClassProperty); + } - PropertyNode* createProperty(const Identifier* propertyName, ExpressionNode* node, PropertyNode::Type type, bool) + NEVER_INLINE PropertyNode* createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation& location, PropertyNode::Type type, bool, + double name, const ParserFunctionInfo<ASTBuilder>& functionInfo, bool isClassProperty) { - if (node->isFuncExprNode()) - static_cast<FuncExprNode*>(node)->body()->setInferredName(*propertyName); - return new (m_vm) PropertyNode(m_vm, *propertyName, node, 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.parametersStartColumn); + MethodDefinitionNode* methodDef = new (m_parserArena) MethodDefinitionNode(location, vm->propertyNames->nullIdentifier, functionInfo.body, source); + return new (m_parserArena) PropertyNode(ident, methodDef, type, PropertyNode::Unknown, SuperBinding::Needed, isClassProperty); } - PropertyNode* createProperty(VM*, double propertyName, ExpressionNode* node, PropertyNode::Type type, bool) { return new (m_vm) PropertyNode(m_vm, propertyName, node, type); } - PropertyNode* createProperty(VM*, ExpressionNode* propertyName, ExpressionNode* node, PropertyNode::Type type, bool) { 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); } + 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); } - ParameterNode* createFormalParameterList(DeconstructionPattern pattern) { return new (m_vm) ParameterNode(pattern); } - ParameterNode* createFormalParameterList(ParameterNode* list, DeconstructionPattern pattern) { return new (m_vm) ParameterNode(list, pattern); } + PropertyNode* createProperty(const Identifier* propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool, SuperBinding superBinding, bool isClassProperty) + { + if (node->isBaseFuncExprNode()) { + auto metadata = static_cast<BaseFuncExprNode*>(node)->metadata(); + metadata->setEcmaName(*propertyName); + metadata->setInferredName(*propertyName); + } else if (node->isClassExprNode()) + static_cast<ClassExprNode*>(node)->setEcmaName(*propertyName); + return new (m_parserArena) PropertyNode(*propertyName, node, type, putType, superBinding, isClassProperty); + } + PropertyNode* createProperty(VM* vm, ParserArena& parserArena, double propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool, SuperBinding superBinding, bool isClassProperty) + { + return new (m_parserArena) PropertyNode(parserArena.identifierArena().makeNumericIdentifier(vm, propertyName), node, type, putType, superBinding, isClassProperty); + } + PropertyNode* createProperty(ExpressionNode* propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool, SuperBinding superBinding, bool isClassProperty) { return new (m_parserArena) PropertyNode(propertyName, node, type, putType, superBinding, isClassProperty); } + PropertyListNode* createPropertyList(const JSTokenLocation& location, PropertyNode* property) { return new (m_parserArena) PropertyListNode(location, property); } + PropertyListNode* createPropertyList(const JSTokenLocation& location, PropertyNode* property, PropertyListNode* tail) { return new (m_parserArena) PropertyListNode(location, property, tail); } + + ElementNode* createElementList(int elisions, ExpressionNode* expr) { return new (m_parserArena) ElementNode(elisions, expr); } + ElementNode* createElementList(ElementNode* elems, int elisions, ExpressionNode* expr) { return new (m_parserArena) ElementNode(elems, elisions, expr); } + 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); + tryInferNameInPattern(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.parametersStartColumn)); + if (*functionInfo.name == m_vm->propertyNames->arguments) usesArguments(); - m_scope.m_funcDeclarations->data.append(decl->body()); - body->setLoc(bodyStartLine, bodyEndLine, location.startOffset, location.lineStartOffset); + 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, DeclarationStacks::FunctionStack&& functionStack) + { + BlockNode* block = new (m_parserArena) BlockNode(location, elements, lexicalVariables, WTFMove(functionStack)); 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, ExpressionNode* lhs, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end) + StatementNode* createForInLoop(const JSTokenLocation& location, ExpressionNode* lhs, ExpressionNode* iter, StatementNode* statements, const JSTokenLocation&, 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); + 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, PassRefPtr<DeconstructionPatternNode> pattern, 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 JSTokenLocation& declLocation, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end, VariableEnvironment& lexicalVariables) { - ForInNode* result = new (m_vm) ForInNode(m_vm, location, pattern.get(), iter, statements); - result->setLoc(start, end, location.startOffset, location.lineStartOffset); - setExceptionLocation(result, eStart, eDivot, eEnd); - return result; + auto lexpr = new (m_parserArena) DestructuringAssignmentNode(declLocation, pattern, nullptr); + return createForInLoop(location, lexpr, iter, statements, declLocation, 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) + StatementNode* createForOfLoop(const JSTokenLocation& location, ExpressionNode* lhs, ExpressionNode* iter, StatementNode* statements, const JSTokenLocation&, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end, VariableEnvironment& lexicalVariables) { - ForOfNode* result = new (m_vm) ForOfNode(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, PassRefPtr<DeconstructionPatternNode> pattern, ExpressionNode* iter, StatementNode* statements, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end) + StatementNode* createForOfLoop(const JSTokenLocation& location, DestructuringPatternNode* pattern, ExpressionNode* iter, StatementNode* statements, const JSTokenLocation& declLocation, const JSTextPosition& eStart, const JSTextPosition& eDivot, const JSTextPosition& eEnd, int start, int end, VariableEnvironment& lexicalVariables) { - ForOfNode* result = new (m_vm) ForOfNode(m_vm, location, pattern.get(), iter, statements); - result->setLoc(start, end, location.startOffset, location.lineStartOffset); - setExceptionLocation(result, eStart, eDivot, eEnd); - return result; + auto lexpr = new (m_parserArena) DestructuringAssignmentNode(declLocation, pattern, nullptr); + return createForOfLoop(location, lexpr, iter, statements, declLocation, eStart, eDivot, eEnd, start, end, lexicalVariables); } - StatementNode* createEmptyStatement(const JSTokenLocation& location) { return new (m_vm) EmptyStatementNode(location); } + bool isBindingNode(const DestructuringPattern& pattern) + { + return pattern->isBindingNode(); + } - StatementNode* createVarStatement(const JSTokenLocation& location, ExpressionNode* expr, int start, int end) + 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); + } + + bool shouldSkipPauseLocation(StatementNode* statement) const + { + return !statement || statement->isLabel(); + } + + StatementNode* createEmptyStatement(const JSTokenLocation& location) { return new (m_parserArena) EmptyStatementNode(location); } + + 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; @@ -452,46 +669,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, DeclarationStacks::FunctionStack&& functionStack) { - 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, WTFMove(functionStack)); 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; } @@ -499,14 +714,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; @@ -514,24 +729,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) @@ -539,23 +799,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(); - ASSERT(ident->impl()->isIdentifier()); - 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; } @@ -572,9 +827,14 @@ public: operatorStackDepth--; m_binaryOperatorStack.removeLast(); } - bool operatorStackHasHigherPrecedence(int&, int precedence) + bool operatorStackShouldReduce(int precedence) { - return precedence <= m_binaryOperatorStack.last().second; + // If the current precedence of the operator stack is the same to the one of the given operator, + // it depends on the associative whether we reduce the stack. + // If the operator is right associative, we should not reduce the stack right now. + if (precedence == m_binaryOperatorStack.last().second) + return !(m_binaryOperatorStack.last().first & RightAssociativeBinaryOpTokenFlag); + return precedence < m_binaryOperatorStack.last().second; } const BinaryOperand& getFromOperandStack(int i) { return m_binaryOperandStack[m_binaryOperandStack.size() + i]; } void shrinkOperandStackBy(int& operandStackDepth, int amount) @@ -638,58 +898,110 @@ 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* createDeconstructingAssignment(const JSTokenLocation& location, PassRefPtr<DeconstructionPatternNode> pattern, ExpressionNode* initializer) + ExpressionNode* createDestructuringAssignment(const JSTokenLocation& location, DestructuringPattern pattern, ExpressionNode* initializer) { - return new (m_vm) DeconstructingAssignmentNode(location, pattern.get(), initializer); + return new (m_parserArena) DestructuringAssignmentNode(location, pattern, initializer); } ArrayPattern createArrayPattern(const JSTokenLocation&) { - return ArrayPatternNode::create(m_vm); + return new (m_parserArena) ArrayPatternNode(); } void appendArrayPatternSkipEntry(ArrayPattern node, const JSTokenLocation& location) { - node->appendIndex(location, 0); + node->appendIndex(ArrayPatternNode::BindingType::Elision, location, 0, nullptr); } - void appendArrayPatternEntry(ArrayPattern node, const JSTokenLocation& location, DeconstructionPattern pattern) + void appendArrayPatternEntry(ArrayPattern node, const JSTokenLocation& location, DestructuringPattern pattern, ExpressionNode* defaultValue) { - node->appendIndex(location, pattern.get()); + node->appendIndex(ArrayPatternNode::BindingType::Element, location, pattern, defaultValue); + tryInferNameInPattern(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 ObjectPatternNode::create(m_vm); + return new (m_parserArena) ObjectPatternNode(); } - void appendObjectPatternEntry(ObjectPattern node, const JSTokenLocation& location, bool wasString, const Identifier& identifier, DeconstructionPattern pattern) + void appendObjectPatternEntry(ObjectPattern node, const JSTokenLocation& location, bool wasString, const Identifier& identifier, DestructuringPattern pattern, ExpressionNode* defaultValue) { - node->appendEntry(location, identifier, wasString, pattern.get()); + node->appendEntry(location, identifier, wasString, pattern, defaultValue); + tryInferNameInPattern(pattern, defaultValue); } - - BindingPattern createBindingLocation(const JSTokenLocation&, const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end) + + void appendObjectPatternEntry(ObjectPattern node, const JSTokenLocation& location, ExpressionNode* propertyExpression, DestructuringPattern pattern, ExpressionNode* defaultValue) + { + node->appendEntry(location, propertyExpression, pattern, defaultValue); + tryInferNameInPattern(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(DestructuringPatternNode* pattern, size_t numParametersToSkip) + { + return new (m_parserArena) RestParameterNode(pattern, numParametersToSkip); + } + + 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 BindingNode::create(m_vm, boundProperty, start, end); + return node->endOffset(); } + + void setStartOffset(CaseClauseNode* node, int offset) + { + node->setStartOffset(offset); + } + + void setStartOffset(Node* node, int offset) + { + node->setStartOffset(offset); + } + + JSTextPosition breakpointLocation(Node* node) + { + node->setNeedsDebugHook(); + return node->position(); + } + + 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; int m_features; int m_numConstants; }; @@ -702,20 +1014,58 @@ 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 usesSuperCall() { m_scope.m_features |= SuperCallFeature; } + void usesSuperProperty() { m_scope.m_features |= SuperPropertyFeature; } void usesEval() { m_evalCount++; m_scope.m_features |= EvalFeature; } - ExpressionNode* createNumber(const JSTokenLocation& location, double d) + void usesNewTarget() { m_scope.m_features |= NewTargetFeature; } + 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); + } + + void tryInferNameInPattern(DestructuringPattern pattern, ExpressionNode* defaultValue) + { + if (!defaultValue) + return; + + if (!pattern->isBindingNode()) + return; + + const Identifier& ident = static_cast<const BindingNode*>(pattern)->boundProperty(); + + if (defaultValue->isBaseFuncExprNode()) { + auto metadata = static_cast<BaseFuncExprNode*>(defaultValue)->metadata(); + metadata->setEcmaName(ident); + metadata->setInferredName(ident); + } else if (defaultValue->isClassExprNode()) + static_cast<ClassExprNode*>(defaultValue)->setEcmaName(ident); + } + VM* m_vm; + ParserArena& m_parserArena; SourceCode* m_sourceCode; Scope m_scope; Vector<BinaryOperand, 10, UnsafeVectorOverflow> m_binaryOperandStack; @@ -729,171 +1079,242 @@ 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::makePowNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) +{ + auto* strippedExpr1 = expr1->stripUnaryPlus(); + auto* strippedExpr2 = expr2->stripUnaryPlus(); + + if (strippedExpr1->isNumber() && strippedExpr2->isNumber()) { + const NumberNode& numberExpr1 = static_cast<NumberNode&>(*strippedExpr1); + const NumberNode& numberExpr2 = static_cast<NumberNode&>(*strippedExpr2); + return createNumberFromBinaryOperation(location, operationMathPow(numberExpr1.value(), numberExpr2.value()), numberExpr1, numberExpr2); + } + + if (strippedExpr1->isNumber()) + expr1 = strippedExpr1; + if (strippedExpr2->isNumber()) + expr2 = strippedExpr2; + + return new (m_parserArena) PowNode(location, expr1, expr2, rightHasAssignments); } ExpressionNode* ASTBuilder::makeMultNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) { + // FIXME: Unary + change the evaluation order. + // https://bugs.webkit.org/show_bug.cgi?id=159968 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) { + // FIXME: Unary + change the evaluation order. + // https://bugs.webkit.org/show_bug.cgi?id=159968 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) { + // FIXME: Unary + change the evaluation order. + // https://bugs.webkit.org/show_bug.cgi?id=159968 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) { + // FIXME: Unary + change the evaluation order. + // https://bugs.webkit.org/show_bug.cgi?id=159968 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->isSuperNode()) + usesSuperCall(); + + 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; } @@ -902,10 +1323,10 @@ ExpressionNode* ASTBuilder::makeBinaryNode(const JSTokenLocation& location, int { 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); @@ -917,37 +1338,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; } @@ -975,6 +1396,9 @@ ExpressionNode* ASTBuilder::makeBinaryNode(const JSTokenLocation& location, int case MOD: return makeModNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); + + case POW: + return makePowNode(location, lhs.first, rhs.first, rhs.second.hasAssignment); } CRASH(); return 0; @@ -983,50 +1407,55 @@ 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); + if (expr->isBaseFuncExprNode()) { + auto metadata = static_cast<BaseFuncExprNode*>(expr)->metadata(); + metadata->setEcmaName(resolve->identifier()); + metadata->setInferredName(resolve->identifier()); + } else if (expr->isClassExprNode()) + static_cast<ClassExprNode*>(expr)->setEcmaName(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; } ASSERT(loc->isDotAccessorNode()); 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); + if (expr->isBaseFuncExprNode()) { + // We don't also set the ecma name here because ES6 specifies that the + // function should not pick up the name of the dot->identifier(). + static_cast<BaseFuncExprNode*>(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); } } - -#endif diff --git a/Source/JavaScriptCore/parser/Keywords.table b/Source/JavaScriptCore/parser/Keywords.table index 527eada60..a0e516319 100644 --- a/Source/JavaScriptCore/parser/Keywords.table +++ b/Source/JavaScriptCore/parser/Keywords.table @@ -7,16 +7,21 @@ true TRUETOKEN false FALSETOKEN # Keywords. +async ASYNC +await AWAIT 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 +33,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 ca4bb4f0a..daea56d0e 100644 --- a/Source/JavaScriptCore/parser/Lexer.cpp +++ b/Source/JavaScriptCore/parser/Lexer.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012, 2013 Apple Inc. All Rights Reserved. + * Copyright (C) 2006-2009, 2011-2013, 2016 Apple Inc. All Rights Reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * Copyright (C) 2010 Zoltan Herczeg (zherczeg@inf.u-szeged.hu) * Copyright (C) 2012 Mathias Bynens (mathias@qiwi.be) @@ -25,28 +25,26 @@ #include "config.h" #include "Lexer.h" -#include "JSFunctionInlines.h" - -#include "JSGlobalObjectFunctions.h" +#include "BuiltinNames.h" #include "Identifier.h" -#include "NodeInfo.h" +#include "JSCInlines.h" +#include "JSFunctionInlines.h" +#include "KeywordLookup.h" +#include "Lexer.lut.h" #include "Nodes.h" -#include <wtf/dtoa.h> +#include "ParseInt.h" +#include "Parser.h" #include <ctype.h> #include <limits.h> #include <string.h> #include <wtf/Assertions.h> - -#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 { @@ -70,6 +68,7 @@ enum CharacterType { CharacterQuestion, CharacterTilde, CharacterQuote, + CharacterBackQuote, CharacterDot, CharacterSlash, CharacterBackSlash, @@ -90,6 +89,7 @@ enum CharacterType { // Other types (only one so far) CharacterWhiteSpace, + CharacterPrivateIdentifierStart }; // 256 Latin-1 codes @@ -158,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, @@ -190,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, @@ -486,10 +486,19 @@ static const LChar singleCharacterEscapeValuesForASCII[128] = { }; template <typename T> -Lexer<T>::Lexer(VM* vm) - : m_isReparsing(false) +Lexer<T>::Lexer(VM* vm, JSParserBuiltinMode builtinMode, JSParserScriptMode scriptMode) + : m_isReparsingFunction(false) , m_vm(vm) + , m_parsingBuiltinFunction(builtinMode == JSParserBuiltinMode::Builtin) + , m_scriptMode(scriptMode) +{ +} + +static inline JSTokenType tokenTypeForIntegerLikeToken(double doubleValue) { + if ((doubleValue || !std::signbit(doubleValue)) && static_cast<int64_t>(doubleValue) == doubleValue) + return INTEGER; + return DOUBLE; } template <typename T> @@ -502,21 +511,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)); } } @@ -532,13 +541,13 @@ void Lexer<T>::setCode(const SourceCode& source, ParserArena* arena) { m_arena = &arena->identifierArena(); - m_lineNumber = source.firstLine(); + m_lineNumber = source.firstLine().oneBasedInt(); 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; @@ -551,9 +560,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; @@ -595,22 +607,93 @@ 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() +struct ParsedUnicodeEscapeValue { + ParsedUnicodeEscapeValue(UChar32 value) + : m_value(value) + { + ASSERT(isValid()); + } + + enum SpecialValueType { Incomplete = -2, Invalid = -1 }; + ParsedUnicodeEscapeValue(SpecialValueType type) + : m_value(type) + { + } + + bool isValid() const { return m_value >= 0; } + bool isIncomplete() const { return m_value == Incomplete; } + + UChar32 value() const + { + ASSERT(isValid()); + return m_value; + } + +private: + UChar32 m_value; +}; + +template<typename CharacterType> +ParsedUnicodeEscapeValue Lexer<CharacterType>::parseUnicodeEscape() { - T char1 = peek(1); - T char2 = peek(2); - T char3 = peek(3); + 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) { + // For raw template literal syntax, we consume `NotEscapeSequence`. + // Here, we consume NotCodePoint's HexDigits. + // + // NotEscapeSequence :: + // u { [lookahread not one of HexDigit] + // u { NotCodePoint + // u { CodePoint [lookahead != }] + // + // NotCodePoint :: + // HexDigits but not if MV of HexDigits <= 0x10FFFF + // + // CodePoint :: + // HexDigits but not if MV of HexDigits > 0x10FFFF + shift(); + while (isASCIIHexDigit(m_current)) + shift(); - if (UNLIKELY(!isASCIIHexDigit(m_current) || !isASCIIHexDigit(char1) || !isASCIIHexDigit(char2) || !isASCIIHexDigit(char3))) - return UnicodeHexValue((m_code + 4) >= m_codeEnd ? UnicodeHexValue::IncompleteHex : UnicodeHexValue::InvalidHex); + return atEnd() ? ParsedUnicodeEscapeValue::Incomplete : ParsedUnicodeEscapeValue::Invalid; + } + shift(); + } while (m_current != '}'); + shift(); + return codePoint; + } + + auto character2 = peek(1); + auto character3 = peek(2); + auto character4 = peek(3); + if (UNLIKELY(!isASCIIHexDigit(m_current) || !isASCIIHexDigit(character2) || !isASCIIHexDigit(character3) || !isASCIIHexDigit(character4))) { + auto result = (m_code + 4) >= m_codeEnd ? ParsedUnicodeEscapeValue::Incomplete : ParsedUnicodeEscapeValue::Invalid; + + // For raw template literal syntax, we consume `NotEscapeSequence`. + // + // NotEscapeSequence :: + // u [lookahead not one of HexDigit][lookahead != {] + // u HexDigit [lookahead not one of HexDigit] + // u HexDigit HexDigit [lookahead not one of HexDigit] + // u HexDigit HexDigit HexDigit [lookahead not one of HexDigit] + while (isASCIIHexDigit(m_current)) + shift(); + + return result; + } - int result = convertUnicode(m_current, char1, char2, char3); + auto result = convertUnicode(m_current, character2, character3, character4); shift(); shift(); shift(); shift(); - return UnicodeHexValue(result); + return result; } template <typename T> @@ -635,6 +718,13 @@ ALWAYS_INLINE bool Lexer<T>::lastTokenWasRestrKeyword() const return m_lastToken == CONTINUE || m_lastToken == BREAK || m_lastToken == RETURN || m_lastToken == THROW; } +template <typename T> +ALWAYS_INLINE void Lexer<T>::skipWhitespace() +{ + while (isWhiteSpace(m_current)) + shift(); +} + static NEVER_INLINE bool isNonLatin1IdentStart(UChar c) { return U_GET_GC_MASK(c) & U_GC_L_MASK; @@ -650,18 +740,24 @@ 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) { + // 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; } @@ -673,11 +769,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) { @@ -753,7 +899,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) { @@ -765,7 +944,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(); @@ -779,18 +962,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->builtinNames().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; @@ -814,6 +1008,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(); @@ -826,6 +1024,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); } @@ -837,21 +1036,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->builtinNames().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; @@ -864,11 +1074,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) { @@ -887,19 +1095,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; @@ -912,23 +1119,19 @@ template <bool shouldCreateIdentifier> JSTokenType Lexer<T>::parseIdentifierSlow tokenData->ident = ident; } else - tokenData->ident = 0; + tokenData->ident = nullptr; - if (LIKELY(!bufferRequired && !(lexerFlags & LexerFlagsIgnoreReservedWords))) { + m_buffer16.shrink(0); + + 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; } @@ -961,7 +1164,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); @@ -971,7 +1174,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; @@ -982,7 +1185,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(); @@ -992,7 +1195,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); } @@ -1003,7 +1206,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; @@ -1011,7 +1214,112 @@ 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, LexerEscapeParseMode escapeParseMode> ALWAYS_INLINE auto Lexer<T>::parseComplexEscape(bool strictMode, T stringQuoteCharacter) -> StringParseResult +{ + if (m_current == 'x') { + shift(); + if (!isASCIIHexDigit(m_current) || !isASCIIHexDigit(peek(1))) { + // For raw template literal syntax, we consume `NotEscapeSequence`. + // + // NotEscapeSequence :: + // x [lookahread not one of HexDigit] + // x HexDigit [lookahread not one of HexDigit] + if (isASCIIHexDigit(m_current)) + shift(); + ASSERT(!isASCIIHexDigit(m_current)); + + m_lexErrorMessage = ASCIILiteral("\\x can only be followed by a hex character sequence"); + return atEnd() ? StringUnterminated : StringCannotBeParsed; + } + + T prev = m_current; + shift(); + if (shouldBuildStrings) + record16(convertHex(prev, m_current)); + shift(); + + return StringParsedSuccessfully; + } + + if (m_current == 'u') { + shift(); + + if (escapeParseMode == LexerEscapeParseMode::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 atEnd() ? 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)) { + // For raw template literal syntax, we consume `NotEscapeSequence`. + // + // NotEscapeSequence :: + // 0 DecimalDigit + // DecimalDigit but not 0 + if (character1 == '0') + shift(); + + m_lexErrorMessage = ASCIILiteral("The only valid numeric escape in strict mode is '\\0'"); + return atEnd() ? StringUnterminated : 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(); @@ -1033,94 +1341,225 @@ 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; + else { + StringParseResult result = parseComplexEscape<shouldBuildStrings, LexerEscapeParseMode::String>(strictMode, stringQuoteCharacter); + if (result != StringParsedSuccessfully) + return result; + } + + stringStart = currentSourcePtr(); + continue; + } + // Fast check for characters that require special handling. + // Catches 0, \n, \r, 0x2028, and 0x2029 as efficiently + // as possible, and lets through all common ASCII characters. + if (UNLIKELY(((static_cast<unsigned>(m_current) - 0xE) & 0x2000))) { + // New-line or end of input is not allowed + if (atEnd() || isLineTerminator(m_current)) { + m_lexErrorMessage = ASCIILiteral("Unexpected EOF"); + return atEnd() ? StringUnterminated : StringCannotBeParsed; + } + // Anything else is just a normal character + } + shift(); + } + + if (currentSourcePtr() != stringStart && shouldBuildStrings) + append16(stringStart, currentSourcePtr() - stringStart); + if (shouldBuildStrings) + tokenData->ident = makeIdentifier(m_buffer16.data(), m_buffer16.size()); + else + tokenData->ident = 0; + + m_buffer16.shrink(0); + return StringParsedSuccessfully; +} + +// 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> +typename Lexer<T>::StringParseResult Lexer<T>::parseTemplateLiteral(JSTokenData* tokenData, RawStringsBuildMode rawStringsBuildMode) +{ + bool parseCookedFailed = false; + 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()) + append16(stringStart, currentSourcePtr() - stringStart); + shift(); + + LChar escape = singleEscape(m_current); + + // Most common escape sequences first. + if (escape) { + record16(escape); shift(); - if (isASCIIOctalDigit(m_current)) { - // Two octal characters - T character2 = m_current; + } else if (UNLIKELY(isLineTerminator(m_current))) { + // Normalize <CR>, <CR><LF> to <LF>. + if (m_current == '\r') { + ASSERT_WITH_MESSAGE(rawStringStart != currentSourcePtr(), "We should have at least shifted the escape."); + + if (rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings) { + m_bufferForRawTemplateString16.append(rawStringStart, currentSourcePtr() - rawStringStart); + m_bufferForRawTemplateString16.append('\n'); + } + + lineNumberAdder.add(m_current); shift(); - if (character1 >= '0' && character1 <= '3' && isASCIIOctalDigit(m_current)) { - if (shouldBuildStrings) - record16((character1 - '0') * 64 + (character2 - '0') * 8 + m_current - '0'); + if (m_current == '\n') { + lineNumberAdder.add(m_current); shift(); - } else { - if (shouldBuildStrings) - record16((character1 - '0') * 8 + character2 - '0'); } + + rawStringStart = currentSourcePtr(); } else { - if (shouldBuildStrings) - record16(character1 - '0'); + lineNumberAdder.add(m_current); + shift(); } - } else if (!atEnd()) { - if (shouldBuildStrings) - record16(m_current); - shift(); } else { - m_lexErrorMessage = "Unterminated string constant"; - return StringUnterminated; + bool strictMode = true; + StringParseResult result = parseComplexEscape<true, LexerEscapeParseMode::Template>(strictMode, '`'); + if (result != StringParsedSuccessfully) { + if (rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings && result == StringCannotBeParsed) + parseCookedFailed = true; + else + 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))) { - // New-line or end of input is not allowed - if (atEnd() || isLineTerminator(m_current)) { - m_lexErrorMessage = "Unexpected EOF"; + // 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 (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(); } - if (currentSourcePtr() != stringStart && shouldBuildStrings) + bool isTail = m_current == '`'; + + if (currentSourcePtr() != stringStart) append16(stringStart, currentSourcePtr() - stringStart); - if (shouldBuildStrings) - tokenData->ident = makeIdentifier(m_buffer16.data(), m_buffer16.size()); + if (rawStringStart != currentSourcePtr() && rawStringsBuildMode == RawStringsBuildMode::BuildRawStrings) + m_bufferForRawTemplateString16.append(rawStringStart, currentSourcePtr() - rawStringStart); + + if (!parseCookedFailed) + tokenData->cooked = makeIdentifier(m_buffer16.data(), m_buffer16.size()); else - tokenData->ident = 0; + tokenData->cooked = nullptr; + + // 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 = nullptr; + + tokenData->isTail = isTail; + + m_buffer16.shrink(0); + m_bufferForRawTemplateString16.shrink(0); + + if (isTail) { + // Skip ` + shift(); + } else { + // Skip $ and { + shift(); + shift(); + } - m_buffer16.resize(0); return StringParsedSuccessfully; } @@ -1131,9 +1570,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(); @@ -1165,28 +1601,69 @@ 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)) { + returnValue = 0; + 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)) { @@ -1194,8 +1671,10 @@ ALWAYS_INLINE bool Lexer<T>::parseOctal(double& returnValue) shift(); } - if (isASCIIDigit(m_current)) + if (isASCIIDigit(m_current)) { + returnValue = 0; return false; + } returnValue = parseIntOverflow(m_buffer8.data(), m_buffer8.size(), 8); return true; @@ -1210,24 +1689,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]); } @@ -1293,6 +1773,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; @@ -1303,10 +1833,24 @@ bool Lexer<T>::nextTokenIsColon() } template <typename T> +void Lexer<T>::fillTokenInfo(JSToken* tokenRecord, JSTokenType token, int lineNumber, int endOffset, int lineStartOffset, JSTextPosition endPosition) +{ + JSTokenLocation* tokenLocation = &tokenRecord->m_location; + tokenLocation->line = lineNumber; + tokenLocation->endOffset = endOffset; + tokenLocation->lineStartOffset = lineStartOffset; + ASSERT(tokenLocation->endOffset >= tokenLocation->lineStartOffset); + tokenRecord->m_endPosition = endPosition; + m_lastToken = token; +} + +template <typename T> JSTokenType Lexer<T>::lex(JSToken* tokenRecord, unsigned lexerFlags, bool strictMode) { JSTokenData* tokenData = &tokenRecord->m_data; JSTokenLocation* tokenLocation = &tokenRecord->m_location; + m_lastTokenLocation = JSTokenLocation(tokenRecord->m_location); + ASSERT(!m_error); ASSERT(m_buffer8.isEmpty()); ASSERT(m_buffer16.isEmpty()); @@ -1315,8 +1859,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; @@ -1365,7 +1908,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(); @@ -1379,11 +1933,14 @@ start: } token = EQUAL; break; + } case CharacterLess: shift(); if (m_current == '!' && peek(1) == '-' && peek(2) == '-') { - // <!-- marks the beginning of a line comment (for www usage) - goto inSingleLineComment; + if (m_scriptMode == JSParserScriptMode::Classic) { + // <!-- marks the beginning of a line comment (for www usage) + goto inSingleLineComment; + } } if (m_current == '<') { shift(); @@ -1435,8 +1992,10 @@ start: if (m_current == '-') { shift(); if (m_atLineStart && m_current == '>') { - shift(); - goto inSingleLineComment; + if (m_scriptMode == JSParserScriptMode::Classic) { + shift(); + goto inSingleLineComment; + } } token = (!m_terminator) ? MINUSMINUS : AUTOMINUSMINUS; break; @@ -1455,19 +2014,29 @@ start: token = MULTEQUAL; break; } + if (m_current == '*') { + shift(); + if (m_current == '=') { + shift(); + token = POWEQUAL; + break; + } + token = POW; + break; + } token = TIMES; break; case CharacterSlash: 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; } @@ -1526,6 +2095,9 @@ start: break; case CharacterOpenParen: token = OPENPAREN; + tokenData->line = lineNumber(); + tokenData->offset = currentOffset(); + tokenData->lineStartOffset = currentLineStartOffset(); shift(); break; case CharacterCloseParen: @@ -1560,6 +2132,10 @@ start: shift(); token = SEMICOLON; break; + case CharacterBackQuote: + shift(); + token = BACKQUOTE; + break; case CharacterOpenBrace: tokenData->line = lineNumber(); tokenData->offset = currentOffset(); @@ -1591,73 +2167,132 @@ start: 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); } } 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 CharacterIdentifierStart: ASSERT(isIdentStart(m_current)); FALLTHROUGH; case CharacterBackSlash: + parseIdent: if (lexerFlags & LexexFlagsDontBuildKeywords) token = parseIdentifier<false>(tokenData, lexerFlags, strictMode); else @@ -1670,13 +2305,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; } @@ -1684,38 +2324,46 @@ inNumberAfterDecimalPoint: m_atLineStart = false; goto returnToken; -inSingleLineComment: - while (!isLineTerminator(m_current)) { - if (atEnd()) - return EOFTOK; +inSingleLineCommentCheckForDirectives: + // Script comment directives like "//# sourceURL=test.js". + if (UNLIKELY((m_current == '#' || m_current == '@') && isWhiteSpace(peek(1)))) { shift(); + shift(); + parseCommentDirective(); } - shiftLineTerminator(); - m_atLineStart = true; - m_terminator = true; - m_lineStart = m_code; - if (!lastTokenWasRestrKeyword()) - goto start; + // Fall through to complete single line comment parsing. - token = SEMICOLON; - // Fall through into returnToken. +inSingleLineComment: + { + auto lineNumber = m_lineNumber; + auto endOffset = currentOffset(); + auto lineStartOffset = currentLineStartOffset(); + auto endPosition = currentPosition(); + + while (!isLineTerminator(m_current)) { + if (atEnd()) + return EOFTOK; + shift(); + } + shiftLineTerminator(); + m_atLineStart = true; + m_terminator = true; + m_lineStart = m_code; + if (!lastTokenWasRestrKeyword()) + goto start; + + token = SEMICOLON; + fillTokenInfo(tokenRecord, token, lineNumber, endOffset, lineStartOffset, endPosition); + return token; + } returnToken: - tokenLocation->line = m_lineNumber; - tokenLocation->endOffset = currentOffset(); - tokenLocation->lineStartOffset = currentLineStartOffset(); - ASSERT(tokenLocation->endOffset >= tokenLocation->lineStartOffset); - tokenRecord->m_endPosition = currentPosition(); - m_lastToken = token; + fillTokenInfo(tokenRecord, token, m_lineNumber, currentOffset(), currentLineStartOffset(), currentPosition()); return token; returnError: m_error = true; - tokenLocation->line = m_lineNumber; - tokenLocation->endOffset = currentOffset(); - tokenLocation->lineStartOffset = currentLineStartOffset(); - ASSERT(tokenLocation->endOffset >= tokenLocation->lineStartOffset); - tokenRecord->m_endPosition = currentPosition(); + fillTokenInfo(tokenRecord, token, m_lineNumber, currentOffset(), currentLineStartOffset(), currentPosition()); RELEASE_ASSERT(token & ErrorTokenFlag); return token; } @@ -1733,8 +2381,9 @@ inline void orCharacter<UChar>(UChar& orAccumulator, UChar character) } template <typename T> -bool Lexer<T>::scanRegExp(const Identifier*& pattern, const Identifier*& flags, UChar patternPrefix) +JSTokenType Lexer<T>::scanRegExp(JSToken* tokenRecord, UChar patternPrefix) { + JSTokenData* tokenData = &tokenRecord->m_data; ASSERT(m_buffer16.isEmpty()); bool lastWasEscape = false; @@ -1750,8 +2399,12 @@ bool Lexer<T>::scanRegExp(const Identifier*& pattern, const Identifier*& flags, while (true) { if (isLineTerminator(m_current) || atEnd()) { - m_buffer16.resize(0); - return false; + m_buffer16.shrink(0); + JSTokenType token = UNTERMINATED_REGEXP_LITERAL_ERRORTOK; + fillTokenInfo(tokenRecord, token, m_lineNumber, currentOffset(), currentLineStartOffset(), currentPosition()); + m_error = true; + m_lexErrorMessage = makeString("Unterminated regular expression literal '", getToken(*tokenRecord), "'"); + return token; } T prev = m_current; @@ -1782,9 +2435,9 @@ bool Lexer<T>::scanRegExp(const Identifier*& pattern, const Identifier*& flags, } } - pattern = makeRightSizedIdentifier(m_buffer16.data(), m_buffer16.size(), charactersOredTogether); + tokenData->pattern = makeRightSizedIdentifier(m_buffer16.data(), m_buffer16.size(), charactersOredTogether); - m_buffer16.resize(0); + m_buffer16.shrink(0); charactersOredTogether = 0; while (isIdentPart(m_current)) { @@ -1793,51 +2446,38 @@ bool Lexer<T>::scanRegExp(const Identifier*& pattern, const Identifier*& flags, shift(); } - flags = makeRightSizedIdentifier(m_buffer16.data(), m_buffer16.size(), charactersOredTogether); - m_buffer16.resize(0); + tokenData->flags = makeRightSizedIdentifier(m_buffer16.data(), m_buffer16.size(), charactersOredTogether); + m_buffer16.shrink(0); - return true; + // Since RegExp always ends with /, m_atLineStart always becomes false. + m_atLineStart = false; + + JSTokenType token = REGEXP; + fillTokenInfo(tokenRecord, token, m_lineNumber, currentOffset(), currentLineStartOffset(), currentPosition()); + return token; } template <typename T> -bool Lexer<T>::skipRegExp() +JSTokenType Lexer<T>::scanTemplateString(JSToken* tokenRecord, RawStringsBuildMode rawStringsBuildMode) { - bool lastWasEscape = false; - bool inBrackets = false; - - while (true) { - if (isLineTerminator(m_current) || atEnd()) - return false; - - T prev = m_current; - - shift(); - - if (prev == '/' && !lastWasEscape && !inBrackets) - break; - - if (lastWasEscape) { - lastWasEscape = false; - continue; - } - - switch (prev) { - case '[': - inBrackets = true; - break; - case ']': - inBrackets = false; - break; - case '\\': - lastWasEscape = true; - break; - } - } + JSTokenData* tokenData = &tokenRecord->m_data; + ASSERT(!m_error); + ASSERT(m_buffer16.isEmpty()); - while (isIdentPart(m_current)) - shift(); + // Leading backquote ` (for template head) or closing brace } (for template trailing) are already shifted in the previous token scan. + // So in this re-scan phase, shift() is not needed here. + StringParseResult result = parseTemplateLiteral(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; - return true; + // Since TemplateString always ends with ` or }, m_atLineStart always becomes false. + m_atLineStart = false; + fillTokenInfo(tokenRecord, token, m_lineNumber, currentOffset(), currentLineStartOffset(), currentPosition()); + return token; } template <typename T> @@ -1851,7 +2491,10 @@ void Lexer<T>::clear() Vector<UChar> newBuffer16; m_buffer16.swap(newBuffer16); - m_isReparsing = false; + Vector<UChar> newBufferForRawTemplateString16; + m_bufferForRawTemplateString16.swap(newBufferForRawTemplateString16); + + m_isReparsingFunction = false; } // Instantiate the two flavors of Lexer we need instead of putting most of this file in Lexer.h diff --git a/Source/JavaScriptCore/parser/Lexer.h b/Source/JavaScriptCore/parser/Lexer.h index 28c61ba19..7283fe7ca 100644 --- a/Source/JavaScriptCore/parser/Lexer.h +++ b/Source/JavaScriptCore/parser/Lexer.h @@ -20,59 +20,36 @@ * */ -#ifndef Lexer_h -#define Lexer_h +#pragma once #include "Lookup.h" #include "ParserArena.h" #include "ParserTokens.h" #include "SourceCode.h" #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; - - explicit Keywords(VM&); - - VM& m_vm; - const HashTable m_keywordTable; -}; - enum LexerFlags { LexerFlagsIgnoreReservedWords = 1, LexerFlagsDontBuildStrings = 2, LexexFlagsDontBuildKeywords = 4 }; +enum class LexerEscapeParseMode { Template, String }; + +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, JSParserScriptMode); ~Lexer(); // Character manipulation functions. @@ -83,8 +60,8 @@ 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; } JSTokenType lex(JSToken*, unsigned, bool strictMode); bool nextTokenIsColon(); @@ -96,15 +73,21 @@ public: return JSTextPosition(m_lineNumber, currentOffset(), currentLineStartOffset()); } JSTextPosition positionBeforeLastNewline() const { return m_positionBeforeLastNewline; } + JSTokenLocation lastTokenLocation() const { return m_lastTokenLocation; } void setLastLineNumber(int lastLineNumber) { m_lastLineNumber = lastLineNumber; } int lastLineNumber() const { return m_lastLineNumber; } bool prevTerminator() const { return m_terminator; } - bool scanRegExp(const Identifier*& pattern, const Identifier*& flags, UChar patternPrefix = 0); - bool skipRegExp(); + JSTokenType scanRegExp(JSToken*, UChar patternPrefix = 0); + enum class RawStringsBuildMode { BuildRawStrings, DontBuildRawStrings }; + JSTokenType scanTemplateString(JSToken*, RawStringsBuildMode); // Functions for use after parsing. bool sawError() const { return m_error; } + void setSawError(bool sawError) { m_error = sawError; } String getErrorMessage() const { return m_lexErrorMessage; } + void setErrorMessage(const String& errorMessage) { m_lexErrorMessage = errorMessage; } + String sourceURL() const { return m_sourceURLDirective; } + String sourceMappingURL() const { return m_sourceMappingURLDirective; } void clear(); void setOffset(int offset, int lineStartOffset) { @@ -126,52 +109,34 @@ public: { m_lineNumber = line; } - - SourceProvider* sourceProvider() const { return m_source->provider(); } + void setTerminator(bool terminator) + { + m_terminator = terminator; + } JSTokenType lexExpectIdentifier(JSToken*, unsigned, bool strictMode); + ALWAYS_INLINE StringView getToken(const JSToken& token) + { + SourceProvider* sourceProvider = m_source->provider(); + ASSERT_WITH_MESSAGE(token.m_location.startOffset <= token.m_location.endOffset, "Calling this function with the baked token."); + return sourceProvider->getRange(token.m_location.startOffset, token.m_location.endOffset); + } + private: void record8(int); void append8(const T*, size_t); void record16(int); void record16(T); + void recordUnicodeCodePoint(UChar32); void append16(const LChar*, size_t); void append16(const UChar* characters, size_t length) { m_buffer16.append(characters, length); } ALWAYS_INLINE void shift(); ALWAYS_INLINE bool atEnd() const; ALWAYS_INLINE T peek(int offset) const; - 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 +146,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 +154,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 +171,25 @@ private: }; template <bool shouldBuildStrings> ALWAYS_INLINE StringParseResult parseString(JSTokenData*, bool strictMode); template <bool shouldBuildStrings> NEVER_INLINE StringParseResult parseStringSlowCase(JSTokenData*, bool strictMode); + + template <bool shouldBuildStrings, LexerEscapeParseMode escapeParseMode> ALWAYS_INLINE StringParseResult parseComplexEscape(bool strictMode, T stringQuoteCharacter); + 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]); + + void fillTokenInfo(JSToken*, JSTokenType, int lineNumber, int endOffset, int lineStartOffset, JSTextPosition endPosition); + static const size_t initialReadBufferCapacity = 32; int m_lineNumber; @@ -217,6 +197,7 @@ private: Vector<LChar> m_buffer8; Vector<UChar> m_buffer16; + Vector<UChar> m_bufferForRawTemplateString16; bool m_terminator; int m_lastToken; @@ -228,16 +209,22 @@ private: const T* m_codeStartPlusOffset; const T* m_lineStart; JSTextPosition m_positionBeforeLastNewline; - bool m_isReparsing; + JSTokenLocation m_lastTokenLocation; + 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; + JSParserScriptMode m_scriptMode; }; template <> @@ -249,7 +236,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)) : (u_charType(ch) == U_SPACE_SEPARATOR || 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 <> @@ -303,18 +291,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> @@ -335,6 +329,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) { @@ -370,10 +370,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); @@ -381,6 +386,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; @@ -389,5 +401,3 @@ slowCase: } } // namespace JSC - -#endif // Lexer_h diff --git a/Source/JavaScriptCore/parser/ModuleAnalyzer.cpp b/Source/JavaScriptCore/parser/ModuleAnalyzer.cpp new file mode 100644 index 000000000..a7b180b11 --- /dev/null +++ b/Source/JavaScriptCore/parser/ModuleAnalyzer.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2015-2016 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 "JSCInlines.h" +#include "JSGlobalObject.h" +#include "JSModuleRecord.h" +#include "ModuleScopeData.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, exec->vm(), exec->lexicalGlobalObject()->moduleRecordStructure(), moduleKey, sourceCode, declaredVariables, lexicalVariables)) +{ +} + +void ModuleAnalyzer::exportVariable(ModuleProgramNode& moduleProgramNode, 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; + + // Exported module local variable. + if (!variable.isImported()) { + for (auto& exportName : moduleProgramNode.moduleScopeData().exportedBindings().get(localName.get())) + moduleRecord()->addExportEntry(JSModuleRecord::ExportEntry::createLocal(Identifier::fromUid(m_vm, exportName.get()), Identifier::fromUid(m_vm, localName.get()))); + return; + } + + if (variable.isImportedNamespace()) { + // Exported namespace binding. + // import * as namespace from "mod" + // export { namespace } + // + // Sec 15.2.1.16.1 step 11-a-ii-2-b https://tc39.github.io/ecma262/#sec-parsemodule + // Namespace export is handled as local export since a namespace object binding itself is implemented as a local binding. + for (auto& exportName : moduleProgramNode.moduleScopeData().exportedBindings().get(localName.get())) + moduleRecord()->addExportEntry(JSModuleRecord::ExportEntry::createLocal(Identifier::fromUid(m_vm, exportName.get()), Identifier::fromUid(m_vm, localName.get()))); + return; + } + + // Indirectly exported binding. + // import a from "mod" + // export { a } + std::optional<JSModuleRecord::ImportEntry> optionalImportEntry = moduleRecord()->tryGetImportEntry(localName.get()); + ASSERT(optionalImportEntry); + const JSModuleRecord::ImportEntry& importEntry = *optionalImportEntry; + for (auto& exportName : moduleProgramNode.moduleScopeData().exportedBindings().get(localName.get())) + moduleRecord()->addExportEntry(JSModuleRecord::ExportEntry::createIndirect(Identifier::fromUid(m_vm, exportName.get()), 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(moduleProgramNode, pair.key, pair.value); + + for (const auto& pair : m_moduleRecord->lexicalVariables()) + exportVariable(moduleProgramNode, 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..cdc7e0061 --- /dev/null +++ b/Source/JavaScriptCore/parser/ModuleAnalyzer.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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. + */ + +#pragma once + +#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(); } + +private: + void exportVariable(ModuleProgramNode&, const RefPtr<UniquedStringImpl>&, const VariableEnvironmentEntry&); + + VM* m_vm; + Strong<JSModuleRecord> m_moduleRecord; +}; + +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/ModuleScopeData.h b/Source/JavaScriptCore/parser/ModuleScopeData.h new file mode 100644 index 000000000..2a1eca264 --- /dev/null +++ b/Source/JavaScriptCore/parser/ModuleScopeData.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * Copyright (C) 2016 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. + */ + +#pragma once + +#include "Identifier.h" +#include <wtf/RefPtr.h> + +namespace JSC { + +class ModuleScopeData : public RefCounted<ModuleScopeData> { +WTF_MAKE_NONCOPYABLE(ModuleScopeData); +WTF_MAKE_FAST_ALLOCATED; +public: + typedef HashMap<RefPtr<UniquedStringImpl>, Vector<RefPtr<UniquedStringImpl>>, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> IdentifierAliasMap; + + static Ref<ModuleScopeData> create() { return adoptRef(*new ModuleScopeData); } + + const IdentifierAliasMap& exportedBindings() const { return m_exportedBindings; } + + bool exportName(const Identifier& exportedName) + { + return m_exportedNames.add(exportedName.impl()).isNewEntry; + } + + void exportBinding(const Identifier& localName, const Identifier& exportedName) + { + m_exportedBindings.add(localName.impl(), Vector<RefPtr<UniquedStringImpl>>()).iterator->value.append(exportedName.impl()); + } + + void exportBinding(const Identifier& localName) + { + exportBinding(localName, localName); + } + +private: + ModuleScopeData() = default; + + IdentifierSet m_exportedNames { }; + IdentifierAliasMap m_exportedBindings { }; +}; + +} // namespace diff --git a/Source/JavaScriptCore/parser/NodeConstructors.h b/Source/JavaScriptCore/parser/NodeConstructors.h index 917126975..da71e8acd 100644 --- a/Source/JavaScriptCore/parser/NodeConstructors.h +++ b/Source/JavaScriptCore/parser/NodeConstructors.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2013, 2015-2016 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -18,32 +18,33 @@ * */ -#ifndef NodeConstructors_h -#define NodeConstructors_h +#pragma once #include "Nodes.h" #include "Lexer.h" +#include "Opcode.h" #include "Parser.h" 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) @@ -100,7 +162,23 @@ namespace JSC { { } -inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifier& ident, const JSTextPosition& start) + inline SuperNode::SuperNode(const JSTokenLocation& location) + : ExpressionNode(location) + { + } + + inline ImportNode::ImportNode(const JSTokenLocation& location, ExpressionNode* expr) + : ExpressionNode(location) + , m_expr(expr) + { + } + + inline NewTargetNode::NewTargetNode(const JSTokenLocation& location) + : ExpressionNode(location) + { + } + + inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifier& ident, const JSTextPosition& start) : ExpressionNode(location) , m_ident(ident) , m_start(start) @@ -147,25 +225,24 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie { } - inline PropertyNode::PropertyNode(VM*, const Identifier& name, ExpressionNode* assign, Type type) + inline PropertyNode::PropertyNode(const Identifier& name, ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding, bool isClassProperty) : m_name(&name) , m_assign(assign) , m_type(type) + , m_needsSuperBinding(superBinding == SuperBinding::Needed) + , m_putType(putType) + , m_isClassProperty(isClassProperty) { } - inline PropertyNode::PropertyNode(VM* vm, double name, ExpressionNode* assign, Type type) - : m_name(&vm->parserArena->identifierArena().makeNumericIdentifier(vm, name)) - , m_assign(assign) - , m_type(type) - { - } - - inline PropertyNode::PropertyNode(VM*, ExpressionNode* name, ExpressionNode* assign, Type type) + inline PropertyNode::PropertyNode(ExpressionNode* name, ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding, bool isClassProperty) : m_name(0) , m_expression(name) , m_assign(assign) , m_type(type) + , m_needsSuperBinding(superBinding == SuperBinding::Needed) + , m_putType(putType) + , m_isClassProperty(isClassProperty) { } @@ -281,12 +358,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) { } @@ -299,6 +377,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) { @@ -415,6 +503,11 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie { } + inline PowNode::PowNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) + : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_pow, rightHasAssignments) + { + } + inline MultNode::MultNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments) : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_mul, rightHasAssignments) { @@ -557,10 +650,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) { } @@ -615,22 +709,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) - { - ASSERT(expr1); - ASSERT(expr2); - 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) { } @@ -650,11 +738,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) @@ -678,8 +836,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) @@ -688,24 +847,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) @@ -740,43 +887,85 @@ 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(PassRefPtr<DeconstructionPatternNode> pattern) - : m_pattern(pattern) - , m_next(0) + inline FunctionParameters::FunctionParameters() { - ASSERT(m_pattern); } - inline ParameterNode::ParameterNode(ParameterNode* l, PassRefPtr<DeconstructionPatternNode> pattern) - : m_pattern(pattern) - , m_next(0) + + inline BaseFuncExprNode::BaseFuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source, FunctionMode functionMode) + : ExpressionNode(location) + , m_metadata(metadata) + { + m_metadata->finishParsing(source, ident, functionMode); + } + + inline FuncExprNode::FuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source) + : BaseFuncExprNode(location, ident, metadata, source, FunctionMode::FunctionExpression) { - l->m_next = this; - ASSERT(m_pattern); - ASSERT(l->m_pattern); } - inline FuncExprNode::FuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionBodyNode* body, const SourceCode& source, ParameterNode* parameter) + inline FuncExprNode::FuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source, FunctionMode functionMode) + : BaseFuncExprNode(location, ident, metadata, source, functionMode) + { + } + + inline FuncDeclNode::FuncDeclNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source) + : StatementNode(location) + , m_metadata(metadata) + { + m_metadata->finishParsing(source, ident, FunctionMode::FunctionDeclaration); + } + + inline ArrowFuncExprNode::ArrowFuncExprNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source) + : BaseFuncExprNode(location, ident, metadata, source, FunctionMode::FunctionExpression) + { + } + + inline MethodDefinitionNode::MethodDefinitionNode(const JSTokenLocation& location, const Identifier& ident, FunctionMetadataNode* metadata, const SourceCode& source) + : FuncExprNode(location, ident, metadata, source, FunctionMode::MethodDefinition) + { + } + + inline YieldExprNode::YieldExprNode(const JSTokenLocation& location, ExpressionNode* argument, bool delegate) + : ExpressionNode(location) + , m_argument(argument) + , m_delegate(delegate) + { + } + + inline AwaitExprNode::AwaitExprNode(const JSTokenLocation& location, ExpressionNode* argument) : ExpressionNode(location) - , m_body(body) + , m_argument(argument) { - 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, const SourceCode& classSource, VariableEnvironment& classEnvironment, ExpressionNode* constructorExpression, ExpressionNode* classHeritage, PropertyListNode* instanceMethods, PropertyListNode* staticMethods) + : ExpressionNode(location) + , VariableEnvironmentNode(classEnvironment) + , m_classSource(classSource) + , m_name(name) + , m_ecmaName(&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) @@ -805,103 +994,81 @@ 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, FunctionStack&& functionStack) : StatementNode(location) + , VariableEnvironmentNode(lexicalVariables, WTFMove(functionStack)) , 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, FunctionStack&& functionStack) : StatementNode(location) + , VariableEnvironmentNode(lexicalVariables, WTFMove(functionStack)) , m_statements(statements) { } - inline EnumerationNode::EnumerationNode(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_lexpr(l) + , VariableEnvironmentNode(lexicalVariables) + , m_lexpr(lexpr) , m_expr(expr) , m_statement(statement) { - ASSERT(l); + ASSERT(lexpr); } - inline EnumerationNode::EnumerationNode(VM* vm, const JSTokenLocation& location, DeconstructionPatternNode* pattern, ExpressionNode* expr, StatementNode* statement) - : StatementNode(location) - , m_lexpr(new (vm) DeconstructingAssignmentNode(location, pattern, 0)) - , m_expr(expr) - , m_statement(statement) + inline ForInNode::ForInNode(const JSTokenLocation& location, ExpressionNode* lexpr, ExpressionNode* expr, StatementNode* statement, VariableEnvironment& lexicalVariables) + : EnumerationNode(location, lexpr, expr, statement, lexicalVariables) { - ASSERT(pattern); } - inline ForInNode::ForInNode(const JSTokenLocation& location, ExpressionNode* l, ExpressionNode* expr, StatementNode* statement) - : EnumerationNode(location, l, expr, statement) + inline ForOfNode::ForOfNode(const JSTokenLocation& location, ExpressionNode* lexpr, ExpressionNode* expr, StatementNode* statement, VariableEnvironment& lexicalVariables) + : EnumerationNode(location, lexpr, expr, statement, lexicalVariables) { } - inline ForInNode::ForInNode(VM* vm, const JSTokenLocation& location, DeconstructionPatternNode* pattern, ExpressionNode* expr, StatementNode* statement) - : EnumerationNode(vm, location, pattern, expr, statement) - { - } - - inline ForOfNode::ForOfNode(const JSTokenLocation& location, ExpressionNode* l, ExpressionNode* expr, StatementNode* statement) - : EnumerationNode(location, l, expr, statement) - { - } - - inline ForOfNode::ForOfNode(VM* vm, const JSTokenLocation& location, DeconstructionPatternNode* pattern, ExpressionNode* expr, StatementNode* statement) - : EnumerationNode(vm, location, pattern, expr, statement) - { - } - - inline DeconstructionPatternNode::DeconstructionPatternNode(VM*) + inline DestructuringPatternNode::DestructuringPatternNode() { } - inline ArrayPatternNode::ArrayPatternNode(VM* vm) - : DeconstructionPatternNode(vm) + inline ArrayPatternNode::ArrayPatternNode() + : DestructuringPatternNode() { } - inline PassRefPtr<ArrayPatternNode> ArrayPatternNode::create(VM* vm) + inline ObjectPatternNode::ObjectPatternNode() + : DestructuringPatternNode() { - return adoptRef(new ArrayPatternNode(vm)); } - inline ObjectPatternNode::ObjectPatternNode(VM* vm) - : DeconstructionPatternNode(vm) - { - } - - inline PassRefPtr<ObjectPatternNode> ObjectPatternNode::create(VM* vm) + 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) { - return adoptRef(new ObjectPatternNode(vm)); } - inline PassRefPtr<BindingNode> BindingNode::create(VM* vm, const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end) - { - return adoptRef(new BindingNode(vm, boundProperty, start, end)); - } - - inline BindingNode::BindingNode(VM* vm, const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end) - : DeconstructionPatternNode(vm) + inline AssignmentElementNode::AssignmentElementNode(ExpressionNode* assignmentTarget, const JSTextPosition& start, const JSTextPosition& end) + : DestructuringPatternNode() , m_divotStart(start) , m_divotEnd(end) - , m_boundProperty(boundProperty) + , m_assignmentTarget(assignmentTarget) { } - - inline DeconstructingAssignmentNode::DeconstructingAssignmentNode(const JSTokenLocation& location, PassRefPtr<DeconstructionPatternNode> bindings, ExpressionNode* initializer) + + inline RestParameterNode::RestParameterNode(DestructuringPatternNode* pattern, unsigned numParametersToSkip) + : DestructuringPatternNode() + , m_pattern(pattern) + , m_numParametersToSkip(numParametersToSkip) + { + ASSERT(!pattern->isRestParameter()); + } + + inline DestructuringAssignmentNode::DestructuringAssignmentNode(const JSTokenLocation& location, DestructuringPatternNode* bindings, ExpressionNode* initializer) : ExpressionNode(location) , m_bindings(bindings) , m_initializer(initializer) @@ -909,5 +1076,3 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie } } // 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 fc9ccd9f6..3a08b6354 100644 --- a/Source/JavaScriptCore/parser/Nodes.cpp +++ b/Source/JavaScriptCore/parser/Nodes.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) -* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved. +* Copyright (C) 2003-2009, 2013, 2016 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> @@ -27,29 +27,13 @@ #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 "Parser.h" -#include "PropertyNameArray.h" -#include "RegExpObject.h" -#include "SamplingTool.h" +#include "JSCInlines.h" #include <wtf/Assertions.h> -#include <wtf/RefCountedLeakCounter.h> -#include <wtf/Threading.h> using namespace WTF; namespace JSC { - // ------------------------------ StatementNode -------------------------------- void StatementNode::setLoc(unsigned firstLine, unsigned lastLine, int startOffset, int lineStartOffset) @@ -65,46 +49,52 @@ 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) , m_features(inStrictContext ? StrictModeFeature : NoFeatures) + , m_innerArrowFunctionCodeFeatures(NoInnerArrowFunctionFeatures) , m_numConstants(0) , m_statements(0) { } -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, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants) : StatementNode(endLocation) - , ParserArenaRefCounted(vm) + , ParserArenaRoot(parserArena) + , VariableEnvironmentNode(lexicalVariables, WTFMove(funcStack)) , m_startLineNumber(startLocation.line) , m_startStartOffset(startLocation.startOffset) , m_startLineStartOffset(startLocation.lineStartOffset) , m_features(features) + , m_innerArrowFunctionCodeFeatures(innerArrowFunctionCodeFeatures) , m_source(source) + , m_sloppyModeHoistedFunctions(WTFMove(sloppyModeHoistedFunctions)) , 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); } StatementNode* ScopeNode::singleStatement() const @@ -114,121 +104,107 @@ StatementNode* ScopeNode::singleStatement() const // ------------------------------ ProgramNode ----------------------------- -inline ProgramNode::ProgramNode(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, 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, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants, RefPtr<ModuleScopeData>&&) + : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants) , m_startColumn(startColumn) , m_endColumn(endColumn) { } -PassRefPtr<ProgramNode> ProgramNode::create(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, IdentifierSet& capturedVariables, const SourceCode& source, CodeFeatures features, int numConstants) -{ - RefPtr<ProgramNode> node = new ProgramNode(vm, startLocation, endLocation, startColumn, endColumn, children, varStack, funcStack, capturedVariables, source, features, numConstants); - - ASSERT(node->m_arena.last() == node); - node->m_arena.removeLast(); - ASSERT(!node->m_arena.contains(node.get())); +// ------------------------------ ModuleProgramNode ----------------------------- - 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, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants, RefPtr<ModuleScopeData>&& moduleScopeData) + : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants) + , m_startColumn(startColumn) + , m_endColumn(endColumn) + , m_moduleScopeData(*WTFMove(moduleScopeData)) +{ } // ------------------------------ EvalNode ----------------------------- -inline EvalNode::EvalNode(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned endColumn, 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, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants, RefPtr<ModuleScopeData>&&) + : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants) , m_endColumn(endColumn) { } -PassRefPtr<EvalNode> EvalNode::create(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned, unsigned endColumn, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, IdentifierSet& capturedVariables, const SourceCode& source, CodeFeatures features, int numConstants) -{ - RefPtr<EvalNode> node = new EvalNode(vm, startLocation, endLocation, endColumn, 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(DeconstructionPatternNode*) * 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, FunctionMode functionMode) { - unsigned i = 0; - for (ParameterNode* parameter = firstParameter; parameter; parameter = parameter->nextParam()) { - auto pattern = parameter->pattern(); - pattern->ref(); - patterns()[i++] = pattern; - } + m_source = source; + m_ident = ident; + m_functionMode = functionMode; } -FunctionParameters::~FunctionParameters() +void FunctionMetadataNode::setEndPosition(JSTextPosition position) { - for (unsigned i = 0; i < m_size; ++i) - patterns()[i]->deref(); + m_lastLine = position.line; + m_endColumn = position.offset - position.lineStartOffset; } -inline FunctionBodyNode::FunctionBodyNode(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, bool inStrictContext) - : ScopeNode(vm, startLocation, endLocation, inStrictContext) - , m_startColumn(startColumn) - , m_endColumn(endColumn) -{ -} +// ------------------------------ FunctionNode ----------------------------- -inline FunctionBodyNode::FunctionBodyNode(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, 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, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters* parameters, const SourceCode& sourceCode, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants, RefPtr<ModuleScopeData>&&) + : ScopeNode(parserArena, startLocation, endLocation, sourceCode, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, 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, FunctionMode functionMode) { ASSERT(!source().isNull()); - m_parameters = parameters; m_ident = ident; - m_functionNameIsInScopeToggle = functionNameIsInScopeToggle; + m_functionMode = functionMode; } -FunctionBodyNode* FunctionBodyNode::create(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, bool inStrictContext) +bool PropertyListNode::hasStaticallyNamedProperty(const Identifier& propName) { - return new FunctionBodyNode(vm, startLocation, endLocation, startColumn, endColumn, inStrictContext); + PropertyListNode* list = this; + while (list) { + const Identifier* currentNodeName = list->m_node->name(); + if (currentNodeName && *currentNodeName == propName) + return true; + list = list->m_next; + } + return false; } -PassRefPtr<FunctionBodyNode> FunctionBodyNode::create(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, 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, endColumn , 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); } -void FunctionBodyNode::setEndPosition(JSTextPosition position) +VariableEnvironmentNode::VariableEnvironmentNode(VariableEnvironment& lexicalVariables, FunctionStack&& functionStack) { - m_lastLine = position.line; - m_endColumn = position.offset - position.lineStartOffset; + m_lexicalVariables.swap(lexicalVariables); + m_functionStack = WTFMove(functionStack); } } // namespace JSC diff --git a/Source/JavaScriptCore/parser/Nodes.h b/Source/JavaScriptCore/parser/Nodes.h index d779a178d..70606824d 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-2009, 2013, 2015-2016 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> @@ -23,31 +23,39 @@ * */ -#ifndef Nodes_h -#define Nodes_h +#pragma once +#include "BuiltinNames.h" #include "Error.h" +#include "Interpreter.h" #include "JITCode.h" -#include "Opcode.h" #include "ParserArena.h" #include "ParserTokens.h" #include "ResultType.h" #include "SourceCode.h" #include "SymbolTable.h" +#include "VariableEnvironment.h" #include <wtf/MathExtras.h> +#include <wtf/SmallPtrSet.h> namespace JSC { + enum OpcodeID : unsigned; + class ArgumentListNode; class BytecodeGenerator; - class FunctionBodyNode; + class FunctionMetadataNode; + class FunctionParameters; class Label; + class ModuleAnalyzer; + class ModuleScopeData; class PropertyListNode; class ReadModifyResolveNode; class RegisterID; - class JSScope; class ScopeNode; + typedef SmallPtrSet<UniquedStringImpl*> UniquedStringImplPtrSet; + enum Operator { OpEqual, OpPlusEq, @@ -60,6 +68,7 @@ namespace JSC { OpXOrEq, OpOrEq, OpModEq, + OpPowEq, OpLShift, OpRShift, OpURShift @@ -76,12 +85,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<Identifier, unsigned>> VarStack; - typedef Vector<FunctionBodyNode*> FunctionStack; + typedef Vector<FunctionMetadataNode*> FunctionStack; } struct SwitchInfo { @@ -90,11 +95,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,24 +114,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*); - }; - - template <typename T> - struct ParserArenaData : ParserArenaDeletable { - T data; + void* operator new(size_t, ParserArena&); }; - class ParserArenaRefCounted : public RefCounted<ParserArenaRefCounted> { - WTF_FASTMALLOC_OPERATORS; + 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 { @@ -130,13 +137,21 @@ 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; } + + bool needsDebugHook() const { return m_needsDebugHook; } + void setNeedsDebugHook() { m_needsDebugHook = true; } protected: JSTextPosition m_position; + int m_endOffset; + bool m_needsDebugHook { false }; }; class ExpressionNode : public Node { @@ -148,23 +163,34 @@ 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 isAssignResolveNode() const { return false; } virtual bool isBracketAccessorNode() const { return false; } virtual bool isDotAccessorNode() const { return false; } - virtual bool isDeconstructionNode() const { return false; } + virtual bool isDestructuringNode() const { return false; } + virtual bool isBaseFuncExprNode() const { return false; } virtual bool isFuncExprNode() const { return false; } + virtual bool isArrowFuncExprNode() const { return false; } + virtual bool isClassExprNode() 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 isImportNode() const { return false; } + virtual bool isNewTarget() const { return false; } + virtual bool isBytecodeIntrinsicNode() const { return false; } - virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label*, Label*, FallThroughMode); + virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label&, Label&, FallThroughMode); virtual ExpressionNode* stripUnaryPlus() { return this; } @@ -182,29 +208,57 @@ 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 isDebuggerStatement() const { return false; } + virtual bool isFunctionNode() 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 isLabel() const { return false; } virtual bool isBlock() const { return false; } + virtual bool isFuncDeclNode() const { return false; } + virtual bool isModuleDeclarationNode() const { return false; } + virtual bool isForOfNode() const { return false; } protected: + StatementNode* m_next; int m_lastLine; }; + class VariableEnvironmentNode : public ParserArenaDeletable { + public: + typedef DeclarationStacks::FunctionStack FunctionStack; + + VariableEnvironmentNode() + { + } + + VariableEnvironmentNode(VariableEnvironment& lexicalDeclaredVariables); + VariableEnvironmentNode(VariableEnvironment& lexicalDeclaredVariables, FunctionStack&&); + + VariableEnvironment& lexicalVariables() { return m_lexicalVariables; } + FunctionStack& functionStack() { return m_functionStack; } + + protected: + VariableEnvironment m_lexicalVariables; + FunctionStack m_functionStack; + }; + class ConstantNode : public ExpressionNode { public: ConstantNode(const JSTokenLocation&, ResultType); - virtual bool isPure(BytecodeGenerator&) const override { return true; } - virtual bool isConstant() const override { return true; } + bool isPure(BytecodeGenerator&) const override { return true; } + bool isConstant() const override { return true; } virtual JSValue jsValue(BytecodeGenerator&) const = 0; private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitBytecodeInConditionContext(BytecodeGenerator&, Label& trueTarget, Label& falseTarget, FallThroughMode) override; }; class NullNode : public ConstantNode { @@ -212,8 +266,8 @@ namespace JSC { NullNode(const JSTokenLocation&); private: - virtual bool isNull() const override { return true; } - virtual JSValue jsValue(BytecodeGenerator&) const override { return jsNull(); } + bool isNull() const override { return true; } + JSValue jsValue(BytecodeGenerator&) const override { return jsNull(); } }; class BooleanNode : public ConstantNode { @@ -222,8 +276,8 @@ namespace JSC { bool value() { return m_value; } private: - virtual bool isBoolean() const override { return true; } - virtual JSValue jsValue(BytecodeGenerator&) const override { return jsBoolean(m_value); } + bool isBoolean() const override { return true; } + JSValue jsValue(BytecodeGenerator&) const override { return jsBoolean(m_value); } bool m_value; }; @@ -231,28 +285,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; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) final; private: - virtual bool isNumber() const override { return true; } - virtual JSValue jsValue(BytecodeGenerator&) const override { return jsNumber(m_value); } + bool isNumber() const final { return true; } + JSValue jsValue(BytecodeGenerator&) const override { return jsNumber(m_value); } double m_value; }; + class DoubleNode : public NumberNode { + public: + DoubleNode(const JSTokenLocation&, double value); + + private: + 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); + bool isIntegerNode() const final { return true; } + }; + class StringNode : public ConstantNode { public: StringNode(const JSTokenLocation&, const Identifier&); const Identifier& value() { return m_value; } private: - virtual bool isString() const override { return true; } - virtual JSValue jsValue(BytecodeGenerator&) const override; + bool isString() const override { return true; } + JSValue jsValue(BytecodeGenerator&) const override; const Identifier& m_value; }; - + class ThrowableExpressionData { public: ThrowableExpressionData() @@ -405,12 +475,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: + 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: + 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: + 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) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_pattern; const Identifier& m_flags; @@ -421,7 +559,36 @@ namespace JSC { ThisNode(const JSTokenLocation&); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class SuperNode final : public ExpressionNode { + public: + SuperNode(const JSTokenLocation&); + + private: + bool isSuperNode() const override { return true; } + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + + class ImportNode : public ExpressionNode, public ThrowableExpressionData { + public: + ImportNode(const JSTokenLocation&, ExpressionNode*); + + private: + bool isImportNode() const override { return true; } + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_expr; + }; + + class NewTargetNode final : public ExpressionNode { + public: + NewTargetNode(const JSTokenLocation&); + + private: + bool isNewTarget() const final { return true; } + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class ResolveNode : public ExpressionNode { @@ -431,11 +598,11 @@ namespace JSC { const Identifier& identifier() const { return m_ident; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isPure(BytecodeGenerator&) const override; - virtual bool isLocation() const override { return true; } - virtual bool isResolveNode() const override { return true; } + bool isPure(BytecodeGenerator&) const override; + bool isLocation() const override { return true; } + bool isResolveNode() const override { return true; } const Identifier& m_ident; JSTextPosition m_start; @@ -462,13 +629,15 @@ namespace JSC { ArrayNode(const JSTokenLocation&, ElementNode*); ArrayNode(const JSTokenLocation&, int elision, ElementNode*); - ArgumentListNode* toArgumentList(VM*, int, int) const; + 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) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isSimpleArray() const override; + bool isSimpleArray() const override; ElementNode* m_element; int m_elision; @@ -477,23 +646,29 @@ 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(const Identifier&, ExpressionNode*, Type, PutType, SuperBinding, bool isClassProperty); + PropertyNode(ExpressionNode* propertyName, ExpressionNode*, Type, PutType, SuperBinding, bool isClassProperty); - PropertyNode(VM*, const Identifier&, ExpressionNode*, Type); - PropertyNode(VM*, double, ExpressionNode*, Type); - PropertyNode(VM*, ExpressionNode* propertyName, ExpressionNode*, Type); - ExpressionNode* expressionName() const { return m_expression; } const Identifier* name() const { return m_name; } - Type type() const { return m_type; } + Type type() const { return static_cast<Type>(m_type); } + bool needsSuperBinding() const { return m_needsSuperBinding; } + bool isClassProperty() const { return m_isClassProperty; } + PutType putType() const { return static_cast<PutType>(m_putType); } private: friend class PropertyListNode; 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; + unsigned m_isClassProperty: 1; }; class PropertyListNode : public ExpressionNode { @@ -501,9 +676,12 @@ namespace JSC { PropertyListNode(const JSTokenLocation&, PropertyNode*); PropertyListNode(const JSTokenLocation&, PropertyNode*, PropertyListNode*); - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + bool hasStaticallyNamedProperty(const Identifier& propName); private: + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitPutConstantProperty(BytecodeGenerator&, RegisterID*, PropertyNode&); + PropertyNode* m_node; PropertyListNode* m_next; }; @@ -512,9 +690,10 @@ namespace JSC { public: ObjectLiteralNode(const JSTokenLocation&); ObjectLiteralNode(const JSTokenLocation&, PropertyListNode*); + bool isObjectLiteral() const override { return true; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; PropertyListNode* m_list; }; @@ -529,10 +708,10 @@ namespace JSC { bool subscriptHasAssignments() const { return m_subscriptHasAssignments; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isLocation() const override { return true; } - virtual bool isBracketAccessorNode() const override { return true; } + bool isLocation() const override { return true; } + bool isBracketAccessorNode() const override { return true; } ExpressionNode* m_base; ExpressionNode* m_subscript; @@ -547,10 +726,10 @@ namespace JSC { const Identifier& identifier() const { return m_ident; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isLocation() const override { return true; } - virtual bool isDotAccessorNode() const override { return true; } + bool isLocation() const override { return true; } + bool isDotAccessorNode() const override { return true; } ExpressionNode* m_base; const Identifier& m_ident; @@ -563,9 +742,9 @@ namespace JSC { ExpressionNode* expression() const { return m_expression; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isSpreadExpression() const override { return true; } + bool isSpreadExpression() const override { return true; } ExpressionNode* m_expression; }; @@ -578,7 +757,7 @@ namespace JSC { ExpressionNode* m_expr; private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class ArgumentsNode : public ParserArenaFreeable { @@ -595,7 +774,7 @@ namespace JSC { NewExprNode(const JSTokenLocation&, ExpressionNode*, ArgumentsNode*); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; ArgumentsNode* m_args; @@ -606,7 +785,7 @@ namespace JSC { EvalFunctionCallNode(const JSTokenLocation&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ArgumentsNode* m_args; }; @@ -616,7 +795,7 @@ namespace JSC { FunctionCallValueNode(const JSTokenLocation&, ExpressionNode*, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; ArgumentsNode* m_args; @@ -627,7 +806,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) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_ident; ArgumentsNode* m_args; @@ -635,14 +814,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) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_base; ExpressionNode* m_subscript; ArgumentsNode* m_args; + bool m_subscriptHasAssignments; }; class FunctionCallDotNode : public ExpressionNode, public ThrowableSubExpressionData { @@ -650,7 +830,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) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; protected: ExpressionNode* m_base; @@ -658,12 +838,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); + + 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: + 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) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class ApplyFunctionCallDotNode : public FunctionCallDotNode { @@ -671,7 +882,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) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class DeleteResolveNode : public ExpressionNode, public ThrowableExpressionData { @@ -679,7 +890,7 @@ namespace JSC { DeleteResolveNode(const JSTokenLocation&, const Identifier&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_ident; }; @@ -689,7 +900,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) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_base; ExpressionNode* m_subscript; @@ -700,7 +911,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) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_base; const Identifier& m_ident; @@ -711,7 +922,7 @@ namespace JSC { DeleteValueNode(const JSTokenLocation&, ExpressionNode*); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; }; @@ -721,7 +932,7 @@ namespace JSC { VoidNode(const JSTokenLocation&, ExpressionNode*); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; }; @@ -733,7 +944,7 @@ namespace JSC { const Identifier& identifier() const { return m_ident; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_ident; }; @@ -743,7 +954,7 @@ namespace JSC { TypeOfValueNode(const JSTokenLocation&, ExpressionNode*); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; }; @@ -753,7 +964,7 @@ namespace JSC { PrefixNode(const JSTokenLocation&, ExpressionNode*, Operator, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); protected: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; virtual RegisterID* emitResolve(BytecodeGenerator&, RegisterID* = 0); virtual RegisterID* emitBracket(BytecodeGenerator&, RegisterID* = 0); virtual RegisterID* emitDot(BytecodeGenerator&, RegisterID* = 0); @@ -767,10 +978,10 @@ namespace JSC { PostfixNode(const JSTokenLocation&, ExpressionNode*, Operator, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual RegisterID* emitResolve(BytecodeGenerator&, RegisterID* = 0) override; - virtual RegisterID* emitBracket(BytecodeGenerator&, RegisterID* = 0) override; - virtual RegisterID* emitDot(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitResolve(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBracket(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitDot(BytecodeGenerator&, RegisterID* = 0) override; }; class UnaryOpNode : public ExpressionNode { @@ -780,11 +991,10 @@ namespace JSC { protected: ExpressionNode* expr() { return m_expr; } const ExpressionNode* expr() const { return m_expr; } + OpcodeID opcodeID() const { return m_opcodeID; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - - OpcodeID opcodeID() const { return m_opcodeID; } + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; OpcodeID m_opcodeID; @@ -795,7 +1005,9 @@ namespace JSC { UnaryPlusNode(const JSTokenLocation&, ExpressionNode*); private: - virtual ExpressionNode* stripUnaryPlus() override { return expr(); } + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* stripUnaryPlus() override { return expr(); } }; class NegateNode : public UnaryOpNode { @@ -812,7 +1024,7 @@ namespace JSC { const ExpressionNode* expr() const { return m_expr; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; }; @@ -821,7 +1033,7 @@ namespace JSC { public: LogicalNotNode(const JSTokenLocation&, ExpressionNode*); private: - virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode) override; + void emitBytecodeInConditionContext(BytecodeGenerator&, Label& trueTarget, Label& falseTarget, FallThroughMode) override; }; class BinaryOpNode : public ExpressionNode { @@ -830,14 +1042,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); - virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode) override; + void emitBytecodeInConditionContext(BytecodeGenerator&, Label& trueTarget, Label& falseTarget, FallThroughMode) override; ExpressionNode* lhs() { return m_expr1; }; ExpressionNode* rhs() { return m_expr2; }; private: void tryFoldToBranch(BytecodeGenerator&, TriState& branchCondition, ExpressionNode*& branchExpression); - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; protected: OpcodeID opcodeID() const { return m_opcodeID; } @@ -851,6 +1063,11 @@ namespace JSC { bool m_rightHasAssignments; }; + class PowNode : public BinaryOpNode { + public: + PowNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + }; + class MultNode : public BinaryOpNode { public: MultNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); @@ -870,14 +1087,14 @@ namespace JSC { public: AddNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); - virtual bool isAdd() const override { return true; } + bool isAdd() const override { return true; } }; class SubNode : public BinaryOpNode { public: SubNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); - virtual bool isSubtract() const override { return true; } + bool isSubtract() const override { return true; } }; class LeftShiftNode : public BinaryOpNode { @@ -921,7 +1138,7 @@ namespace JSC { ThrowableBinaryOpNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, OpcodeID, bool rightHasAssignments); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class InstanceOfNode : public ThrowableBinaryOpNode { @@ -929,12 +1146,15 @@ namespace JSC { InstanceOfNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class InNode : public ThrowableBinaryOpNode { public: InNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); + + private: + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class EqualNode : public BinaryOpNode { @@ -942,7 +1162,7 @@ namespace JSC { EqualNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class NotEqualNode : public BinaryOpNode { @@ -955,7 +1175,7 @@ namespace JSC { StrictEqualNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class NotStrictEqualNode : public BinaryOpNode { @@ -984,8 +1204,8 @@ namespace JSC { LogicalOpNode(const JSTokenLocation&, ExpressionNode* expr1, ExpressionNode* expr2, LogicalOperator); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, FallThroughMode) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitBytecodeInConditionContext(BytecodeGenerator&, Label& trueTarget, Label& falseTarget, FallThroughMode) override; ExpressionNode* m_expr1; ExpressionNode* m_expr2; @@ -998,7 +1218,7 @@ namespace JSC { ConditionalNode(const JSTokenLocation&, ExpressionNode* logical, ExpressionNode* expr1, ExpressionNode* expr2); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_logical; ExpressionNode* m_expr1; @@ -1010,7 +1230,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) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_ident; ExpressionNode* m_right; @@ -1020,13 +1240,16 @@ namespace JSC { class AssignResolveNode : public ExpressionNode, public ThrowableExpressionData { public: - AssignResolveNode(const JSTokenLocation&, const Identifier&, ExpressionNode* right); + AssignResolveNode(const JSTokenLocation&, const Identifier&, ExpressionNode* right, AssignmentContext); + bool isAssignResolveNode() const override { return true; } + const Identifier& identifier() const { return m_ident; } private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_ident; ExpressionNode* m_right; + AssignmentContext m_assignmentContext; }; class ReadModifyBracketNode : public ExpressionNode, public ThrowableSubExpressionData { @@ -1034,12 +1257,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) override; + 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; }; @@ -1049,7 +1272,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) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_base; ExpressionNode* m_subscript; @@ -1063,7 +1286,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) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_base; const Identifier& m_ident; @@ -1076,12 +1299,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) override; + 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; }; @@ -1090,57 +1313,25 @@ namespace JSC { AssignErrorNode(const JSTokenLocation&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + 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) { ASSERT(expr); m_expressions.append(expr); } + void setNext(CommaNode* next) { m_next = next; } + CommaNode* next() { return m_next; } private: - virtual bool isCommaNode() const override { return true; } - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + bool isCommaNode() const override { return true; } + 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) override; - 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) override; - - ConstDeclNode* m_next; - }; - - class SourceElements : public ParserArenaDeletable { + class SourceElements final : public ParserArenaFreeable { public: SourceElements(); @@ -1150,22 +1341,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&, FunctionStack&&); StatementNode* singleStatement() const; StatementNode* lastStatement() const; private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isBlock() const override { return true; } + bool isBlock() const override { return true; } SourceElements* m_statements; }; @@ -1175,17 +1370,19 @@ namespace JSC { EmptyStatementNode(const JSTokenLocation&); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isEmptyStatement() const override { return true; } + bool isEmptyStatement() const override { return true; } }; class DebuggerStatementNode : public StatementNode { public: DebuggerStatementNode(const JSTokenLocation&); + + bool isDebuggerStatement() const override { return true; } private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class ExprStatementNode : public StatementNode { @@ -1195,28 +1392,48 @@ namespace JSC { ExpressionNode* expr() const { return m_expr; } private: - virtual bool isExprStatement() const override { return true; } + bool isExprStatement() const override { return true; } - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + 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) override; + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; }; + class EmptyVarExpression : public ExpressionNode { + public: + EmptyVarExpression(const JSTokenLocation&, const Identifier&); + + private: + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_ident; + }; + + class EmptyLetExpression : public ExpressionNode { + public: + EmptyLetExpression(const JSTokenLocation&, const Identifier&); + + private: + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + const Identifier& m_ident; + }; + class IfElseNode : public StatementNode { public: IfElseNode(const JSTokenLocation&, ExpressionNode* condition, StatementNode* ifBlock, StatementNode* elseBlock); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; bool tryFoldBreakAndContinue(BytecodeGenerator&, StatementNode* ifBlock, Label*& trueTarget, FallThroughMode&); @@ -1230,7 +1447,7 @@ namespace JSC { DoWhileNode(const JSTokenLocation&, StatementNode*, ExpressionNode*); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; StatementNode* m_statement; ExpressionNode* m_expr; @@ -1241,18 +1458,20 @@ namespace JSC { WhileNode(const JSTokenLocation&, ExpressionNode*, StatementNode*); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + 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) override; + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr1; ExpressionNode* m_expr2; @@ -1260,13 +1479,17 @@ namespace JSC { StatementNode* m_statement; }; - class DeconstructionPatternNode; + class DestructuringPatternNode; - class EnumerationNode : public StatementNode, public ThrowableExpressionData { + class EnumerationNode : public StatementNode, public ThrowableExpressionData, public VariableEnvironmentNode { public: - EnumerationNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*); - EnumerationNode(VM*, const JSTokenLocation&, DeconstructionPatternNode*, ExpressionNode*, StatementNode*); - + using ParserArenaDeletable::operator new; + + EnumerationNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*, VariableEnvironment&); + + ExpressionNode* lexpr() const { return m_lexpr; } + ExpressionNode* expr() const { return m_expr; } + protected: ExpressionNode* m_lexpr; ExpressionNode* m_expr; @@ -1275,44 +1498,44 @@ namespace JSC { class ForInNode : public EnumerationNode { public: - ForInNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*); - ForInNode(VM*, const JSTokenLocation&, DeconstructionPatternNode*, ExpressionNode*, StatementNode*); + ForInNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*, VariableEnvironment&); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* tryGetBoundLocal(BytecodeGenerator&); + void emitLoopHeader(BytecodeGenerator&, RegisterID* propertyName); + + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; }; class ForOfNode : public EnumerationNode { public: - ForOfNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*); - ForOfNode(VM*, const JSTokenLocation&, DeconstructionPatternNode*, ExpressionNode*, StatementNode*); - + ForOfNode(const JSTokenLocation&, ExpressionNode*, ExpressionNode*, StatementNode*, VariableEnvironment&); + bool isForOfNode() const override { return true; } + private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + 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 override { return true; } - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + bool isContinue() const override { return true; } + 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 override { return true; } - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + bool isBreak() const override { return true; } + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_ident; }; @@ -1324,9 +1547,9 @@ namespace JSC { ExpressionNode* value() { return m_value; } private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isReturnNode() const override { return true; } + bool isReturnNode() const override { return true; } ExpressionNode* m_value; }; @@ -1336,7 +1559,7 @@ namespace JSC { WithNode(const JSTokenLocation&, ExpressionNode*, StatementNode*, const JSTextPosition& divot, uint32_t expressionLength); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; StatementNode* m_statement; @@ -1348,8 +1571,10 @@ namespace JSC { public: LabelNode(const JSTokenLocation&, const Identifier& name, StatementNode*); + bool isLabel() const override { return true; } + private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; const Identifier& m_name; StatementNode* m_statement; @@ -1360,55 +1585,33 @@ namespace JSC { ThrowNode(const JSTokenLocation&, ExpressionNode*); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + 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) override; + 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 ParserArenaDeletable { + class ScopeNode : public StatementNode, public ParserArenaRoot, public VariableEnvironmentNode { public: - ParameterNode(PassRefPtr<DeconstructionPatternNode>); - ParameterNode(ParameterNode*, PassRefPtr<DeconstructionPatternNode>); - DeconstructionPatternNode* pattern() const { return m_pattern.get(); } - ParameterNode* nextParam() const { return m_next; } + ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, bool inStrictContext); + ScopeNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, const SourceCode&, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants); - private: - RefPtr<DeconstructionPatternNode> m_pattern; - 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); - - 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(); } @@ -1420,21 +1623,31 @@ namespace JSC { void setFeatures(CodeFeatures features) { m_features = features; } CodeFeatures features() { return m_features; } + InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures() { return m_innerArrowFunctionCodeFeatures; } + bool doAnyInnerArrowFunctionsUseAnyFeature() { return m_innerArrowFunctionCodeFeatures != NoInnerArrowFunctionFeatures; } + bool doAnyInnerArrowFunctionsUseArguments() { return m_innerArrowFunctionCodeFeatures & ArgumentsInnerArrowFunctionFeature; } + bool doAnyInnerArrowFunctionsUseSuperCall() { return m_innerArrowFunctionCodeFeatures & SuperCallInnerArrowFunctionFeature; } + bool doAnyInnerArrowFunctionsUseSuperProperty() { return m_innerArrowFunctionCodeFeatures & SuperPropertyInnerArrowFunctionFeature; } + bool doAnyInnerArrowFunctionsUseEval() { return m_innerArrowFunctionCodeFeatures & EvalInnerArrowFunctionFeature; } + bool doAnyInnerArrowFunctionsUseThis() { return m_innerArrowFunctionCodeFeatures & ThisInnerArrowFunctionFeature; } + bool doAnyInnerArrowFunctionsUseNewTarget() { return m_innerArrowFunctionCodeFeatures & NewTargetInnerArrowFunctionFeature; } bool usesEval() const { return m_features & EvalFeature; } bool usesArguments() const { return (m_features & ArgumentsFeature) && !(m_features & ShadowsArgumentsFeature); } - bool modifiesParameter() const { return m_features & ModifiedParameterFeature; } + bool usesArrowFunction() const { return m_features & ArrowFunctionFeature; } 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 usesSuperCall() const { return m_features & SuperCallFeature; } + bool usesSuperProperty() const { return m_features & SuperPropertyFeature; } + bool usesNewTarget() const { return m_features & NewTargetFeature; } + 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()); } + bool hasSloppyModeHoistedFunction(UniquedStringImpl* uid) const { return m_sloppyModeHoistedFunctions.contains(uid); } - VarStack& varStack() { return m_varStack; } - FunctionStack& functionStack() { return m_functionStack; } + VariableEnvironment& varDeclarations() { return m_varDeclarations; } int neededConstants() { @@ -1446,29 +1659,27 @@ 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; private: CodeFeatures m_features; + InnerArrowFunctionCodeFeatures m_innerArrowFunctionCodeFeatures; SourceCode m_source; - VarStack m_varStack; - FunctionStack m_functionStack; + VariableEnvironment m_varDeclarations; + UniquedStringImplPtrSet m_sloppyModeHoistedFunctions; 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, unsigned endColumn, 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&, UniquedStringImplPtrSet&&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants, RefPtr<ModuleScopeData>&&); unsigned startColumn() const { return m_startColumn; } unsigned endColumn() const { return m_endColumn; } @@ -1476,18 +1687,14 @@ namespace JSC { static const bool scopeIsFunction = false; private: - ProgramNode(VM*, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VarStack*, FunctionStack*, IdentifierSet&, const SourceCode&, CodeFeatures, int numConstants); - - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - + 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, unsigned endColumn, 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&, UniquedStringImplPtrSet&&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants, RefPtr<ModuleScopeData>&&); ALWAYS_INLINE unsigned startColumn() const { return 0; } unsigned endColumn() const { return m_endColumn; } @@ -1495,200 +1702,565 @@ namespace JSC { static const bool scopeIsFunction = false; private: - EvalNode(VM*, const JSTokenLocation& start, const JSTokenLocation& end, unsigned endColumn, SourceElements*, VarStack*, FunctionStack*, IdentifierSet&, const SourceCode&, CodeFeatures, int numConstants); + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + unsigned m_endColumn; + }; + + class ModuleProgramNode : public ScopeNode { + public: + ModuleProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants, RefPtr<ModuleScopeData>&&); - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + unsigned startColumn() const { return m_startColumn; } + unsigned endColumn() const { return m_endColumn; } + + static const bool scopeIsFunction = false; + ModuleScopeData& moduleScopeData() + { + return m_moduleScopeData; + } + + private: + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + unsigned m_startColumn; unsigned m_endColumn; + Ref<ModuleScopeData> m_moduleScopeData; }; - class FunctionParameters : public RefCounted<FunctionParameters> { - WTF_MAKE_FAST_ALLOCATED; - WTF_MAKE_NONCOPYABLE(FunctionParameters); + 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; + bool isModuleDeclarationNode() const override { 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: + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + 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: + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + 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: + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void analyzeModule(ModuleAnalyzer&) override; + StatementNode* m_declaration; + const Identifier& m_localName; + }; + + class ExportLocalDeclarationNode : public ModuleDeclarationNode { + public: + ExportLocalDeclarationNode(const JSTokenLocation&, StatementNode*); + + const StatementNode& declaration() const { return *m_declaration; } + + private: + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void analyzeModule(ModuleAnalyzer&) override; + StatementNode* m_declaration; + }; + + class ExportSpecifierNode : public Node { public: - static PassRefPtr<FunctionParameters> create(ParameterNode*); - ~FunctionParameters(); + ExportSpecifierNode(const JSTokenLocation&, const Identifier& localName, const Identifier& exportedName); - unsigned size() const { return m_size; } - DeconstructionPatternNode* at(unsigned index) { ASSERT(index < m_size); return patterns()[index]; } + const Identifier& exportedName() { return m_exportedName; } + const Identifier& localName() { return m_localName; } private: - FunctionParameters(ParameterNode*, unsigned size); + const Identifier& m_localName; + const Identifier& m_exportedName; + }; - DeconstructionPatternNode** patterns() { return &m_storage; } + class ExportSpecifierListNode : public ParserArenaDeletable { + public: + typedef Vector<ExportSpecifierNode*, 3> Specifiers; - unsigned m_size; - DeconstructionPatternNode* m_storage; + const Specifiers& specifiers() const { return m_specifiers; } + void append(ExportSpecifierNode* specifier) + { + m_specifiers.append(specifier); + } + + private: + Specifiers m_specifiers; }; - class FunctionBodyNode : public ScopeNode { + class ExportNamedDeclarationNode : public ModuleDeclarationNode { public: - static const bool isFunctionNode = true; - static FunctionBodyNode* create(VM*, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, bool isStrictMode); - static PassRefPtr<FunctionBodyNode> create(VM*, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VarStack*, FunctionStack*, IdentifierSet&, const SourceCode&, CodeFeatures, int numConstants); + ExportNamedDeclarationNode(const JSTokenLocation&, ExportSpecifierListNode*, ModuleNameNode*); + + ExportSpecifierListNode* specifierList() const { return m_specifierList; } + ModuleNameNode* moduleName() const { return m_moduleName; } + + private: + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void analyzeModule(ModuleAnalyzer&) override; + ExportSpecifierListNode* m_specifierList; + ModuleNameNode* m_moduleName { nullptr }; + }; - FunctionParameters* parameters() const { return m_parameters.get(); } - size_t parameterCount() const { return m_parameters->size(); } + class FunctionMetadataNode final : public Node, public ParserArenaDeletable { + public: + using ParserArenaDeletable::operator new; - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + FunctionMetadataNode( + ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, + unsigned startColumn, unsigned endColumn, int functionKeywordStart, + int functionNameStart, int parametersStart, bool isInStrictContext, + ConstructorKind, SuperBinding, unsigned parameterCount, + SourceParseMode, bool isArrowFunctionBodyExpression); - void finishParsing(const SourceCode&, ParameterNode*, const Identifier&, FunctionNameIsInScopeToggle); - void finishParsing(PassRefPtr<FunctionParameters>, const Identifier&, FunctionNameIsInScopeToggle); + void finishParsing(const SourceCode&, const Identifier&, FunctionMode); + void overrideName(const Identifier& ident) { m_ident = ident; } const Identifier& ident() { return m_ident; } + void setEcmaName(const Identifier& ecmaName) { m_ecmaName = ecmaName; } + const Identifier& ecmaName() { return m_ident.isEmpty() ? m_ecmaName : 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 setFunctionNameStart(int functionNameStart) { m_functionNameStart = functionNameStart; } int functionNameStart() const { return m_functionNameStart; } + int functionKeywordStart() const { return m_functionKeywordStart; } + int parametersStart() const { return m_parametersStart; } unsigned startColumn() const { return m_startColumn; } unsigned endColumn() const { return m_endColumn; } + unsigned parameterCount() const { return m_parameterCount; } + SourceParseMode parseMode() const { return m_parseMode; } void setEndPosition(JSTextPosition); - static const bool scopeIsFunction = true; + const SourceCode& source() const { return m_source; } + const SourceCode& classSource() const { return m_classSource; } + void setClassSource(const SourceCode& source) { m_classSource = source; } - private: - FunctionBodyNode(VM*, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, bool inStrictContext); - FunctionBodyNode(VM*, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VarStack*, FunctionStack*, IdentifierSet&, const SourceCode&, CodeFeatures, int numConstants); + 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_ecmaName; Identifier m_inferredName; - FunctionNameIsInScopeToggle m_functionNameIsInScopeToggle; - RefPtr<FunctionParameters> m_parameters; + FunctionMode m_functionMode; + unsigned m_startColumn; + unsigned m_endColumn; + int m_functionKeywordStart; int m_functionNameStart; + int m_parametersStart; + SourceCode m_source; + SourceCode m_classSource; + 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&, UniquedStringImplPtrSet&&, FunctionParameters*, const SourceCode&, CodeFeatures, InnerArrowFunctionCodeFeatures, int numConstants, RefPtr<ModuleScopeData>&&); + + FunctionParameters* parameters() const { return m_parameters; } + + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + bool isFunctionNode() const override { return true; } + + void finishParsing(const Identifier&, FunctionMode); + + const Identifier& ident() { return m_ident; } + + FunctionMode functionMode() const { return m_functionMode; } + + unsigned startColumn() const { return m_startColumn; } + unsigned endColumn() const { return m_endColumn; } + + static const bool scopeIsFunction = true; + + private: + Identifier m_ident; + FunctionMode m_functionMode; + FunctionParameters* m_parameters; unsigned m_startColumn; unsigned m_endColumn; }; - class FuncExprNode : public ExpressionNode { + class BaseFuncExprNode : public ExpressionNode { + public: + FunctionMetadataNode* metadata() { return m_metadata; } + + bool isBaseFuncExprNode() const override { return true; } + + protected: + BaseFuncExprNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&, FunctionMode); + + FunctionMetadataNode* m_metadata; + }; + + + class FuncExprNode : public BaseFuncExprNode { public: - FuncExprNode(const JSTokenLocation&, const Identifier&, FunctionBodyNode*, const SourceCode&, ParameterNode* = 0); + FuncExprNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&); - FunctionBodyNode* body() { return m_body; } + protected: + FuncExprNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&, FunctionMode); private: - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - virtual bool isFuncExprNode() const override { return true; } + bool isFuncExprNode() const override { return true; } + }; - FunctionBodyNode* m_body; + class ArrowFuncExprNode : public BaseFuncExprNode { + public: + ArrowFuncExprNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&); + + private: + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + bool isArrowFuncExprNode() const override { return true; } }; - class DeconstructionPatternNode : public RefCounted<DeconstructionPatternNode> { - WTF_MAKE_NONCOPYABLE(DeconstructionPatternNode); - WTF_MAKE_FAST_ALLOCATED; + class MethodDefinitionNode : public FuncExprNode { + public: + MethodDefinitionNode(const JSTokenLocation&, const Identifier&, FunctionMetadataNode*, const SourceCode&); + + private: + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + }; + 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: + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_argument; + bool m_delegate; + }; + + class AwaitExprNode final : public ExpressionNode, public ThrowableExpressionData { + public: + AwaitExprNode(const JSTokenLocation&, ExpressionNode* argument); + + ExpressionNode* argument() const { return m_argument; } + + private: + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_argument; + }; + + class ClassExprNode final : public ExpressionNode, public VariableEnvironmentNode { + public: + using ParserArenaDeletable::operator new; + + ClassExprNode(const JSTokenLocation&, const Identifier&, const SourceCode& classSource, + VariableEnvironment& classEnvironment, ExpressionNode* constructorExpresssion, + ExpressionNode* parentClass, PropertyListNode* instanceMethods, PropertyListNode* staticMethods); + + const Identifier& name() { return m_name; } + const Identifier& ecmaName() { return m_ecmaName ? *m_ecmaName : m_name; } + void setEcmaName(const Identifier& name) { m_ecmaName = m_name.isNull() ? &name : &m_name; } + + bool hasStaticProperty(const Identifier& propName) { return m_staticMethods ? m_staticMethods->hasStaticallyNamedProperty(propName) : false; } + + private: + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + bool isClassExprNode() const override { return true; } + + SourceCode m_classSource; + const Identifier& m_name; + const Identifier* m_ecmaName; + ExpressionNode* m_constructorExpression; + ExpressionNode* m_classHeritage; + PropertyListNode* m_instanceMethods; + PropertyListNode* m_staticMethods; + }; + + class DestructuringPatternNode : public ParserArenaFreeable { + public: + virtual ~DestructuringPatternNode() { } virtual void collectBoundIdentifiers(Vector<Identifier>&) const = 0; virtual void bindValue(BytecodeGenerator&, RegisterID* source) const = 0; virtual void toString(StringBuilder&) const = 0; virtual bool isBindingNode() const { return false; } + virtual bool isRestParameter() const { return false; } virtual RegisterID* emitDirectBinding(BytecodeGenerator&, RegisterID*, ExpressionNode*) { return 0; } - virtual ~DeconstructionPatternNode() = 0; - protected: - DeconstructionPatternNode(VM*); + DestructuringPatternNode(); }; - class ArrayPatternNode : public DeconstructionPatternNode { + class ArrayPatternNode : public DestructuringPatternNode, public ThrowableExpressionData, public ParserArenaDeletable { public: - static PassRefPtr<ArrayPatternNode> create(VM*); - void appendIndex(const JSTokenLocation&, DeconstructionPatternNode* node) + using ParserArenaDeletable::operator new; + + ArrayPatternNode(); + enum class BindingType { + Elision, + Element, + RestElement + }; + + void appendIndex(BindingType bindingType, const JSTokenLocation&, DestructuringPatternNode* node, ExpressionNode* defaultValue) { - m_targetPatterns.append(node); + m_targetPatterns.append({ bindingType, node, defaultValue }); } private: - ArrayPatternNode(VM*); - 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; + struct Entry { + BindingType bindingType; + DestructuringPatternNode* pattern; + ExpressionNode* defaultValue; + }; + void collectBoundIdentifiers(Vector<Identifier>&) const override; + void bindValue(BytecodeGenerator&, RegisterID*) const override; + RegisterID* emitDirectBinding(BytecodeGenerator&, RegisterID* dst, ExpressionNode*) override; + void toString(StringBuilder&) const override; - Vector<RefPtr<DeconstructionPatternNode>> m_targetPatterns; + Vector<Entry> m_targetPatterns; }; - class ObjectPatternNode : public DeconstructionPatternNode { + class ObjectPatternNode : public DestructuringPatternNode, public ParserArenaDeletable { public: - static PassRefPtr<ObjectPatternNode> create(VM*); - void appendEntry(const JSTokenLocation&, const Identifier& identifier, bool wasString, DeconstructionPatternNode* pattern) + using ParserArenaDeletable::operator new; + + ObjectPatternNode(); + void appendEntry(const JSTokenLocation&, const Identifier& identifier, bool wasString, DestructuringPatternNode* pattern, ExpressionNode* defaultValue) { - m_targetPatterns.append(Entry(identifier, wasString, pattern)); + m_targetPatterns.append(Entry{ identifier, nullptr, wasString, pattern, defaultValue }); } - + + void appendEntry(const JSTokenLocation&, ExpressionNode* propertyExpression, DestructuringPatternNode* pattern, ExpressionNode* defaultValue) + { + m_targetPatterns.append(Entry{ Identifier(), propertyExpression, false, pattern, defaultValue }); + } + private: - ObjectPatternNode(VM*); - virtual void collectBoundIdentifiers(Vector<Identifier>&) const override; - virtual void bindValue(BytecodeGenerator&, RegisterID*) const override; - virtual void toString(StringBuilder&) const override; + void collectBoundIdentifiers(Vector<Identifier>&) const override; + void bindValue(BytecodeGenerator&, RegisterID*) const override; + void toString(StringBuilder&) const override; struct Entry { - Entry(const Identifier& propertyName, bool wasString, DeconstructionPatternNode* pattern) - : propertyName(propertyName) - , wasString(wasString) - , pattern(pattern) - { - } - Identifier propertyName; + const Identifier& propertyName; + ExpressionNode* propertyExpression; bool wasString; - RefPtr<DeconstructionPatternNode> pattern; + DestructuringPatternNode* pattern; + ExpressionNode* defaultValue; }; Vector<Entry> m_targetPatterns; }; - class BindingNode : public DeconstructionPatternNode { + class BindingNode : public DestructuringPatternNode { public: - static PassRefPtr<BindingNode> create(VM*, const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end); + 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: - BindingNode(VM*, const Identifier& boundProperty, const JSTextPosition& start, const JSTextPosition& end); - - virtual void collectBoundIdentifiers(Vector<Identifier>&) const override; - virtual void bindValue(BytecodeGenerator&, RegisterID*) const override; - virtual void toString(StringBuilder&) const override; + void collectBoundIdentifiers(Vector<Identifier>&) const override; + void bindValue(BytecodeGenerator&, RegisterID*) const override; + void toString(StringBuilder&) const override; - virtual bool isBindingNode() const override { return true; } + bool isBindingNode() const override { return true; } JSTextPosition m_divotStart; JSTextPosition m_divotEnd; - Identifier m_boundProperty; + const Identifier& m_boundProperty; + AssignmentContext m_bindingContext; }; - class DeconstructingAssignmentNode : public ExpressionNode, public ParserArenaDeletable { + class RestParameterNode : public DestructuringPatternNode { public: - DeconstructingAssignmentNode(const JSTokenLocation&, PassRefPtr<DeconstructionPatternNode>, ExpressionNode*); - DeconstructionPatternNode* bindings() { return m_bindings.get(); } - - using ParserArenaDeletable::operator new; + RestParameterNode(DestructuringPatternNode*, unsigned numParametersToSkip); + + bool isRestParameter() const override { return true; } + + void emit(BytecodeGenerator&); + + private: + void collectBoundIdentifiers(Vector<Identifier>&) const override; + void bindValue(BytecodeGenerator&, RegisterID*) const override; + void toString(StringBuilder&) const override; + + DestructuringPatternNode* m_pattern; + unsigned m_numParametersToSkip; + }; + + 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 bool isLocation() const override { return true; } - virtual bool isDeconstructionNode() const override { return true; } - virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void collectBoundIdentifiers(Vector<Identifier>&) const override; + void bindValue(BytecodeGenerator&, RegisterID*) const override; + void toString(StringBuilder&) const override; - RefPtr<DeconstructionPatternNode> m_bindings; + 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: + bool isAssignmentLocation() const override { return true; } + bool isDestructuringNode() const override { return true; } + RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + DestructuringPatternNode* m_bindings; ExpressionNode* m_initializer; }; + 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]; } + ALWAYS_INLINE void append(DestructuringPatternNode* pattern, ExpressionNode* defaultValue) + { + ASSERT(pattern); + + // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-functiondeclarationinstantiation + // This implements IsSimpleParameterList in the Ecma 2015 spec. + // If IsSimpleParameterList is false, we will create a strict-mode like arguments object. + // IsSimpleParameterList is false if the argument list contains any default parameter values, + // a rest parameter, or any destructuring patterns. + // If we do have default parameters, destructuring parameters, or a rest parameter, our parameters will be allocated in a different scope. + + bool hasDefaultParameterValue = defaultValue; + bool isSimpleParameter = !hasDefaultParameterValue && pattern->isBindingNode(); + m_isSimpleParameterList &= isSimpleParameter; + + m_patterns.append(std::make_pair(pattern, defaultValue)); + } + ALWAYS_INLINE bool isSimpleParameterList() const { return m_isSimpleParameterList; } + + private: + + Vector<std::pair<DestructuringPatternNode*, ExpressionNode*>, 3> m_patterns; + bool m_isSimpleParameterList { true }; + }; + 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; } + bool isFuncDeclNode() const override { return true; } + FunctionMetadataNode* metadata() { return m_metadata; } private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; - FunctionBodyNode* m_body; + FunctionMetadataNode* m_metadata; + }; + + class ClassDeclNode final : public StatementNode { + public: + ClassDeclNode(const JSTokenLocation&, ExpressionNode* classExpression); + + private: + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + + ExpressionNode* m_classDeclaration; }; class CaseClauseNode : public ParserArenaFreeable { @@ -1698,10 +2270,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 { @@ -1731,12 +2305,14 @@ namespace JSC { 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&, FunctionStack&&); private: - virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; + void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override; ExpressionNode* m_expr; CaseBlockNode* m_block; @@ -1757,21 +2333,9 @@ namespace JSC { ArgumentListNode* tail; }; - struct ConstDeclList { - ConstDeclNode* head; - ConstDeclNode* tail; - }; - - struct ParameterList { - ParameterNode* head; - ParameterNode* tail; - }; - struct ClauseList { ClauseListNode* head; ClauseListNode* tail; }; } // namespace JSC - -#endif // Nodes_h diff --git a/Source/JavaScriptCore/parser/NodesAnalyzeModule.cpp b/Source/JavaScriptCore/parser/NodesAnalyzeModule.cpp new file mode 100644 index 000000000..426e53b9d --- /dev/null +++ b/Source/JavaScriptCore/parser/NodesAnalyzeModule.cpp @@ -0,0 +1,92 @@ +/* + * 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&) +{ +} + +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())); + } + } +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/Parser.cpp b/Source/JavaScriptCore/parser/Parser.cpp index 297666e72..3b79d0aa7 100644 --- a/Source/JavaScriptCore/parser/Parser.cpp +++ b/Source/JavaScriptCore/parser/Parser.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006-2010, 2013, 2016 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,40 +24,33 @@ #include "Parser.h" #include "ASTBuilder.h" -#include "CodeBlock.h" -#include "Debugger.h" -#include "JSCJSValueInlines.h" -#include "Lexer.h" -#include "NodeInfo.h" -#include "SourceProvider.h" +#include "DebuggerParseData.h" +#include "JSCInlines.h" #include "VM.h" #include <utility> -#include <wtf/HashFunctions.h> -#include <wtf/OwnPtr.h> +#include <wtf/SetForScope.h> #include <wtf/StringPrintStream.h> -#include <wtf/WTFThreadData.h> - #define updateErrorMessage(shouldPrintToken, ...) do {\ propagateError(); \ logError(shouldPrintToken, __VA_ARGS__); \ } while (0) -#define propagateError() do { if (hasError()) return 0; } while (0) +#define propagateError() do { if (UNLIKELY(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 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 failIfStackOverflow() do { if (UNLIKELY(!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 semanticFailIfTrue(cond, ...) do { if (cond) internalFailWithMessage(false, __VA_ARGS__); } while (0) #define semanticFailIfFalse(cond, ...) do { if (!(cond)) internalFailWithMessage(false, __VA_ARGS__); } while (0) #define regexFail(failure) do { setErrorMessage(failure); return 0; } while (0) #define failDueToUnexpectedToken() do {\ @@ -69,6 +62,10 @@ consumeOrFail(token, "Expected '", tokenString, "' to ", operation, " a ", production);\ } while (0) +#define handleProductionOrFail2(token, tokenString, operation, production) do {\ + consumeOrFail(token, "Expected '", tokenString, "' to ", operation, " an ", 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"); \ @@ -76,6 +73,8 @@ 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__); \ + if (isDisallowedIdentifierAwait(m_token)) \ + semanticFail("Can't use 'await' as a ", __VA_ARGS__, " ", disallowedIdentifierAwaitReason()); \ } while (0) using namespace std; @@ -89,53 +88,11 @@ void Parser<LexerType>::logError(bool) 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()); + setErrorMessage(stream.toStringWithLatin1Fallback()); } -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) +template <typename LexerType> template <typename... Args> +void Parser<LexerType>::logError(bool shouldPrintToken, Args&&... args) { if (hasError()) return; @@ -144,109 +101,118 @@ void Parser<LexerType>::logError(bool shouldPrintToken, const A& value1, const B 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()); + stream.print(std::forward<Args>(args)..., "."); + setErrorMessage(stream.toStringWithLatin1Fallback()); } template <typename LexerType> -Parser<LexerType>::Parser(VM* vm, const SourceCode& source, FunctionParameters* parameters, const Identifier& name, JSParserStrictness strictness, JSParserMode parserMode) +Parser<LexerType>::Parser(VM* vm, const SourceCode& source, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, SourceParseMode parseMode, SuperBinding superBinding, ConstructorKind defaultConstructorKind, DerivedContextType derivedContextType, bool isEvalContext, EvalContextType evalContextType, DebuggerParseData* debuggerParseData) : m_vm(vm) , m_source(&source) , m_hasStackOverflow(false) , m_allowsIn(true) - , m_assignmentCount(0) - , m_nonLHSCount(0) , m_syntaxAlreadyValidated(source.provider()->isValid()) , m_statementDepth(0) - , m_nonTrivialExpressionCount(0) - , m_lastIdentifier(0) - , m_lastFunctionName(nullptr) , m_sourceElements(0) + , m_parsingBuiltin(builtinMode == JSParserBuiltinMode::Builtin) + , m_scriptMode(scriptMode) + , m_superBinding(superBinding) + , m_defaultConstructorKind(defaultConstructorKind) + , m_immediateParentAllowsFunctionDeclarationInStatement(false) + , m_debuggerParseData(debuggerParseData) { - m_lexer = adoptPtr(new LexerType(vm)); - m_arena = m_vm->parserArena.get(); - m_lexer->setCode(source, m_arena); - m_token.m_location.line = source.firstLine(); + m_lexer = std::make_unique<LexerType>(vm, builtinMode, scriptMode); + m_lexer->setCode(source, &m_parserArena); + m_token.m_location.line = source.firstLine().oneBasedInt(); 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->setStrictMode(); - if (parameters) { - for (unsigned i = 0; i < parameters->size(); i++) { - auto parameter = parameters->at(i); - if (!parameter->isBindingNode()) - continue; - scope->declareParameter(&static_cast<BindingNode*>(parameter)->boundProperty()); - } + scope->setSourceParseMode(parseMode); + scope->setIsEvalContext(isEvalContext); + if (isEvalContext) + scope->setEvalContextType(evalContextType); + + if (derivedContextType == DerivedContextType::DerivedConstructorContext) { + scope->setConstructorKind(ConstructorKind::Extends); + scope->setExpectedSuperBinding(SuperBinding::Needed); } - if (!name.isNull()) - scope->declareCallee(&name); + + if (derivedContextType == DerivedContextType::DerivedMethodContext) + scope->setExpectedSuperBinding(SuperBinding::Needed); + + if (strictMode == JSParserStrictMode::Strict) + scope->setStrictMode(); + + if (parseMode == SourceParseMode::ModuleAnalyzeMode || parseMode == SourceParseMode::ModuleEvaluateMode) + m_moduleScopeData = ModuleScopeData::create(); + next(); } +class Scope::MaybeParseAsGeneratorForScope : public SetForScope<bool> { +public: + MaybeParseAsGeneratorForScope(ScopeRef& scope, bool shouldParseAsGenerator) + : SetForScope<bool>(scope->m_isGenerator, shouldParseAsGenerator) { } +}; + template <typename LexerType> 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(context, CheckForStrictMode); - if (!sourceElements || !consume(EOFTOK)) { + scope->setIsLexicalScope(); + SetForScope<FunctionParsePhase> functionParsePhasePoisoner(m_parserState.functionParsePhase, FunctionParsePhase::Body); + + bool isArrowFunctionBodyExpression = parseMode == SourceParseMode::AsyncArrowFunctionBodyMode && !match(OPENBRACE); + if (m_lexer->isReparsingFunction()) { + ParserFunctionInfo<ASTBuilder> functionInfo; + if (isGeneratorOrAsyncFunctionBodyParseMode(parseMode)) + m_parameters = createGeneratorParameters(context, functionInfo.parameterCount); + else + m_parameters = parseFunctionParameters(context, parseMode, functionInfo); + + if (SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode).contains(parseMode) && !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 (isAsyncFunctionWrapperParseMode(parseMode)) + sourceElements = parseAsyncFunctionSourceElements(context, parseMode, isArrowFunctionBodyExpression, CheckForStrictMode); + else if (isArrowFunctionBodyExpression) + sourceElements = parseArrowFunctionSingleExpressionBodySourceElements(context); + else if (isModuleParseMode(parseMode)) + sourceElements = parseModuleSourceElements(context, parseMode); + else if (parseMode == SourceParseMode::GeneratorWrapperFunctionMode) + sourceElements = parseGeneratorFunctionSourceElements(context, calleeName, CheckForStrictMode); + else + sourceElements = parseSourceElements(context, CheckForStrictMode); + } + + bool validEnding = consume(EOFTOK); + if (!sourceElements || !validEnding) { if (hasError()) parseError = m_errorMessage; else @@ -254,35 +220,93 @@ String Parser<LexerType>::parseInner() } IdentifierSet capturedVariables; - bool modifiedParameter = false; - scope->getCapturedVariables(capturedVariables, modifiedParameter); + UniquedStringImplPtrSet sloppyModeHoistedFunctions; + scope->getSloppyModeHoistedFunctions(sloppyModeHoistedFunctions); + scope->getCapturedVars(capturedVariables); + + VariableEnvironment& varDeclarations = scope->declaredVariables(); + for (auto& entry : capturedVariables) + varDeclarations.markVariableAsCaptured(entry); + + if (SourceParseModeSet(SourceParseMode::GeneratorWrapperFunctionMode).contains(parseMode) || isAsyncFunctionWrapperParseMode(parseMode)) { + if (scope->usedVariablesContains(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; - didFinishParsing(sourceElements, context.varDeclarations(), context.funcDeclarations(), features, - context.numConstants(), capturedVariables); +#ifndef NDEBUG + if (m_parsingBuiltin && isProgramParseMode(parseMode)) { + VariableEnvironment& lexicalVariables = scope->lexicalVariables(); + const HashSet<UniquedStringImpl*>& closedVariableCandidates = scope->closedVariableCandidates(); + for (UniquedStringImpl* candidate : closedVariableCandidates) { + if (!lexicalVariables.contains(candidate) && !varDeclarations.contains(candidate) && !candidate->isSymbol()) { + dataLog("Bad global capture in builtin: '", candidate, "'\n"); + dataLog(m_source->view()); + CRASH(); + } + } + } +#endif // NDEBUG + didFinishParsing(sourceElements, scope->takeFunctionDeclarations(), varDeclarations, WTFMove(sloppyModeHoistedFunctions), 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, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, CodeFeatures features, int numConstants) { m_sourceElements = sourceElements; - m_varDeclarations = varStack; - m_funcDeclarations = funcStack; - m_capturedVariables.swap(capturedVars); + m_funcDeclarations = WTFMove(funcStack); + m_varDeclarations.swap(varDeclarations); m_features = features; + m_sloppyModeHoistedFunctions = WTFMove(sloppyModeHoistedFunctions); m_numConstants = numConstants; } template <typename LexerType> +bool Parser<LexerType>::isArrowFunctionParameters() +{ + if (match(OPENPAREN)) { + SavePoint saveArrowFunctionPoint = createSavePoint(); + next(); + bool isArrowFunction = false; + 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; + bool isArrowFunctionParameterList = true; + isArrowFunction = parseFormalParameters(syntaxChecker, syntaxChecker.createFormalParameterList(), isArrowFunctionParameterList, parametersCount) && consume(CLOSEPAREN) && match(ARROWFUNCTION); + propagateError(); + popScope(fakeScope, syntaxChecker.NeedsFreeVariableInfo); + } + restoreSavePoint(saveArrowFunctionPoint); + return isArrowFunction; + } + + if (matchSpecIdentifier()) { + SavePoint saveArrowFunctionPoint = createSavePoint(); + next(); + bool isArrowFunction = match(ARROWFUNCTION); + restoreSavePoint(saveArrowFunctionPoint); + return isArrowFunction; + } + + return false; +} + +template <typename LexerType> bool Parser<LexerType>::allowAutomaticSemicolon() { return match(CLOSEBRACE) || match(EOFTOK) || m_lexer->prevTerminator(); @@ -293,37 +317,47 @@ template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseSourceEl { const unsigned lengthOfUseStrictLiteral = 12; // "use strict".length TreeSourceElements sourceElements = context.createSourceElements(); - bool seenNonDirective = false; const Identifier* directive = 0; unsigned directiveLiteralLength = 0; auto savePoint = createSavePoint(); - bool hasSetStrict = false; - while (TreeStatement statement = parseStatement(context, directive, &directiveLiteralLength)) { - if (mode == CheckForStrictMode && !seenNonDirective) { + bool shouldCheckForUseStrict = mode == CheckForStrictMode; + + while (TreeStatement statement = parseStatementListItem(context, directive, &directiveLiteralLength)) { + if (shouldCheckForUseStrict) { if (directive) { // "use strict" must be the exact literal without escape sequences or line continuation. - if (!hasSetStrict && directiveLiteralLength == lengthOfUseStrictLiteral && m_vm->propertyNames->useStrictIdentifier == *directive) { + if (directiveLiteralLength == lengthOfUseStrictLiteral && m_vm->propertyNames->useStrictIdentifier == *directive) { setStrictMode(); - hasSetStrict = true; + shouldCheckForUseStrict = false; // We saw "use strict", there is no need to keep checking for it. if (!isValidStrictMode()) { - if (m_lastFunctionName) { - if (m_vm->propertyNames->arguments == *m_lastFunctionName) + 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_lastFunctionName) + 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"); + semanticFailIfTrue(currentScope()->hasNonSimpleParameterList(), "'use strict' directive not allowed inside a function with a non-simple parameter list"); semanticFailIfFalse(isValidStrictMode(), "Invalid parameters or function name in strict mode"); } + // Since strict mode is changed, restoring lexer state by calling next() may cause errors. restoreSavePoint(savePoint); propagateError(); continue; } - } else - seenNonDirective = true; + + // We saw a directive, but it wasn't "use strict". We reset our state to + // see if the next statement we parse is also a directive. + directive = nullptr; + } else { + // We saw a statement that wasn't in the form of a directive. The spec says that "use strict" + // is only allowed as the first statement, or after a sequence of directives before it, but + // not after non-directive statements. + shouldCheckForUseStrict = false; + } } context.appendStatement(sourceElements, statement); } @@ -333,35 +367,270 @@ template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseSourceEl } 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; - TreeDeconstructionPattern scratch1 = 0; - TreeExpression scratch2 = 0; - JSTextPosition scratch3; - TreeExpression varDecls = parseVarDeclarationList(context, scratch, scratch1, scratch2, scratch3, scratch3, scratch3); + TreeSourceElements sourceElements = context.createSourceElements(); + SyntaxChecker syntaxChecker(const_cast<VM*>(m_vm), m_lexer.get()); + + while (true) { + TreeStatement statement = 0; + switch (m_token.m_type) { + case EXPORT: + statement = parseExportDeclaration(context); + if (statement) + recordPauseLocation(context.breakpointLocation(statement)); + break; + + case IMPORT: { + SavePoint savePoint = createSavePoint(); + next(); + bool isImportDeclaration = !match(OPENPAREN); + restoreSavePoint(savePoint); + if (isImportDeclaration) { + statement = parseImportDeclaration(context); + if (statement) + recordPauseLocation(context.breakpointLocation(statement)); + break; + } + + // This is `import("...")` call case. + FALLTHROUGH; + } + + default: { + const Identifier* directive = 0; + unsigned directiveLiteralLength = 0; + if (parseMode == SourceParseMode::ModuleAnalyzeMode) { + if (!parseStatementListItem(syntaxChecker, directive, &directiveLiteralLength)) + goto end; + continue; + } + statement = parseStatementListItem(context, directive, &directiveLiteralLength); + break; + } + } + + if (!statement) + goto end; + context.appendStatement(sourceElements, statement); + } + +end: propagateError(); - failIfFalse(autoSemiColon(), "Expected ';' after var declaration"); - - return context.createVarStatement(location, varDecls, start, end); + + for (const auto& pair : m_moduleScopeData->exportedBindings()) { + const auto& uid = pair.key; + 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, const Identifier& name, 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; + createGeneratorParameters(context, info.parameterCount); + info.startOffset = parametersStart; + info.startLine = tokenLine(); + + { + AutoPopScopeRef generatorBodyScope(this, pushScope()); + generatorBodyScope->setSourceParseMode(SourceParseMode::GeneratorBodyMode); + generatorBodyScope->setConstructorKind(ConstructorKind::None); + generatorBodyScope->setExpectedSuperBinding(m_superBinding); + + 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.parametersStartColumn = startColumn; + + auto functionExpr = context.createGeneratorFunctionBody(startLocation, info, name); + 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> TreeSourceElements Parser<LexerType>::parseAsyncFunctionSourceElements(TreeBuilder& context, SourceParseMode parseMode, bool isArrowFunctionBodyExpression, SourceElementsMode mode) { - ASSERT(match(CONSTTOKEN)); + ASSERT(isAsyncFunctionWrapperParseMode(parseMode)); + 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; + createGeneratorParameters(context, info.parameterCount); + info.startOffset = parametersStart; + info.startLine = tokenLine(); + SourceParseMode innerParseMode = parseMode == SourceParseMode::AsyncArrowFunctionMode + ? SourceParseMode::AsyncArrowFunctionBodyMode + : SourceParseMode::AsyncFunctionBodyMode; + { + AutoPopScopeRef asyncFunctionBodyScope(this, pushScope()); + asyncFunctionBodyScope->setSourceParseMode(innerParseMode); + SyntaxChecker syntaxChecker(const_cast<VM*>(m_vm), m_lexer.get()); + if (isArrowFunctionBodyExpression) { + if (m_debuggerParseData) + failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(context), "Cannot parse the body of async arrow function"); + else + failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(syntaxChecker), "Cannot parse the body of async arrow function"); + } else { + if (m_debuggerParseData) + failIfFalse(parseSourceElements(context, mode), "Cannot parse the body of async function"); + else + failIfFalse(parseSourceElements(syntaxChecker, mode), "Cannot parse the body of async function"); + } + popScope(asyncFunctionBodyScope, TreeBuilder::NeedsFreeVariableInfo); + } + info.body = context.createFunctionMetadata(startLocation, tokenLocation(), startColumn, tokenColumn(), functionKeywordStart, functionNameStart, parametersStart, strictMode(), ConstructorKind::None, m_superBinding, info.parameterCount, innerParseMode, isArrowFunctionBodyExpression); + + info.endLine = tokenLine(); + info.endOffset = isArrowFunctionBodyExpression ? tokenLocation().endOffset : m_token.m_data.offset; + info.parametersStartColumn = startColumn; + + auto functionExpr = context.createAsyncFunctionBody(startLocation, info, innerParseMode); + 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>::parseStatementListItem(TreeBuilder& context, const Identifier*& directive, unsigned* directiveLiteralLength) +{ + // The grammar is documented here: + // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-statements + DepthManager statementDepth(&m_statementDepth); + m_statementDepth++; + TreeStatement result = 0; + bool shouldSetEndOffset = true; + bool shouldSetPauseLocation = false; + + switch (m_token.m_type) { + case CONSTTOKEN: + result = parseVariableDeclaration(context, DeclarationType::ConstDeclaration); + shouldSetPauseLocation = true; + break; + case LET: { + bool shouldParseVariableDeclaration = true; + if (!strictMode()) { + SavePoint savePoint = createSavePoint(); + next(); + // Intentionally use `isIdentifierOrAnyContextualKeyword(m_token)` 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 (!isIdentifierOrAnyContextualKeyword(m_token) && !match(OPENBRACE) && !match(OPENBRACKET)) + shouldParseVariableDeclaration = false; + restoreSavePoint(savePoint); + } + if (shouldParseVariableDeclaration) + result = parseVariableDeclaration(context, DeclarationType::LetDeclaration); + else { + bool allowFunctionDeclarationAsStatement = true; + result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement); + } + shouldSetPauseLocation = !context.shouldSkipPauseLocation(result); + break; + } + case CLASSTOKEN: + result = parseClassDeclaration(context); + break; + case FUNCTION: + result = parseFunctionDeclaration(context); + break; + case ASYNC: { + // Eagerly parse as AsyncFunctionDeclaration. This is the uncommon case, + // but could be mistakenly parsed as an AsyncFunctionExpression. + SavePoint savePoint = createSavePoint(); + next(); + if (UNLIKELY(match(FUNCTION) && !m_lexer->prevTerminator())) { + result = parseAsyncFunctionDeclaration(context); + break; + } + restoreSavePoint(savePoint); + FALLTHROUGH; + } + case IDENT: + case AWAIT: + case YIELD: { + // This is a convenient place to notice labeled statements + // (even though we also parse them as normal statements) + // because we allow the following type of code in sloppy mode: + // ``` function foo() { label: function bar() { } } ``` + bool allowFunctionDeclarationAsStatement = true; + result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement); + shouldSetPauseLocation = !context.shouldSkipPauseLocation(result); + break; + } + default: + m_statementDepth--; // parseStatement() increments the depth. + result = parseStatement(context, directive, directiveLiteralLength); + shouldSetEndOffset = false; + break; + } + + if (result) { + if (shouldSetEndOffset) + context.setEndOffset(result, m_lastTokenEndPosition.offset); + if (shouldSetPauseLocation) + recordPauseLocation(context.breakpointLocation(result)); + } + + 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); + 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 const declaration"); + failIfFalse(autoSemiColon(), "Expected ';' after variable declaration"); - return context.createConstStatement(location, constDecls, start, end); + return context.createDeclarationStatement(location, variableDecls, start, end); } template <typename LexerType> @@ -382,6 +651,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseDoWhileStatem semanticFailIfTrue(match(CLOSEPAREN), "Must provide an expression as a do-while loop condition"); TreeExpression expr = parseExpression(context); failIfFalse(expr, "Unable to parse do-while loop condition"); + recordPauseLocation(context.breakpointLocation(expr)); handleProductionOrFail(CLOSEPAREN, ")", "end", "do-while loop condition"); if (match(SEMICOLON)) next(); // Always performs automatic semicolon insertion. @@ -400,9 +670,10 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseWhileStatemen semanticFailIfTrue(match(CLOSEPAREN), "Must provide an expression as a while loop condition"); TreeExpression expr = parseExpression(context); failIfFalse(expr, "Unable to parse while loop condition"); + recordPauseLocation(context.breakpointLocation(expr)); int endLine = tokenLine(); handleProductionOrFail(CLOSEPAREN, ")", "end", "while loop condition"); - + const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); @@ -412,261 +683,435 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseWhileStatemen } template <typename LexerType> -template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVarDeclarationList(TreeBuilder& context, int& declarations, TreeDeconstructionPattern& lastPattern, 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 { lastIdent = 0; - lastPattern = 0; + lastPattern = TreeDestructuringPattern(0); JSTokenLocation location(tokenLocation()); next(); TreeExpression node = 0; declarations++; bool hasInitializer = false; - if (match(IDENT)) { + if (matchSpecIdentifier()) { + failIfTrue(match(LET) && (declarationType == DeclarationType::LetDeclaration || declarationType == DeclarationType::ConstDeclaration), + "Can't use 'let' as an identifier name for a LexicalDeclaration"); + semanticFailIfTrue(isDisallowedIdentifierAwait(m_token), "Can't use 'await' as a ", declarationTypeToVariableKind(declarationType), " ", disallowedIdentifierAwaitReason()); 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); - failIfFalseIfStrict(declareVariable(name), "Cannot declare a variable named ", name->impl(), " in strict mode"); - context.addVar(name, (hasInitializer || (!m_allowsIn && (match(INTOKEN) || isofToken()))) ? DeclarationStacks::HasInitializer : 0); + 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(), "'"); + m_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()); + 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 = parseDeconstructionPattern(context, DeconstructToVariables); - failIfFalse(pattern, "Cannot parse this deconstruction pattern"); + 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 = parseExpression(context); - node = context.createDeconstructingAssignment(location, pattern, rhs); + TreeExpression rhs = parseAssignmentExpression(context); + propagateError(); + ASSERT(rhs); + node = context.createDestructuringAssignment(location, pattern, rhs); + lastInitializer = rhs; } } - - if (hasInitializer) { - if (!varDecls) - varDecls = node; - else - varDecls = context.combineCommaNodes(location, varDecls, node); + + 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)); if (lastIdent) - lastPattern = createBindingPattern(context, DeconstructToVariables, *lastIdent, 0); - return varDecls; + lastPattern = context.createBindingLocation(lastIdentToken.m_location, *lastIdent, lastIdentToken.m_startPosition, lastIdentToken.m_endPosition, assignmentContext); + + return head; +} + +template <typename LexerType> +bool Parser<LexerType>::declareRestOrNormalParameter(const Identifier& name, const Identifier** duplicateIdentifier) +{ + 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 (!m_lexer->isReparsingFunction() && 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> TreeDeconstructionPattern Parser<LexerType>::createBindingPattern(TreeBuilder& context, DeconstructionKind kind, const Identifier& name, int depth) +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.isEmpty()); ASSERT(!name.isNull()); - ASSERT(name.impl()->isIdentifier()); - if (depth) { - if (kind == DeconstructToVariables) - failIfFalseIfStrict(declareVariable(&name), "Cannot deconstruct to a variable named '", name.impl(), "' in strict mode"); - if (kind == DeconstructToParameters) { - auto bindingResult = declareBoundParameter(&name); - if (bindingResult == Scope::StrictBindingFailed && strictMode()) { - semanticFailIfTrue(m_vm->propertyNames->arguments == name || m_vm->propertyNames->eval == name, "Cannot deconstruct to a parameter name '", name.impl(), "' in strict mode"); - if (m_lastFunctionName && name == *m_lastFunctionName) - semanticFail("Cannot deconstruct to '", name.impl(), "' as it shadows the name of a strict mode function"); - semanticFailureDueToKeyword("bound parameter name"); - if (hasDeclaredParameter(name)) - semanticFail("Cannot deconstruct to '", name.impl(), "' as it has already been declared"); - semanticFail("Cannot bind to a parameter named '", name.impl(), "' in strict mode"); - } - if (bindingResult == Scope::BindingFailed) { - semanticFailureDueToKeyword("bound parameter name"); - if (hasDeclaredParameter(name)) - semanticFail("Cannot deconstruct to '", name.impl(), "' as it has already been declared"); - semanticFail("Cannot deconstruct to a parameter named '", name.impl(), "'"); - } - } - if (kind != DeconstructToExpressions) - context.addVar(&name, kind == DeconstructToParameters ? 0 : DeclarationStacks::HasInitializer); - } else { - if (kind == DeconstructToVariables) { - failIfFalseIfStrict(declareVariable(&name), "Cannot declare a variable named '", name.impl(), "' in strict mode"); - context.addVar(&name, DeclarationStacks::HasInitializer); - } - - if (kind == DeconstructToParameters) { - bool declarationResult = declareParameter(&name); - if (!declarationResult && strictMode()) { - semanticFailIfTrue(m_vm->propertyNames->arguments == name || m_vm->propertyNames->eval == name, "Cannot deconstruct to a parameter name '", name.impl(), "' in strict mode"); - if (m_lastFunctionName && name == *m_lastFunctionName) - semanticFail("Cannot declare a parameter named '", name.impl(), "' as it shadows the name of a strict mode function"); - semanticFailureDueToKeyword("parameter name"); - if (hasDeclaredParameter(name)) - semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode as it has already been declared"); - semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode"); - } + 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; } - return context.createBindingLocation(m_token.m_location, name, m_token.m_startPosition, m_token.m_endPosition); + + case DestructuringKind::DestructureToExpressions: { + break; + } + } + + if (exportType == ExportType::Exported) { + semanticFailIfFalse(exportName(name), "Cannot export a duplicate name '", name.impl(), "'"); + m_moduleScopeData->exportBinding(name); + } + return context.createBindingLocation(token.m_location, name, token.m_startPosition, token.m_endPosition, bindingContext); } template <typename LexerType> -template <class TreeBuilder> TreeDeconstructionPattern Parser<LexerType>::tryParseDeconstructionPatternExpression(TreeBuilder& context) +template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern Parser<LexerType>::createAssignmentElement(TreeBuilder& context, TreeExpression& assignmentTarget, const JSTextPosition& startPosition, const JSTextPosition& endPosition) { - return parseDeconstructionPattern(context, DeconstructToExpressions); + return context.createAssignmentElement(assignmentTarget, startPosition, endPosition); } template <typename LexerType> -template <class TreeBuilder> TreeDeconstructionPattern Parser<LexerType>::parseDeconstructionPattern(TreeBuilder& context, DeconstructionKind kind, int depth) +template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseArrowFunctionSingleExpressionBodySourceElements(TreeBuilder& context) { + ASSERT(!match(OPENBRACE)); + + JSTokenLocation location(tokenLocation()); + JSTextPosition start = tokenStartPosition(); + failIfStackOverflow(); - int nonLHSCount = m_nonLHSCount; - TreeDeconstructionPattern pattern; + TreeExpression expr = parseAssignmentExpression(context); + failIfFalse(expr, "Cannot parse the arrow function expression"); + + context.setEndOffset(expr, m_lastTokenEndPosition.offset); + + JSTextPosition end = tokenEndPosition(); + + TreeSourceElements sourceElements = context.createSourceElements(); + TreeStatement body = context.createReturnStatement(location, expr, start, end); + context.setEndOffset(body, m_lastTokenEndPosition.offset); + recordPauseLocation(context.breakpointLocation(body)); + 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(BACKQUOTE)) + 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(); - if (kind == DeconstructToExpressions && match(CLOSEBRACKET)) - return 0; - failIfTrue(match(CLOSEBRACKET), "There must be at least one bound property in an array deconstruction pattern"); + + 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"); + context.appendArrayPatternRestEntry(arrayPattern, location, innerPattern); + restElementWasFound = true; + break; + } + JSTokenLocation location = m_token.m_location; - auto innerPattern = parseDeconstructionPattern(context, kind, depth + 1); - if (kind == DeconstructToExpressions && !innerPattern) + auto innerPattern = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); + if (kind == DestructuringKind::DestructureToExpressions && !innerPattern) return 0; - failIfFalse(innerPattern, "Cannot parse this deconstruction pattern"); - context.appendArrayPatternEntry(arrayPattern, location, innerPattern); + failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); + TreeExpression defaultValue = parseDefaultValueForDestructuringPattern(context); + propagateError(); + context.appendArrayPatternEntry(arrayPattern, location, innerPattern, defaultValue); } while (consume(COMMA)); - - if (kind == DeconstructToExpressions && !match(CLOSEBRACKET)) - return 0; - consumeOrFail(CLOSEBRACKET, "Expected either a closing ']' or a ',' following an element deconstruction pattern"); + consumeOrFail(CLOSEBRACKET, restElementWasFound ? "Expected a closing ']' following a rest element destructuring pattern" : "Expected either a closing ']' or a ',' following an element destructuring pattern"); + context.finishArrayPattern(arrayPattern, divotStart, divotStart, lastTokenEndPosition()); pattern = arrayPattern; break; } case OPENBRACE: { + auto objectPattern = context.createObjectPattern(m_token.m_location); next(); - - if (kind == DeconstructToExpressions && match(CLOSEBRACE)) - return 0; - failIfTrue(match(CLOSEBRACE), "There must be at least one bound property in an object deconstruction pattern"); - auto objectPattern = context.createObjectPattern(m_token.m_location); - bool wasString = false; + if (hasDestructuringPattern) + *hasDestructuringPattern = true; + do { - Identifier propertyName; - TreeDeconstructionPattern innerPattern = 0; + bool wasString = false; + + if (match(CLOSEBRACE)) + break; + + const Identifier* propertyName = nullptr; + TreeExpression propertyExpression = 0; + TreeDestructuringPattern innerPattern = 0; JSTokenLocation location = m_token.m_location; - if (match(IDENT)) { - propertyName = *m_token.m_data.ident; + 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 = parseDeconstructionPattern(context, kind, depth + 1); - else - innerPattern = createBindingPattern(context, kind, propertyName, depth); + 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"); + } + semanticFailIfTrue(isDisallowedIdentifierAwait(identifierToken), "Can't use 'await' as a ", destructuringKindToVariableKindName(kind), " ", disallowedIdentifierAwaitReason()); + innerPattern = createBindingPattern(context, kind, exportType, *propertyName, identifierToken, bindingContext, duplicateIdentifier); + } } else { JSTokenType tokenType = m_token.m_type; switch (m_token.m_type) { - case NUMBER: - propertyName = Identifier::from(m_vm, m_token.m_data.doubleValue); + 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; + 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 == DeconstructToExpressions) + if (kind == DestructuringKind::DestructureToExpressions) return 0; failWithMessage("Expected a property name"); } - propertyName = *m_token.m_data.ident; + propertyName = m_token.m_data.ident; break; } next(); if (!consume(COLON)) { - if (kind == DeconstructToExpressions) + if (kind == DestructuringKind::DestructureToExpressions) return 0; - semanticFailIfTrue(tokenType == RESERVED, "Cannot use abbreviated deconstruction syntax for reserved name '", propertyName.impl(), "'"); - semanticFailIfTrue(tokenType == RESERVED_IF_STRICT, "Cannot use abbreviated deconstruction syntax for reserved name '", propertyName.impl(), "' in strict mode"); - semanticFailIfTrue(tokenType & KeywordTokenFlag, "Cannot use abbreviated deconstruction syntax for keyword '", propertyName.impl(), "'"); + 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 named property deconstruction"); + failWithMessage("Expected a ':' prior to a named destructuring property"); } - innerPattern = parseDeconstructionPattern(context, kind, depth + 1); + innerPattern = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); } - if (kind == DeconstructToExpressions && !innerPattern) + if (kind == DestructuringKind::DestructureToExpressions && !innerPattern) return 0; - failIfFalse(innerPattern, "Cannot parse this deconstruction pattern"); - context.appendObjectPatternEntry(objectPattern, location, wasString, propertyName, innerPattern); + failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); + TreeExpression defaultValue = parseDefaultValueForDestructuringPattern(context); + propagateError(); + 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 == DeconstructToExpressions && !match(CLOSEBRACE)) + + if (kind == DestructuringKind::DestructureToExpressions && !match(CLOSEBRACE)) return 0; - consumeOrFail(CLOSEBRACE, "Expected either a closing '}' or an ',' after a property deconstruction pattern"); + consumeOrFail(CLOSEBRACE, "Expected either a closing '}' or an ',' after a property destructuring pattern"); pattern = objectPattern; break; } default: { - if (!match(IDENT)) { - if (kind == DeconstructToExpressions) + if (!matchSpecIdentifier()) { + if (kind == DestructuringKind::DestructureToExpressions) return 0; - semanticFailureDueToKeyword("variable name"); + semanticFailureDueToKeyword(destructuringKindToVariableKindName(kind)); failWithMessage("Expected a parameter pattern or a ')' in parameter list"); } - pattern = createBindingPattern(context, kind, *m_token.m_data.ident, depth); + failIfTrue(match(LET) && (kind == DestructuringKind::DestructureToLet || kind == DestructuringKind::DestructureToConst), "Can't use 'let' as an identifier name for a LexicalDeclaration"); + semanticFailIfTrue(isDisallowedIdentifierAwait(m_token), "Can't use 'await' as a ", destructuringKindToVariableKindName(kind), " ", disallowedIdentifierAwaitReason()); + pattern = createBindingPattern(context, kind, exportType, *m_token.m_data.ident, m_token, bindingContext, duplicateIdentifier); next(); break; } } - m_nonLHSCount = nonLHSCount; + m_parserState.nonLHSCount = nonLHSCount; return pattern; } template <typename LexerType> -template <class TreeBuilder> TreeConstDeclList Parser<LexerType>::parseConstDeclarationList(TreeBuilder& context) +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseDefaultValueForDestructuringPattern(TreeBuilder& context) { - failIfTrue(strictMode(), "Const declarations are not supported in strict mode"); - TreeConstDeclList constDecls = 0; - TreeConstDeclList tail = 0; - do { - JSTokenLocation location(tokenLocation()); - next(); - matchOrFail(IDENT, "Expected an identifier name in const declaration"); - const Identifier* name = m_token.m_data.ident; - 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); - failIfFalse(!!initializer, "Unable to parse initializer"); - } - tail = context.appendConstDecl(location, tail, name, initializer); - if (!constDecls) - constDecls = tail; - } while (match(COMMA)); - return constDecls; + if (!match(EQUAL)) + return 0; + + next(TreeBuilder::DontBuildStrings); // consume '=' + return parseAssignmentExpression(context); } template <typename LexerType> @@ -677,33 +1122,71 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement( int startLine = tokenLine(); next(); handleProductionOrFail(OPENPAREN, "(", "start", "for-loop header"); - int nonLHSCount = m_nonLHSCount; + int nonLHSCount = m_parserState.nonLHSCount; int declarations = 0; + JSTokenLocation declLocation(tokenLocation()); JSTextPosition declsStart; JSTextPosition declsEnd; TreeExpression decls = 0; - TreeDeconstructionPattern pattern = 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 varDeclarationList; expressionOpt; expressionOpt) + for (var/let/const IDENT in/of expression) statement + for (var/let/const varDeclarationList; expressionOpt; expressionOpt) */ - TreeDeconstructionPattern 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; propagateError(); // Remainder of a standard for loop is handled identically if (match(SEMICOLON)) goto standardForLoop; - - failIfFalse(declarations == 1, "must declare variables after 'var'"); - failIfTrue(forInInitializer, "Cannot use initialiser syntax in a for-in loop"); - + + failIfFalse(declarations == 1, "can only declare a single variable in an enumeration"); + // Handle for-in with var declaration JSTextPosition inLocation = tokenStartPosition(); bool isOfEnumeration = false; @@ -712,9 +1195,16 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement( isOfEnumeration = true; next(); } - + bool hasAnyAssignments = !!forInInitializer; + if (hasAnyAssignments) { + if (isOfEnumeration) + internalFailWithMessage(false, "Cannot assign to the loop variable inside a for-of loop header"); + if (strictMode() || (isLetDeclaration || isConstDeclaration) || !context.isBindingNode(forInTarget)) + internalFailWithMessage(false, "Cannot assign to the loop variable inside a for-in loop header"); + } TreeExpression expr = parseExpression(context); failIfFalse(expr, "Expected expression to enumerate"); + recordPauseLocation(context.breakpointLocation(expr)); JSTextPosition exprEnd = lastTokenEndPosition(); int endLine = tokenLine(); @@ -726,20 +1216,29 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement( TreeStatement statement = parseStatement(context, unused); endLoop(); failIfFalse(statement, "Expected statement as body of for-", isOfEnumeration ? "of" : "in", " statement"); + gatherLexicalVariablesIfNecessary(); + TreeStatement result; if (isOfEnumeration) - return context.createForOfLoop(location, forInTarget, expr, statement, declsStart, inLocation, exprEnd, startLine, endLine); - return context.createForInLoop(location, forInTarget, expr, statement, declsStart, inLocation, exprEnd, startLine, endLine); + result = context.createForOfLoop(location, forInTarget, expr, statement, declLocation, declsStart, inLocation, exprEnd, startLine, endLine, *lexicalVariables); + else { + if (isVarDeclaraton && forInInitializer) + result = context.createForInLoop(location, decls, expr, statement, declLocation, declsStart, inLocation, exprEnd, startLine, endLine, *lexicalVariables); + else + result = context.createForInLoop(location, forInTarget, expr, statement, declLocation, declsStart, inLocation, exprEnd, startLine, endLine, *lexicalVariables); + } + popLexicalScopeIfNecessary(); + return result; } if (!match(SEMICOLON)) { if (match(OPENBRACE) || match(OPENBRACKET)) { SavePoint savePoint = createSavePoint(); declsStart = tokenStartPosition(); - pattern = tryParseDeconstructionPatternExpression(context); + 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 = 0; + pattern = TreeDestructuringPattern(0); restoreSavePoint(savePoint); } m_allowsIn = false; @@ -748,17 +1247,22 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement( declsEnd = lastTokenEndPosition(); m_allowsIn = true; failIfFalse(decls, "Cannot parse for loop declarations"); + recordPauseLocation(context.breakpointLocation(decls)); } if (match(SEMICOLON)) { standardForLoop: // Standard for loop + if (decls) + recordPauseLocation(context.breakpointLocation(decls)); next(); TreeExpression condition = 0; + failIfTrue(forLoopConstDoesNotHaveInitializer && isConstDeclaration, "const variables in for loops must have initializers"); if (!match(SEMICOLON)) { condition = parseExpression(context); failIfFalse(condition, "Cannot parse for loop condition expression"); + recordPauseLocation(context.breakpointLocation(condition)); } consumeOrFail(SEMICOLON, "Expected a ';' after the for loop condition expression"); @@ -766,6 +1270,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement( if (!match(CLOSEPAREN)) { increment = parseExpression(context); failIfFalse(increment, "Cannot parse for loop iteration expression"); + recordPauseLocation(context.breakpointLocation(increment)); } int endLine = tokenLine(); handleProductionOrFail(CLOSEPAREN, ")", "end", "for-loop header"); @@ -774,12 +1279,15 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement( TreeStatement statement = parseStatement(context, unused); endLoop(); failIfFalse(statement, "Expected a statement as the body of a for loop"); - return context.createForLoop(location, decls, condition, increment, statement, startLine, endLine); + gatherLexicalVariablesIfNecessary(); + TreeStatement result = context.createForLoop(location, decls, condition, increment, statement, startLine, endLine, *lexicalVariables); + popLexicalScopeIfNecessary(); + return result; } - // For-in loop + // For-in and For-of loop enumerationLoop: - failIfFalse(nonLHSCount == m_nonLHSCount, "Expected a reference on the left hand side of an enumeration statement"); + 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"); @@ -788,6 +1296,7 @@ enumerationLoop: } TreeExpression expr = parseExpression(context); failIfFalse(expr, "Cannot parse subject for-", isOfEnumeration ? "of" : "in", " statement"); + recordPauseLocation(context.breakpointLocation(expr)); JSTextPosition exprEnd = lastTokenEndPosition(); int endLine = tokenLine(); @@ -797,15 +1306,24 @@ enumerationLoop: TreeStatement statement = parseStatement(context, unused); endLoop(); failIfFalse(statement, "Expected a statement as the body of a for-", isOfEnumeration ? "of" : "in", "loop"); + gatherLexicalVariablesIfNecessary(); + TreeStatement result; if (pattern) { ASSERT(!decls); if (isOfEnumeration) - return context.createForOfLoop(location, pattern, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine); - return context.createForInLoop(location, pattern, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine); + result = context.createForOfLoop(location, pattern, expr, statement, declLocation, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables); + else + result = context.createForInLoop(location, pattern, expr, statement, declLocation, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables); + + popLexicalScopeIfNecessary(); + return result; } if (isOfEnumeration) - return context.createForOfLoop(location, decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine); - return context.createForInLoop(location, decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine); + result = context.createForOfLoop(location, decls, expr, statement, declLocation, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables); + else + result = context.createForInLoop(location, decls, expr, statement, declLocation, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables); + popLexicalScopeIfNecessary(); + return result; } template <typename LexerType> @@ -819,9 +1337,9 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseBreakStatemen if (autoSemiColon()) { semanticFailIfFalse(breakIsValid(), "'break' is only valid inside a switch or loop statement"); - return context.createBreakStatement(location, start, end); + return context.createBreakStatement(location, &m_vm->propertyNames->nullIdentifier, start, end); } - matchOrFail(IDENT, "Expected an identifier as the target for a break statement"); + failIfFalse(matchSpecIdentifier(), "Expected an identifier as the target for a break statement"); const Identifier* ident = m_token.m_data.ident; semanticFailIfFalse(getLabel(ident), "Cannot use the undeclared label '", ident->impl(), "'"); end = tokenEndPosition(); @@ -841,13 +1359,13 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseContinueState if (autoSemiColon()) { semanticFailIfFalse(continueIsValid(), "'continue' is only valid inside a loop statement"); - return context.createContinueStatement(location, start, end); + return context.createContinueStatement(location, &m_vm->propertyNames->nullIdentifier, start, end); } - matchOrFail(IDENT, "Expected an identifier as the target for a continue statement"); + failIfFalse(matchSpecIdentifier(), "Expected an identifier as the target for a continue statement"); const Identifier* ident = m_token.m_data.ident; ScopeLabelInfo* label = getLabel(ident); semanticFailIfFalse(label, "Cannot use the undeclared label '", ident->impl(), "'"); - semanticFailIfFalse(label->m_isLoop, "Cannot continue to the label '", ident->impl(), "' as it is not targeting a loop"); + semanticFailIfFalse(label->isLoop, "Cannot continue to the label '", ident->impl(), "' as it is not targeting a loop"); end = tokenEndPosition(); next(); failIfFalse(autoSemiColon(), "Expected a ';' following a targeted continue statement"); @@ -913,6 +1431,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseWithStatement int start = tokenStart(); TreeExpression expr = parseExpression(context); failIfFalse(expr, "Cannot parse 'with' subject expression"); + recordPauseLocation(context.breakpointLocation(expr)); JSTextPosition end = lastTokenEndPosition(); int endLine = tokenLine(); handleProductionOrFail(CLOSEPAREN, ")", "start", "subject of a 'with' statement"); @@ -933,10 +1452,14 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseSwitchStateme handleProductionOrFail(OPENPAREN, "(", "start", "subject of a 'switch'"); TreeExpression expr = parseExpression(context); failIfFalse(expr, "Cannot parse switch subject expression"); + recordPauseLocation(context.breakpointLocation(expr)); int endLine = tokenLine(); handleProductionOrFail(CLOSEPAREN, ")", "end", "subject of a 'switch'"); handleProductionOrFail(OPENBRACE, "{", "start", "body of a 'switch'"); + AutoPopScopeRef lexicalScope(this, pushScope()); + lexicalScope->setIsLexicalScope(); + lexicalScope->preventVarDeclarations(); startSwitch(); TreeClauseList firstClauses = parseSwitchClauses(context); propagateError(); @@ -949,8 +1472,9 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseSwitchStateme endSwitch(); handleProductionOrFail(CLOSEBRACE, "}", "end", "body of a 'switch'"); - return context.createSwitchStatement(location, expr, firstClauses, defaultClause, secondClauses, startLine, endLine); - + TreeStatement result = context.createSwitchStatement(location, expr, firstClauses, defaultClause, secondClauses, startLine, endLine, lexicalScope->finalizeLexicalEnvironment(), lexicalScope->takeFunctionDeclarations()); + popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); + return result; } template <typename LexerType> @@ -958,6 +1482,7 @@ template <class TreeBuilder> TreeClauseList Parser<LexerType>::parseSwitchClause { if (!match(CASE)) return 0; + unsigned startOffset = tokenStart(); next(); TreeExpression condition = parseExpression(context); failIfFalse(condition, "Cannot parse switch clause"); @@ -965,10 +1490,12 @@ template <class TreeBuilder> TreeClauseList Parser<LexerType>::parseSwitchClause TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode); failIfFalse(statements, "Cannot parse the body of a switch clause"); TreeClause clause = context.createClause(condition, statements); + context.setStartOffset(clause, startOffset); TreeClauseList clauseList = context.createClauseList(clause); TreeClauseList tail = clauseList; while (match(CASE)) { + startOffset = tokenStart(); next(); TreeExpression condition = parseExpression(context); failIfFalse(condition, "Cannot parse switch case expression"); @@ -976,6 +1503,7 @@ template <class TreeBuilder> TreeClauseList Parser<LexerType>::parseSwitchClause 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; @@ -986,11 +1514,14 @@ template <class TreeBuilder> TreeClause Parser<LexerType>::parseSwitchDefaultCla { if (!match(DEFAULT)) return 0; + unsigned startOffset = tokenStart(); next(); consumeOrFail(COLON, "Expected a ':' after switch default clause"); TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode); failIfFalse(statements, "Cannot parse the body of a switch default clause"); - return context.createClause(0, statements); + TreeClause result = context.createClause(0, statements); + context.setStartOffset(result, startOffset); + return result; } template <typename LexerType> @@ -999,7 +1530,7 @@ 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(); @@ -1009,26 +1540,31 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseTryStatement( tryBlock = parseBlockStatement(context); failIfFalse(tryBlock, "Cannot parse the body of try block"); int lastLine = m_lastTokenEndPosition.line; - + VariableEnvironment catchEnvironment; if (match(CATCH)) { - currentScope()->setNeedsFullActivation(); next(); handleProductionOrFail(OPENPAREN, "(", "start", "'catch' target"); - if (!match(IDENT)) { - semanticFailureDueToKeyword("catch variable name"); - failWithMessage("Expected identifier name as catch target"); - } - ident = m_token.m_data.ident; - next(); AutoPopScopeRef catchScope(this, pushScope()); - failIfFalseIfStrict(declareVariable(ident), "Cannot declare a catch variable named '", ident->impl(), "' in strict mode"); - catchScope->preventNewDecls(); + 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); failIfFalse(catchBlock, "Unable to parse 'catch' block"); - failIfFalse(popScope(catchScope, TreeBuilder::NeedsFreeVariableInfo), "Parse error"); + catchEnvironment = catchScope->finalizeLexicalEnvironment(); + RELEASE_ASSERT(!ident || (catchEnvironment.size() == 1 && catchEnvironment.contains(ident->impl()))); + popScope(catchScope, TreeBuilder::NeedsFreeVariableInfo); } if (match(FINALLY)) { @@ -1038,7 +1574,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseTryStatement( failIfFalse(finallyBlock, "Cannot parse finally body"); } failIfFalse(catchBlock || finallyBlock, "Try statements must have at least a catch or finally block"); - return context.createTryStatement(location, tryBlock, ident, catchBlock, finallyBlock, firstLine, lastLine); + return context.createTryStatement(location, tryBlock, catchPattern, catchBlock, finallyBlock, firstLine, lastLine, catchEnvironment); } template <typename LexerType> @@ -1059,18 +1595,45 @@ 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; + DeclarationStacks::FunctionStack emptyFunctionStack; 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, shouldPushLexicalScope ? currentScope()->takeFunctionDeclarations() : WTFMove(emptyFunctionStack)); + context.setStartOffset(result, startOffset); + context.setEndOffset(result, endOffset); + if (shouldPushLexicalScope) + popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); + return result; } TreeSourceElements subtree = parseSourceElements(context, DontCheckForStrictMode); failIfFalse(subtree, "Cannot parse the body of the block statement"); matchOrFail(CLOSEBRACE, "Expected a closing '}' at the end of a block statement"); + int endOffset = m_token.m_data.offset; next(); - return context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line); + TreeStatement result = context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line, shouldPushLexicalScope ? currentScope()->finalizeLexicalEnvironment() : emptyEnvironment, shouldPushLexicalScope ? currentScope()->takeFunctionDeclarations() : WTFMove(emptyFunctionStack)); + context.setStartOffset(result, startOffset); + context.setEndOffset(result, endOffset); + if (shouldPushLexicalScope) + popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); + + return result; } template <typename LexerType> @@ -1078,245 +1641,1080 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre { DepthManager statementDepth(&m_statementDepth); m_statementDepth++; - directive = 0; int nonTrivialExpressionCount = 0; failIfStackOverflow(); + TreeStatement result = 0; + bool shouldSetEndOffset = true; + bool shouldSetPauseLocation = false; + bool parentAllowsFunctionDeclarationAsStatement = m_immediateParentAllowsFunctionDeclarationInStatement; + m_immediateParentAllowsFunctionDeclarationInStatement = false; + 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); - case FUNCTION: - failIfFalseIfStrict(m_statementDepth == 1, "Nested functions cannot be declared in strict mode"); - return parseFunctionDeclaration(context); + result = parseVariableDeclaration(context, DeclarationType::VarDeclaration); + shouldSetPauseLocation = true; + break; + case FUNCTION: { + const bool isAsync = false; + result = parseFunctionDeclarationStatement(context, isAsync, parentAllowsFunctionDeclarationAsStatement); + break; + } case SEMICOLON: { JSTokenLocation location(tokenLocation()); next(); - return context.createEmptyStatement(location); + result = context.createEmptyStatement(location); + shouldSetPauseLocation = true; + 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); + shouldSetPauseLocation = true; + break; case BREAK: - return parseBreakStatement(context); + result = parseBreakStatement(context); + shouldSetPauseLocation = true; + break; case RETURN: - return parseReturnStatement(context); + result = parseReturnStatement(context); + shouldSetPauseLocation = true; + 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); + shouldSetPauseLocation = true; + break; case TRY: - return parseTryStatement(context); + result = parseTryStatement(context); + break; case DEBUGGER: - return parseDebuggerStatement(context); + result = parseDebuggerStatement(context); + shouldSetPauseLocation = true; + break; case EOFTOK: case CASE: case CLOSEBRACE: case DEFAULT: // These tokens imply the end of a set of source elements return 0; + case LET: { + if (!strictMode()) + goto identcase; + goto defaultCase; + } + case ASYNC: + if (maybeParseAsyncFunctionDeclarationStatement(context, result, parentAllowsFunctionDeclarationAsStatement)) + break; + FALLTHROUGH; case IDENT: - return parseExpressionOrLabelStatement(context); + case AWAIT: + case YIELD: { + identcase: + bool allowFunctionDeclarationAsStatement = false; + result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement); + shouldSetPauseLocation = !context.shouldSkipPauseLocation(result); + 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: + defaultCase: TreeStatement exprStatement = parseExpressionStatement(context); - if (directive && nonTrivialExpressionCount != m_nonTrivialExpressionCount) - directive = 0; - return exprStatement; + if (directive && nonTrivialExpressionCount != m_parserState.nonTrivialExpressionCount) + directive = nullptr; + result = exprStatement; + shouldSetPauseLocation = true; + break; + } + + if (result) { + if (shouldSetEndOffset) + context.setEndOffset(result, m_lastTokenEndPosition.offset); + if (shouldSetPauseLocation) + recordPauseLocation(context.breakpointLocation(result)); } + + return result; } template <typename LexerType> -template <class TreeBuilder> TreeFormalParameterList Parser<LexerType>::parseFormalParameters(TreeBuilder& context) +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclarationStatement(TreeBuilder& context, bool isAsync, bool parentAllowsFunctionDeclarationAsStatement) { - auto parameter = parseDeconstructionPattern(context, DeconstructToParameters); - failIfFalse(parameter, "Cannot parse parameter pattern"); - TreeFormalParameterList list = context.createFormalParameterList(parameter); - TreeFormalParameterList tail = list; - while (consume(COMMA)) { - parameter = parseDeconstructionPattern(context, DeconstructToParameters); - failIfFalse(parameter, "Cannot parse parameter pattern"); - tail = context.createFormalParameterList(tail, parameter); + semanticFailIfTrue(strictMode(), "Function declarations are only allowed inside blocks or switch statements in strict mode"); + failIfFalse(parentAllowsFunctionDeclarationAsStatement, "Function declarations are only allowed inside block statements or at the top level of a program"); + if (!currentScope()->isFunction()) { + // We only implement annex B.3.3 if we're in function mode. Otherwise, we fall back + // to hoisting behavior. + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=155813 + DepthManager statementDepth(&m_statementDepth); + m_statementDepth = 1; + if (isAsync) + return parseAsyncFunctionDeclaration(context); + return parseFunctionDeclaration(context); } - return list; + + // Any function declaration that isn't in a block is a syntax error unless it's + // in an if/else statement. If it's in an if/else statement, we will magically + // treat it as if the if/else statement is inside a block statement. + // to the very top like a "var". For example: + // function a() { + // if (cond) function foo() { } + // } + // will be rewritten as: + // function a() { + // if (cond) { function foo() { } } + // } + AutoPopScopeRef blockScope(this, pushScope()); + blockScope->setIsLexicalScope(); + blockScope->preventVarDeclarations(); + JSTokenLocation location(tokenLocation()); + int start = tokenLine(); + + TreeStatement function = 0; + if (!isAsync) + function = parseFunctionDeclaration(context); + else + function = parseAsyncFunctionDeclaration(context); + propagateError(); + failIfFalse(function, "Expected valid function statement after 'function' keyword"); + TreeSourceElements sourceElements = context.createSourceElements(); + context.appendStatement(sourceElements, function); + TreeStatement result = context.createBlockStatement(location, sourceElements, start, m_lastTokenEndPosition.line, currentScope()->finalizeLexicalEnvironment(), currentScope()->takeFunctionDeclarations()); + popScope(blockScope, TreeBuilder::NeedsFreeVariableInfo); + return result; } template <typename LexerType> -template <class TreeBuilder> TreeFunctionBody Parser<LexerType>::parseFunctionBody(TreeBuilder& context) +template <class TreeBuilder> bool Parser<LexerType>::maybeParseAsyncFunctionDeclarationStatement(TreeBuilder& context, TreeStatement& result, bool parentAllowsFunctionDeclarationAsStatement) { - JSTokenLocation startLocation(tokenLocation()); - unsigned startColumn = tokenColumn(); + ASSERT(match(ASYNC)); + SavePoint savePoint = createSavePoint(); next(); + if (match(FUNCTION) && !m_lexer->prevTerminator()) { + const bool isAsync = true; + result = parseFunctionDeclarationStatement(context, isAsync, parentAllowsFunctionDeclarationAsStatement); + return true; + } + restoreSavePoint(savePoint); + return false; +} - if (match(CLOSEBRACE)) { - unsigned endColumn = tokenColumn(); - return context.createFunctionBody(startLocation, tokenLocation(), startColumn, endColumn, strictMode()); +template <typename LexerType> +template <class TreeBuilder> bool Parser<LexerType>::parseFormalParameters(TreeBuilder& context, TreeFormalParameterList list, bool isArrowFunction, unsigned& parameterCount) +{ +#define failIfDuplicateIfViolation() \ + if (duplicateParameter) {\ + semanticFailIfTrue(hasDefaultParameterValues, "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");\ + semanticFailIfTrue(isArrowFunction, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in an arrow function");\ + } + + bool hasDefaultParameterValues = false; + bool hasDestructuringPattern = false; + bool isRestParameter = false; + const Identifier* duplicateParameter = nullptr; + unsigned restParameterStart = 0; + do { + TreeDestructuringPattern parameter = 0; + TreeExpression defaultValue = 0; + + if (UNLIKELY(match(CLOSEPAREN))) + break; + + if (match(DOTDOTDOT)) { + next(); + semanticFailIfTrue(!m_parserState.allowAwait && match(AWAIT), "Can't use 'await' as a parameter name in an async function"); + TreeDestructuringPattern destructuringPattern = parseDestructuringPattern(context, DestructuringKind::DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern); + propagateError(); + parameter = context.createRestParameter(destructuringPattern, restParameterStart); + 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); + if (defaultValue) + hasDefaultParameterValues = true; + } + propagateError(); + failIfDuplicateIfViolation(); + if (isRestParameter || defaultValue || hasDestructuringPattern) + currentScope()->setHasNonSimpleParameterList(); + context.appendParameter(list, parameter, defaultValue); + if (!isRestParameter) { + restParameterStart++; + if (!hasDefaultParameterValues) + parameterCount++; + } + } while (!isRestParameter && consume(COMMA)); + + return true; +#undef failIfDuplicateIfViolation +} + +template <typename LexerType> +template <class TreeBuilder> TreeFunctionBody Parser<LexerType>::parseFunctionBody( + TreeBuilder& context, SyntaxChecker& syntaxChecker, const JSTokenLocation& startLocation, int startColumn, int functionKeywordStart, int functionNameStart, int parametersStart, + ConstructorKind constructorKind, SuperBinding superBinding, FunctionBodyType bodyType, unsigned parameterCount, SourceParseMode parseMode) +{ + bool isArrowFunctionBodyExpression = bodyType == ArrowFunctionBodyExpression; + if (!isArrowFunctionBodyExpression) { + next(); + if (match(CLOSEBRACE)) { + unsigned endColumn = tokenColumn(); + SuperBinding functionSuperBinding = adjustSuperBindingForBaseConstructor(constructorKind, superBinding, currentScope()); + return context.createFunctionMetadata(startLocation, tokenLocation(), startColumn, endColumn, functionKeywordStart, functionNameStart, parametersStart, strictMode(), constructorKind, functionSuperBinding, parameterCount, parseMode, isArrowFunctionBodyExpression); + } } + DepthManager statementDepth(&m_statementDepth); m_statementDepth = 0; - typename TreeBuilder::FunctionBodyBuilder bodyBuilder(const_cast<VM*>(m_vm), m_lexer.get()); - failIfFalse(parseSourceElements(bodyBuilder, CheckForStrictMode), "Cannot parse body of this function"); + if (bodyType == ArrowFunctionBodyExpression) { + if (m_debuggerParseData) + failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(context), "Cannot parse body of this arrow function"); + else + failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(syntaxChecker), "Cannot parse body of this arrow function"); + } else { + if (m_debuggerParseData) + failIfFalse(parseSourceElements(context, CheckForStrictMode), bodyType == StandardFunctionBodyBlock ? "Cannot parse body of this function" : "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.createFunctionBody(startLocation, tokenLocation(), startColumn, endColumn, strictMode()); + SuperBinding functionSuperBinding = adjustSuperBindingForBaseConstructor(constructorKind, superBinding, currentScope()); + return context.createFunctionMetadata(startLocation, tokenLocation(), startColumn, endColumn, functionKeywordStart, functionNameStart, parametersStart, strictMode(), constructorKind, functionSuperBinding, parameterCount, parseMode, isArrowFunctionBodyExpression); } -static const char* stringForFunctionMode(FunctionParseMode mode) +static const char* stringForFunctionMode(SourceParseMode mode) { switch (mode) { - case GetterMode: + case SourceParseMode::GetterMode: return "getter"; - case SetterMode: + case SourceParseMode::SetterMode: return "setter"; - case FunctionMode: + 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::AsyncFunctionMode: + case SourceParseMode::AsyncFunctionBodyMode: + return "async function"; + case SourceParseMode::AsyncMethodMode: + return "async method"; + case SourceParseMode::AsyncArrowFunctionBodyMode: + case SourceParseMode::AsyncArrowFunctionMode: + return "async 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> bool Parser<LexerType>::parseFunctionInfo(TreeBuilder& context, FunctionRequirements requirements, FunctionParseMode mode, bool nameIsInContainingScope, const Identifier*& name, TreeFormalParameterList& parameters, TreeFunctionBody& body, unsigned& openBraceOffset, unsigned& closeBraceOffset, int& bodyStartLine, unsigned& bodyStartColumn) +template <typename LexerType> template <class TreeBuilder, class FunctionInfoType> typename TreeBuilder::FormalParameterList Parser<LexerType>::parseFunctionParameters(TreeBuilder& context, SourceParseMode mode, FunctionInfoType& functionInfo) { - AutoPopScopeRef functionScope(this, pushScope()); - functionScope->setIsFunction(); - int functionNameStart = m_token.m_location.startOffset; - const Identifier* lastFunctionName = m_lastFunctionName; - m_lastFunctionName = nullptr; - if (match(IDENT)) { - name = m_token.m_data.ident; - m_lastFunctionName = name; - next(); - if (!nameIsInContainingScope) - failIfFalseIfStrict(functionScope->declareVariable(name), "'", name->impl(), "' is not a valid ", stringForFunctionMode(mode), " name in strict mode"); - } else if (requirements == FunctionNeedsName) { - if (match(OPENPAREN) && mode == FunctionMode) - semanticFail("Function statements must have a name"); - semanticFailureDueToKeyword(stringForFunctionMode(mode), " name"); - failDueToUnexpectedToken(); - return false; + RELEASE_ASSERT(!(SourceParseModeSet(SourceParseMode::ProgramMode, SourceParseMode::ModuleAnalyzeMode, SourceParseMode::ModuleEvaluateMode).contains(mode))); + TreeFormalParameterList parameterList = context.createFormalParameterList(); + SetForScope<FunctionParsePhase> functionParsePhasePoisoner(m_parserState.functionParsePhase, FunctionParsePhase::Parameters); + + if (UNLIKELY((SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode).contains(mode)))) { + if (!matchSpecIdentifier() && !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 { + bool isArrowFunction = true; + failIfFalse(parseFormalParameters(context, parameterList, isArrowFunction, 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 parameterList; } + if (!consume(OPENPAREN)) { semanticFailureDueToKeyword(stringForFunctionMode(mode), " name"); failWithMessage("Expected an opening '(' before a ", stringForFunctionMode(mode), "'s parameter list"); } - if (!match(CLOSEPAREN)) { - parameters = parseFormalParameters(context); - failIfFalse(parameters, "Cannot parse parameters for this ", stringForFunctionMode(mode)); + + 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; + bool hasDestructuringPattern = false; + auto parameter = parseDestructuringPattern(context, DestructuringKind::DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern); + failIfFalse(parameter, "setter functions must have one parameter"); + auto defaultValue = parseDefaultValueForDestructuringPattern(context); + propagateError(); + if (defaultValue || hasDestructuringPattern) { + semanticFailIfTrue(duplicateParameter, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in function with non-simple parameter list"); + currentScope()->setHasNonSimpleParameterList(); + } + context.appendParameter(parameterList, parameter, defaultValue); + functionInfo.parameterCount = defaultValue ? 0 : 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 { + bool isArrowFunction = false; + failIfFalse(parseFormalParameters(context, parameterList, isArrowFunction, functionInfo.parameterCount), "Cannot parse parameters for this ", stringForFunctionMode(mode)); + } + consumeOrFail(CLOSEPAREN, "Expected a ')' or a ',' after a parameter declaration"); } - consumeOrFail(CLOSEPAREN, "Expected a ')' or a ',' after a parameter declaration"); - matchOrFail(OPENBRACE, "Expected an opening '{' at the start of a ", stringForFunctionMode(mode), " body"); - - openBraceOffset = m_token.m_data.offset; - bodyStartLine = tokenLine(); - bodyStartColumn = m_token.m_data.offset - m_token.m_data.lineStartOffset; - JSTokenLocation startLocation(tokenLocation()); - - // 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 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; - - bool endColumnIsOnStartLine = (endLocation.line == bodyStartLine); - ASSERT(endLocation.startOffset >= endLocation.lineStartOffset); - unsigned bodyEndColumn = endColumnIsOnStartLine ? - endLocation.startOffset - m_token.m_data.lineStartOffset : - endLocation.startOffset - endLocation.lineStartOffset; - - body = context.createFunctionBody(startLocation, endLocation, bodyStartColumn, bodyEndColumn, cachedInfo->strictMode); + + return parameterList; +} + +template <typename LexerType> +template <class TreeBuilder> typename TreeBuilder::FormalParameterList Parser<LexerType>::createGeneratorParameters(TreeBuilder& context, unsigned& parameterCount) +{ + auto parameters = context.createFormalParameterList(); + + JSTokenLocation location(tokenLocation()); + JSTextPosition position = tokenStartPosition(); + + auto addParameter = [&](const Identifier& name) { + declareParameter(&name); + auto binding = context.createBindingLocation(location, name, position, position, AssignmentContext::DeclarationStatement); + context.appendParameter(parameters, binding, 0); + ++parameterCount; + }; + + // @generator + addParameter(m_vm->propertyNames->builtinNames().generatorPrivateName()); + // @generatorState + addParameter(m_vm->propertyNames->builtinNames().generatorStatePrivateName()); + // @generatorValue + addParameter(m_vm->propertyNames->builtinNames().generatorValuePrivateName()); + // @generatorResumeMode + addParameter(m_vm->propertyNames->builtinNames().generatorResumeModePrivateName()); + // @generatorFrame + addParameter(m_vm->propertyNames->builtinNames().generatorFramePrivateName()); + + return parameters; +} + +template <typename LexerType> +template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuilder& context, FunctionNameRequirements requirements, SourceParseMode mode, bool nameIsInContainingScope, ConstructorKind constructorKind, SuperBinding expectedSuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>& functionInfo, FunctionDefinitionType functionDefinitionType) +{ + RELEASE_ASSERT(isFunctionParseMode(mode)); + + ScopeRef parentScope = currentScope(); + + bool isDisallowedAwaitFunctionName = isDisallowedIdentifierAwait(m_token); + const char* isDisallowedAwaitFunctionNameReason = isDisallowedAwaitFunctionName ? disallowedIdentifierAwaitReason() : nullptr; + + AutoPopScopeRef functionScope(this, pushScope()); + functionScope->setSourceParseMode(mode); + functionScope->setExpectedSuperBinding(expectedSuperBinding); + functionScope->setConstructorKind(constructorKind); + 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 = -1; + JSTokenLocation startLocation; + int startColumn = -1; + FunctionBodyType functionBodyType; + + auto loadCachedFunction = [&] () -> bool { + if (UNLIKELY(!Options::useSourceProviderCache())) + return false; + + if (UNLIKELY(m_debuggerParseData)) + return false; + + ASSERT(parametersStart != -1); + ASSERT(startColumn != -1); + + // 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(parametersStart) : 0) { + // If we're in a strict context, the cached function info must say it was strict too. + ASSERT(!strictMode() || cachedInfo->strictMode); + JSTokenLocation endLocation; + + ConstructorKind constructorKind = static_cast<ConstructorKind>(cachedInfo->constructorKind); + SuperBinding expectedSuperBinding = static_cast<SuperBinding>(cachedInfo->expectedSuperBinding); + + endLocation.line = cachedInfo->lastTokenLine; + endLocation.startOffset = cachedInfo->lastTokenStartOffset; + endLocation.lineStartOffset = cachedInfo->lastTokenLineStartOffset; + ASSERT(endLocation.startOffset >= endLocation.lineStartOffset); + + bool endColumnIsOnStartLine = endLocation.line == functionInfo.startLine; + unsigned currentLineStartOffset = m_lexer->currentLineStartOffset(); + unsigned bodyEndColumn = endColumnIsOnStartLine ? endLocation.startOffset - currentLineStartOffset : endLocation.startOffset - endLocation.lineStartOffset; + + ASSERT(endLocation.startOffset >= endLocation.lineStartOffset); + + FunctionBodyType functionBodyType; + if (UNLIKELY(SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode).contains(mode))) + functionBodyType = cachedInfo->isBodyArrowExpression ? ArrowFunctionBodyExpression : ArrowFunctionBodyBlock; + else + functionBodyType = StandardFunctionBodyBlock; + + SuperBinding functionSuperBinding = adjustSuperBindingForBaseConstructor(constructorKind, expectedSuperBinding, cachedInfo->needsSuperBinding, cachedInfo->usesEval, cachedInfo->innerArrowFunctionFeatures); + + functionInfo.body = context.createFunctionMetadata( + startLocation, endLocation, startColumn, bodyEndColumn, + functionKeywordStart, functionNameStart, parametersStart, + cachedInfo->strictMode, constructorKind, functionSuperBinding, + cachedInfo->parameterCount, + mode, functionBodyType == ArrowFunctionBodyExpression); + functionInfo.endOffset = cachedInfo->endFunctionOffset; + functionInfo.parameterCount = cachedInfo->parameterCount; + + functionScope->restoreFromSourceProviderCache(cachedInfo); + popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo); + + m_token = cachedInfo->endFunctionToken(); + + if (endColumnIsOnStartLine) + m_token.m_location.lineStartOffset = currentLineStartOffset; + + m_lexer->setOffset(m_token.m_location.endOffset, m_token.m_location.lineStartOffset); + m_lexer->setLineNumber(m_token.m_location.line); + + 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; + } + + return false; + }; + + SyntaxChecker syntaxChecker(const_cast<VM*>(m_vm), m_lexer.get()); + + if (UNLIKELY((SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode).contains(mode)))) { + startLocation = tokenLocation(); + functionInfo.startLine = tokenLine(); + startColumn = tokenColumn(); + + parametersStart = m_token.m_location.startOffset; + functionInfo.startOffset = parametersStart; + functionInfo.parametersStartColumn = startColumn; + + if (loadCachedFunction()) + return true; + + { + // Parse formal parameters with [+Yield] parameterization, in order to ban YieldExpressions + // in ArrowFormalParameters, per ES6 #sec-arrow-function-definitions-static-semantics-early-errors. + Scope::MaybeParseAsGeneratorForScope parseAsGenerator(functionScope, parentScope->isGenerator()); + SetForScope<bool> overrideAllowAwait(m_parserState.allowAwait, !isAsyncFunctionParseMode(mode)); + parseFunctionParameters(syntaxChecker, mode, functionInfo); + propagateError(); + } + + matchOrFail(ARROWFUNCTION, "Expected a '=>' after arrow function parameter declaration"); + + if (m_lexer->prevTerminator()) + failDueToUnexpectedToken(); + + ASSERT(constructorKind == ConstructorKind::None); + + // Check if arrow body start with {. If it true it mean that arrow function is Fat arrow function + // and we need use common approach to parse function body + next(); + functionBodyType = match(OPENBRACE) ? ArrowFunctionBodyBlock : ArrowFunctionBodyExpression; + } 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 and AsyncFunctionExpression can accept "yield" even in the context of generator. + bool upperScopeIsGenerator = false; + if (!(functionDefinitionType == FunctionDefinitionType::Expression && SourceParseModeSet(SourceParseMode::NormalFunctionMode, SourceParseMode::AsyncFunctionMode).contains(mode))) + upperScopeIsGenerator = upperScope(1)->isGenerator(); + + if (requirements != FunctionNameRequirements::Unnamed) { + ASSERT_WITH_MESSAGE(!(requirements == FunctionNameRequirements::None && !functionInfo.name), "When specifying FunctionNameRequirements::None, we need to initialize functionInfo.name with the default value in the caller side."); + if (matchSpecIdentifier(upperScopeIsGenerator)) { + functionInfo.name = m_token.m_data.ident; + m_parserState.lastFunctionName = functionInfo.name; + if (UNLIKELY(isDisallowedAwaitFunctionName)) + semanticFailIfTrue(functionDefinitionType == FunctionDefinitionType::Declaration || isAsyncFunctionWrapperParseMode(mode), "Cannot declare function named 'await' ", isDisallowedAwaitFunctionNameReason); + else if (isAsyncFunctionWrapperParseMode(mode) && match(AWAIT) && functionDefinitionType == FunctionDefinitionType::Expression) + semanticFail("Cannot declare async function named 'await'"); + 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 == FunctionNameRequirements::Named) { + if (match(OPENPAREN)) { + semanticFailIfTrue(mode == SourceParseMode::NormalFunctionMode, "Function statements must have a name"); + semanticFailIfTrue(mode == SourceParseMode::AsyncFunctionMode, "Async function statements must have a name"); + } + semanticFailureDueToKeyword(stringForFunctionMode(mode), " name"); + failDueToUnexpectedToken(); + return false; + } + ASSERT(functionInfo.name); + } + + startLocation = tokenLocation(); + functionInfo.startLine = tokenLine(); + startColumn = tokenColumn(); + functionInfo.parametersStartColumn = startColumn; + + parametersStart = m_token.m_location.startOffset; + functionInfo.startOffset = parametersStart; + + if (loadCachedFunction()) + return true; + { + SetForScope<bool> overrideAllowAwait(m_parserState.allowAwait, !isAsyncFunctionParseMode(mode)); + parseFunctionParameters(syntaxChecker, mode, functionInfo); + propagateError(); + } - functionScope->restoreFromSourceProviderCache(cachedInfo); - failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo), "Parser error"); + matchOrFail(OPENBRACE, "Expected an opening '{' at the start of a ", stringForFunctionMode(mode), " body"); - closeBraceOffset = cachedInfo->closeBraceOffset; + // BytecodeGenerator emits code to throw TypeError when a class constructor is "call"ed. + // Set ConstructorKind to None for non-constructor methods of classes. + + if (m_defaultConstructorKind != ConstructorKind::None) { + constructorKind = m_defaultConstructorKind; + expectedSuperBinding = m_defaultConstructorKind == ConstructorKind::Extends ? SuperBinding::Needed : SuperBinding::NotNeeded; + } + + functionBodyType = StandardFunctionBodyBlock; + } - context.setFunctionNameStart(body, functionNameStart); - m_token = cachedInfo->closeBraceToken(); + functionScope->setConstructorKind(constructorKind); + functionScope->setExpectedSuperBinding(expectedSuperBinding); - m_lexer->setOffset(m_token.m_location.endOffset, m_token.m_location.lineStartOffset); - m_lexer->setLineNumber(m_token.m_location.line); - - next(); - return true; + m_parserState.lastFunctionName = lastFunctionName; + ParserState oldState = internalSaveParserState(); + + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=156962 + // This loop collects the set of capture candidates that aren't + // part of the set of this function's declared parameters. We will + // figure out which parameters are captured for this function when + // we actually generate code for it. For now, we just propagate to + // our parent scopes which variables we might have closed over that + // belong to them. This is necessary for correctness when using + // the source provider cache because we can't close over a variable + // that we don't claim to close over. The source provider cache must + // know this information to properly cache this function. + // This might work itself out nicer if we declared a different + // Scope struct for the parameters (because they are indeed implemented + // as their own scope). + UniquedStringImplPtrSet nonLocalCapturesFromParameterExpressions; + functionScope->forEachUsedVariable([&] (UniquedStringImpl* impl) { + if (!functionScope->hasDeclaredParameter(impl)) { + nonLocalCapturesFromParameterExpressions.add(impl); + if (TreeBuilder::NeedsFreeVariableInfo) + parentScope->addClosedVariableCandidateUnconditionally(impl); + } + }); + + auto performParsingFunctionBody = [&] { + return parseFunctionBody(context, syntaxChecker, startLocation, startColumn, functionKeywordStart, functionNameStart, parametersStart, constructorKind, expectedSuperBinding, functionBodyType, functionInfo.parameterCount, mode); + }; + + if (isGeneratorOrAsyncFunctionWrapperParseMode(mode)) { + AutoPopScopeRef generatorBodyScope(this, pushScope()); + SourceParseMode innerParseMode = SourceParseMode::GeneratorBodyMode; + if (isAsyncFunctionWrapperParseMode(mode)) { + innerParseMode = mode == SourceParseMode::AsyncArrowFunctionMode + ? SourceParseMode::AsyncArrowFunctionBodyMode + : SourceParseMode::AsyncFunctionBodyMode; + } + generatorBodyScope->setSourceParseMode(innerParseMode); + generatorBodyScope->setConstructorKind(ConstructorKind::None); + generatorBodyScope->setExpectedSuperBinding(expectedSuperBinding); + + // Disallow 'use strict' directives in the implicit inner function if + // needed. + if (functionScope->hasNonSimpleParameterList()) + generatorBodyScope->setHasNonSimpleParameterList(); + + 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(); + + 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() && requirements != FunctionNameRequirements::Unnamed) { + ASSERT(functionInfo.name); + RELEASE_ASSERT(SourceParseModeSet(SourceParseMode::NormalFunctionMode, SourceParseMode::MethodMode, SourceParseMode::ArrowFunctionMode, SourceParseMode::GeneratorBodyMode, SourceParseMode::GeneratorWrapperFunctionMode).contains(mode) || isAsyncFunctionWrapperParseMode(mode)); + 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"); } - m_lastFunctionName = lastFunctionName; - ParserState oldState = saveState(); - body = parseFunctionBody(context); - restoreState(oldState); - failIfFalse(body, "Cannot parse the body of this ", stringForFunctionMode(mode)); - if (functionScope->strictMode() && name) { - RELEASE_ASSERT(mode == FunctionMode); - semanticFailIfTrue(m_vm->propertyNames->arguments == *name, "'", name->impl(), "' is not a valid function name in strict mode"); - semanticFailIfTrue(m_vm->propertyNames->eval == *name, "'", name->impl(), "' is not a valid function name in strict mode"); - } - closeBraceOffset = m_token.m_data.offset; - unsigned closeBraceLine = m_token.m_data.line; - unsigned closeBraceLineStartOffset = m_token.m_data.lineStartOffset; + + JSTokenLocation location = JSTokenLocation(m_token.m_location); + functionInfo.endOffset = m_token.m_data.offset; + if (functionBodyType == ArrowFunctionBodyExpression) { + location = locationBeforeLastToken(); + functionInfo.endOffset = location.endOffset; + } else { + recordFunctionEntryLocation(JSTextPosition(startLocation.line, startLocation.startOffset, startLocation.lineStartOffset)); + recordFunctionLeaveLocation(JSTextPosition(location.line, location.startOffset, location.lineStartOffset)); + } + // 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; + // For arrow function is 8 = x=>x + 4 symbols; + // For ordinary function is 16 = function(){} + 4 symbols + const int minimumSourceLengthToCache = functionBodyType == StandardFunctionBodyBlock ? 16 : 8; std::unique_ptr<SourceProviderCacheItem> newInfo; - int functionLength = closeBraceOffset - openBraceOffset; - if (TreeBuilder::CanUseFunctionCache && m_functionCache && functionLength > minimumFunctionLengthToCache) { + int sourceLength = functionInfo.endOffset - functionInfo.startOffset; + if (TreeBuilder::CanUseFunctionCache && m_functionCache && sourceLength > minimumSourceLengthToCache) { SourceProviderCacheItemCreationParameters parameters; + parameters.endFunctionOffset = functionInfo.endOffset; parameters.functionNameStart = functionNameStart; - parameters.closeBraceLine = closeBraceLine; - parameters.closeBraceOffset = closeBraceOffset; - parameters.closeBraceLineStartOffset = closeBraceLineStartOffset; - functionScope->fillParametersForSourceProviderCache(parameters); + parameters.lastTokenLine = location.line; + parameters.lastTokenStartOffset = location.startOffset; + parameters.lastTokenEndOffset = location.endOffset; + parameters.lastTokenLineStartOffset = location.lineStartOffset; + parameters.parameterCount = functionInfo.parameterCount; + parameters.constructorKind = constructorKind; + parameters.expectedSuperBinding = expectedSuperBinding; + if (functionBodyType == ArrowFunctionBodyExpression) { + parameters.isBodyArrowExpression = true; + parameters.tokenType = m_token.m_type; + } + functionScope->fillParametersForSourceProviderCache(parameters, nonLocalCapturesFromParameterExpressions); newInfo = SourceProviderCacheItem::create(parameters); - } - context.setFunctionNameStart(body, functionNameStart); - failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo), "Parser error"); - matchOrFail(CLOSEBRACE, "Expected a closing '}' after a ", stringForFunctionMode(mode), " body"); + popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo); + if (functionBodyType != ArrowFunctionBodyExpression) { + matchOrFail(CLOSEBRACE, "Expected a closing '}' after a ", stringForFunctionMode(mode), " body"); + next(); + } + if (newInfo) - m_functionCache->add(openBraceOffset, std::move(newInfo)); + m_functionCache->add(functionInfo.startOffset, WTFMove(newInfo)); - next(); + functionInfo.endLine = m_lastTokenEndPosition.line; return true; } +static NO_RETURN_DUE_TO_CRASH FunctionMetadataNode* getMetadata(ParserFunctionInfo<SyntaxChecker>&) { RELEASE_ASSERT_NOT_REACHED(); } +static FunctionMetadataNode* getMetadata(ParserFunctionInfo<ASTBuilder>& info) { return info.body; } + template <typename LexerType> -template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context) +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext) { 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(context, FunctionNeedsName, FunctionMode, true, name, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn)), "Cannot parse this function"); - failIfFalse(name, "Function statements must have a name"); - failIfFalseIfStrict(declareVariable(name), "Cannot declare a function named '", name->impl(), "' in strict mode"); - return context.createFuncDeclStatement(location, name, body, parameters, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn); + SourceParseMode parseMode = SourceParseMode::NormalFunctionMode; + if (consume(TIMES)) + parseMode = SourceParseMode::GeneratorWrapperFunctionMode; + + ParserFunctionInfo<TreeBuilder> functionInfo; + FunctionNameRequirements requirements = FunctionNameRequirements::Named; + if (declarationDefaultContext == DeclarationDefaultContext::ExportDefault) { + // Under the "export default" context, function declaration does not require the function name. + // + // ExportDeclaration: + // ... + // export default HoistableDeclaration[~Yield, +Default] + // ... + // + // HoistableDeclaration[Yield, Default]: + // FunctionDeclaration[?Yield, ?Default] + // GeneratorDeclaration[?Yield, ?Default] + // + // FunctionDeclaration[Yield, Default]: + // ... + // [+Default] function ( FormalParameters[~Yield] ) { FunctionBody[~Yield] } + // + // GeneratorDeclaration[Yield, Default]: + // ... + // [+Default] function * ( FormalParameters[+Yield] ) { GeneratorBody } + // + // In this case, we use "*default*" as this function declaration's name. + requirements = FunctionNameRequirements::None; + functionInfo.name = &m_vm->propertyNames->builtinNames().starDefaultPrivateName(); + } + + failIfFalse((parseFunctionInfo(context, requirements, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration)), "Cannot parse this function"); + ASSERT(functionInfo.name); + + std::pair<DeclarationResultMask, ScopeRef> functionDeclaration = declareFunction(functionInfo.name); + DeclarationResultMask declarationResult = functionDeclaration.first; + 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/function variable '", functionInfo.name->impl(), "' in strict mode"); + if (exportType == ExportType::Exported) { + ASSERT_WITH_MESSAGE(declarationDefaultContext != DeclarationDefaultContext::ExportDefault, "Export default case will export the name and binding in the caller."); + semanticFailIfFalse(exportName(*functionInfo.name), "Cannot export a duplicate function name: '", functionInfo.name->impl(), "'"); + m_moduleScopeData->exportBinding(*functionInfo.name); + } + + TreeStatement result = context.createFuncDeclStatement(location, functionInfo); + if (TreeBuilder::CreatesAST) + functionDeclaration.second->appendFunction(getMetadata(functionInfo)); + return result; +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseAsyncFunctionDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext) +{ + ASSERT(match(FUNCTION)); + JSTokenLocation location(tokenLocation()); + unsigned functionKeywordStart = tokenStart(); + next(); + ParserFunctionInfo<TreeBuilder> functionInfo; + SourceParseMode parseMode = SourceParseMode::AsyncFunctionMode; + FunctionNameRequirements requirements = FunctionNameRequirements::Named; + if (declarationDefaultContext == DeclarationDefaultContext::ExportDefault) { + // Under the "export default" context, function declaration does not require the function name. + // + // ExportDeclaration: + // ... + // export default HoistableDeclaration[~Yield, +Default] + // ... + // + // HoistableDeclaration[Yield, Default]: + // FunctionDeclaration[?Yield, ?Default] + // GeneratorDeclaration[?Yield, ?Default] + // + // FunctionDeclaration[Yield, Default]: + // ... + // [+Default] function ( FormalParameters[~Yield] ) { FunctionBody[~Yield] } + // + // GeneratorDeclaration[Yield, Default]: + // ... + // [+Default] function * ( FormalParameters[+Yield] ) { GeneratorBody } + // + // In this case, we use "*default*" as this function declaration's name. + requirements = FunctionNameRequirements::None; + functionInfo.name = &m_vm->propertyNames->builtinNames().starDefaultPrivateName(); + } + + failIfFalse((parseFunctionInfo(context, requirements, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration)), "Cannot parse this async function"); + failIfFalse(functionInfo.name, "Async function statements must have a name"); + + std::pair<DeclarationResultMask, ScopeRef> functionDeclaration = declareFunction(functionInfo.name); + DeclarationResultMask declarationResult = functionDeclaration.first; + failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare an async function named '", functionInfo.name->impl(), "' in strict mode"); + if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) + internalFailWithMessage(false, "Cannot declare an async function that shadows a let/const/class/function variable '", functionInfo.name->impl(), "' in strict mode"); + if (exportType == ExportType::Exported) { + semanticFailIfFalse(exportName(*functionInfo.name), "Cannot export a duplicate function name: '", functionInfo.name->impl(), "'"); + m_moduleScopeData->exportBinding(*functionInfo.name); + } + + TreeStatement result = context.createFuncDeclStatement(location, functionInfo); + if (TreeBuilder::CreatesAST) + functionDeclaration.second->appendFunction(getMetadata(functionInfo)); + return result; +} + +template <typename LexerType> +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseClassDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext) +{ + ASSERT(match(CLASSTOKEN)); + JSTokenLocation location(tokenLocation()); + JSTextPosition classStart = tokenStartPosition(); + unsigned classStartLine = tokenLine(); + + ParserClassInfo<TreeBuilder> info; + FunctionNameRequirements requirements = FunctionNameRequirements::Named; + if (declarationDefaultContext == DeclarationDefaultContext::ExportDefault) { + // Under the "export default" context, class declaration does not require the class name. + // + // ExportDeclaration: + // ... + // export default ClassDeclaration[~Yield, +Default] + // ... + // + // ClassDeclaration[Yield, Default]: + // ... + // [+Default] class ClassTail[?Yield] + // + // In this case, we use "*default*" as this class declaration's name. + requirements = FunctionNameRequirements::None; + info.className = &m_vm->propertyNames->builtinNames().starDefaultPrivateName(); + } + + TreeClassExpression classExpr = parseClass(context, requirements, info); + failIfFalse(classExpr, "Failed to parse class"); + ASSERT(info.className); + + 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) { + ASSERT_WITH_MESSAGE(declarationDefaultContext != DeclarationDefaultContext::ExportDefault, "Export default case will export the name and binding in the caller."); + semanticFailIfFalse(exportName(*info.className), "Cannot export a duplicate class name: '", info.className->impl(), "'"); + m_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, FunctionNameRequirements requirements, ParserClassInfo<TreeBuilder>& info) +{ + ASSERT(match(CLASSTOKEN)); + JSTokenLocation location(tokenLocation()); + info.startLine = location.line; + info.startColumn = tokenColumn(); + info.startOffset = location.startOffset; + next(); + + AutoPopScopeRef classScope(this, pushScope()); + classScope->setIsLexicalScope(); + classScope->preventVarDeclarations(); + classScope->setStrictMode(); + + ASSERT_WITH_MESSAGE(requirements != FunctionNameRequirements::Unnamed, "Currently, there is no caller that uses FunctionNameRequirements::Unnamed for class syntax."); + ASSERT_WITH_MESSAGE(!(requirements == FunctionNameRequirements::None && !info.className), "When specifying FunctionNameRequirements::None, we need to initialize info.className with the default value in the caller side."); + if (match(IDENT)) { + info.className = m_token.m_data.ident; + next(); + failIfTrue(classScope->declareLexicalVariable(info.className, true) & DeclarationResult::InvalidStrictMode, "'", info.className->impl(), "' is not a valid class name"); + } else if (requirements == FunctionNameRequirements::Named) { + if (match(OPENBRACE)) + semanticFail("Class statements must have a name"); + semanticFailureDueToKeyword("class name"); + failDueToUnexpectedToken(); + } + ASSERT(info.className); + + TreeExpression parentClass = 0; + if (consume(EXTENDS)) { + parentClass = parseMemberExpression(context); + failIfFalse(parentClass, "Cannot parse the parent class name"); + } + const ConstructorKind constructorKind = parentClass ? ConstructorKind::Extends : 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; + bool isAsync = false; + bool isAsyncMethod = false; + if (consume(TIMES)) + isGenerator = true; + +parseMethod: + switch (m_token.m_type) { + namedKeyword: + case STRING: + ident = m_token.m_data.ident; + ASSERT(ident); + next(); + break; + case ASYNC: + isAsync = !isGenerator && !isAsyncMethod; + FALLTHROUGH; + case IDENT: + case AWAIT: + ident = m_token.m_data.ident; + ASSERT(ident); + next(); + if (!isGenerator && !isAsyncMethod && (matchIdentifierOrKeyword() || match(STRING) || match(DOUBLE) || match(INTEGER) || match(OPENBRACKET))) { + isGetter = *ident == propertyNames.get; + isSetter = *ident == propertyNames.set; + + if (UNLIKELY(isAsync && !m_lexer->prevTerminator())) { + isAsyncMethod = true; + goto parseMethod; + } + } + 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) { + bool isClassProperty = true; + property = parseGetterSetter(context, alwaysStrictInsideClass, isGetter ? PropertyNode::Getter : PropertyNode::Setter, methodStart, + ConstructorKind::None, isClassProperty); + failIfFalse(property, "Cannot parse this method"); + } else { + ParserFunctionInfo<TreeBuilder> methodInfo; + bool isConstructor = !isStaticMethod && *ident == propertyNames.constructor; + SourceParseMode parseMode = SourceParseMode::MethodMode; + if (isAsyncMethod) { + isConstructor = false; + parseMode = SourceParseMode::AsyncMethodMode; + semanticFailIfTrue(*ident == m_vm->propertyNames->prototype, "Cannot declare an async method named 'prototype'"); + semanticFailIfTrue(*ident == m_vm->propertyNames->constructor, "Cannot declare an async method named 'constructor'"); + } else 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'"); + } + methodInfo.name = isConstructor ? info.className : ident; + failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, parseMode, false, isConstructor ? constructorKind : ConstructorKind::None, SuperBinding::Needed, methodStart, methodInfo, FunctionDefinitionType::Method)), "Cannot parse this method"); + + TreeExpression method = context.createMethodDefinition(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'"); + + bool isClassProperty = true; + if (computedPropertyName) { + property = context.createProperty(computedPropertyName, method, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Computed), + PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed, isClassProperty); + } else + property = context.createProperty(methodInfo.name, method, PropertyNode::Constant, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed, isClassProperty); + } + + 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; + } + } + + info.endOffset = tokenLocation().endOffset - 1; + consumeOrFail(CLOSEBRACE, "Expected a closing '}' after a class body"); + + auto classExpression = context.createClassExpr(location, info, classScope->finalizeLexicalEnvironment(), constructor, parentClass, instanceMethods, staticMethods); + popScope(classScope, TreeBuilder::NeedsFreeVariableInfo); + return classExpression; } struct LabelInfo { @@ -1333,7 +2731,7 @@ struct LabelInfo { }; template <typename LexerType> -template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrLabelStatement(TreeBuilder& context) +template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrLabelStatement(TreeBuilder& context, bool allowFunctionDeclarationAsStatement) { /* Expression and Label statements are ambiguous at LL(1), so we have a @@ -1354,6 +2752,10 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrL failDueToUnexpectedToken(); return context.createExprStatement(location, expression, start, m_lastTokenEndPosition.line); } + + if (UNLIKELY(match(AWAIT))) + semanticFailIfTrue(isDisallowedIdentifierAwait(m_token), "Can't use 'await' as a label ", disallowedIdentifierAwaitReason()); + const Identifier* ident = m_token.m_data.ident; JSTextPosition end = tokenEndPosition(); next(); @@ -1366,7 +2768,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExpressionOrL 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: @@ -1379,14 +2781,16 @@ 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); } + m_immediateParentAllowsFunctionDeclarationInStatement = allowFunctionDeclarationAsStatement; TreeStatement statement = parseStatement(context, unused); if (!m_syntaxAlreadyValidated) { for (size_t i = 0; i < labels.size(); i++) - popLabel(); + popLabel(labelScope); } failIfFalse(statement, "Cannot parse statement"); for (size_t i = 0; i < labels.size(); i++) { @@ -1399,6 +2803,19 @@ 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); @@ -1414,14 +2831,16 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseIfStatement(T JSTokenLocation ifLocation(tokenLocation()); int start = tokenLine(); next(); - handleProductionOrFail(OPENPAREN, "(", "start", "'if' condition"); + handleProductionOrFail2(OPENPAREN, "(", "start", "'if' condition"); TreeExpression condition = parseExpression(context); failIfFalse(condition, "Expected a expression as the condition for an if statement"); + recordPauseLocation(context.breakpointLocation(condition)); int end = tokenLine(); - handleProductionOrFail(CLOSEPAREN, ")", "end", "'if' condition"); + handleProductionOrFail2(CLOSEPAREN, ")", "end", "'if' condition"); const Identifier* unused = 0; + m_immediateParentAllowsFunctionDeclarationInStatement = true; TreeStatement trueBlock = parseStatement(context, unused); failIfFalse(trueBlock, "Expected a statement as the body of an if block"); @@ -1438,6 +2857,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseIfStatement(T next(); if (!match(IF)) { const Identifier* unused = 0; + m_immediateParentAllowsFunctionDeclarationInStatement = true; TreeStatement block = parseStatement(context, unused); failIfFalse(block, "Expected a statement as the body of an else block"); statementStack.append(block); @@ -1447,13 +2867,15 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseIfStatement(T int innerStart = tokenLine(); next(); - handleProductionOrFail(OPENPAREN, "(", "start", "'if' condition"); + handleProductionOrFail2(OPENPAREN, "(", "start", "'if' condition"); TreeExpression innerCondition = parseExpression(context); failIfFalse(innerCondition, "Expected a expression as the condition for an if statement"); + recordPauseLocation(context.breakpointLocation(innerCondition)); int innerEnd = tokenLine(); - handleProductionOrFail(CLOSEPAREN, ")", "end", "'if' condition"); + handleProductionOrFail2(CLOSEPAREN, ")", "end", "'if' condition"); const Identifier* unused = 0; + m_immediateParentAllowsFunctionDeclarationInStatement = true; TreeStatement innerTrueBlock = parseStatement(context, unused); failIfFalse(innerTrueBlock, "Expected a statement as the body of an if block"); tokenLocationStack.append(tempLocation); @@ -1471,7 +2893,9 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseIfStatement(T 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()) { @@ -1485,57 +2909,533 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseIfStatement(T 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(); + + failIfFalse(matchSpecIdentifier(), "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(); + failIfFalse(matchSpecIdentifier(), "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(matchSpecIdentifier()); + localNameToken = m_token; + localName = m_token.m_data.ident; + importedName = &m_vm->propertyNames->defaultKeyword; + next(); + break; + } + } + + semanticFailIfTrue(localNameToken.m_type == AWAIT, "Cannot use 'await' as an imported binding name"); + 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 (matchSpecIdentifier()) { + // 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; + } + handleProductionOrFail2(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<std::pair<const Identifier*, const Identifier*>>& maybeExportedLocalNames, 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(), "'"); + maybeExportedLocalNames.append(std::make_pair(localName, exportedName)); + 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; + + bool startsWithFunction = match(FUNCTION); + if (startsWithFunction || match(CLASSTOKEN)) { + SavePoint savePoint = createSavePoint(); + isFunctionOrClassDeclaration = true; + next(); + + // ES6 Generators + if (startsWithFunction && match(TIMES)) + next(); + if (match(IDENT)) + localName = m_token.m_data.ident; + restoreSavePoint(savePoint); + } else if (match(ASYNC)) { + SavePoint savePoint = createSavePoint(); + next(); + if (match(FUNCTION) && !m_lexer->prevTerminator()) { + next(); + if (match(IDENT)) + localName = m_token.m_data.ident; + isFunctionOrClassDeclaration = true; + } + restoreSavePoint(savePoint); + } + + if (!localName) + localName = &m_vm->propertyNames->builtinNames().starDefaultPrivateName(); + + if (isFunctionOrClassDeclaration) { + if (startsWithFunction) { + ASSERT(match(FUNCTION)); + DepthManager statementDepth(&m_statementDepth); + m_statementDepth = 1; + result = parseFunctionDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::ExportDefault); + } else if (match(CLASSTOKEN)) { + result = parseClassDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::ExportDefault); + } else { + ASSERT(match(ASYNC)); + next(); + DepthManager statementDepth(&m_statementDepth); + m_statementDepth = 1; + result = parseAsyncFunctionDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::ExportDefault); + } + } 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->builtinNames().starDefaultPrivateName(), DeclarationType::ConstDeclaration); + if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) + internalFailWithMessage(false, "Only one 'default' export is allowed"); + + TreeExpression assignment = context.createAssignResolve(location, m_vm->propertyNames->builtinNames().starDefaultPrivateName(), expression, start, start, tokenEndPosition(), AssignmentContext::ConstDeclarationStatement); + result = context.createExprStatement(location, assignment, start, tokenEndPosition()); + failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration"); + } + failIfFalse(result, "Cannot parse the declaration"); + + semanticFailIfFalse(exportName(m_vm->propertyNames->defaultKeyword), "Only one 'default' export is allowed"); + m_moduleScopeData->exportBinding(*localName, m_vm->propertyNames->defaultKeyword); + 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<std::pair<const Identifier*, const Identifier*>> maybeExportedLocalNames; + + bool hasKeywordForLocalBindings = false; + while (!match(CLOSEBRACE)) { + failIfFalse(matchIdentifierOrKeyword(), "Expected a variable name for the export declaration"); + auto specifier = parseExportSpecifier(context, maybeExportedLocalNames, hasKeywordForLocalBindings); + failIfFalse(specifier, "Cannot parse the named export"); + context.appendExportSpecifier(specifierList, specifier); + if (!consume(COMMA)) + break; + } + handleProductionOrFail2(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 auto& pair : maybeExportedLocalNames) { + const Identifier* localName = pair.first; + const Identifier* exportedName = pair.second; + m_moduleScopeData->exportBinding(*localName, *exportedName); + } + } + + 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: { + DepthManager statementDepth(&m_statementDepth); + m_statementDepth = 1; + result = parseFunctionDeclaration(context, ExportType::Exported); + break; + } + + case CLASSTOKEN: + result = parseClassDeclaration(context, ExportType::Exported); + break; + + case ASYNC: + next(); + semanticFailIfFalse(match(FUNCTION) && !m_lexer->prevTerminator(), "Expected 'function' keyword following 'async' keyword with no preceding line terminator"); + result = parseAsyncFunctionDeclaration(context, ExportType::Exported); + break; + + default: + failWithMessage("Expected either a declaration or a variable statement"); + break; + } + + failIfFalse(result, "Cannot parse the declaration"); + return context.createExportLocalDeclaration(exportLocation, result); + } + } + + RELEASE_ASSERT_NOT_REACHED(); + return 0; +} + +template <typename LexerType> template <class TreeBuilder> TreeExpression Parser<LexerType>::parseExpression(TreeBuilder& context) { failIfStackOverflow(); JSTokenLocation location(tokenLocation()); TreeExpression node = parseAssignmentExpression(context); failIfFalse(node, "Cannot parse expression"); + context.setEndOffset(node, m_lastTokenEndPosition.offset); if (!match(COMMA)) return node; next(); - m_nonTrivialExpressionCount++; - m_nonLHSCount++; + m_parserState.nonTrivialExpressionCount++; + m_parserState.nonLHSCount++; TreeExpression right = parseAssignmentExpression(context); failIfFalse(right, "Cannot parse expression in a comma expression"); - typename TreeBuilder::Comma commaNode = context.createCommaExpr(location, node, right); + context.setEndOffset(right, m_lastTokenEndPosition.offset); + typename TreeBuilder::Comma head = context.createCommaExpr(location, node); + typename TreeBuilder::Comma tail = context.appendToCommaExpr(location, head, head, right); while (match(COMMA)) { next(TreeBuilder::DontBuildStrings); right = parseAssignmentExpression(context); failIfFalse(right, "Cannot parse expression in a comma expression"); - context.appendToComma(commaNode, right); + 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(); + + if (match(YIELD) && !isYIELDMaskedAsIDENT(currentScope()->isGenerator())) + return parseYieldExpression(context); + JSTextPosition start = tokenStartPosition(); JSTokenLocation location(tokenLocation()); - int initialAssignmentCount = m_assignmentCount; - int initialNonLHSCount = m_nonLHSCount; - if (match(OPENBRACE) || match(OPENBRACKET)) { - SavePoint savePoint = createSavePoint(); - auto pattern = tryParseDeconstructionPatternExpression(context); - if (pattern && consume(EQUAL)) { - auto rhs = parseAssignmentExpression(context); - if (rhs) - return context.createDeconstructingAssignment(location, pattern, rhs); + int initialAssignmentCount = m_parserState.assignmentCount; + int initialNonLHSCount = m_parserState.nonLHSCount; + bool maybeAssignmentPattern = match(OPENBRACE) || match(OPENBRACKET); + bool wasOpenParen = match(OPENPAREN); + // Do not use matchSpecIdentifier() here since it is slower than isIdentifierOrKeyword. + // Whether spec identifier is will be validated by isArrowFunctionParameters(). + bool wasIdentifierOrKeyword = isIdentifierOrKeyword(m_token); + bool maybeValidArrowFunctionStart = wasOpenParen || wasIdentifierOrKeyword; + SavePoint savePoint = createSavePoint(); + size_t usedVariablesSize = 0; + + if (wasOpenParen) { + usedVariablesSize = currentScope()->currentUsedVariablesSize(); + currentScope()->pushUsedVariableSet(); + } + + TreeExpression lhs = parseConditionalExpression(context); + + if (maybeValidArrowFunctionStart && !match(EOFTOK)) { + bool isArrowFunctionToken = match(ARROWFUNCTION); + if (!lhs || isArrowFunctionToken) { + SavePointWithError errorRestorationSavePoint = createSavePointForError(); + restoreSavePoint(savePoint); + bool isAsyncArrow = false; + if (UNLIKELY(classifier.indicatesPossibleAsyncArrowFunction())) { + ASSERT(match(ASYNC)); + next(); + isAsyncArrow = !m_lexer->prevTerminator(); + } + if (isArrowFunctionParameters()) { + if (wasOpenParen) + currentScope()->revertToPreviousUsedVariables(usedVariablesSize); + return parseArrowFunctionExpression(context, isAsyncArrow); + } + if (isArrowFunctionToken) + propagateError(); + restoreSavePointWithError(errorRestorationSavePoint); + if (isArrowFunctionToken) + failDueToUnexpectedToken(); } + } + + if (!lhs && (!maybeAssignmentPattern || !classifier.indicatesPossiblePattern())) + propagateError(); + + if (maybeAssignmentPattern && (!lhs || (context.isObjectOrArrayLiteral(lhs) && match(EQUAL)))) { + SavePointWithError expressionErrorLocation = createSavePointForError(); restoreSavePoint(savePoint); + auto pattern = tryParseDestructuringPatternExpression(context, AssignmentContext::AssignmentExpression); + if (classifier.indicatesPossiblePattern() && (!pattern || !match(EQUAL))) { + restoreSavePointWithError(expressionErrorLocation); + return 0; + } + 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); } - TreeExpression lhs = parseConditionalExpression(context); + failIfFalse(lhs, "Cannot parse expression"); - if (initialNonLHSCount != m_nonLHSCount) { + 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"); @@ -1559,24 +3459,26 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen case XOREQUAL: op = OpXOrEq; break; case OREQUAL: op = OpOrEq; break; case MODEQUAL: op = OpModEq; break; + case POWEQUAL: op = OpPowEq; break; default: goto end; } - m_nonTrivialExpressionCount++; + m_parserState.nonTrivialExpressionCount++; hadAssignment = true; - context.assignmentStackAppend(assignmentStack, lhs, start, tokenStartPosition(), m_assignmentCount, op); + if (UNLIKELY(context.isNewTarget(lhs))) + internalFailWithMessage(false, "new.target can't be the left hand side of an assignment expression"); + 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)) { - failIfTrueIfStrict(m_vm->propertyNames->eval == *m_lastIdentifier, "Cannot modify 'eval' in strict mode"); - failIfTrueIfStrict(m_vm->propertyNames->arguments == *m_lastIdentifier, "Cannot modify 'arguments' in strict mode"); - declareWrite(m_lastIdentifier); - m_lastIdentifier = 0; + 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"); + m_parserState.lastIdentifier = 0; } lhs = parseAssignmentExpression(context); failIfFalse(lhs, "Cannot parse the right hand side of an assignment expression"); - if (initialNonLHSCount != m_nonLHSCount) { + 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; @@ -1584,18 +3486,66 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen } 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() && !currentScope()->isArrowFunctionBoundary(), "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>::parseAwaitExpression(TreeBuilder& context) +{ + ASSERT(match(AWAIT)); + ASSERT(currentScope()->isAsyncFunction()); + failIfTrue(m_parserState.functionParsePhase == FunctionParsePhase::Parameters, "Cannot use await expression within parameters"); + JSTokenLocation location(tokenLocation()); + JSTextPosition divotStart = tokenStartPosition(); + next(); + JSTextPosition argumentStart = tokenStartPosition(); + TreeExpression argument = parseUnaryExpression(context); + failIfFalse(argument, "Failed to parse await expression"); + return context.createAwait(location, argument, divotStart, argumentStart, lastTokenEndPosition()); +} + +template <typename LexerType> template <class TreeBuilder> TreeExpression Parser<LexerType>::parseConditionalExpression(TreeBuilder& context) { JSTokenLocation location(tokenLocation()); @@ -1603,21 +3553,25 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseConditionalE 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); failIfFalse(lhs, "Cannot parse left hand side of ternary operator"); + context.setEndOffset(lhs, m_lastTokenEndPosition.offset); consumeOrFailWithFlags(COLON, TreeBuilder::DontBuildStrings, "Expected ':' in ternary operator"); TreeExpression rhs = parseAssignmentExpression(context); failIfFalse(rhs, "Cannot parse right hand side of ternary operator"); + context.setEndOffset(rhs, m_lastTokenEndPosition.offset); return context.createConditionalExpr(location, cond, lhs, rhs); } -ALWAYS_INLINE static bool isUnaryOp(JSTokenType token) +ALWAYS_INLINE static bool isUnaryOpExcludingUpdateOp(JSTokenType token) { - return token & UnaryOpTokenFlag; + if (isUpdateOp(token)) + return false; + return isUnaryOp(token); } template <typename LexerType> @@ -1631,27 +3585,51 @@ 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; + JSTokenType leadingTokenTypeForUnaryExpression = m_token.m_type; TreeExpression current = parseUnaryExpression(context); 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); + + // 12.6 https://tc39.github.io/ecma262/#sec-exp-operator + // ExponentiationExpresion is described as follows. + // + // ExponentiationExpression[Yield]: + // UnaryExpression[?Yield] + // UpdateExpression[?Yield] ** ExponentiationExpression[?Yield] + // + // As we can see, the left hand side of the ExponentiationExpression is UpdateExpression, not UnaryExpression. + // So placing UnaryExpression not included in UpdateExpression here is a syntax error. + // This is intentional. For example, if UnaryExpression is allowed, we can have the code like `-x**y`. + // But this is confusing: `-(x**y)` OR `(-x)**y`, which interpretation is correct? + // To avoid this problem, ECMA262 makes unparenthesized exponentiation expression as operand of unary operators an early error. + // More rationale: https://mail.mozilla.org/pipermail/es-discuss/2015-September/044232.html + // + // Here, we guarantee that the left hand side of this expression is not unary expression by checking the leading operator of the parseUnaryExpression. + // This check just works. Let's consider the example, + // y <> -x ** z + // ^ + // Check this. + // If the binary operator <> has higher precedence than one of "**", this check does not work. + // But it's OK for ** because the operator "**" has the highest operator precedence in the binary operators. + failIfTrue(match(POW) && isUnaryOpExcludingUpdateOp(leadingTokenTypeForUnaryExpression), "Amiguous unary expression in the left hand side of the exponentiation expression; parenthesis must be used to disambiguate the expression"); + 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); - while (operatorStackDepth && context.operatorStackHasHigherPrecedence(operatorStackDepth, precedence)) { + while (operatorStackDepth && context.operatorStackShouldReduce(precedence)) { ASSERT(operandStackDepth > 1); typename TreeBuilder::BinaryOperand rhs = context.getFromOperandStack(-1); @@ -1678,94 +3656,230 @@ template <typename LexerType> template <class TreeBuilder> TreeProperty Parser<LexerType>::parseProperty(TreeBuilder& context, bool complete) { bool wasIdent = false; + bool isAsync = false; + bool isGenerator = false; + bool isClassProperty = false; + bool isAsyncMethod = false; + if (consume(TIMES)) + isGenerator = true; + +parseProperty: switch (m_token.m_type) { - namedProperty: + case ASYNC: + isAsync = !isGenerator && !isAsyncMethod; + FALLTHROUGH; case IDENT: + case AWAIT: wasIdent = true; FALLTHROUGH; case STRING: { +namedProperty: 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)) || isAsync) nextExpectIdentifier(LexerFlagsIgnoreReservedWords); else nextExpectIdentifier(LexerFlagsIgnoreReservedWords | TreeBuilder::DontBuildKeywords); - - if (match(COLON)) { + + if (!isGenerator && !isAsyncMethod && match(COLON)) { next(); - TreeExpression node = parseAssignmentExpression(context); + TreeExpression node = parseAssignmentExpressionOrPropagateErrorClass(context); failIfFalse(node, "Cannot parse expression for property declaration"); - return context.createProperty(ident, node, PropertyNode::Constant, complete); + context.setEndOffset(node, m_lexer->currentOffset()); + return context.createProperty(ident, node, PropertyNode::Constant, PropertyNode::Unknown, complete, SuperBinding::NotNeeded, isClassProperty); + } + + if (match(OPENPAREN)) { + auto method = parsePropertyMethod(context, ident, isGenerator, isAsyncMethod); + propagateError(); + return context.createProperty(ident, method, PropertyNode::Constant, PropertyNode::KnownDirect, complete, SuperBinding::Needed, isClassProperty); } + failIfTrue(isGenerator || isAsyncMethod, "Expected a parenthesis for argument list"); + failIfFalse(wasIdent, "Expected an identifier as property name"); - const Identifier* accessorName = 0; - TreeFormalParameterList parameters = 0; - TreeFunctionBody body = 0; - unsigned openBraceOffset = 0; - unsigned closeBraceOffset = 0; - int bodyStartLine = 0; - unsigned bodyStartColumn = 0; + + if (match(COMMA) || match(CLOSEBRACE)) { + JSTextPosition start = tokenStartPosition(); + JSTokenLocation location(tokenLocation()); + currentScope()->useVariable(ident, m_vm->propertyNames->eval == *ident); + if (currentScope()->isArrowFunction()) + currentScope()->setInnerArrowFunctionUsesEval(); + TreeExpression node = context.createResolve(location, *ident, start, lastTokenEndPosition()); + return context.createProperty(ident, node, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Shorthand), PropertyNode::KnownDirect, complete, SuperBinding::NotNeeded, isClassProperty); + } + + 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 + else if (UNLIKELY(isAsync && !isAsyncMethod)) { + isAsyncMethod = true; + failIfTrue(m_lexer->prevTerminator(), "Expected a property name following keyword 'async'"); + goto parseProperty; + } else failWithMessage("Expected a ':' following the property name '", ident->impl(), "'"); - 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 - failDueToUnexpectedToken(); - JSTokenLocation location(tokenLocation()); - next(); - if (type == PropertyNode::Getter) { - failIfFalse(match(OPENPAREN), "Expected a parameter list for getter definition"); - failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, GetterMode, false, accessorName, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn)), "Cannot parse getter definition"); - } else { - failIfFalse(match(OPENPAREN), "Expected a parameter list for setter definition"); - failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SetterMode, false, accessorName, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn)), "Cannot parse setter definition"); - } - if (stringPropertyName) - return context.createGetterOrSetterProperty(location, type, complete, stringPropertyName, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn); - return context.createGetterOrSetterProperty(const_cast<VM*>(m_vm), location, type, complete, numericPropertyName, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, m_lastTokenEndPosition.line, bodyStartColumn); + return parseGetterSetter(context, complete, type, getterOrSetterStartOffset, ConstructorKind::None, isClassProperty); } - case NUMBER: { + case DOUBLE: + case INTEGER: { double propertyName = m_token.m_data.doubleValue; next(); + + if (match(OPENPAREN)) { + const Identifier& ident = m_parserArena.identifierArena().makeNumericIdentifier(const_cast<VM*>(m_vm), propertyName); + auto method = parsePropertyMethod(context, &ident, isGenerator, isAsyncMethod); + propagateError(); + return context.createProperty(&ident, method, PropertyNode::Constant, PropertyNode::Unknown, complete, SuperBinding::Needed, isClassProperty); + } + failIfTrue(isGenerator || isAsyncMethod, "Expected a parenthesis for argument list"); + consumeOrFail(COLON, "Expected ':' after property name"); TreeExpression node = parseAssignmentExpression(context); failIfFalse(node, "Cannot parse expression for property declaration"); - return context.createProperty(const_cast<VM*>(m_vm), propertyName, node, PropertyNode::Constant, complete); + context.setEndOffset(node, m_lexer->currentOffset()); + return context.createProperty(const_cast<VM*>(m_vm), m_parserArena, propertyName, node, PropertyNode::Constant, PropertyNode::Unknown, complete, SuperBinding::NotNeeded, isClassProperty); } case OPENBRACKET: { next(); - auto propertyName = parseExpression(context); + 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, isAsyncMethod); + propagateError(); + return context.createProperty(propertyName, method, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Computed), PropertyNode::KnownDirect, complete, SuperBinding::Needed, isClassProperty); + } + failIfTrue(isGenerator || isAsyncMethod, "Expected a parenthesis for argument list"); + consumeOrFail(COLON, "Expected ':' after property name"); TreeExpression node = parseAssignmentExpression(context); failIfFalse(node, "Cannot parse expression for property declaration"); - return context.createProperty(const_cast<VM*>(m_vm), propertyName, node, PropertyNode::Constant, complete); + context.setEndOffset(node, m_lexer->currentOffset()); + return context.createProperty(propertyName, node, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Computed), PropertyNode::Unknown, complete, SuperBinding::NotNeeded, isClassProperty); } default: failIfFalse(m_token.m_type & KeywordTokenFlag, "Expected a property name"); + wasIdent = true; // Treat keyword token as an identifier goto namedProperty; } } template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePropertyMethod(TreeBuilder& context, const Identifier* methodName, bool isGenerator, bool isAsyncMethod) +{ + JSTokenLocation methodLocation(tokenLocation()); + unsigned methodStart = tokenStart(); + ParserFunctionInfo<TreeBuilder> methodInfo; + methodInfo.name = methodName; + SourceParseMode parseMode = isGenerator ? SourceParseMode::GeneratorWrapperFunctionMode : isAsyncMethod ? SourceParseMode::AsyncMethodMode : SourceParseMode::MethodMode; + failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, parseMode, false, ConstructorKind::None, SuperBinding::Needed, methodStart, methodInfo, FunctionDefinitionType::Method)), "Cannot parse this method"); + return context.createMethodDefinition(methodLocation, methodInfo); +} + +template <typename LexerType> +template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(TreeBuilder& context, bool strict, PropertyNode::Type type, unsigned getterOrSetterStartOffset, + ConstructorKind constructorKind, bool isClassProperty) +{ + const Identifier* stringPropertyName = 0; + double numericPropertyName = 0; + TreeExpression computedPropertyName = 0; + + JSTokenLocation location(tokenLocation()); + + if (matchSpecIdentifier() || match(STRING) || m_token.m_type & KeywordTokenFlag) { + stringPropertyName = m_token.m_data.ident; + semanticFailIfTrue(isClassProperty && *stringPropertyName == m_vm->propertyNames->prototype, + "Cannot declare a static method named 'prototype'"); + semanticFailIfTrue(isClassProperty && *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, FunctionNameRequirements::Unnamed, SourceParseMode::GetterMode, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse getter definition"); + } else { + failIfFalse(match(OPENPAREN), "Expected a parameter list for setter definition"); + failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, SourceParseMode::SetterMode, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse setter definition"); + } + + if (stringPropertyName) + return context.createGetterOrSetterProperty(location, type, strict, stringPropertyName, info, isClassProperty); + + if (computedPropertyName) + return context.createGetterOrSetterProperty(location, static_cast<PropertyNode::Type>(type | PropertyNode::Computed), strict, computedPropertyName, info, isClassProperty); + + return context.createGetterOrSetterProperty(const_cast<VM*>(m_vm), m_parserArena, location, type, strict, numericPropertyName, info, isClassProperty); +} + +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> +void Parser<LexerType>::recordPauseLocation(const JSTextPosition& position) +{ + if (LIKELY(!m_debuggerParseData)) + return; + + if (position.line < 0) + return; + + m_debuggerParseData->pausePositions.appendPause(position); +} + +template <typename LexerType> +void Parser<LexerType>::recordFunctionEntryLocation(const JSTextPosition& position) +{ + if (LIKELY(!m_debuggerParseData)) + return; + + m_debuggerParseData->pausePositions.appendEntry(position); +} + +template <typename LexerType> +void Parser<LexerType>::recordFunctionLeaveLocation(const JSTextPosition& position) +{ + if (LIKELY(!m_debuggerParseData)) + return; + + m_debuggerParseData->pausePositions.appendLeave(position); +} + +template <typename LexerType> template <class TreeBuilder> TreeExpression Parser<LexerType>::parseObjectLiteral(TreeBuilder& context) { - auto savePoint = createSavePoint(); + SavePoint savePoint = createSavePoint(); consumeOrFailWithFlags(OPENBRACE, TreeBuilder::DontBuildStrings, "Expected opening '{' at the start of an object literal"); - JSTokenLocation location(tokenLocation()); - int oldNonLHSCount = m_nonLHSCount; - + int oldNonLHSCount = m_parserState.nonLHSCount; + + JSTokenLocation location(tokenLocation()); if (match(CLOSEBRACE)) { next(); return context.createObjectLiteral(location); @@ -1773,31 +3887,42 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseObjectLitera TreeProperty property = parseProperty(context, false); failIfFalse(property, "Cannot parse object literal property"); - if (!m_syntaxAlreadyValidated && context.getType(property) != PropertyNode::Constant) { + + 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(context, false); failIfFalse(property, "Cannot parse object literal property"); - if (!m_syntaxAlreadyValidated && context.getType(property) != PropertyNode::Constant) { + if (!m_syntaxAlreadyValidated && context.getType(property) & (PropertyNode::Getter | PropertyNode::Setter)) { restoreSavePoint(savePoint); return parseStrictObjectLiteral(context); } + if (shouldCheckPropertyForUnderscoreProtoDuplicate(context, property)) { + if (*context.getName(property) == m_vm->propertyNames->underscoreProto) { + semanticFailIfTrue(seenUnderscoreProto, "Attempted to redefine __proto__ property"); + seenUnderscoreProto = true; + } + } tail = context.createPropertyList(propertyLocation, property, tail); } location = tokenLocation(); - handleProductionOrFail(CLOSEBRACE, "}", "end", "object literal"); + handleProductionOrFail2(CLOSEBRACE, "}", "end", "object literal"); - m_nonLHSCount = oldNonLHSCount; + m_parserState.nonLHSCount = oldNonLHSCount; return context.createObjectLiteral(location, propertyList); } @@ -1807,7 +3932,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseStrictObject { 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)) { @@ -1817,39 +3942,33 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseStrictObject TreeProperty property = parseProperty(context, true); failIfFalse(property, "Cannot parse object literal property"); - - typedef HashMap<RefPtr<StringImpl>, unsigned, IdentifierRepHash> ObjectValidationMap; - ObjectValidationMap objectValidator; - // Add the first property - if (!m_syntaxAlreadyValidated && context.getName(property)) - objectValidator.add(context.getName(property)->impl(), context.getType(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(context, true); failIfFalse(property, "Cannot parse object literal property"); - if (!m_syntaxAlreadyValidated && context.getName(property)) { - ObjectValidationMap::AddResult propertyEntry = objectValidator.add(context.getName(property)->impl(), context.getType(property)); - if (!propertyEntry.isNewEntry) { - semanticFailIfTrue(propertyEntry.iterator->value == PropertyNode::Constant, "Attempted to redefine property '", propertyEntry.iterator->key.get(), "'"); - semanticFailIfTrue(context.getType(property) == PropertyNode::Constant, "Attempted to redefine property '", propertyEntry.iterator->key.get(), "'"); - semanticFailIfTrue(context.getType(property) & propertyEntry.iterator->value, "Attempted to redefine property '", propertyEntry.iterator->key.get(), "'"); - propertyEntry.iterator->value |= context.getType(property); + if (shouldCheckPropertyForUnderscoreProtoDuplicate(context, property)) { + if (*context.getName(property) == m_vm->propertyNames->underscoreProto) { + semanticFailIfTrue(seenUnderscoreProto, "Attempted to redefine __proto__ property"); + seenUnderscoreProto = true; } } tail = context.createPropertyList(propertyLocation, property, tail); } location = tokenLocation(); - handleProductionOrFail(CLOSEBRACE, "}", "end", "object literal"); + handleProductionOrFail2(CLOSEBRACE, "}", "end", "object literal"); - m_nonLHSCount = oldNonLHSCount; + m_parserState.nonLHSCount = oldNonLHSCount; return context.createObjectLiteral(location, propertyList); } @@ -1859,7 +3978,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArrayLiteral { 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)) { @@ -1878,11 +3997,11 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArrayLiteral auto start = m_token.m_startPosition; auto divot = m_token.m_endPosition; next(); - auto spreadExpr = parseAssignmentExpression(context); + auto spreadExpr = parseAssignmentExpressionOrPropagateErrorClass(context); failIfFalse(spreadExpr, "Cannot parse subject of a spread operation"); elem = context.createSpreadExpression(spreadLocation, spreadExpr, start, divot, m_lastTokenEndPosition); } else - elem = parseAssignmentExpression(context); + elem = parseAssignmentExpressionOrPropagateErrorClass(context); failIfFalse(elem, "Cannot parse array literal element"); typename TreeBuilder::ElementList elementList = context.createElementList(elisions, elem); typename TreeBuilder::ElementList tail = elementList; @@ -1906,13 +4025,13 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArrayLiteral auto start = m_token.m_startPosition; auto divot = m_token.m_endPosition; next(); - TreeExpression elem = parseAssignmentExpression(context); + 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 = parseAssignmentExpression(context); + TreeExpression elem = parseAssignmentExpressionOrPropagateErrorClass(context); failIfFalse(elem, "Cannot parse array literal element"); tail = context.createElementList(tail, elisions, elem); } @@ -1923,16 +4042,128 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArrayLiteral 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> TreeClassExpression Parser<LexerType>::parseClassExpression(TreeBuilder& context) +{ + ASSERT(match(CLASSTOKEN)); + ParserClassInfo<TreeBuilder> info; + info.className = &m_vm->propertyNames->nullIdentifier; + return parseClass(context, FunctionNameRequirements::None, info); +} + +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 (consume(TIMES)) + parseMode = SourceParseMode::GeneratorWrapperFunctionMode; + failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::None, 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> TreeExpression Parser<LexerType>::parseAsyncFunctionExpression(TreeBuilder& context) +{ + ASSERT(match(FUNCTION)); + JSTokenLocation location(tokenLocation()); + unsigned functionKeywordStart = tokenStart(); + next(); + ParserFunctionInfo<TreeBuilder> functionInfo; + functionInfo.name = &m_vm->propertyNames->nullIdentifier; + SourceParseMode parseMode = SourceParseMode::AsyncFunctionMode; + failIfFalse(parseFunctionInfo(context, FunctionNameRequirements::None, parseMode, false, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Expression), "Cannot parse async 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) + ASSERT(match(BACKQUOTE)); + else + 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->scanTemplateString(&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) +{ + ASSERT(match(BACKQUOTE)); + 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 <class LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::createResolveAndUseVariable(TreeBuilder& context, const Identifier* ident, bool isEval, const JSTextPosition& start, const JSTokenLocation& location) +{ + currentScope()->useVariable(ident, isEval); + m_parserState.lastIdentifier = ident; + return context.createResolve(location, *ident, start, lastTokenEndPosition()); +} + +template <typename LexerType> template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpression(TreeBuilder& context) { failIfStackOverflow(); switch (m_token.m_type) { + case FUNCTION: + return parseFunctionExpression(context); + case CLASSTOKEN: + return parseClassExpression(context); case OPENBRACE: if (strictMode()) return parseStrictObjectLiteral(context); @@ -1941,25 +4172,50 @@ 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; + m_parserState.nonLHSCount = oldNonLHSCount; handleProductionOrFail(CLOSEPAREN, ")", "end", "compound expression"); return result; } case THISTOKEN: { JSTokenLocation location(tokenLocation()); next(); - return context.thisExpr(location); + if (currentScope()->isArrowFunction()) + currentScope()->setInnerArrowFunctionUsesThis(); + return context.createThisExpr(location); + } + case AWAIT: + if (m_parserState.functionParsePhase == FunctionParsePhase::Parameters) + failIfFalse(m_parserState.allowAwait, "Cannot use await expression within parameters"); + goto identifierExpression; + case ASYNC: { + JSTextPosition start = tokenStartPosition(); + const Identifier* ident = m_token.m_data.ident; + JSTokenLocation location(tokenLocation()); + next(); + if (match(FUNCTION) && !m_lexer->prevTerminator()) + return parseAsyncFunctionExpression(context); + + // Avoid using variable if it is an arrow function parameter + if (UNLIKELY(match(ARROWFUNCTION))) + return 0; + + const bool isEval = false; + return createResolveAndUseVariable(context, ident, isEval, start, location); } 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); + + // Avoid using variable if it is an arrow function parameter + if (UNLIKELY(match(ARROWFUNCTION))) + return 0; + + return createResolveAndUseVariable(context, ident, *ident == m_vm->propertyNames->eval, start, location); } case STRING: { const Identifier* ident = m_token.m_data.ident; @@ -1967,11 +4223,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.createNumberExpr(location, d); + return context.createDoubleExpr(location, d); + } + case INTEGER: { + double d = m_token.m_data.doubleValue; + JSTokenLocation location(tokenLocation()); + next(); + return context.createIntegerExpr(location, d); } case NULLTOKEN: { JSTokenLocation location(tokenLocation()); @@ -1991,30 +4253,41 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre case DIVEQUAL: case DIVIDE: { /* regexp */ - const Identifier* pattern; - const Identifier* flags; if (match(DIVEQUAL)) - failIfFalse(m_lexer->scanRegExp(pattern, flags, '='), "Invalid regular expression"); + m_token.m_type = m_lexer->scanRegExp(&m_token, '='); else - failIfFalse(m_lexer->scanRegExp(pattern, flags), "Invalid regular expression"); - + m_token.m_type = m_lexer->scanRegExp(&m_token); + matchOrFail(REGEXP, "Invalid regular expression"); + + const Identifier* pattern = m_token.m_data.pattern; + const Identifier* flags = m_token.m_data.flags; JSTextPosition start = tokenStartPosition(); JSTokenLocation location(tokenLocation()); next(); TreeExpression re = context.createRegExp(location, *pattern, *flags, start); if (!re) { - const char* yarrErrorMsg = Yarr::checkSyntax(pattern->string()); + const char* yarrErrorMsg = Yarr::checkSyntax(pattern->string(), flags->string()); regexFail(yarrErrorMsg); } return re; } + case BACKQUOTE: + return parseTemplateLiteral(context, LexerType::RawStringsBuildMode::DontBuildRawStrings); + case YIELD: + if (!strictMode() && !currentScope()->isGenerator()) + goto identifierExpression; + failDueToUnexpectedToken(); + case LET: + if (!strictMode()) + goto identifierExpression; + FALLTHROUGH; default: failDueToUnexpectedToken(); } } template <typename LexerType> -template <class TreeBuilder> TreeArguments Parser<LexerType>::parseArguments(TreeBuilder& context, SpreadMode mode) +template <class TreeBuilder> TreeArguments Parser<LexerType>::parseArguments(TreeBuilder& context) { consumeOrFailWithFlags(OPENPAREN, TreeBuilder::DontBuildStrings, "Expected opening '(' at start of argument list"); JSTokenLocation location(tokenLocation()); @@ -2022,122 +4295,257 @@ template <class TreeBuilder> TreeArguments Parser<LexerType>::parseArguments(Tre next(TreeBuilder::DontBuildStrings); return context.createArguments(); } - if (match(DOTDOTDOT) && mode == AllowSpread) { - JSTokenLocation spreadLocation(tokenLocation()); - auto start = m_token.m_startPosition; - auto divot = m_token.m_endPosition; - next(); - auto spreadExpr = parseAssignmentExpression(context); - auto end = m_lastTokenEndPosition; - if (!spreadExpr) - failWithMessage("Cannot parse spread expression"); - if (!consume(CLOSEPAREN)) { - if (match(COMMA)) - semanticFail("Spread operator may only be applied to the last argument passed to a function"); - handleProductionOrFail(CLOSEPAREN, ")", "end", "argument list"); - } - auto spread = context.createSpreadExpression(spreadLocation, spreadExpr, start, divot, end); - TreeArgumentsList argList = context.createArgumentsList(location, spread); - return context.createArguments(argList); - } - TreeExpression firstArg = parseAssignmentExpression(context); + 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, "Cannot parse function argument"); + + if (UNLIKELY(match(CLOSEPAREN))) + break; + + 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); } - semanticFailIfTrue(match(DOTDOTDOT), "The '...' operator should come before the target expression"); - handleProductionOrFail(CLOSEPAREN, ")", "end", "argument list"); + + handleProductionOrFail2(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(); + + bool baseIsSuper = match(SUPER); + bool baseIsImport = match(IMPORT); + semanticFailIfTrue((baseIsSuper || baseIsImport) && newCount, "Cannot use new with ", getToken()); + + bool baseIsNewTarget = false; + if (newCount && match(DOT)) { next(); - failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, FunctionMode, false, name, parameters, body, openBraceOffset, closeBraceOffset, bodyStartLine, bodyStartColumn)), "Cannot parse function expression"); - 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() || closestParentOrdinaryFunctionNonLexicalScope()->evalContextType() == EvalContextType::FunctionEvalContext, "new.target is only valid inside functions"); + baseIsNewTarget = true; + if (currentScope()->isArrowFunction()) + currentScope()->setInnerArrowFunctionUsesNewTarget(); + base = context.createNewTargetExpr(location); + newCount--; + next(); + } else + failWithMessage("\"new.\" can only followed with target"); + } else + failDueToUnexpectedToken(); + } + + bool baseIsAsyncKeyword = false; + + if (baseIsSuper) { + ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope(); + semanticFailIfFalse(currentScope()->isFunction() || (closestOrdinaryFunctionScope->isEvalContext() && closestOrdinaryFunctionScope->expectedSuperBinding() == SuperBinding::Needed), "super is not valid in this context"); + base = context.createSuperExpr(location); + next(); + ScopeRef functionScope = currentFunctionScope(); + if (!functionScope->setNeedsSuperBinding()) { + // It unnecessary to check of using super during reparsing one more time. Also it can lead to syntax error + // in case of arrow function because during reparsing we don't know whether we currently parse the arrow function + // inside of the constructor or method. + if (!m_lexer->isReparsingFunction()) { + SuperBinding functionSuperBinding = !functionScope->isArrowFunction() && !closestOrdinaryFunctionScope->isEvalContext() + ? functionScope->expectedSuperBinding() + : closestOrdinaryFunctionScope->expectedSuperBinding(); + semanticFailIfTrue(functionSuperBinding == SuperBinding::NotNeeded, "super is not valid in this context"); + } + } + } else if (baseIsImport) { + next(); + JSTextPosition expressionEnd = lastTokenEndPosition(); + consumeOrFail(OPENPAREN, "import call expects exactly one argument"); + TreeExpression expr = parseAssignmentExpression(context); + failIfFalse(expr, "Cannot parse expression"); + consumeOrFail(CLOSEPAREN, "import call expects exactly one argument"); + base = context.createImportExpr(location, expr, expressionStart, expressionEnd, lastTokenEndPosition()); + } else if (!baseIsNewTarget) { + const bool isAsync = match(ASYNC); + base = parsePrimaryExpression(context); - + failIfFalse(base, "Cannot parse base expression"); + if (UNLIKELY(isAsync && context.isResolve(base) && !m_lexer->prevTerminator())) { + if (matchSpecIdentifier()) { + // AsyncArrowFunction + forceClassifyExpressionError(ErrorIndicatesAsyncArrowFunction); + failDueToUnexpectedToken(); + } + baseIsAsyncKeyword = true; + } + } + 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, "Cannot parse subscript expression"); - base = context.createBracketAccess(location, base, property, initialAssignments != m_assignmentCount, expressionStart, expressionEnd, tokenEndPosition()); + base = context.createBracketAccess(startLocation, base, property, initialAssignments != m_parserState.assignmentCount, expressionStart, expressionEnd, tokenEndPosition()); + + if (UNLIKELY(baseIsSuper && currentScope()->isArrowFunction())) + currentFunctionScope()->setInnerArrowFunctionUsesSuperProperty(); + handleProductionOrFail(CLOSEBRACKET, "]", "end", "subscript expression"); - m_nonLHSCount = nonLHSCount; + 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, DontAllowSpread); + TreeArguments arguments = parseArguments(context); failIfFalse(arguments, "Cannot parse call arguments"); base = context.createNewExpr(location, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition()); } else { + size_t usedVariablesSize = currentScope()->currentUsedVariablesSize(); JSTextPosition expressionEnd = lastTokenEndPosition(); - TreeArguments arguments = parseArguments(context, AllowSpread); + TreeArguments arguments = parseArguments(context); + + if (baseIsAsyncKeyword && (!arguments || match(ARROWFUNCTION))) { + currentScope()->revertToPreviousUsedVariables(usedVariablesSize); + forceClassifyExpressionError(ErrorIndicatesAsyncArrowFunction); + failDueToUnexpectedToken(); + } + failIfFalse(arguments, "Cannot parse call arguments"); - base = context.makeFunctionCallNode(location, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition()); + if (baseIsSuper) { + ScopeRef functionScope = currentFunctionScope(); + if (!functionScope->setHasDirectSuper()) { + // It unnecessary to check of using super during reparsing one more time. Also it can lead to syntax error + // in case of arrow function because during reparsing we don't know whether we currently parse the arrow function + // inside of the constructor or method. + if (!m_lexer->isReparsingFunction()) { + ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope(); + ConstructorKind functionConstructorKind = !functionScope->isArrowFunction() && !closestOrdinaryFunctionScope->isEvalContext() + ? functionScope->constructorKind() + : closestOrdinaryFunctionScope->constructorKind(); + semanticFailIfTrue(functionConstructorKind == ConstructorKind::None, "super is not valid in this context"); + semanticFailIfTrue(functionConstructorKind != ConstructorKind::Extends, "super is not valid in this context"); + } + } + if (currentScope()->isArrowFunction()) + functionScope->setInnerArrowFunctionUsesSuperCall(); + } + 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, "Expected a property name after '.'"); - base = context.createDotAccess(location, base, m_token.m_data.ident, expressionStart, expressionEnd, tokenEndPosition()); + base = context.createDotAccess(startLocation, base, m_token.m_data.ident, expressionStart, expressionEnd, tokenEndPosition()); + if (UNLIKELY(baseIsSuper && currentScope()->isArrowFunction())) + currentFunctionScope()->setInnerArrowFunctionUsesSuperProperty(); next(); break; } + case BACKQUOTE: { + 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(startLocation, base, templateLiteral, expressionStart, expressionEnd, lastTokenEndPosition()); + m_parserState.nonLHSCount = nonLHSCount; + break; + } default: goto endMemberExpression; } + baseIsSuper = false; } endMemberExpression: + semanticFailIfTrue(baseIsSuper, "super is not valid in this context"); while (newCount--) base = context.createNewExpr(location, base, expressionStart, lastTokenEndPosition()); return base; } +template <typename LexerType> +template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArrowFunctionExpression(TreeBuilder& context, bool isAsync) +{ + JSTokenLocation location; + + unsigned functionKeywordStart = tokenStart(); + location = tokenLocation(); + ParserFunctionInfo<TreeBuilder> info; + info.name = &m_vm->propertyNames->nullIdentifier; + + SourceParseMode parseMode = isAsync ? SourceParseMode::AsyncArrowFunctionMode : SourceParseMode::ArrowFunctionMode; + failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, parseMode, 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) { @@ -2177,6 +4585,12 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress bool modifiesExpr = false; bool requiresLExpr = false; unsigned lastOperator = 0; + + if (UNLIKELY(match(AWAIT) && currentFunctionScope()->isAsyncFunctionBoundary())) + return parseAwaitExpression(context); + + JSTokenLocation location(tokenLocation()); + while (isUnaryOp(m_token.m_type)) { if (strictMode()) { switch (m_token.m_type) { @@ -2198,43 +4612,48 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress } } lastOperator = m_token.m_type; - m_nonLHSCount++; + 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); if (!expr) { if (lastOperator) failWithMessage("Cannot parse subexpression of ", operatorString(true, lastOperator), "operator"); failWithMessage("Cannot parse member expression"); } + if (UNLIKELY(lastOperator && context.isNewTarget(expr))) + internalFailWithMessage(false, "new.target can't come after a prefix operator"); 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; } - failIfTrueIfStrict(isEvalOrArguments && modifiesExpr, "Cannot modify '", m_lastIdentifier->impl(), "' 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++; + if (UNLIKELY(context.isNewTarget(expr))) + internalFailWithMessage(false, "new.target can't come before a postfix operator"); + m_parserState.nonTrivialExpressionCount++; + m_parserState.nonLHSCount++; expr = context.makePostfixNode(location, expr, OpPlusPlus, subExprStart, lastTokenEndPosition(), tokenEndPosition()); - m_assignmentCount++; - failIfTrueIfStrict(isEvalOrArguments, "Cannot modify '", m_lastIdentifier->impl(), "' in strict mode"); + 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++; + if (UNLIKELY(context.isNewTarget(expr))) + internalFailWithMessage(false, "new.target can't come before a postfix operator"); + m_parserState.nonTrivialExpressionCount++; + m_parserState.nonLHSCount++; expr = context.makePostfixNode(location, expr, OpMinusMinus, subExprStart, lastTokenEndPosition(), tokenEndPosition()); - m_assignmentCount++; - failIfTrueIfStrict(isEvalOrArguments, "'", m_lastIdentifier->impl(), "' cannot be modified in strict mode"); + 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(); @@ -2248,8 +4667,6 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress if (!TreeBuilder::CreatesAST && (m_syntaxAlreadyValidated || !strictMode())) return expr; - location = tokenLocation(); - location.line = m_lexer->lastLineNumber(); while (tokenStackDepth) { switch (context.unaryTokenStackLastType(tokenStackDepth)) { case EXCLAMATION: @@ -2267,12 +4684,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); @@ -2281,7 +4698,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress expr = context.createVoid(location, expr); break; case DELETETOKEN: - failIfTrueIfStrict(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: @@ -2294,7 +4711,6 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress return expr; } - template <typename LexerType> void Parser<LexerType>::printUnexpectedTokenText(WTF::PrintStream& out) { switch (m_token.m_type) { @@ -2323,7 +4739,7 @@ template <typename LexerType> void Parser<LexerType>::printUnexpectedTokenText(W case INVALID_NUMERIC_LITERAL_ERRORTOK: out.print("Invalid numeric literal: '", getToken(), "'"); return; - case INVALID_OCTAL_NUMBER_ERRORTOK: + case UNTERMINATED_OCTAL_NUMBER_ERRORTOK: out.print("Invalid use of octal: '", getToken(), "'"); return; case INVALID_STRING_LITERAL_ERRORTOK: @@ -2335,7 +4751,8 @@ template <typename LexerType> void Parser<LexerType>::printUnexpectedTokenText(W case STRING: out.print("Unexpected string literal ", getToken()); return; - case NUMBER: + case INTEGER: + case DOUBLE: out.print("Unexpected number '", getToken(), "'"); return; @@ -2346,7 +4763,13 @@ template <typename LexerType> void Parser<LexerType>::printUnexpectedTokenText(W case RESERVED: out.print("Unexpected use of reserved word '", getToken(), "'"); return; + + case INVALID_PRIVATE_NAME_ERRORTOK: + out.print("Invalid private name '", getToken(), "'"); + return; + case ASYNC: + case AWAIT: case IDENT: out.print("Unexpected identifier '", getToken(), "'"); return; diff --git a/Source/JavaScriptCore/parser/Parser.h b/Source/JavaScriptCore/parser/Parser.h index b698ef936..d8f92c00f 100644 --- a/Source/JavaScriptCore/parser/Parser.h +++ b/Source/JavaScriptCore/parser/Parser.h @@ -20,44 +20,34 @@ * */ -#ifndef Parser_h -#define Parser_h +#pragma once -#include "Debugger.h" +#include "ExecutableInfo.h" #include "ExceptionHelpers.h" -#include "Executable.h" -#include "JSGlobalObject.h" #include "Lexer.h" +#include "ModuleScopeData.h" #include "Nodes.h" #include "ParserArena.h" #include "ParserError.h" +#include "ParserFunctionInfo.h" #include "ParserTokens.h" #include "SourceProvider.h" #include "SourceProviderCache.h" #include "SourceProviderCacheItem.h" +#include "VariableEnvironment.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> -#include <wtf/OwnPtr.h> #include <wtf/RefPtr.h> -namespace JSC { -struct Scope; -} - -namespace WTF { -template <> struct VectorTraits<JSC::Scope> : SimpleClassVectorTraits { - static const bool canInitializeWithMemset = false; // Not all Scope data members initialize to 0. -}; -} namespace JSC { -class ExecState; -class FunctionBodyNode; +class FunctionMetadataNode; class FunctionParameters; class Identifier; class VM; -class ProgramNode; class SourceCode; +class SyntaxChecker; +struct DebuggerParseData; // Macros to make the more common TreeBuilder types a little less verbose #define TreeStatement typename TreeBuilder::Statement @@ -66,93 +56,168 @@ 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 TreeDeconstructionPattern typename TreeBuilder::DeconstructionPattern +#define TreeDestructuringPattern typename TreeBuilder::DestructuringPattern COMPILE_ASSERT(LastUntaggedToken < 64, LessThan64UntaggedTokens); enum SourceElementsMode { CheckForStrictMode, DontCheckForStrictMode }; -enum FunctionRequirements { FunctionNoRequirements, FunctionNeedsName }; -enum FunctionParseMode { FunctionMode, GetterMode, SetterMode }; -enum DeconstructionKind { - DeconstructToVariables, - DeconstructToParameters, - DeconstructToExpressions +enum FunctionBodyType { ArrowFunctionBodyExpression, ArrowFunctionBodyBlock, StandardFunctionBodyBlock }; +enum class FunctionNameRequirements { None, Named, Unnamed }; + +enum class DestructuringKind { + DestructureToVariables, + DestructureToLet, + DestructureToConst, + DestructureToCatchParameters, + DestructureToParameters, + DestructureToExpressions }; -template <typename T> inline bool isEvalNode() { return false; } -template <> inline bool isEvalNode<EvalNode>() { return true; } +enum class DeclarationType { + VarDeclaration, + LetDeclaration, + ConstDeclaration +}; -struct DepthManager { - DepthManager(int* depth) - : m_originalDepth(*depth) - , m_depth(depth) - { - } +enum class DeclarationImportType { + Imported, + ImportedNamespace, + NotImported +}; - ~DepthManager() - { - *m_depth = m_originalDepth; - } +enum DeclarationResult { + Valid = 0, + InvalidStrictMode = 1 << 0, + InvalidDuplicateDeclaration = 1 << 1 +}; -private: - int m_originalDepth; - int* m_depth; +typedef uint8_t DeclarationResultMask; + +enum class DeclarationDefaultContext { + Standard, + ExportDefault, }; -struct ScopeLabelInfo { - ScopeLabelInfo(StringImpl* ident, bool isLoop) - : m_ident(ident) - , m_isLoop(isLoop) - { - } +template <typename T> inline bool isEvalNode() { return false; } +template <> inline bool isEvalNode<EvalNode>() { return true; } - StringImpl* m_ident; - bool m_isLoop; +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; +} +// _Any_ContextualKeyword includes keywords such as "let" or "yield", which have a specific meaning depending on the current parse mode +// or strict mode. These helpers allow to treat all contextual keywords as identifiers as required. +ALWAYS_INLINE static bool isAnyContextualKeyword(const JSToken& token) +{ + return token.m_type >= FirstContextualKeywordToken && token.m_type <= LastContextualKeywordToken; +} +ALWAYS_INLINE static bool isIdentifierOrAnyContextualKeyword(const JSToken& token) +{ + return token.m_type == IDENT || isAnyContextualKeyword(token); +} +// _Safe_ContextualKeyword includes only contextual keywords which can be treated as identifiers independently from parse mode. The exeption +// to this rule is `await`, but matchSpecIdentifier() always treats it as an identifier regardless. +ALWAYS_INLINE static bool isSafeContextualKeyword(const JSToken& token) +{ + return token.m_type >= FirstSafeContextualKeywordToken && token.m_type <= LastSafeContextualKeywordToken; +} + struct Scope { - Scope(const VM* vm, bool isFunction, bool strictMode) + WTF_MAKE_NONCOPYABLE(Scope); + +public: + Scope(const VM* vm, bool isFunction, bool isGenerator, bool strictMode, bool isArrowFunction, bool isAsyncFunction) : 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_isGeneratorBoundary(false) + , m_isArrowFunction(isArrowFunction) + , m_isArrowFunctionBoundary(false) + , m_isAsyncFunction(isAsyncFunction) + , m_isAsyncFunctionBoundary(false) + , m_isLexicalScope(false) , m_isFunctionBoundary(false) , m_isValidStrictMode(true) + , m_hasArguments(false) + , m_isEvalContext(false) + , m_hasNonSimpleParameterList(false) + , m_evalContextType(EvalContextType::None) + , m_constructorKind(static_cast<unsigned>(ConstructorKind::None)) + , m_expectedSuperBinding(static_cast<unsigned>(SuperBinding::NotNeeded)) , m_loopDepth(0) , m_switchDepth(0) + , m_innerArrowFunctionFeatures(0) { - } - - Scope(const Scope& rhs) - : m_vm(rhs.m_vm) - , m_shadowsArguments(rhs.m_shadowsArguments) - , m_usesEval(rhs.m_usesEval) - , m_needsFullActivation(rhs.m_needsFullActivation) - , m_allowsNewDecls(rhs.m_allowsNewDecls) - , m_strictMode(rhs.m_strictMode) - , m_isFunction(rhs.m_isFunction) - , m_isFunctionBoundary(rhs.m_isFunctionBoundary) - , m_isValidStrictMode(rhs.m_isValidStrictMode) - , m_loopDepth(rhs.m_loopDepth) - , m_switchDepth(rhs.m_switchDepth) + m_usedVariables.append(UniquedStringImplPtrSet()); + } + + Scope(Scope&& other) + : m_vm(other.m_vm) + , m_shadowsArguments(other.m_shadowsArguments) + , m_usesEval(other.m_usesEval) + , m_needsFullActivation(other.m_needsFullActivation) + , m_hasDirectSuper(other.m_hasDirectSuper) + , m_needsSuperBinding(other.m_needsSuperBinding) + , m_allowsVarDeclarations(other.m_allowsVarDeclarations) + , m_allowsLexicalDeclarations(other.m_allowsLexicalDeclarations) + , m_strictMode(other.m_strictMode) + , m_isFunction(other.m_isFunction) + , m_isGenerator(other.m_isGenerator) + , m_isGeneratorBoundary(other.m_isGeneratorBoundary) + , m_isArrowFunction(other.m_isArrowFunction) + , m_isArrowFunctionBoundary(other.m_isArrowFunctionBoundary) + , m_isAsyncFunction(other.m_isAsyncFunction) + , m_isAsyncFunctionBoundary(other.m_isAsyncFunctionBoundary) + , m_isLexicalScope(other.m_isLexicalScope) + , m_isFunctionBoundary(other.m_isFunctionBoundary) + , m_isValidStrictMode(other.m_isValidStrictMode) + , m_hasArguments(other.m_hasArguments) + , m_isEvalContext(other.m_isEvalContext) + , m_hasNonSimpleParameterList(other.m_hasNonSimpleParameterList) + , m_constructorKind(other.m_constructorKind) + , m_expectedSuperBinding(other.m_expectedSuperBinding) + , m_loopDepth(other.m_loopDepth) + , m_switchDepth(other.m_switchDepth) + , m_innerArrowFunctionFeatures(other.m_innerArrowFunctionFeatures) + , m_labels(WTFMove(other.m_labels)) + , m_declaredParameters(WTFMove(other.m_declaredParameters)) + , m_declaredVariables(WTFMove(other.m_declaredVariables)) + , m_lexicalVariables(WTFMove(other.m_lexicalVariables)) + , m_usedVariables(WTFMove(other.m_usedVariables)) + , m_closedVariableCandidates(WTFMove(other.m_closedVariableCandidates)) + , m_functionDeclarations(WTFMove(other.m_functionDeclarations)) { - if (rhs.m_labels) { - m_labels = adoptPtr(new 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)); - } } void startSwitch() { m_switchDepth++; } @@ -166,8 +231,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() @@ -182,162 +247,458 @@ 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::AsyncArrowFunctionBodyMode: + setIsAsyncArrowFunctionBody(); + break; + + case SourceParseMode::AsyncFunctionBodyMode: + setIsAsyncFunctionBody(); + break; + + 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::AsyncFunctionMode: + case SourceParseMode::AsyncMethodMode: + setIsAsyncFunction(); + break; + + case SourceParseMode::AsyncArrowFunctionMode: + setIsAsyncArrowFunction(); + break; + + case SourceParseMode::ProgramMode: + case SourceParseMode::ModuleAnalyzeMode: + case SourceParseMode::ModuleEvaluateMode: + break; + } + } + + bool isFunction() const { return m_isFunction; } + bool isFunctionBoundary() const { return m_isFunctionBoundary; } + bool isGenerator() const { return m_isGenerator; } + bool isGeneratorBoundary() const { return m_isGeneratorBoundary; } + bool isAsyncFunction() const { return m_isAsyncFunction; } + bool isAsyncFunctionBoundary() const { return m_isAsyncFunctionBoundary; } + + bool hasArguments() const { return m_hasArguments; } + + void setIsLexicalScope() + { + m_isLexicalScope = true; + m_allowsLexicalDeclarations = true; + } + bool isLexicalScope() { return m_isLexicalScope; } + bool usesEval() { return m_usesEval; } + + const HashSet<UniquedStringImpl*>& 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; + } + + void computeLexicallyCapturedVariablesAndPurgeCandidates() + { + // Because variables may be defined at any time in the range of a lexical scope, we must + // track lexical variables that might be captured. Then, when we're preparing to pop the top + // lexical scope off the stack, we should find which variables are truly captured, and which + // variable still may be captured in a parent scope. + if (m_lexicalVariables.size() && m_closedVariableCandidates.size()) { + for (UniquedStringImpl* impl : m_closedVariableCandidates) + m_lexicalVariables.markVariableAsCapturedIfDefined(impl); + } + + // 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.get()); + } + } } - bool isFunction() { return m_isFunction; } - bool isFunctionBoundary() { return m_isFunctionBoundary; } - void declareCallee(const Identifier* ident) + DeclarationResultMask declareCallee(const Identifier* ident) { - m_declaredVariables.add(ident->string().impl()); + 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; } - bool declareVariable(const Identifier* ident) + DeclarationResultMask declareVariable(const Identifier* ident) { - bool isValidStrictMode = m_vm->propertyNames->eval != *ident && m_vm->propertyNames->arguments != *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; } - bool hasDeclaredVariable(const Identifier& ident) + DeclarationResultMask declareFunction(const Identifier* ident, bool declareAsVar, bool isSloppyModeHoistingCandidate) { - return m_declaredVariables.contains(ident.impl()); + ASSERT(m_allowsVarDeclarations || m_allowsLexicalDeclarations); + DeclarationResultMask result = DeclarationResult::Valid; + bool isValidStrictMode = !isEvalOrArgumentsIdentifier(m_vm, ident); + if (!isValidStrictMode) + result |= DeclarationResult::InvalidStrictMode; + m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode; + auto addResult = declareAsVar ? m_declaredVariables.add(ident->impl()) : m_lexicalVariables.add(ident->impl()); + if (isSloppyModeHoistingCandidate) + addResult.iterator->value.setIsSloppyModeHoistingCandidate(); + if (declareAsVar) { + addResult.iterator->value.setIsVar(); + if (m_lexicalVariables.contains(ident->impl())) + result |= DeclarationResult::InvalidDuplicateDeclaration; + } else { + addResult.iterator->value.setIsLet(); + ASSERT_WITH_MESSAGE(!m_declaredVariables.size(), "We should only declare a function as a lexically scoped variable in scopes where var declarations aren't allowed. I.e, in strict mode and not at the top-level scope of a function or program."); + if (!addResult.isNewEntry) { + if (!isSloppyModeHoistingCandidate || !addResult.iterator->value.isFunction()) + result |= DeclarationResult::InvalidDuplicateDeclaration; + } + } + + addResult.iterator->value.setIsFunction(); + + return result; } - - bool hasDeclaredParameter(const Identifier& ident) + + void addSloppyModeHoistableFunctionCandidate(const Identifier* ident) { - return m_declaredParameters.contains(ident.impl()) || m_declaredVariables.contains(ident.impl()); + ASSERT(m_allowsVarDeclarations); + m_sloppyModeHoistableFunctionCandidates.add(ident->impl()); + } + + void appendFunction(FunctionMetadataNode* node) + { + ASSERT(node); + m_functionDeclarations.append(node); } + DeclarationStacks::FunctionStack&& takeFunctionDeclarations() { return WTFMove(m_functionDeclarations); } - void declareWrite(const Identifier* ident) + + DeclarationResultMask declareLexicalVariable(const Identifier* ident, bool isConstant, DeclarationImportType importType = DeclarationImportType::NotImported) { - ASSERT(m_strictMode); - m_writtenVariables.add(ident->impl()); + 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; } - void preventNewDecls() { m_allowsNewDecls = false; } - bool allowsNewDecls() const { return m_allowsNewDecls; } + bool hasDeclaredVariable(const Identifier& ident) + { + return hasDeclaredVariable(ident.impl()); + } - bool declareParameter(const Identifier* ident) + bool hasDeclaredVariable(const RefPtr<UniquedStringImpl>& ident) { - bool isArguments = m_vm->propertyNames->arguments == *ident; - bool isValidStrictMode = m_declaredVariables.add(ident->string().impl()).isNewEntry && m_vm->propertyNames->eval != *ident && !isArguments; - m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode; - m_declaredParameters.add(ident->string().impl()); + 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". + } - if (isArguments) - m_shadowsArguments = true; - return isValidStrictMode; + bool hasLexicallyDeclaredVariable(const RefPtr<UniquedStringImpl>& ident) const + { + return m_lexicalVariables.contains(ident.get()); } - enum BindingResult { - BindingFailed, - StrictBindingFailed, - BindingSucceeded - }; - BindingResult declareBoundParameter(const Identifier* ident) + ALWAYS_INLINE bool hasDeclaredParameter(const Identifier& ident) { - bool isArguments = m_vm->propertyNames->arguments == *ident; - bool newEntry = m_declaredVariables.add(ident->string().impl()).isNewEntry; - bool isValidStrictMode = newEntry && m_vm->propertyNames->eval != *ident && !isArguments; - m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode; + return hasDeclaredParameter(ident.impl()); + } + + bool hasDeclaredParameter(const RefPtr<UniquedStringImpl>& ident) + { + return m_declaredParameters.contains(ident.get()) || hasDeclaredVariable(ident); + } - if (isArguments) - m_shadowsArguments = true; - if (!newEntry) - return BindingFailed; - return isValidStrictMode ? BindingSucceeded : StrictBindingFailed; + void preventAllVariableDeclarations() + { + m_allowsVarDeclarations = false; + m_allowsLexicalDeclarations = false; } + void preventVarDeclarations() { m_allowsVarDeclarations = false; } + bool allowsVarDeclarations() const { return m_allowsVarDeclarations; } + bool allowsLexicalDeclarations() const { return m_allowsLexicalDeclarations; } + DeclarationResultMask declareParameter(const Identifier* ident) + { + ASSERT(m_allowsVarDeclarations); + DeclarationResultMask result = DeclarationResult::Valid; + bool isArgumentsIdent = isArguments(m_vm, ident); + auto addResult = m_declaredVariables.add(ident->impl()); + bool isValidStrictMode = (addResult.isNewEntry || !addResult.iterator->value.isParameter()) + && m_vm->propertyNames->eval != *ident && !isArgumentsIdent; + addResult.iterator->value.clearIsVar(); + addResult.iterator->value.setIsParameter(); + m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode; + m_declaredParameters.add(ident->impl()); + if (!isValidStrictMode) + result |= DeclarationResult::InvalidStrictMode; + if (isArgumentsIdent) + m_shadowsArguments = true; + if (!addResult.isNewEntry) + result |= DeclarationResult::InvalidDuplicateDeclaration; + return result; + } + + bool usedVariablesContains(UniquedStringImpl* impl) const + { + for (const UniquedStringImplPtrSet& set : m_usedVariables) { + if (set.contains(impl)) + return true; + } + return false; + } + template <typename Func> + void forEachUsedVariable(const Func& func) + { + for (const UniquedStringImplPtrSet& set : m_usedVariables) { + for (UniquedStringImpl* impl : set) + func(impl); + } + } void useVariable(const Identifier* ident, bool isEval) { + useVariable(ident->impl(), isEval); + } + void useVariable(UniquedStringImpl* impl, bool isEval) + { m_usesEval |= isEval; - m_usedVariables.add(ident->string().impl()); + m_usedVariables.last().add(impl); } + void pushUsedVariableSet() { m_usedVariables.append(UniquedStringImplPtrSet()); } + size_t currentUsedVariablesSize() { return m_usedVariables.size(); } + + void revertToPreviousUsedVariables(size_t size) { m_usedVariables.resize(size); } + void setNeedsFullActivation() { m_needsFullActivation = true; } + bool needsFullActivation() const { return m_needsFullActivation; } + bool isArrowFunctionBoundary() { return m_isArrowFunctionBoundary; } + bool isArrowFunction() { return m_isArrowFunction; } + + bool hasDirectSuper() const { return m_hasDirectSuper; } + bool setHasDirectSuper() { return std::exchange(m_hasDirectSuper, true); } + + bool needsSuperBinding() const { return m_needsSuperBinding; } + bool setNeedsSuperBinding() { return std::exchange(m_needsSuperBinding, true); } + + void setEvalContextType(EvalContextType evalContextType) { m_evalContextType = evalContextType; } + EvalContextType evalContextType() { return m_evalContextType; } + + InnerArrowFunctionCodeFeatures innerArrowFunctionFeatures() { return m_innerArrowFunctionFeatures; } + + 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 setInnerArrowFunctionUsesSuperCall() { m_innerArrowFunctionFeatures |= SuperCallInnerArrowFunctionFeature; } + void setInnerArrowFunctionUsesSuperProperty() { m_innerArrowFunctionFeatures |= SuperPropertyInnerArrowFunctionFeature; } + void setInnerArrowFunctionUsesEval() { m_innerArrowFunctionFeatures |= EvalInnerArrowFunctionFeature; } + void setInnerArrowFunctionUsesThis() { m_innerArrowFunctionFeatures |= ThisInnerArrowFunctionFeature; } + void setInnerArrowFunctionUsesNewTarget() { m_innerArrowFunctionFeatures |= NewTargetInnerArrowFunctionFeature; } + void setInnerArrowFunctionUsesArguments() { m_innerArrowFunctionFeatures |= ArgumentsInnerArrowFunctionFeature; } + + bool isEvalContext() const { return m_isEvalContext; } + void setIsEvalContext(bool isEvalContext) { m_isEvalContext = isEvalContext; } - bool collectFreeVariables(Scope* nestedScope, bool shouldTrackClosedVariables) + void setInnerArrowFunctionUsesEvalAndUseArgumentsIfNeeded() + { + ASSERT(m_isArrowFunction); + + if (m_usesEval) + setInnerArrowFunctionUsesEval(); + + if (usedVariablesContains(m_vm->propertyNames->arguments.impl())) + setInnerArrowFunctionUsesArguments(); + } + + void addClosedVariableCandidateUnconditionally(UniquedStringImpl* impl) + { + m_closedVariableCandidates.add(impl); + } + + 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); - } - 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)) - continue; - m_writtenVariables.add(*ptr); + + { + UniquedStringImplPtrSet& destinationSet = m_usedVariables.last(); + for (const UniquedStringImplPtrSet& usedVariablesSet : nestedScope->m_usedVariables) { + for (UniquedStringImpl* impl : usedVariablesSet) { + 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->isArrowFunctionBoundary()) + continue; + + destinationSet.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()) { + auto end = nestedScope->m_closedVariableCandidates.end(); + auto begin = nestedScope->m_closedVariableCandidates.begin(); + m_closedVariableCandidates.add(begin, end); + } + } + + void mergeInnerArrowFunctionFeatures(InnerArrowFunctionCodeFeatures arrowFunctionCodeFeatures) + { + m_innerArrowFunctionFeatures = m_innerArrowFunctionFeatures | arrowFunctionCodeFeatures; + } + + void getSloppyModeHoistedFunctions(UniquedStringImplPtrSet& sloppyModeHoistedFunctions) + { + for (UniquedStringImpl* function : m_sloppyModeHoistableFunctionCandidates) { + // ES6 Annex B.3.3. The only time we can't hoist a function is if a syntax error would + // be caused by declaring a var with that function's name or if we have a parameter with + // that function's name. Note that we would only cause a syntax error if we had a let/const/class + // variable with the same name. + if (!m_lexicalVariables.contains(function)) { + auto iter = m_declaredVariables.find(function); + bool isParameter = iter != m_declaredVariables.end() && iter->value.isParameter(); + if (!isParameter) { + auto addResult = m_declaredVariables.add(function); + addResult.iterator->value.setIsVar(); + sloppyModeHoistedFunctions.add(function); + } } } - - return true; } - void getCapturedVariables(IdentifierSet& capturedVariables, bool& modifiedParameter) + void getCapturedVars(IdentifierSet& capturedVariables) { if (m_needsFullActivation || m_usesEval) { - modifiedParameter = true; - capturedVariables.swap(m_declaredVariables); + 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 (UniquedStringImpl* impl : m_closedVariableCandidates) { + // We refer to m_declaredVariables here directly instead of a hasDeclaredVariable because we want to mark the callee as captured. + if (!m_declaredVariables.contains(impl)) continue; - capturedVariables.add(*ptr); - } - modifiedParameter = false; - if (m_declaredParameters.size()) { - IdentifierSet::iterator end = m_writtenVariables.end(); - for (IdentifierSet::iterator ptr = m_writtenVariables.begin(); ptr != end; ++ptr) { - if (!m_declaredParameters.contains(*ptr)) - continue; - modifiedParameter = true; - break; - } + capturedVariables.add(impl); } } void setStrictMode() { m_strictMode = true; } bool strictMode() const { return m_strictMode; } bool isValidStrictMode() const { return m_isValidStrictMode; } bool shadowsArguments() const { return m_shadowsArguments; } + void setHasNonSimpleParameterList() + { + m_isValidStrictMode = false; + m_hasNonSimpleParameterList = true; + } + bool hasNonSimpleParameterList() const { return m_hasNonSimpleParameterList; } - void copyCapturedVariablesToVector(const IdentifierSet& capturedVariables, Vector<RefPtr<StringImpl>>& vector) + void copyCapturedVariablesToVector(const UniquedStringImplPtrSet& usedVariables, Vector<UniquedStringImpl*, 8>& vector) { - IdentifierSet::iterator end = capturedVariables.end(); - for (IdentifierSet::iterator it = capturedVariables.begin(); it != end; ++it) { - if (m_declaredVariables.contains(*it)) + for (UniquedStringImpl* impl : usedVariables) { + if (m_declaredVariables.contains(impl) || m_lexicalVariables.contains(impl)) continue; - vector.append(*it); + vector.append(impl); } } - void fillParametersForSourceProviderCache(SourceProviderCacheItemCreationParameters& parameters) + void fillParametersForSourceProviderCache(SourceProviderCacheItemCreationParameters& parameters, const UniquedStringImplPtrSet& capturesFromParameterExpressions) { ASSERT(m_isFunction); parameters.usesEval = m_usesEval; parameters.strictMode = m_strictMode; parameters.needsFullActivation = m_needsFullActivation; - copyCapturedVariablesToVector(m_writtenVariables, parameters.writtenVariables); - copyCapturedVariablesToVector(m_usedVariables, parameters.usedVariables); + parameters.innerArrowFunctionFeatures = m_innerArrowFunctionFeatures; + parameters.needsSuperBinding = m_needsSuperBinding; + for (const UniquedStringImplPtrSet& set : m_usedVariables) + copyCapturedVariablesToVector(set, parameters.usedVariables); + + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=156962 + // We add these unconditionally because we currently don't keep a separate + // declaration scope for a function's parameters and its var/let/const declarations. + // This is somewhat unfortunate and we should refactor to do this at some point + // because parameters logically form a parent scope to var/let/const variables. + // But because we don't do this, we must grab capture candidates from a parameter + // list before we parse the body of a function because the body's declarations + // might make us believe something isn't actually a capture candidate when it really + // is. + for (UniquedStringImpl* impl : capturesFromParameterExpressions) + parameters.usedVariables.append(impl); } void restoreFromSourceProviderCache(const SourceProviderCacheItem* info) @@ -345,33 +706,116 @@ struct Scope { ASSERT(m_isFunction); m_usesEval = info->usesEval; m_strictMode = info->strictMode; + m_innerArrowFunctionFeatures = info->innerArrowFunctionFeatures; m_needsFullActivation = info->needsFullActivation; + m_needsSuperBinding = info->needsSuperBinding; + UniquedStringImplPtrSet& destSet = m_usedVariables.last(); for (unsigned i = 0; i < info->usedVariablesCount; ++i) - m_usedVariables.add(info->usedVariables()[i]); - for (unsigned i = 0; i < info->writtenVariablesCount; ++i) - m_writtenVariables.add(info->writtenVariables()[i]); + destSet.add(info->usedVariables()[i]); } + class MaybeParseAsGeneratorForScope; + private: + void setIsFunction() + { + m_isFunction = true; + m_isFunctionBoundary = true; + m_hasArguments = true; + setIsLexicalScope(); + m_isGenerator = false; + m_isGeneratorBoundary = false; + m_isArrowFunctionBoundary = false; + m_isArrowFunction = false; + } + + void setIsGeneratorFunction() + { + setIsFunction(); + m_isGenerator = true; + } + + void setIsGenerator() + { + setIsFunction(); + m_isGenerator = true; + m_isGeneratorBoundary = true; + m_hasArguments = false; + } + + void setIsArrowFunction() + { + setIsFunction(); + m_isArrowFunctionBoundary = true; + m_isArrowFunction = true; + } + + void setIsAsyncArrowFunction() + { + setIsArrowFunction(); + m_isAsyncFunction = true; + } + + void setIsAsyncFunction() + { + setIsFunction(); + m_isAsyncFunction = true; + } + + void setIsAsyncFunctionBody() + { + setIsFunction(); + m_hasArguments = false; + m_isAsyncFunction = true; + m_isAsyncFunctionBoundary = true; + } + + void setIsAsyncArrowFunctionBody() + { + setIsArrowFunction(); + m_hasArguments = false; + m_isAsyncFunction = true; + m_isAsyncFunctionBoundary = true; + } + const VM* m_vm; - bool m_shadowsArguments : 1; - bool m_usesEval : 1; - bool m_needsFullActivation : 1; - bool m_allowsNewDecls : 1; - bool m_strictMode : 1; - bool m_isFunction : 1; - bool m_isFunctionBoundary : 1; - bool m_isValidStrictMode : 1; + bool m_shadowsArguments; + bool m_usesEval; + bool m_needsFullActivation; + bool m_hasDirectSuper; + bool m_needsSuperBinding; + bool m_allowsVarDeclarations; + bool m_allowsLexicalDeclarations; + bool m_strictMode; + bool m_isFunction; + bool m_isGenerator; + bool m_isGeneratorBoundary; + bool m_isArrowFunction; + bool m_isArrowFunctionBoundary; + bool m_isAsyncFunction; + bool m_isAsyncFunctionBoundary; + bool m_isLexicalScope; + bool m_isFunctionBoundary; + bool m_isValidStrictMode; + bool m_hasArguments; + bool m_isEvalContext; + bool m_hasNonSimpleParameterList; + EvalContextType m_evalContextType; + unsigned m_constructorKind; + unsigned m_expectedSuperBinding; int m_loopDepth; int m_switchDepth; + InnerArrowFunctionCodeFeatures m_innerArrowFunctionFeatures; typedef Vector<ScopeLabelInfo, 2> LabelStack; - OwnPtr<LabelStack> m_labels; - IdentifierSet m_declaredParameters; - IdentifierSet m_declaredVariables; - IdentifierSet m_usedVariables; - IdentifierSet m_closedVariables; - IdentifierSet m_writtenVariables; + std::unique_ptr<LabelStack> m_labels; + UniquedStringImplPtrSet m_declaredParameters; + VariableEnvironment m_declaredVariables; + VariableEnvironment m_lexicalVariables; + Vector<UniquedStringImplPtrSet, 6> m_usedVariables; + UniquedStringImplPtrSet m_sloppyModeHoistableFunctionCandidates; + HashSet<UniquedStringImpl*> m_closedVariableCandidates; + DeclarationStacks::FunctionStack m_functionDeclarations; }; typedef Vector<Scope, 10> ScopeStack; @@ -396,24 +840,41 @@ struct ScopeRef { return ScopeRef(m_scopeStack, m_index - 1); } + bool operator==(const ScopeRef& other) + { + ASSERT(other.m_scopeStack == m_scopeStack); + return m_index == other.m_index; + } + + bool operator!=(const ScopeRef& other) + { + return !(*this == other); + } + private: ScopeStack* m_scopeStack; 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, JSParserScriptMode, SourceParseMode, SuperBinding, ConstructorKind defaultConstructorKind = ConstructorKind::None, DerivedContextType = DerivedContextType::None, bool isEvalContext = false, EvalContextType = EvalContextType::None, DebuggerParseData* = nullptr); ~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 { @@ -453,82 +914,362 @@ 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 = 0, + ErrorIndicatesPattern, + ErrorIndicatesAsyncArrowFunction + }; + + 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 forceClassifyExpressionError(ExpressionErrorClass classification) + { + m_class = classification; + } + + void reclassifyExpressionError(ExpressionErrorClass oldClassification, ExpressionErrorClass classification) + { + if (m_class != oldClassification) + return; + m_class = classification; + } + + void propagateExpressionErrorClass() + { + if (m_previous) + m_previous->m_class = m_class; + } + + bool indicatesPossiblePattern() const { return m_class == ErrorIndicatesPattern; } + bool indicatesPossibleAsyncArrowFunction() const { return m_class == ErrorIndicatesAsyncArrowFunction; } + + 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 forceClassifyExpressionError(ExpressionErrorClass classification) + { + if (m_expressionErrorClassifier) + m_expressionErrorClassifier->forceClassifyExpressionError(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 const char* declarationTypeToVariableKind(DeclarationType type) + { + switch (type) { + case DeclarationType::VarDeclaration: + return "variable name"; + case DeclarationType::LetDeclaration: + case DeclarationType::ConstDeclaration: + return "lexical variable name"; + } + RELEASE_ASSERT_NOT_REACHED(); + return "invalid"; + } + + 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 upperScope(int n) + { + ASSERT(m_scopeStack.size() >= size_t(1 + n)); + return ScopeRef(&m_scopeStack, m_scopeStack.size() - 1 - n); + } + 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 currentLexicalDeclarationScope() + { + unsigned i = m_scopeStack.size() - 1; + ASSERT(i < m_scopeStack.size()); + while (!m_scopeStack[i].allowsLexicalDeclarations()) { + 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 closestParentOrdinaryFunctionNonLexicalScope() + { + unsigned i = m_scopeStack.size() - 1; + ASSERT(i < m_scopeStack.size() && m_scopeStack.size()); + while (i && (!m_scopeStack[i].isFunctionBoundary() || m_scopeStack[i].isGeneratorBoundary() || m_scopeStack[i].isAsyncFunctionBoundary() || m_scopeStack[i].isArrowFunctionBoundary())) + i--; + // When reaching the top level scope (it can be non ordinary function scope), we return it. + return ScopeRef(&m_scopeStack, i); + } ScopeRef pushScope() { bool isFunction = false; bool isStrict = false; + bool isGenerator = false; + bool isArrowFunction = false; + bool isAsyncFunction = false; if (!m_scopeStack.isEmpty()) { isStrict = m_scopeStack.last().strictMode(); isFunction = m_scopeStack.last().isFunction(); + isGenerator = m_scopeStack.last().isGenerator(); + isArrowFunction = m_scopeStack.last().isArrowFunction(); + isAsyncFunction = m_scopeStack.last().isAsyncFunction(); } - m_scopeStack.append(Scope(m_vm, isFunction, isStrict)); + m_scopeStack.constructAndAppend(m_vm, isFunction, isGenerator, isStrict, isArrowFunction, isAsyncFunction); 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().isArrowFunction()) + m_scopeStack.last().setInnerArrowFunctionUsesEvalAndUseArgumentsIfNeeded(); + + if (!(m_scopeStack.last().isFunctionBoundary() && !m_scopeStack.last().isArrowFunctionBoundary())) + m_scopeStack[m_scopeStack.size() - 2].mergeInnerArrowFunctionFeatures(m_scopeStack.last().innerArrowFunctionFeatures()); + + 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) { - unsigned i = m_scopeStack.size() - 1; - ASSERT(i < m_scopeStack.size()); - while (!m_scopeStack[i].allowsNewDecls()) { - i--; - ASSERT(i < m_scopeStack.size()); + if (type == DeclarationType::VarDeclaration) + return currentVariableScope()->declareVariable(ident); + + ASSERT(type == DeclarationType::LetDeclaration || type == DeclarationType::ConstDeclaration); + // Lexical variables declared at a top level scope that shadow arguments or vars are not allowed. + if (!m_lexer->isReparsingFunction() && m_statementDepth == 1 && (hasDeclaredParameter(*ident) || hasDeclaredVariable(*ident))) + return DeclarationResult::InvalidDuplicateDeclaration; + + return currentLexicalDeclarationScope()->declareLexicalVariable(ident, type == DeclarationType::ConstDeclaration, importType); + } + + std::pair<DeclarationResultMask, ScopeRef> declareFunction(const Identifier* ident) + { + if (m_statementDepth == 1 || (!strictMode() && !currentScope()->isFunction())) { + // Functions declared at the top-most scope (both in sloppy and strict mode) are declared as vars + // for backwards compatibility. This allows us to declare functions with the same name more than once. + // In sloppy mode, we always declare functions as vars. + bool declareAsVar = true; + bool isSloppyModeHoistingCandidate = false; + ScopeRef variableScope = currentVariableScope(); + return std::make_pair(variableScope->declareFunction(ident, declareAsVar, isSloppyModeHoistingCandidate), variableScope); + } + + if (!strictMode()) { + ASSERT(currentScope()->isFunction()); + + // Functions declared inside a function inside a nested block scope in sloppy mode are subject to this + // crazy rule defined inside Annex B.3.3 in the ES6 spec. It basically states that we will create + // the function as a local block scoped variable, but when we evaluate the block that the function is + // contained in, we will assign the function to a "var" variable only if declaring such a "var" wouldn't + // be a syntax error and if there isn't a parameter with the same name. (It would only be a syntax error if + // there are is a let/class/const with the same name). Note that this mean we only do the "var" hoisting + // binding if the block evaluates. For example, this means we wont won't perform the binding if it's inside + // the untaken branch of an if statement. + bool declareAsVar = false; + bool isSloppyModeHoistingCandidate = true; + ScopeRef lexicalVariableScope = currentLexicalDeclarationScope(); + ScopeRef varScope = currentVariableScope(); + varScope->addSloppyModeHoistableFunctionCandidate(ident); + ASSERT(varScope != lexicalVariableScope); + return std::make_pair(lexicalVariableScope->declareFunction(ident, declareAsVar, isSloppyModeHoistingCandidate), lexicalVariableScope); } - return m_scopeStack[i].declareVariable(ident); + + bool declareAsVar = false; + bool isSloppyModeHoistingCandidate = false; + ScopeRef lexicalVariableScope = currentLexicalDeclarationScope(); + return std::make_pair(lexicalVariableScope->declareFunction(ident, declareAsVar, isSloppyModeHoistingCandidate), lexicalVariableScope); } - + NEVER_INLINE bool hasDeclaredVariable(const Identifier& ident) { unsigned i = m_scopeStack.size() - 1; ASSERT(i < m_scopeStack.size()); - while (!m_scopeStack[i].allowsNewDecls()) { + while (!m_scopeStack[i].allowsVarDeclarations()) { i--; ASSERT(i < m_scopeStack.size()); } return m_scopeStack[i].hasDeclaredVariable(ident); } - + NEVER_INLINE bool hasDeclaredParameter(const Identifier& ident) { + // FIXME: hasDeclaredParameter() is not valid during reparsing of generator or async function bodies, because their formal + // parameters are declared in a scope unavailable during reparsing. Note that it is redundant to call this function during + // reparsing anyways, as the function is already guaranteed to be valid by the original parsing. + // https://bugs.webkit.org/show_bug.cgi?id=164087 + ASSERT(!m_lexer->isReparsingFunction()); + unsigned i = m_scopeStack.size() - 1; ASSERT(i < m_scopeStack.size()); - while (!m_scopeStack[i].allowsNewDecls()) { + while (!m_scopeStack[i].allowsVarDeclarations()) { + i--; + ASSERT(i < m_scopeStack.size()); + } + + if (m_scopeStack[i].isGeneratorBoundary() || m_scopeStack[i].isAsyncFunctionBoundary()) { + // The formal parameters which need to be verified for Generators and Async Function bodies occur + // in the outer wrapper function, so pick the outer scope here. i--; ASSERT(i < m_scopeStack.size()); } return m_scopeStack[i].hasDeclaredParameter(ident); } - void declareWrite(const Identifier* ident) + bool exportName(const Identifier& ident) { - if (!m_syntaxAlreadyValidated || strictMode()) - m_scopeStack.last().declareWrite(ident); + ASSERT(currentScope().index() == 0); + ASSERT(m_moduleScopeData); + return m_moduleScopeData->exportName(ident); } - + ScopeStack m_scopeStack; const SourceProviderCacheItem* findCachedFunctionInfo(int openBracePos) @@ -537,14 +1278,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&, UniquedStringImplPtrSet&&, 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) { @@ -580,9 +1320,9 @@ private: } void printUnexpectedTokenText(WTF::PrintStream&); - ALWAYS_INLINE String getToken() { - SourceProvider* sourceProvider = m_source->provider(); - return sourceProvider->getRange(tokenStart(), tokenEndPosition().offset); + ALWAYS_INLINE StringView getToken() + { + return m_lexer->getToken(m_token); } ALWAYS_INLINE bool match(JSTokenType expected) @@ -590,9 +1330,14 @@ private: return m_token.m_type == expected; } - ALWAYS_INLINE bool isofToken() + ALWAYS_INLINE bool matchContextualKeyword(const Identifier& identifier) + { + return m_token.m_type == IDENT && *m_token.m_data.ident == identifier; + } + + ALWAYS_INLINE bool matchIdentifierOrKeyword() { - return m_token.m_type == IDENT && *m_token.m_data.ident == m_vm->propertyNames->of; + return isIdentifierOrKeyword(m_token); } ALWAYS_INLINE unsigned tokenStart() @@ -630,23 +1375,21 @@ private: return m_token.m_location; } - void setErrorMessage(String msg) + void setErrorMessage(const String& message) { - m_errorMessage = msg; + ASSERT_WITH_MESSAGE(!message.isEmpty(), "Attempted to set the empty string as an error message. Likely caused by invalid UTF8 used when creating the message."); + m_errorMessage = message; + if (m_errorMessage.isEmpty()) + m_errorMessage = ASCIILiteral("Unparseable script"); } 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&); + template <typename... Args> + NEVER_INLINE void logError(bool, Args&&...); - 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) @@ -655,16 +1398,32 @@ private: m_errorMessage = String(msg); ASSERT(!m_errorMessage.isNull()); } - + + ALWAYS_INLINE void recordPauseLocation(const JSTextPosition&); + ALWAYS_INLINE void recordFunctionEntryLocation(const JSTextPosition&); + ALWAYS_INLINE void recordFunctionLeaveLocation(const JSTextPosition&); + void startLoop() { currentScope()->startLoop(); } void endLoop() { currentScope()->endLoop(); } void startSwitch() { currentScope()->startSwitch(); } void endSwitch() { currentScope()->endSwitch(); } void setStrictMode() { currentScope()->setStrictMode(); } bool strictMode() { return currentScope()->strictMode(); } - bool isValidStrictMode() { return currentScope()->isValidStrictMode(); } - bool declareParameter(const Identifier* ident) { return currentScope()->declareParameter(ident); } - Scope::BindingResult declareBoundParameter(const Identifier* ident) { return currentScope()->declareBoundParameter(ident); } + bool isValidStrictMode() + { + int i = m_scopeStack.size() - 1; + if (!m_scopeStack[i].isValidStrictMode()) + return false; + + // In the case of Generator or Async function bodies, also check the wrapper function, whose name or + // arguments may be invalid. + if (UNLIKELY((m_scopeStack[i].isGeneratorBoundary() || m_scopeStack[i].isAsyncFunctionBoundary()) && i)) + return m_scopeStack[i - 1].isValidStrictMode(); + return true; + } + DeclarationResultMask declareParameter(const Identifier* ident) { return currentScope()->declareParameter(ident); } + bool declareRestOrNormalParameter(const Identifier&, const Identifier**); + bool breakIsValid() { ScopeRef current = currentScope(); @@ -686,7 +1445,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(); @@ -698,12 +1457,42 @@ private: } return result; } - + + // 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) || isSafeContextualKeyword(m_token); + } + + ALWAYS_INLINE bool matchSpecIdentifier() + { + return match(IDENT) || isLETMaskedAsIDENT() || isYIELDMaskedAsIDENT(currentScope()->isGenerator()) || isSafeContextualKeyword(m_token); + } + template <class TreeBuilder> TreeSourceElements parseSourceElements(TreeBuilder&, SourceElementsMode); + template <class TreeBuilder> TreeSourceElements parseGeneratorFunctionSourceElements(TreeBuilder&, const Identifier& name, SourceElementsMode); + template <class TreeBuilder> TreeSourceElements parseAsyncFunctionSourceElements(TreeBuilder&, SourceParseMode, bool isArrowFunctionBodyExpression, 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, DeclarationDefaultContext = DeclarationDefaultContext::Standard); + template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported, DeclarationDefaultContext = DeclarationDefaultContext::Standard); + template <class TreeBuilder> TreeStatement parseFunctionDeclarationStatement(TreeBuilder&, bool isAsync, bool parentAllowsFunctionDeclarationAsStatement); + template <class TreeBuilder> TreeStatement parseAsyncFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported, DeclarationDefaultContext = DeclarationDefaultContext::Standard); + template <class TreeBuilder> NEVER_INLINE bool maybeParseAsyncFunctionDeclarationStatement(TreeBuilder& context, TreeStatement& result, bool parentAllowsFunctionDeclarationAsStatement); + 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&); @@ -718,31 +1507,69 @@ private: template <class TreeBuilder> TreeStatement parseTryStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseDebuggerStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseExpressionStatement(TreeBuilder&); - template <class TreeBuilder> TreeStatement parseExpressionOrLabelStatement(TreeBuilder&); + template <class TreeBuilder> TreeStatement parseExpressionOrLabelStatement(TreeBuilder&, bool allowFunctionDeclarationAsStatement); template <class TreeBuilder> TreeStatement parseIfStatement(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&); + template <class TreeBuilder> NEVER_INLINE TreeExpression parseAwaitExpression(TreeBuilder&); template <class TreeBuilder> TreeExpression parseMemberExpression(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeExpression parsePrimaryExpression(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseArrayLiteral(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseObjectLiteral(TreeBuilder&); template <class TreeBuilder> NEVER_INLINE TreeExpression parseStrictObjectLiteral(TreeBuilder&); - enum SpreadMode { AllowSpread, DontAllowSpread }; - template <class TreeBuilder> ALWAYS_INLINE TreeArguments parseArguments(TreeBuilder&, SpreadMode); + template <class TreeBuilder> ALWAYS_INLINE TreeClassExpression parseClassExpression(TreeBuilder&); + template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseFunctionExpression(TreeBuilder&); + template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseAsyncFunctionExpression(TreeBuilder&); + template <class TreeBuilder> ALWAYS_INLINE TreeArguments parseArguments(TreeBuilder&); + template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseArgument(TreeBuilder&, ArgumentType&); template <class TreeBuilder> TreeProperty parseProperty(TreeBuilder&, bool strict); - template <class TreeBuilder> ALWAYS_INLINE TreeFunctionBody parseFunctionBody(TreeBuilder&); - template <class TreeBuilder> ALWAYS_INLINE TreeFormalParameterList parseFormalParameters(TreeBuilder&); - template <class TreeBuilder> TreeExpression parseVarDeclarationList(TreeBuilder&, int& declarations, TreeDeconstructionPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd); - template <class TreeBuilder> NEVER_INLINE TreeConstDeclList parseConstDeclarationList(TreeBuilder&); - - template <class TreeBuilder> NEVER_INLINE TreeDeconstructionPattern createBindingPattern(TreeBuilder&, DeconstructionKind, const Identifier&, int depth); - template <class TreeBuilder> NEVER_INLINE TreeDeconstructionPattern parseDeconstructionPattern(TreeBuilder&, DeconstructionKind, int depth = 0); - template <class TreeBuilder> NEVER_INLINE TreeDeconstructionPattern tryParseDeconstructionPatternExpression(TreeBuilder&); - template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionRequirements, FunctionParseMode, bool nameIsInContainingScope, const Identifier*&, TreeFormalParameterList&, TreeFunctionBody&, unsigned& openBraceOffset, unsigned& closeBraceOffset, int& bodyStartLine, unsigned& bodyStartColumn); + template <class TreeBuilder> TreeExpression parsePropertyMethod(TreeBuilder& context, const Identifier* methodName, bool isGenerator, bool isAsyncMethod); + template <class TreeBuilder> TreeProperty parseGetterSetter(TreeBuilder&, bool strict, PropertyNode::Type, unsigned getterOrSetterStartOffset, ConstructorKind, bool isClassProperty); + template <class TreeBuilder> ALWAYS_INLINE TreeFunctionBody parseFunctionBody(TreeBuilder&, SyntaxChecker&, const JSTokenLocation&, int, int functionKeywordStart, int functionNameStart, int parametersStart, ConstructorKind, SuperBinding, FunctionBodyType, unsigned, SourceParseMode); + template <class TreeBuilder> ALWAYS_INLINE bool parseFormalParameters(TreeBuilder&, TreeFormalParameterList, bool isArrowFunction, 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&, bool isAsync); + 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<std::pair<const Identifier*, const Identifier*>>& maybeExportedLocalNames, bool& hasKeywordForLocalBindings); + template <class TreeBuilder> TreeStatement parseExportDeclaration(TreeBuilder&); + + template <class TreeBuilder> ALWAYS_INLINE TreeExpression createResolveAndUseVariable(TreeBuilder&, const Identifier*, bool isEval, const JSTextPosition&, const JSTokenLocation&); + + enum class FunctionDefinitionType { Expression, Declaration, Method }; + template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionNameRequirements, SourceParseMode, bool nameIsInContainingScope, ConstructorKind, SuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>&, FunctionDefinitionType); + + ALWAYS_INLINE bool isArrowFunctionParameters(); + + template <class TreeBuilder, class FunctionInfoType> NEVER_INLINE typename TreeBuilder::FormalParameterList parseFunctionParameters(TreeBuilder&, SourceParseMode, FunctionInfoType&); + template <class TreeBuilder> NEVER_INLINE typename TreeBuilder::FormalParameterList createGeneratorParameters(TreeBuilder&, unsigned& parameterCount); + + template <class TreeBuilder> NEVER_INLINE TreeClassExpression parseClass(TreeBuilder&, FunctionNameRequirements, 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(); @@ -770,82 +1597,179 @@ private: return !m_errorMessage.isNull(); } - struct SavePoint { + bool isDisallowedIdentifierAwait(const JSToken& token) + { + return token.m_type == AWAIT && (!m_parserState.allowAwait || currentScope()->isAsyncFunctionBoundary() || m_scriptMode == JSParserScriptMode::Module); + } + + ALWAYS_INLINE SuperBinding adjustSuperBindingForBaseConstructor(ConstructorKind constructorKind, SuperBinding superBinding, ScopeRef functionScope) + { + return adjustSuperBindingForBaseConstructor(constructorKind, superBinding, functionScope->needsSuperBinding(), functionScope->usesEval(), functionScope->innerArrowFunctionFeatures()); + } + + ALWAYS_INLINE SuperBinding adjustSuperBindingForBaseConstructor(ConstructorKind constructorKind, SuperBinding superBinding, bool scopeNeedsSuperBinding, bool currentScopeUsesEval, InnerArrowFunctionCodeFeatures innerArrowFunctionFeatures) + { + SuperBinding methodSuperBinding = superBinding; + + if (constructorKind == ConstructorKind::Base) { + bool isSuperUsedInInnerArrowFunction = innerArrowFunctionFeatures & SuperPropertyInnerArrowFunctionFeature; + methodSuperBinding = (scopeNeedsSuperBinding || isSuperUsedInInnerArrowFunction || currentScopeUsesEval) ? SuperBinding::Needed : SuperBinding::NotNeeded; + } + + return methodSuperBinding; + } + + const char* disallowedIdentifierAwaitReason() + { + if (!m_parserState.allowAwait || currentScope()->isAsyncFunctionBoundary()) + return "in an async function"; + if (m_scriptMode == JSParserScriptMode::Module) + return "in a module"; + RELEASE_ASSERT_NOT_REACHED(); + return nullptr; + } + + 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 }; + bool allowAwait { true }; + }; + + // 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; }; - - ALWAYS_INLINE SavePoint createSavePoint() + + // 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() { - ASSERT(!hasError()); - SavePoint result; + 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(); + ASSERT(static_cast<unsigned>(result.startOffset) >= result.oldLineStartOffset); return result; } - - ALWAYS_INLINE void restoreSavePoint(const SavePoint& savePoint) + + ALWAYS_INLINE void restoreLexerState(const LexerState& lexerState) { - m_errorMessage = String(); - m_lexer->setOffset(savePoint.startOffset, savePoint.oldLineStartOffset); + // setOffset clears lexer errors. + m_lexer->setOffset(lexerState.startOffset, lexerState.oldLineStartOffset); + m_lexer->setLineNumber(lexerState.oldLineNumber); next(); - m_lexer->setLastLineNumber(savePoint.oldLastLineNumber); - m_lexer->setLineNumber(savePoint.oldLineNumber); + m_lexer->setLastLineNumber(lexerState.oldLastLineNumber); } - struct ParserState { - int assignmentCount; - int nonLHSCount; - int nonTrivialExpressionCount; + struct SavePoint { + ParserState parserState; + LexerState lexerState; }; - ALWAYS_INLINE ParserState saveState() + struct SavePointWithError : public SavePoint { + bool lexerError; + String lexerErrorMessage; + String parserErrorMessage; + }; + + ALWAYS_INLINE void internalSaveState(SavePoint& savePoint) { - ParserState result; - result.assignmentCount = m_assignmentCount; - result.nonLHSCount = m_nonLHSCount; - result.nonTrivialExpressionCount = m_nonTrivialExpressionCount; - return result; + savePoint.parserState = internalSaveParserState(); + savePoint.lexerState = internalSaveLexerState(); } - ALWAYS_INLINE void restoreState(const ParserState& state) + ALWAYS_INLINE SavePointWithError createSavePointForError() { - m_assignmentCount = state.assignmentCount; - m_nonLHSCount = state.nonLHSCount; - m_nonTrivialExpressionCount = state.nonTrivialExpressionCount; - + SavePointWithError savePoint; + internalSaveState(savePoint); + savePoint.lexerError = m_lexer->sawError(); + savePoint.lexerErrorMessage = m_lexer->getErrorMessage(); + savePoint.parserErrorMessage = m_errorMessage; + return savePoint; } + ALWAYS_INLINE SavePoint createSavePoint() + { + ASSERT(!hasError()); + SavePoint savePoint; + internalSaveState(savePoint); + return savePoint; + } + + ALWAYS_INLINE void internalRestoreState(const SavePoint& savePoint) + { + restoreLexerState(savePoint.lexerState); + restoreParserState(savePoint.parserState); + } + + ALWAYS_INLINE void restoreSavePointWithError(const SavePointWithError& savePoint) + { + internalRestoreState(savePoint); + m_lexer->setSawError(savePoint.lexerError); + m_lexer->setErrorMessage(savePoint.lexerErrorMessage); + m_errorMessage = savePoint.parserErrorMessage; + } + + ALWAYS_INLINE void restoreSavePoint(const SavePoint& savePoint) + { + internalRestoreState(savePoint); + m_errorMessage = 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; bool m_hasStackOverflow; String m_errorMessage; JSToken m_token; bool m_allowsIn; JSTextPosition m_lastTokenEndPosition; - int m_assignmentCount; - int m_nonLHSCount; bool m_syntaxAlreadyValidated; int m_statementDepth; - int m_nonTrivialExpressionCount; - const Identifier* m_lastIdentifier; - const Identifier* m_lastFunctionName; RefPtr<SourceProviderCache> m_functionCache; SourceElements* m_sourceElements; - ParserArenaData<DeclarationStacks::VarStack>* m_varDeclarations; - ParserArenaData<DeclarationStacks::FunctionStack>* m_funcDeclarations; - IdentifierSet m_capturedVariables; + bool m_parsingBuiltin; + JSParserScriptMode m_scriptMode; + SuperBinding m_superBinding; + ConstructorKind m_defaultConstructorKind; + VariableEnvironment m_varDeclarations; + DeclarationStacks::FunctionStack m_funcDeclarations; + UniquedStringImplPtrSet m_sloppyModeHoistedFunctions; CodeFeatures m_features; int m_numConstants; - + ExpressionErrorClassifier* m_expressionErrorClassifier; + bool m_isEvalContext; + bool m_immediateParentAllowsFunctionDeclarationInStatement; + RefPtr<ModuleScopeData> m_moduleScopeData; + DebuggerParseData* m_debuggerParseData; + struct DepthManager { DepthManager(int* depth) : m_originalDepth(*depth) @@ -867,13 +1791,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; @@ -881,10 +1805,10 @@ PassRefPtr<ParsedNode> Parser<LexerType>::parse(ParserError& error) errMsg = String(); JSTokenLocation startLocation(tokenLocation()); - ASSERT(m_source->startColumn() > 0); - unsigned startColumn = m_source->startColumn() - 1; + ASSERT(m_source->startColumn() > OrdinalNumber::beforeFirst()); + unsigned startColumn = m_source->startColumn().zeroBasedInt(); - String parseError = parseInner(); + String parseError = parseInner(calleeName, parseMode); int lineNumber = m_lexer->lineNumber(); bool lexError = m_lexer->sawError(); @@ -898,26 +1822,36 @@ 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->lineNumber(); endLocation.lineStartOffset = m_lexer->currentLineStartOffset(); endLocation.startOffset = m_lexer->currentOffset(); unsigned endColumn = endLocation.startOffset - endLocation.lineStartOffset; - result = ParsedNode::create(m_vm, + 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, + WTFMove(m_funcDeclarations), + currentScope()->finalizeLexicalEnvironment(), + WTFMove(m_sloppyModeHoistedFunctions), + m_parameters, *m_source, m_features, - m_numConstants); - result->setLoc(m_source->firstLine(), m_lexer->lineNumber(), m_lexer->currentOffset(), m_lexer->currentLineStartOffset()); + currentScope()->innerArrowFunctionFeatures(), + m_numConstants, + WTFMove(m_moduleScopeData)); + result->setLoc(m_source->firstLine().oneBasedInt(), 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 @@ -925,14 +1859,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); @@ -941,30 +1880,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, JSTextPosition* positionBeforeLastNewline = 0) +std::unique_ptr<ParsedNode> parse( + VM* vm, const SourceCode& source, + const Identifier& name, JSParserBuiltinMode builtinMode, + JSParserStrictMode strictMode, JSParserScriptMode scriptMode, SourceParseMode parseMode, SuperBinding superBinding, + ParserError& error, JSTextPosition* positionBeforeLastNewline = nullptr, + ConstructorKind defaultConstructorKind = ConstructorKind::None, + DerivedContextType derivedContextType = DerivedContextType::None, + EvalContextType evalContextType = EvalContextType::None, + DebuggerParseData* debuggerParseData = nullptr) { - SamplingRegion samplingRegion("Parsing"); - ASSERT(!source.provider()->source().isNull()); if (source.provider()->source().is8Bit()) { - Parser<Lexer<LChar>> parser(vm, source, parameters, name, strictness, parserMode); - RefPtr<ParsedNode> result = parser.parse<ParsedNode>(error); + Parser<Lexer<LChar>> parser(vm, source, builtinMode, strictMode, scriptMode, parseMode, superBinding, defaultConstructorKind, derivedContextType, isEvalNode<ParsedNode>(), evalContextType, debuggerParseData); + std::unique_ptr<ParsedNode> result = parser.parse<ParsedNode>(error, name, parseMode); if (positionBeforeLastNewline) *positionBeforeLastNewline = parser.positionBeforeLastNewline(); - return result.release(); + 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); - RefPtr<ParsedNode> result = 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, scriptMode, parseMode, superBinding, defaultConstructorKind, derivedContextType, isEvalNode<ParsedNode>(), evalContextType, debuggerParseData); + std::unique_ptr<ParsedNode> result = parser.parse<ParsedNode>(error, name, parseMode); if (positionBeforeLastNewline) *positionBeforeLastNewline = parser.positionBeforeLastNewline(); - return result.release(); + return result; } } // namespace -#endif 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 d8bc2cffa..16e065d61 100644 --- a/Source/JavaScriptCore/parser/ParserArena.h +++ b/Source/JavaScriptCore/parser/ParserArena.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ParserArena_h -#define ParserArena_h +#pragma once #include "CommonIdentifiers.h" #include "Identifier.h" @@ -34,7 +33,6 @@ namespace JSC { class ParserArenaDeletable; - class ParserArenaRefCounted; class IdentifierArena { WTF_MAKE_FAST_ALLOCATED; @@ -46,12 +44,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; @@ -76,24 +73,29 @@ namespace JSC { 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) @@ -105,7 +107,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(); } @@ -119,7 +121,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(); } @@ -136,7 +138,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) @@ -159,18 +160,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; } @@ -189,12 +182,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; }; -} - -#endif +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/ParserError.h b/Source/JavaScriptCore/parser/ParserError.h index baa4465d0..7883efed2 100644 --- a/Source/JavaScriptCore/parser/ParserError.h +++ b/Source/JavaScriptCore/parser/ParserError.h @@ -23,17 +23,18 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ParserError_h -#define ParserError_h +#pragma once #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,62 +50,119 @@ 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 -#endif // ParserError_h +namespace WTF { + +inline void printInternal(PrintStream& out, JSC::ParserError::SyntaxErrorType type) +{ + switch (type) { + case JSC::ParserError::SyntaxErrorNone: + out.print("SyntaxErrorNone"); + return; + case JSC::ParserError::SyntaxErrorIrrecoverable: + out.print("SyntaxErrorIrrecoverable"); + return; + case JSC::ParserError::SyntaxErrorUnterminatedLiteral: + out.print("SyntaxErrorUnterminatedLiteral"); + return; + case JSC::ParserError::SyntaxErrorRecoverable: + out.print("SyntaxErrorRecoverable"); + return; + } + + RELEASE_ASSERT_NOT_REACHED(); +} + +inline void printInternal(PrintStream& out, JSC::ParserError::ErrorType type) +{ + switch (type) { + case JSC::ParserError::ErrorNone: + out.print("ErrorNone"); + return; + case JSC::ParserError::StackOverflow: + out.print("StackOverflow"); + return; + case JSC::ParserError::EvalError: + out.print("EvalError"); + return; + case JSC::ParserError::OutOfMemory: + out.print("OutOfMemory"); + return; + case JSC::ParserError::SyntaxError: + out.print("SyntaxError"); + return; + } + + RELEASE_ASSERT_NOT_REACHED(); +} + +} // namespace WTF diff --git a/Source/JavaScriptCore/parser/ParserFunctionInfo.h b/Source/JavaScriptCore/parser/ParserFunctionInfo.h new file mode 100644 index 000000000..b9fd67956 --- /dev/null +++ b/Source/JavaScriptCore/parser/ParserFunctionInfo.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015-2016 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. + */ + +#pragma once + +namespace JSC { + +template <class TreeBuilder> +struct ParserFunctionInfo { + const Identifier* name = 0; + typename TreeBuilder::FunctionBody body = 0; + unsigned parameterCount = 0; + unsigned functionLength = 0; + unsigned startOffset = 0; + unsigned endOffset = 0; + int startLine = 0; + int endLine = 0; + unsigned parametersStartColumn = 0; +}; + +template <class TreeBuilder> +struct ParserClassInfo { + const Identifier* className { nullptr }; + unsigned startOffset { 0 }; + unsigned endOffset { 0 }; + int startLine { 0 }; + unsigned startColumn { 0 }; +}; + +} diff --git a/Source/JavaScriptCore/parser/ParserModes.h b/Source/JavaScriptCore/parser/ParserModes.h index 4e9a17cd2..6da8837a3 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-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,34 +23,229 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once -#ifndef ParserModes_h -#define ParserModes_h +#include "ConstructAbility.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 JSParserScriptMode { Classic, Module }; +enum class JSParserCodeType { Program, Function, Module }; + +enum class ConstructorKind { None, Base, Extends }; +enum class SuperBinding { Needed, NotNeeded }; -enum ProfilerMode { ProfilerOff, ProfilerOn }; enum DebuggerMode { DebuggerOff, DebuggerOn }; -enum FunctionNameIsInScopeToggle { FunctionNameIsNotInScope, FunctionNameIsInScope }; +enum class FunctionMode { FunctionExpression, FunctionDeclaration, MethodDefinition }; -typedef unsigned CodeFeatures; +enum class SourceParseMode : uint16_t { + NormalFunctionMode = 0b0000000000000001, + GeneratorBodyMode = 0b0000000000000010, + GeneratorWrapperFunctionMode = 0b0000000000000100, + GetterMode = 0b0000000000001000, + SetterMode = 0b0000000000010000, + MethodMode = 0b0000000000100000, + ArrowFunctionMode = 0b0000000001000000, + AsyncFunctionBodyMode = 0b0000000010000000, + AsyncArrowFunctionBodyMode = 0b0000000100000000, + AsyncFunctionMode = 0b0000001000000000, + AsyncMethodMode = 0b0000010000000000, + AsyncArrowFunctionMode = 0b0000100000000000, + ProgramMode = 0b0001000000000000, + ModuleAnalyzeMode = 0b0010000000000000, + ModuleEvaluateMode = 0b0100000000000000, +}; -const CodeFeatures NoFeatures = 0; -const CodeFeatures EvalFeature = 1 << 0; -const CodeFeatures ArgumentsFeature = 1 << 1; -const CodeFeatures WithFeature = 1 << 2; -const CodeFeatures CatchFeature = 1 << 3; -const CodeFeatures ThisFeature = 1 << 4; -const CodeFeatures StrictModeFeature = 1 << 5; -const CodeFeatures ShadowsArgumentsFeature = 1 << 6; -const CodeFeatures ModifiedParameterFeature = 1 << 7; +class SourceParseModeSet { +public: + template<typename... Modes> + SourceParseModeSet(Modes... args) + : m_mask(mergeSourceParseModes(args...)) + { + } -const CodeFeatures AllFeatures = EvalFeature | ArgumentsFeature | WithFeature | CatchFeature | ThisFeature | StrictModeFeature | ShadowsArgumentsFeature | ModifiedParameterFeature; + ALWAYS_INLINE bool contains(SourceParseMode mode) + { + return static_cast<unsigned>(mode) & m_mask; + } -} // namespace JSC +private: + ALWAYS_INLINE static unsigned mergeSourceParseModes(SourceParseMode mode) + { + return static_cast<unsigned>(mode); + } + + template<typename... Rest> + ALWAYS_INLINE static unsigned mergeSourceParseModes(SourceParseMode mode, Rest... rest) + { + return static_cast<unsigned>(mode) | mergeSourceParseModes(rest...); + } + + const unsigned m_mask; +}; + +ALWAYS_INLINE bool isFunctionParseMode(SourceParseMode parseMode) +{ + return SourceParseModeSet( + SourceParseMode::NormalFunctionMode, + SourceParseMode::GeneratorBodyMode, + SourceParseMode::GeneratorWrapperFunctionMode, + SourceParseMode::GetterMode, + SourceParseMode::SetterMode, + SourceParseMode::MethodMode, + SourceParseMode::ArrowFunctionMode, + SourceParseMode::AsyncFunctionBodyMode, + SourceParseMode::AsyncFunctionMode, + SourceParseMode::AsyncMethodMode, + SourceParseMode::AsyncArrowFunctionMode, + SourceParseMode::AsyncArrowFunctionBodyMode).contains(parseMode); +} + +ALWAYS_INLINE bool isAsyncFunctionParseMode(SourceParseMode parseMode) +{ + return SourceParseModeSet( + SourceParseMode::AsyncFunctionBodyMode, + SourceParseMode::AsyncFunctionMode, + SourceParseMode::AsyncMethodMode, + SourceParseMode::AsyncArrowFunctionMode, + SourceParseMode::AsyncArrowFunctionBodyMode).contains(parseMode); +} + +ALWAYS_INLINE bool isAsyncArrowFunctionParseMode(SourceParseMode parseMode) +{ + return SourceParseModeSet( + SourceParseMode::AsyncArrowFunctionMode, + SourceParseMode::AsyncArrowFunctionBodyMode).contains(parseMode); +} + +ALWAYS_INLINE bool isAsyncFunctionWrapperParseMode(SourceParseMode parseMode) +{ + return SourceParseModeSet( + SourceParseMode::AsyncArrowFunctionMode, + SourceParseMode::AsyncFunctionMode, + SourceParseMode::AsyncMethodMode).contains(parseMode); +} + +ALWAYS_INLINE bool isAsyncFunctionBodyParseMode(SourceParseMode parseMode) +{ + return SourceParseModeSet( + SourceParseMode::AsyncFunctionBodyMode, + SourceParseMode::AsyncArrowFunctionBodyMode).contains(parseMode); +} + +ALWAYS_INLINE bool isMethodParseMode(SourceParseMode parseMode) +{ + return SourceParseModeSet( + // FIXME: GeneratorWrapperFunctionMode is not guaranteed to be a method. + SourceParseMode::GeneratorWrapperFunctionMode, + SourceParseMode::GetterMode, + SourceParseMode::SetterMode, + SourceParseMode::MethodMode, + SourceParseMode::AsyncMethodMode).contains(parseMode); +} + +ALWAYS_INLINE bool isGeneratorOrAsyncFunctionBodyParseMode(SourceParseMode parseMode) +{ + return SourceParseModeSet( + SourceParseMode::GeneratorBodyMode, + SourceParseMode::AsyncFunctionBodyMode, + SourceParseMode::AsyncArrowFunctionBodyMode).contains(parseMode); +} + +ALWAYS_INLINE bool isGeneratorOrAsyncFunctionWrapperParseMode(SourceParseMode parseMode) +{ + return SourceParseModeSet( + SourceParseMode::GeneratorWrapperFunctionMode, + SourceParseMode::AsyncFunctionMode, + SourceParseMode::AsyncArrowFunctionMode, + SourceParseMode::AsyncMethodMode).contains(parseMode); +} + +ALWAYS_INLINE bool isArrowFunctionParseMode(SourceParseMode parseMode) +{ + return SourceParseModeSet( + SourceParseMode::ArrowFunctionMode, + SourceParseMode::AsyncArrowFunctionMode, + SourceParseMode::AsyncArrowFunctionBodyMode).contains(parseMode); +} + + +ALWAYS_INLINE bool isModuleParseMode(SourceParseMode parseMode) +{ + return SourceParseModeSet( + SourceParseMode::ModuleAnalyzeMode, + SourceParseMode::ModuleEvaluateMode).contains(parseMode); +} -#endif // ParserModes_h +ALWAYS_INLINE bool isProgramParseMode(SourceParseMode parseMode) +{ + return SourceParseModeSet(SourceParseMode::ProgramMode).contains(parseMode); +} + +ALWAYS_INLINE ConstructAbility constructAbilityForParseMode(SourceParseMode parseMode) +{ + if (parseMode == SourceParseMode::NormalFunctionMode) + return ConstructAbility::CanConstruct; + + return ConstructAbility::CannotConstruct; +} + +inline bool functionNameIsInScope(const Identifier& name, FunctionMode functionMode) +{ + if (name.isNull()) + return false; + + if (functionMode != 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 uint16_t CodeFeatures; + +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 ArrowFunctionFeature = 1 << 6; +const CodeFeatures ArrowFunctionContextFeature = 1 << 7; +const CodeFeatures SuperCallFeature = 1 << 8; +const CodeFeatures SuperPropertyFeature = 1 << 9; +const CodeFeatures NewTargetFeature = 1 << 10; + +const CodeFeatures AllFeatures = EvalFeature | ArgumentsFeature | WithFeature | ThisFeature | StrictModeFeature | ShadowsArgumentsFeature | ArrowFunctionFeature | ArrowFunctionContextFeature | + SuperCallFeature | SuperPropertyFeature | NewTargetFeature; + +typedef uint8_t InnerArrowFunctionCodeFeatures; + +const InnerArrowFunctionCodeFeatures NoInnerArrowFunctionFeatures = 0; +const InnerArrowFunctionCodeFeatures EvalInnerArrowFunctionFeature = 1 << 0; +const InnerArrowFunctionCodeFeatures ArgumentsInnerArrowFunctionFeature = 1 << 1; +const InnerArrowFunctionCodeFeatures ThisInnerArrowFunctionFeature = 1 << 2; +const InnerArrowFunctionCodeFeatures SuperCallInnerArrowFunctionFeature = 1 << 3; +const InnerArrowFunctionCodeFeatures SuperPropertyInnerArrowFunctionFeature = 1 << 4; +const InnerArrowFunctionCodeFeatures NewTargetInnerArrowFunctionFeature = 1 << 5; + +const InnerArrowFunctionCodeFeatures AllInnerArrowFunctionCodeFeatures = EvalInnerArrowFunctionFeature | ArgumentsInnerArrowFunctionFeature | ThisInnerArrowFunctionFeature | SuperCallInnerArrowFunctionFeature | SuperPropertyInnerArrowFunctionFeature | NewTargetInnerArrowFunctionFeature; +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/ParserTokens.h b/Source/JavaScriptCore/parser/ParserTokens.h index 82a26639a..a0554ff36 100644 --- a/Source/JavaScriptCore/parser/ParserTokens.h +++ b/Source/JavaScriptCore/parser/ParserTokens.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ParserTokens_h -#define ParserTokens_h +#pragma once #include "ParserModes.h" #include <limits.h> @@ -35,13 +34,22 @@ namespace JSC { class Identifier; enum { - UnaryOpTokenFlag = 64, - KeywordTokenFlag = 128, - BinaryOpTokenPrecedenceShift = 8, + // Token Bitfield: 0b000000000RTE000IIIIPPPPKUXXXXXXX + // R = right-associative bit + // T = unterminated error flag + // E = error flag + // I = binary operator allows 'in' + // P = binary operator precedence + // K = keyword flag + // U = unary operator flag + UnaryOpTokenFlag = 128, + KeywordTokenFlag = 256, + BinaryOpTokenPrecedenceShift = 9, BinaryOpTokenAllowsInPrecedenceAdditionalShift = 4, BinaryOpTokenPrecedenceMask = 15 << BinaryOpTokenPrecedenceShift, ErrorTokenFlag = 1 << (BinaryOpTokenAllowsInPrecedenceAdditionalShift + BinaryOpTokenPrecedenceShift + 7), - UnterminatedErrorTokenFlag = ErrorTokenFlag << 1 + UnterminatedErrorTokenFlag = ErrorTokenFlag << 1, + RightAssociativeBinaryOpTokenFlag = UnterminatedErrorTokenFlag << 1 }; #define BINARY_OP_PRECEDENCE(prec) (((prec) << BinaryOpTokenPrecedenceShift) | ((prec) << (BinaryOpTokenPrecedenceShift + BinaryOpTokenAllowsInPrecedenceAdditionalShift))) @@ -75,6 +83,24 @@ enum JSTokenType { FINALLY, DEBUGGER, ELSE, + IMPORT, + EXPORT, + CLASSTOKEN, + EXTENDS, + SUPER, + + // Contextual keywords + + LET, + YIELD, + AWAIT, + ASYNC, + + FirstContextualKeywordToken = LET, + LastContextualKeywordToken = ASYNC, + FirstSafeContextualKeywordToken = AWAIT, + LastSafeContextualKeywordToken = LastContextualKeywordToken, + OPENBRACE = 0, CLOSEBRACE, OPENPAREN, @@ -83,9 +109,13 @@ enum JSTokenType { CLOSEBRACKET, COMMA, QUESTION, - NUMBER, + BACKQUOTE, + INTEGER, + DOUBLE, IDENT, STRING, + TEMPLATE, + REGEXP, SEMICOLON, COLON, DOT, @@ -100,18 +130,20 @@ enum JSTokenType { URSHIFTEQUAL, ANDEQUAL, MODEQUAL, + POWEQUAL, XOREQUAL, OREQUAL, DOTDOTDOT, + ARROWFUNCTION, LastUntaggedToken, // Begin tagged tokens PLUSPLUS = 0 | UnaryOpTokenFlag, MINUSMINUS = 1 | UnaryOpTokenFlag, - EXCLAMATION = 2 | UnaryOpTokenFlag, - TILDE = 3 | UnaryOpTokenFlag, - AUTOPLUSPLUS = 4 | UnaryOpTokenFlag, - AUTOMINUSMINUS = 5 | UnaryOpTokenFlag, + AUTOPLUSPLUS = 2 | UnaryOpTokenFlag, + AUTOMINUSMINUS = 3 | UnaryOpTokenFlag, + EXCLAMATION = 4 | UnaryOpTokenFlag, + TILDE = 5 | UnaryOpTokenFlag, TYPEOF = 6 | UnaryOpTokenFlag | KeywordTokenFlag, VOIDTOKEN = 7 | UnaryOpTokenFlag | KeywordTokenFlag, DELETETOKEN = 8 | UnaryOpTokenFlag | KeywordTokenFlag, @@ -138,6 +170,7 @@ enum JSTokenType { TIMES = 20 | BINARY_OP_PRECEDENCE(10), DIVIDE = 21 | BINARY_OP_PRECEDENCE(10), MOD = 22 | BINARY_OP_PRECEDENCE(10), + POW = 23 | BINARY_OP_PRECEDENCE(11) | RightAssociativeBinaryOpTokenFlag, // Make sure that POW has the highest operator precedence. ERRORTOK = 0 | ErrorTokenFlag, UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK = 0 | ErrorTokenFlag | UnterminatedErrorTokenFlag, INVALID_IDENTIFIER_ESCAPE_ERRORTOK = 1 | ErrorTokenFlag, @@ -145,10 +178,16 @@ 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, + UNTERMINATED_REGEXP_LITERAL_ERRORTOK = 14 | ErrorTokenFlag | UnterminatedErrorTokenFlag, + INVALID_TEMPLATE_LITERAL_ERRORTOK = 15 | ErrorTokenFlag, }; struct JSTextPosition { @@ -176,6 +215,15 @@ union JSTokenData { }; double doubleValue; const Identifier* ident; + struct { + const Identifier* cooked; + const Identifier* raw; + bool isTail; + }; + struct { + const Identifier* pattern; + const Identifier* flags; + }; }; struct JSTokenLocation { @@ -202,6 +250,14 @@ struct JSToken { JSTextPosition m_endPosition; }; -} // namespace JSC +ALWAYS_INLINE bool isUpdateOp(JSTokenType token) +{ + return token >= PLUSPLUS && token <= AUTOMINUSMINUS; +} -#endif // ParserTokens_h +ALWAYS_INLINE bool isUnaryOp(JSTokenType token) +{ + return token & UnaryOpTokenFlag; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/ResultType.h b/Source/JavaScriptCore/parser/ResultType.h index ad86c98c7..4f6471b54 100644 --- a/Source/JavaScriptCore/parser/ResultType.h +++ b/Source/JavaScriptCore/parser/ResultType.h @@ -23,56 +23,59 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResultType_h -#define ResultType_h +#pragma once namespace JSC { struct ResultType { + private: friend struct OperandTypes; - typedef char Type; + typedef uint8_t Type; static const Type TypeInt32 = 1; - - static const Type TypeMaybeNumber = 0x04; - static const Type TypeMaybeString = 0x08; - static const Type TypeMaybeNull = 0x10; - static const Type TypeMaybeBool = 0x20; - static const Type TypeMaybeOther = 0x40; + static const Type TypeMaybeNumber = 0x02; + static const Type TypeMaybeString = 0x04; + static const Type TypeMaybeNull = 0x08; + static const Type TypeMaybeBool = 0x10; + static const Type TypeMaybeOther = 0x20; static const Type TypeBits = TypeMaybeNumber | TypeMaybeString | TypeMaybeNull | TypeMaybeBool | TypeMaybeOther; + public: + static const int numBitsNeeded = 6; + static_assert((TypeBits & ((1 << numBitsNeeded) - 1)) == TypeBits, "This is necessary for correctness."); + explicit ResultType(Type type) - : m_type(type) + : m_bits(type) { } - - bool isInt32() + + bool isInt32() const { - return m_type & TypeInt32; + return m_bits & TypeInt32; } - bool definitelyIsNumber() + bool definitelyIsNumber() const { - return (m_type & TypeBits) == TypeMaybeNumber; + return (m_bits & TypeBits) == TypeMaybeNumber; } - bool definitelyIsString() + bool definitelyIsString() const { - return (m_type & TypeBits) == TypeMaybeString; + return (m_bits & TypeBits) == TypeMaybeString; } - bool definitelyIsBoolean() + bool definitelyIsBoolean() const { - return (m_type & TypeBits) == TypeMaybeBool; + return (m_bits & TypeBits) == TypeMaybeBool; } - bool mightBeNumber() + bool mightBeNumber() const { - return m_type & TypeMaybeNumber; + return m_bits & TypeMaybeNumber; } - bool isNotNumber() + bool isNotNumber() const { return !mightBeNumber(); } @@ -139,8 +142,10 @@ namespace JSC { return numberTypeIsInt32(); } + Type bits() const { return m_bits; } + private: - Type m_type; + Type m_bits; }; struct OperandTypes @@ -150,8 +155,8 @@ namespace JSC { // We have to initialize one of the int to ensure that // the entire struct is initialized. m_u.i = 0; - m_u.rds.first = first.m_type; - m_u.rds.second = second.m_type; + m_u.rds.first = first.m_bits; + m_u.rds.second = second.m_bits; } union { @@ -185,5 +190,3 @@ namespace JSC { }; } // namespace JSC - -#endif // ResultType_h diff --git a/Source/JavaScriptCore/parser/SourceCode.h b/Source/JavaScriptCore/parser/SourceCode.h index cf1a4a3d9..90f547a20 100644 --- a/Source/JavaScriptCore/parser/SourceCode.h +++ b/Source/JavaScriptCore/parser/SourceCode.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. * @@ -26,106 +26,75 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SourceCode_h -#define SourceCode_h +#pragma once -#include "SourceProvider.h" -#include <wtf/RefPtr.h> +#include "UnlinkedSourceCode.h" namespace JSC { - class SourceCode { + class SourceCode : public UnlinkedSourceCode { public: SourceCode() - : m_provider(0) - , m_startChar(0) - , m_endChar(0) - , m_firstLine(0) - , m_startColumn(0) + : UnlinkedSourceCode() + , m_firstLine(OrdinalNumber::beforeFirst()) + , m_startColumn(OrdinalNumber::beforeFirst()) { } - SourceCode(WTF::HashTableDeletedValueType) - : m_provider(WTF::HashTableDeletedValue) + SourceCode(Ref<SourceProvider>&& provider) + : UnlinkedSourceCode(WTFMove(provider)) { } - SourceCode(PassRefPtr<SourceProvider> provider) - : m_provider(provider) - , m_startChar(0) - , m_endChar(m_provider->source().length()) - , m_firstLine(1) - , m_startColumn(1) + SourceCode(Ref<SourceProvider>&& provider, int firstLine, int startColumn) + : UnlinkedSourceCode(WTFMove(provider)) + , m_firstLine(OrdinalNumber::fromOneBasedInt(std::max(firstLine, 1))) + , m_startColumn(OrdinalNumber::fromOneBasedInt(std::max(startColumn, 1))) { } - SourceCode(PassRefPtr<SourceProvider> provider, int firstLine, int startColumn) - : m_provider(provider) - , m_startChar(0) - , m_endChar(m_provider->source().length()) - , m_firstLine(std::max(firstLine, 1)) - , m_startColumn(std::max(startColumn, 1)) + SourceCode(Ref<SourceProvider>&& provider, int startOffset, int endOffset, int firstLine, int startColumn) + : UnlinkedSourceCode(WTFMove(provider), startOffset, endOffset) + , m_firstLine(OrdinalNumber::fromOneBasedInt(std::max(firstLine, 1))) + , m_startColumn(OrdinalNumber::fromOneBasedInt(std::max(startColumn, 1))) { } - SourceCode(PassRefPtr<SourceProvider> provider, int start, int end, int firstLine, int startColumn) - : m_provider(provider) - , m_startChar(start) - , m_endChar(end) - , m_firstLine(std::max(firstLine, 1)) - , m_startColumn(std::max(startColumn, 1)) + SourceCode(RefPtr<SourceProvider>&& provider, int startOffset, int endOffset, int firstLine, int startColumn) + : UnlinkedSourceCode(WTFMove(provider), startOffset, endOffset) + , m_firstLine(OrdinalNumber::fromOneBasedInt(std::max(firstLine, 1))) + , m_startColumn(OrdinalNumber::fromOneBasedInt(std::max(startColumn, 1))) { } - bool isHashTableDeletedValue() const { return m_provider.isHashTableDeletedValue(); } + OrdinalNumber firstLine() const { return m_firstLine; } + OrdinalNumber startColumn() const { return m_startColumn; } - String toString() const - { - if (!m_provider) - return String(); - return m_provider->getRange(m_startChar, m_endChar); - } - - CString toUTF8() const; - intptr_t providerID() const { if (!m_provider) return SourceProvider::nullID; return m_provider->asID(); } - - bool isNull() const { return !m_provider; } + SourceProvider* provider() const { return m_provider.get(); } - int firstLine() const { return m_firstLine; } - int startColumn() const { return m_startColumn; } - int startOffset() const { return m_startChar; } - int endOffset() const { return m_endChar; } - int length() const { return m_endChar - m_startChar; } - + SourceCode subExpression(unsigned openBrace, unsigned closeBrace, int firstLine, int startColumn); private: - RefPtr<SourceProvider> m_provider; - int m_startChar; - int m_endChar; - int m_firstLine; - int m_startColumn; + OrdinalNumber m_firstLine; + OrdinalNumber m_startColumn; }; - inline SourceCode makeSource(const String& source, const String& url = String(), const TextPosition& startPosition = TextPosition::minimumPosition()) + inline SourceCode makeSource(const String& source, const SourceOrigin& sourceOrigin, const String& url = String(), const TextPosition& startPosition = TextPosition(), SourceProviderSourceType sourceType = SourceProviderSourceType::Program) { - return SourceCode(StringSourceProvider::create(source, url, startPosition), startPosition.m_line.oneBasedInt(), startPosition.m_column.oneBasedInt()); + return SourceCode(StringSourceProvider::create(source, sourceOrigin, url, startPosition, sourceType), 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); + return SourceCode(RefPtr<SourceProvider> { provider() }, openBrace, closeBrace + 1, firstLine, startColumn); } } // namespace JSC - -#endif // SourceCode_h diff --git a/Source/JavaScriptCore/parser/SourceCodeKey.h b/Source/JavaScriptCore/parser/SourceCodeKey.h new file mode 100644 index 000000000..abe41812d --- /dev/null +++ b/Source/JavaScriptCore/parser/SourceCodeKey.h @@ -0,0 +1,133 @@ +/* + * 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. + */ + +#pragma once + +#include "ParserModes.h" +#include "UnlinkedSourceCode.h" +#include <wtf/HashTraits.h> + +namespace JSC { + +enum class SourceCodeType { EvalType, ProgramType, FunctionType, ModuleType }; +enum class TypeProfilerEnabled { No, Yes }; +enum class ControlFlowProfilerEnabled { No, Yes }; + +class SourceCodeFlags { +public: + SourceCodeFlags() = default; + + SourceCodeFlags( + SourceCodeType codeType, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, + DerivedContextType derivedContextType, EvalContextType evalContextType, bool isArrowFunctionContext, + DebuggerMode debuggerMode, TypeProfilerEnabled typeProfilerEnabled, ControlFlowProfilerEnabled controlFlowProfilerEnabled) + : m_flags( + (static_cast<unsigned>(debuggerMode) << 8) | + (static_cast<unsigned>(typeProfilerEnabled) << 7) | + (static_cast<unsigned>(controlFlowProfilerEnabled) << 6) | + (static_cast<unsigned>(scriptMode) << 5) | + (static_cast<unsigned>(isArrowFunctionContext) << 4) | + (static_cast<unsigned>(evalContextType) << 3) | + (static_cast<unsigned>(derivedContextType) << 2) | + (static_cast<unsigned>(codeType) << 1) | + (static_cast<unsigned>(strictMode)) + ) + { + } + + inline bool operator==(const SourceCodeFlags& rhs) const + { + return m_flags == rhs.m_flags; + } + + unsigned bits() { return m_flags; } + +private: + unsigned m_flags { 0 }; +}; + +class SourceCodeKey { +public: + SourceCodeKey() + { + } + + SourceCodeKey( + const UnlinkedSourceCode& sourceCode, const String& name, SourceCodeType codeType, JSParserStrictMode strictMode, + JSParserScriptMode scriptMode, DerivedContextType derivedContextType, EvalContextType evalContextType, bool isArrowFunctionContext, + DebuggerMode debuggerMode, TypeProfilerEnabled typeProfilerEnabled, ControlFlowProfilerEnabled controlFlowProfilerEnabled) + : m_sourceCode(sourceCode) + , m_name(name) + , m_flags(codeType, strictMode, scriptMode, derivedContextType, evalContextType, isArrowFunctionContext, debuggerMode, typeProfilerEnabled, controlFlowProfilerEnabled) + , m_hash(sourceCode.hash() ^ m_flags.bits()) + { + } + + 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(); + } + + struct Hash { + 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 HashTraits : SimpleClassHashTraits<SourceCodeKey> { + static const bool hasIsEmptyValueFunction = true; + static bool isEmptyValue(const SourceCodeKey& key) { return key.isNull(); } + }; + +private: + UnlinkedSourceCode m_sourceCode; + String m_name; + SourceCodeFlags m_flags; + unsigned m_hash; +}; + +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/SourceProvider.cpp b/Source/JavaScriptCore/parser/SourceProvider.cpp index d12045592..94d9fbdaa 100644 --- a/Source/JavaScriptCore/parser/SourceProvider.cpp +++ b/Source/JavaScriptCore/parser/SourceProvider.cpp @@ -25,14 +25,17 @@ #include "config.h" #include "SourceProvider.h" -#include <wtf/StdLibExtras.h> -#include <wtf/TCSpinLock.h> + +#include "JSCInlines.h" +#include <wtf/Lock.h> namespace JSC { -SourceProvider::SourceProvider(const String& url, const TextPosition& startPosition) - : m_url(url) +SourceProvider::SourceProvider(const SourceOrigin& sourceOrigin, const String& url, const TextPosition& startPosition, SourceProviderSourceType sourceType) + : m_sourceOrigin(sourceOrigin) + , m_url(url) , m_startPosition(startPosition) + , m_sourceType(sourceType) , m_validated(false) , m_id(0) { @@ -42,11 +45,11 @@ SourceProvider::~SourceProvider() { } -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; diff --git a/Source/JavaScriptCore/parser/SourceProvider.h b/Source/JavaScriptCore/parser/SourceProvider.h index 72c0de41e..2e68e8202 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. * @@ -26,37 +26,46 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SourceProvider_h -#define SourceProvider_h +#pragma once -#include <wtf/PassOwnPtr.h> +#include "SourceOrigin.h" #include <wtf/RefCounted.h> #include <wtf/text/TextPosition.h> #include <wtf/text/WTFString.h> namespace JSC { + enum class SourceProviderSourceType { + Program, + Module, + WebAssembly, + }; + class SourceProvider : public RefCounted<SourceProvider> { public: static const intptr_t nullID = 1; - JS_EXPORT_PRIVATE SourceProvider(const String& url, const TextPosition& startPosition); + JS_EXPORT_PRIVATE SourceProvider(const SourceOrigin&, const String& url, const TextPosition& startPosition, SourceProviderSourceType); 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 SourceOrigin& sourceOrigin() const { return m_sourceOrigin; } + 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; } + SourceProviderSourceType sourceType() const { return m_sourceType; } + intptr_t asID() { - ASSERT(this); - if (!this) // Be defensive in release mode. - return nullID; if (!m_id) getID(); return m_id; @@ -65,39 +74,83 @@ namespace JSC { bool isValid() const { return m_validated; } void setValid() { m_validated = true; } - private: + void setSourceURLDirective(const String& sourceURL) { m_sourceURLDirective = sourceURL; } + void setSourceMappingURLDirective(const String& sourceMappingURL) { m_sourceMappingURLDirective = sourceMappingURL; } + private: JS_EXPORT_PRIVATE void getID(); - Vector<size_t>& lineStarts(); + SourceOrigin m_sourceOrigin; String m_url; + String m_sourceURLDirective; + String m_sourceMappingURLDirective; TextPosition m_startPosition; + SourceProviderSourceType m_sourceType; bool m_validated : 1; uintptr_t m_id : sizeof(uintptr_t) * 8 - 1; }; 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 SourceOrigin& sourceOrigin, const String& url, const TextPosition& startPosition = TextPosition(), SourceProviderSourceType sourceType = SourceProviderSourceType::Program) + { + return adoptRef(*new StringSourceProvider(source, sourceOrigin, url, startPosition, sourceType)); + } + + unsigned hash() const override + { + return m_source.get().hash(); + } + + StringView source() const override { - return adoptRef(new StringSourceProvider(source, url, startPosition)); + return m_source.get(); } - virtual const String& source() const override + private: + StringSourceProvider(const String& source, const SourceOrigin& sourceOrigin, const String& url, const TextPosition& startPosition, SourceProviderSourceType sourceType) + : SourceProvider(sourceOrigin, url, startPosition, sourceType) + , m_source(source.isNull() ? *StringImpl::empty() : *source.impl()) + { + } + + Ref<StringImpl> m_source; + }; + +#if ENABLE(WEBASSEMBLY) + class WebAssemblySourceProvider : public SourceProvider { + public: + static Ref<WebAssemblySourceProvider> create(const Vector<uint8_t>& data, const SourceOrigin& sourceOrigin, const String& url) + { + return adoptRef(*new WebAssemblySourceProvider(data, sourceOrigin, url)); + } + + unsigned hash() const override + { + return m_source.impl()->hash(); + } + + StringView source() const override { return m_source; } + const Vector<uint8_t>& data() const + { + return m_data; + } + private: - StringSourceProvider(const String& source, const String& url, const TextPosition& startPosition) - : SourceProvider(url, startPosition) - , m_source(source) + WebAssemblySourceProvider(const Vector<uint8_t>& data, const SourceOrigin& sourceOrigin, const String& url) + : SourceProvider(sourceOrigin, url, TextPosition(), SourceProviderSourceType::WebAssembly) + , m_source("[WebAssembly source]") + , m_data(data) { } String m_source; + Vector<uint8_t> m_data; }; - -} // namespace JSC +#endif -#endif // SourceProvider_h +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/SourceProviderCache.cpp b/Source/JavaScriptCore/parser/SourceProviderCache.cpp index bc28fb166..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() @@ -40,7 +42,7 @@ void SourceProviderCache::clear() void SourceProviderCache::add(int sourcePosition, std::unique_ptr<SourceProviderCacheItem> item) { - m_map.add(sourcePosition, std::move(item)); + m_map.add(sourcePosition, WTFMove(item)); } } diff --git a/Source/JavaScriptCore/parser/SourceProviderCache.h b/Source/JavaScriptCore/parser/SourceProviderCache.h index 7558a09f4..05a851f9a 100644 --- a/Source/JavaScriptCore/parser/SourceProviderCache.h +++ b/Source/JavaScriptCore/parser/SourceProviderCache.h @@ -23,13 +23,10 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SourceProviderCache_h -#define SourceProviderCache_h +#pragma once #include "SourceProviderCacheItem.h" #include <wtf/HashMap.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> #include <wtf/RefCounted.h> namespace JSC { @@ -45,9 +42,7 @@ public: const SourceProviderCacheItem* get(int sourcePosition) const { return m_map.get(sourcePosition); } private: - HashMap<int, std::unique_ptr<SourceProviderCacheItem>> m_map; + HashMap<int, std::unique_ptr<SourceProviderCacheItem>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> m_map; }; -} - -#endif // SourceProviderCache_h +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/SourceProviderCacheItem.h b/Source/JavaScriptCore/parser/SourceProviderCacheItem.h index 396211861..eeac6fe78 100644 --- a/Source/JavaScriptCore/parser/SourceProviderCacheItem.h +++ b/Source/JavaScriptCore/parser/SourceProviderCacheItem.h @@ -23,26 +23,34 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SourceProviderCacheItem_h -#define SourceProviderCacheItem_h +#pragma once #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 functionNameStart; - unsigned closeBraceLine; - unsigned closeBraceOffset; - unsigned closeBraceLineStartOffset; + unsigned lastTokenLine; + unsigned lastTokenStartOffset; + unsigned lastTokenEndOffset; + unsigned lastTokenLineStartOffset; + unsigned endFunctionOffset; + unsigned parameterCount; + unsigned functionLength; bool needsFullActivation; bool usesEval; bool strictMode; - Vector<RefPtr<StringImpl>> usedVariables; - Vector<RefPtr<StringImpl>> writtenVariables; + bool needsSuperBinding; + InnerArrowFunctionCodeFeatures innerArrowFunctionFeatures; + Vector<UniquedStringImpl*, 8> usedVariables; + bool isBodyArrowExpression { false }; + JSTokenType tokenType { CLOSEBRACE }; + ConstructorKind constructorKind; + SuperBinding expectedSuperBinding; }; #if COMPILER(MSVC) @@ -56,15 +64,15 @@ public: 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 = lastTokenStartOffset; + token.m_location.startOffset = lastTokenStartOffset; + token.m_location.endOffset = lastTokenEndOffset; + token.m_location.line = lastTokenLine; + token.m_location.lineStartOffset = lastTokenLineStartOffset; // token.m_location.sourceOffset is initialized once by the client. So, // we do not need to set it here. return token; @@ -72,36 +80,41 @@ public: unsigned functionNameStart : 31; bool needsFullActivation : 1; - - unsigned closeBraceLine : 31; + unsigned endFunctionOffset : 31; bool usesEval : 1; - - unsigned closeBraceOffset : 31; + unsigned lastTokenLine : 31; bool strictMode : 1; - - unsigned closeBraceLineStartOffset; + unsigned lastTokenStartOffset : 31; + unsigned lastTokenEndOffset: 31; + unsigned constructorKind : 2; // ConstructorKind + unsigned parameterCount : 31; + unsigned expectedSuperBinding : 1; // SuperBinding + bool needsSuperBinding: 1; + unsigned functionLength; + unsigned lastTokenLineStartOffset; unsigned usedVariablesCount; - unsigned writtenVariablesCount; + InnerArrowFunctionCodeFeatures innerArrowFunctionFeatures; + bool isBodyArrowExpression; + JSTokenType tokenType; - 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); } private: SourceProviderCacheItem(const SourceProviderCacheItemCreationParameters&); - StringImpl* m_variables[0]; + UniquedStringImpl* m_variables[0]; }; inline SourceProviderCacheItem::~SourceProviderCacheItem() { - for (unsigned i = 0; i < usedVariablesCount + writtenVariablesCount; ++i) + for (unsigned i = 0; i < usedVariablesCount; ++i) m_variables[i]->deref(); } inline std::unique_ptr<SourceProviderCacheItem> SourceProviderCacheItem::create(const SourceProviderCacheItemCreationParameters& parameters) { - size_t variableCount = parameters.writtenVariables.size() + parameters.usedVariables.size(); - size_t objectSize = sizeof(SourceProviderCacheItem) + sizeof(StringImpl*) * variableCount; + size_t variableCount = parameters.usedVariables.size(); + size_t objectSize = sizeof(SourceProviderCacheItem) + sizeof(UniquedStringImpl*) * variableCount; void* slot = fastMalloc(objectSize); return std::unique_ptr<SourceProviderCacheItem>(new (slot) SourceProviderCacheItem(parameters)); } @@ -109,22 +122,26 @@ inline std::unique_ptr<SourceProviderCacheItem> SourceProviderCacheItem::create( inline SourceProviderCacheItem::SourceProviderCacheItem(const SourceProviderCacheItemCreationParameters& parameters) : functionNameStart(parameters.functionNameStart) , needsFullActivation(parameters.needsFullActivation) - , closeBraceLine(parameters.closeBraceLine) + , endFunctionOffset(parameters.endFunctionOffset) , usesEval(parameters.usesEval) - , closeBraceOffset(parameters.closeBraceOffset) + , lastTokenLine(parameters.lastTokenLine) , strictMode(parameters.strictMode) - , closeBraceLineStartOffset(parameters.closeBraceLineStartOffset) + , lastTokenStartOffset(parameters.lastTokenStartOffset) + , lastTokenEndOffset(parameters.lastTokenEndOffset) + , constructorKind(static_cast<unsigned>(parameters.constructorKind)) + , parameterCount(parameters.parameterCount) + , expectedSuperBinding(static_cast<unsigned>(parameters.expectedSuperBinding)) + , needsSuperBinding(parameters.needsSuperBinding) + , functionLength(parameters.functionLength) + , lastTokenLineStartOffset(parameters.lastTokenLineStartOffset) , usedVariablesCount(parameters.usedVariables.size()) - , writtenVariablesCount(parameters.writtenVariables.size()) + , innerArrowFunctionFeatures(parameters.innerArrowFunctionFeatures) + , isBodyArrowExpression(parameters.isBodyArrowExpression) + , tokenType(parameters.tokenType) { - unsigned j = 0; - for (unsigned i = 0; i < usedVariablesCount; ++i, ++j) { - m_variables[j] = parameters.usedVariables[i].get(); - m_variables[j]->ref(); - } - for (unsigned i = 0; i < writtenVariablesCount; ++i, ++j) { - m_variables[j] = parameters.writtenVariables[i].get(); - m_variables[j]->ref(); + for (unsigned i = 0; i < usedVariablesCount; ++i) { + m_variables[i] = parameters.usedVariables[i]; + m_variables[i]->ref(); } } @@ -132,6 +149,4 @@ inline SourceProviderCacheItem::SourceProviderCacheItem(const SourceProviderCach #pragma warning(pop) #endif -} - -#endif // SourceProviderCacheItem_h +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/SyntaxChecker.h b/Source/JavaScriptCore/parser/SyntaxChecker.h index 0328b1273..035a5f95c 100644 --- a/Source/JavaScriptCore/parser/SyntaxChecker.h +++ b/Source/JavaScriptCore/parser/SyntaxChecker.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2013, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,44 +23,44 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SyntaxChecker_h -#define SyntaxChecker_h +#pragma once #include "Lexer.h" +#include "ParserFunctionInfo.h" #include "YarrSyntaxChecker.h" namespace JSC { - + class SyntaxChecker { public: struct BinaryExprContext { BinaryExprContext(SyntaxChecker& context) : m_context(&context) { - m_context->m_topBinaryExprs.append(m_context->m_topBinaryExpr); + m_token = m_context->m_topBinaryExpr; m_context->m_topBinaryExpr = 0; } ~BinaryExprContext() { - m_context->m_topBinaryExpr = m_context->m_topBinaryExprs.last(); - m_context->m_topBinaryExprs.removeLast(); + m_context->m_topBinaryExpr = m_token; } private: + int m_token; SyntaxChecker* m_context; }; struct UnaryExprContext { UnaryExprContext(SyntaxChecker& context) : m_context(&context) { - m_context->m_topUnaryTokens.append(m_context->m_topUnaryToken); + m_token = m_context->m_topUnaryToken; m_context->m_topUnaryToken = 0; } ~UnaryExprContext() { - m_context->m_topUnaryToken = m_context->m_topUnaryTokens.last(); - m_context->m_topUnaryTokens.removeLast(); + m_context->m_topUnaryToken = m_token; } private: + int m_token; SyntaxChecker* m_context; }; @@ -68,14 +68,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, ImportExpr, 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, AwaitExpr, + ModuleNameResult, + ImportSpecifierResult, ImportSpecifierListResult, + ExportSpecifierResult, ExportSpecifierListResult + }; typedef int ExpressionType; typedef ExpressionType Expression; @@ -104,16 +115,26 @@ 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 DeconstructionPattern; - typedef int ArrayPattern; - typedef int ObjectPattern; + typedef int DestructuringPattern; + typedef DestructuringPattern ArrayPattern; + typedef DestructuringPattern ObjectPattern; + typedef DestructuringPattern RestPattern; static const bool CreatesAST = false; static const bool NeedsFreeVariableInfo = false; @@ -121,10 +142,10 @@ public: 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; } @@ -135,100 +156,142 @@ 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 createImportExpr(const JSTokenLocation&, ExpressionType, int, int, int) { return ImportExpr; } + ExpressionType createThisExpr(const JSTokenLocation&) { return ThisExpr; } + ExpressionType createSuperExpr(const JSTokenLocation&) { return SuperExpr; } + ExpressionType createNewTargetExpr(const JSTokenLocation&) { return NewTargetExpr; } + ALWAYS_INLINE bool isNewTarget(ExpressionType type) { return type == 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; } ExpressionType createBracketAccess(const JSTokenLocation&, ExpressionType, ExpressionType, bool, int, int, int) { return BracketExpr; } ExpressionType createDotAccess(const JSTokenLocation&, ExpressionType, const Identifier*, int, int, int) { return DotExpr; } - ExpressionType createRegExp(const JSTokenLocation&, const Identifier& pattern, const Identifier&, int) { return Yarr::checkSyntax(pattern.string()) ? 0 : RegExpExpr; } + ExpressionType createRegExp(const JSTokenLocation&, const Identifier& pattern, const Identifier& flags, int) { return Yarr::checkSyntax(pattern.string(), flags.string()) ? 0 : RegExpExpr; } ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int, int, int) { return NewExpr; } ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int) { return NewExpr; } ExpressionType createConditionalExpr(const JSTokenLocation&, ExpressionType, ExpressionType, ExpressionType) { return ConditionalExpr; } - ExpressionType createAssignResolve(const JSTokenLocation&, const Identifier&, ExpressionType, int, int, int) { return AssignmentExpr; } - ExpressionType createFunctionExpr(const JSTokenLocation&, const Identifier*, int, int, int, int, int, int, int) { return FunctionExpr; } - int createFunctionBody(const JSTokenLocation&, const JSTokenLocation&, int, int, bool) { return 1; } + 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; } + ExpressionType createAwait(const JSTokenLocation&, ExpressionType, int, int, int) { return AwaitExpr; } + ClassExpression createClassExpr(const JSTokenLocation&, const ParserClassInfo<SyntaxChecker>&, VariableEnvironment&, ExpressionType, ExpressionType, PropertyList, PropertyList) { return ClassExpr; } + ExpressionType createFunctionExpr(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return FunctionExpr; } + ExpressionType createGeneratorFunctionBody(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&, const Identifier&) { return FunctionExpr; } + ExpressionType createAsyncFunctionBody(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return FunctionExpr; } + int createFunctionMetadata(const JSTokenLocation&, const JSTokenLocation&, int, int, bool, int, int, int, ConstructorKind, SuperBinding, unsigned, SourceParseMode, bool, InnerArrowFunctionCodeFeatures = NoInnerArrowFunctionFeatures) { return FunctionBodyResult; } + ExpressionType createArrowFunctionExpr(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return FunctionExpr; } + ExpressionType createMethodDefinition(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&) { return FunctionExpr; } void setFunctionNameStart(int, int) { } - int createArguments() { return 1; } - int createArguments(int) { return 1; } - ExpressionType createSpreadExpression(const JSTokenLocation&, ExpressionType, int, int, int) { return 1; } - int createArgumentsList(const JSTokenLocation&, int) { return 1; } - int createArgumentsList(const JSTokenLocation&, int, int) { return 1; } - Property createProperty(const Identifier* name, int, PropertyNode::Type type, bool complete) + 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, bool) { if (!complete) return Property(type); ASSERT(name); return Property(name, type); } - Property createProperty(VM* vm, double name, int, PropertyNode::Type type, bool complete) + Property createProperty(VM* vm, ParserArena& parserArena, double name, int, PropertyNode::Type type, PropertyNode::PutType, bool complete, SuperBinding, bool) { if (!complete) return Property(type); - return Property(&vm->parserArena->identifierArena().makeNumericIdentifier(vm, name), type); + return Property(&parserArena.identifierArena().makeNumericIdentifier(vm, name), type); } - Property createProperty(VM*, ExpressionNode*, int, PropertyNode::Type type, bool) + Property createProperty(int, int, PropertyNode::Type type, PropertyNode::PutType, bool, SuperBinding, bool) { return Property(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(DeconstructionPattern) { return 1; } - int createFormalParameterList(int, DeconstructionPattern) { 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&, int, int, int, int, int, int, int, int) { return 1; } - int createForOfLoop(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; } - Property createGetterOrSetterProperty(const JSTokenLocation&, PropertyNode::Type type, bool strict, const Identifier* name, int, int, int, int, int, int, int) + 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&, DeclarationStacks::FunctionStack&&) { 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, const JSTokenLocation&, int, int, int, int, int, VariableEnvironment&) { return StatementResult; } + int createForOfLoop(const JSTokenLocation&, int, int, int, const JSTokenLocation&, 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&, DeclarationStacks::FunctionStack&&) { 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>&, bool) { ASSERT(name); if (!strict) return Property(type); return Property(name, type); } - Property createGetterOrSetterProperty(VM* vm, const JSTokenLocation&, PropertyNode::Type type, bool strict, double name, int, int, int, int, int, int, int) + Property createGetterOrSetterProperty(const JSTokenLocation&, PropertyNode::Type type, bool, int, const ParserFunctionInfo<SyntaxChecker>&, bool) + { + return Property(type); + } + Property createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation&, PropertyNode::Type type, bool strict, double name, const ParserFunctionInfo<SyntaxChecker>&, bool) { 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) { @@ -241,7 +304,7 @@ public: // Logic to handle datastructures used during parsing of binary expressions void operatorStackPop(int& operatorStackDepth) { operatorStackDepth--; } - bool operatorStackHasHigherPrecedence(int&, int) { return true; } + bool operatorStackShouldReduce(int) { return true; } BinaryOperand getFromOperandStack(int) { return m_topBinaryExpr; } void shrinkOperandStackBy(int& operandStackDepth, int amount) { operandStackDepth -= amount; } void appendBinaryOperation(const JSTokenLocation&, int& operandStackDepth, int&, BinaryOperand, BinaryOperand) { operandStackDepth++; } @@ -254,43 +317,93 @@ 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 createDeconstructingAssignment(const JSTokenLocation&, int, ExpressionType) + ExpressionType createDestructuringAssignment(const JSTokenLocation&, int, ExpressionType) { - return 1; + return DestructuringAssignment; } ArrayPattern createArrayPattern(const JSTokenLocation&) { - return 1; + return ArrayDestructuring; } void appendArrayPatternSkipEntry(ArrayPattern, const JSTokenLocation&) { } - void appendArrayPatternEntry(ArrayPattern, const JSTokenLocation&, DeconstructionPattern) + 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 1; + return ObjectDestructuring; + } + void appendObjectPatternEntry(ArrayPattern, const JSTokenLocation&, bool, const Identifier&, DestructuringPattern, int) + { + } + void appendObjectPatternEntry(ArrayPattern, const JSTokenLocation&, Expression, DestructuringPattern, Expression) + { } - void appendObjectPatternEntry(ArrayPattern, const JSTokenLocation&, bool, const Identifier&, DeconstructionPattern) + + DestructuringPattern createBindingLocation(const JSTokenLocation&, const Identifier&, const JSTextPosition&, const JSTextPosition&, AssignmentContext) { + return BindingDestructuring; + } + RestPattern createRestParameter(DestructuringPattern, size_t) + { + return RestParameter; } - DeconstructionPattern createBindingLocation(const JSTokenLocation&, const Identifier&, const JSTextPosition&, const JSTextPosition&) + DestructuringPattern createAssignmentElement(const Expression&, const JSTextPosition&, const JSTextPosition&) { - return 1; + 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); + } + + bool shouldSkipPauseLocation(int) const { return true; } + + void setEndOffset(int, int) { } + int endOffset(int) { return 0; } + void setStartOffset(int, int) { } + + JSTextPosition breakpointLocation(int) { return JSTextPosition(-1, 0, 0); } + + void propagateArgumentsUse() { } + private: int m_topBinaryExpr; int m_topUnaryToken; - Vector<int, 8> m_topBinaryExprs; - Vector<int, 8> m_topUnaryTokens; }; -} - -#endif +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/SourceCode.cpp b/Source/JavaScriptCore/parser/UnlinkedSourceCode.cpp index 7c2d6adbc..e1e990b22 100644 --- a/Source/JavaScriptCore/parser/SourceCode.cpp +++ b/Source/JavaScriptCore/parser/UnlinkedSourceCode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,18 +24,19 @@ */ #include "config.h" -#include "SourceCode.h" +#include "UnlinkedSourceCode.h" +#include "JSCInlines.h" #include <wtf/text/CString.h> namespace JSC { -CString SourceCode::toUTF8() const +CString UnlinkedSourceCode::toUTF8() const { if (!m_provider) return CString("", 0); - return m_provider->source().impl()->utf8ForRange(m_startChar, m_endChar - m_startChar); + return m_provider->source().substring(m_startOffset, m_endOffset - m_startOffset).utf8(); } } // namespace JSC diff --git a/Source/JavaScriptCore/parser/UnlinkedSourceCode.h b/Source/JavaScriptCore/parser/UnlinkedSourceCode.h new file mode 100644 index 000000000..e68ce8838 --- /dev/null +++ b/Source/JavaScriptCore/parser/UnlinkedSourceCode.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2008, 2013, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "SourceProvider.h" +#include <wtf/RefPtr.h> + +namespace JSC { + + class UnlinkedSourceCode { + public: + UnlinkedSourceCode() + : m_provider(0) + , m_startOffset(0) + , m_endOffset(0) + { + } + + UnlinkedSourceCode(WTF::HashTableDeletedValueType) + : m_provider(WTF::HashTableDeletedValue) + { + } + + UnlinkedSourceCode(Ref<SourceProvider>&& provider) + : m_provider(WTFMove(provider)) + , m_startOffset(0) + , m_endOffset(m_provider->source().length()) + { + } + + UnlinkedSourceCode(Ref<SourceProvider>&& provider, int startOffset, int endOffset) + : m_provider(WTFMove(provider)) + , m_startOffset(startOffset) + , m_endOffset(endOffset) + { + } + + UnlinkedSourceCode(RefPtr<SourceProvider>&& provider, int startOffset, int endOffset) + : m_provider(WTFMove(provider)) + , m_startOffset(startOffset) + , m_endOffset(endOffset) + { + } + + UnlinkedSourceCode(const UnlinkedSourceCode& other) + : m_provider(other.m_provider) + , m_startOffset(other.m_startOffset) + , m_endOffset(other.m_endOffset) + { + } + + bool isHashTableDeletedValue() const { return m_provider.isHashTableDeletedValue(); } + + unsigned hash() const + { + ASSERT(m_provider); + return m_provider->hash(); + } + + StringView view() const + { + if (!m_provider) + return StringView(); + return m_provider->getRange(m_startOffset, m_endOffset); + } + + CString toUTF8() const; + + bool isNull() const { return !m_provider; } + int startOffset() const { return m_startOffset; } + int endOffset() const { return m_endOffset; } + int length() const { return m_endOffset - m_startOffset; } + + protected: + // FIXME: Make it Ref<SourceProvidier>. + // https://bugs.webkit.org/show_bug.cgi?id=168325 + RefPtr<SourceProvider> m_provider; + int m_startOffset; + int m_endOffset; + }; + +} // namespace JSC diff --git a/Source/JavaScriptCore/parser/VariableEnvironment.cpp b/Source/JavaScriptCore/parser/VariableEnvironment.cpp new file mode 100644 index 000000000..692592cb2 --- /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& value : m_map.values()) + value.setIsCaptured(); +} + +bool VariableEnvironment::hasCapturedVariables() const +{ + if (m_isEverythingCaptured) + return size() > 0; + for (auto& value : m_map.values()) { + if (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..eb0560592 --- /dev/null +++ b/Source/JavaScriptCore/parser/VariableEnvironment.h @@ -0,0 +1,117 @@ +/* + * 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. + */ + +#pragma once + +#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 bool isFunction() const { return m_bits & IsFunction; } + ALWAYS_INLINE bool isParameter() const { return m_bits & IsParameter; } + ALWAYS_INLINE bool isSloppyModeHoistingCandidate() const { return m_bits & IsSloppyModeHoistingCandidate; } + + 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 setIsFunction() { m_bits |= IsFunction; } + ALWAYS_INLINE void setIsParameter() { m_bits |= IsParameter; } + ALWAYS_INLINE void setIsSloppyModeHoistingCandidate() { m_bits |= IsSloppyModeHoistingCandidate; } + + ALWAYS_INLINE void clearIsVar() { m_bits &= ~IsVar; } + +private: + enum Traits : uint16_t { + IsCaptured = 1 << 0, + IsConst = 1 << 1, + IsVar = 1 << 2, + IsLet = 1 << 3, + IsExported = 1 << 4, + IsImported = 1 << 5, + IsImportedNamespace = 1 << 6, + IsFunction = 1 << 7, + IsParameter = 1 << 8, + IsSloppyModeHoistingCandidate = 1 << 9 + }; + uint16_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: + VariableEnvironment() + { } + VariableEnvironment(VariableEnvironment&& other) + : m_map(WTFMove(other.m_map)) + , m_isEverythingCaptured(other.m_isEverythingCaptured) + { } + VariableEnvironment(const VariableEnvironment&) = default; + VariableEnvironment& operator=(const VariableEnvironment&) = default; + + 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 |