< prev index next >

src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java

Print this page

        

*** 26,64 **** --- 26,84 ---- package jdk.nashorn.internal.parser; import static jdk.nashorn.internal.codegen.CompilerConstants.ANON_FUNCTION_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL; import static jdk.nashorn.internal.codegen.CompilerConstants.PROGRAM; + import static jdk.nashorn.internal.parser.TokenType.ARROW; import static jdk.nashorn.internal.parser.TokenType.ASSIGN; import static jdk.nashorn.internal.parser.TokenType.CASE; import static jdk.nashorn.internal.parser.TokenType.CATCH; + import static jdk.nashorn.internal.parser.TokenType.CLASS; import static jdk.nashorn.internal.parser.TokenType.COLON; import static jdk.nashorn.internal.parser.TokenType.COMMARIGHT; + import static jdk.nashorn.internal.parser.TokenType.COMMENT; import static jdk.nashorn.internal.parser.TokenType.CONST; import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX; import static jdk.nashorn.internal.parser.TokenType.DECPREFIX; + import static jdk.nashorn.internal.parser.TokenType.ELLIPSIS; import static jdk.nashorn.internal.parser.TokenType.ELSE; import static jdk.nashorn.internal.parser.TokenType.EOF; import static jdk.nashorn.internal.parser.TokenType.EOL; + import static jdk.nashorn.internal.parser.TokenType.EQ_STRICT; + import static jdk.nashorn.internal.parser.TokenType.ESCSTRING; + import static jdk.nashorn.internal.parser.TokenType.EXPORT; + import static jdk.nashorn.internal.parser.TokenType.EXTENDS; import static jdk.nashorn.internal.parser.TokenType.FINALLY; import static jdk.nashorn.internal.parser.TokenType.FUNCTION; import static jdk.nashorn.internal.parser.TokenType.IDENT; import static jdk.nashorn.internal.parser.TokenType.IF; + import static jdk.nashorn.internal.parser.TokenType.IMPORT; import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX; import static jdk.nashorn.internal.parser.TokenType.LBRACE; + import static jdk.nashorn.internal.parser.TokenType.LBRACKET; import static jdk.nashorn.internal.parser.TokenType.LET; import static jdk.nashorn.internal.parser.TokenType.LPAREN; + import static jdk.nashorn.internal.parser.TokenType.MUL; + import static jdk.nashorn.internal.parser.TokenType.PERIOD; import static jdk.nashorn.internal.parser.TokenType.RBRACE; import static jdk.nashorn.internal.parser.TokenType.RBRACKET; import static jdk.nashorn.internal.parser.TokenType.RPAREN; import static jdk.nashorn.internal.parser.TokenType.SEMICOLON; + import static jdk.nashorn.internal.parser.TokenType.SPREAD_ARRAY; + import static jdk.nashorn.internal.parser.TokenType.STATIC; + import static jdk.nashorn.internal.parser.TokenType.STRING; + import static jdk.nashorn.internal.parser.TokenType.SUPER; import static jdk.nashorn.internal.parser.TokenType.TEMPLATE; import static jdk.nashorn.internal.parser.TokenType.TEMPLATE_HEAD; import static jdk.nashorn.internal.parser.TokenType.TEMPLATE_MIDDLE; import static jdk.nashorn.internal.parser.TokenType.TEMPLATE_TAIL; import static jdk.nashorn.internal.parser.TokenType.TERNARY; + import static jdk.nashorn.internal.parser.TokenType.VAR; + import static jdk.nashorn.internal.parser.TokenType.VOID; import static jdk.nashorn.internal.parser.TokenType.WHILE; + import static jdk.nashorn.internal.parser.TokenType.YIELD; + import static jdk.nashorn.internal.parser.TokenType.YIELD_STAR; import java.io.Serializable; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections;
*** 66,75 **** --- 86,97 ---- import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; + import java.util.Objects; + import java.util.function.Consumer; import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.Namespace; import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.BaseNode; import jdk.nashorn.internal.ir.BinaryNode;
*** 77,100 **** --- 99,126 ---- import jdk.nashorn.internal.ir.BlockStatement; import jdk.nashorn.internal.ir.BreakNode; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; + import jdk.nashorn.internal.ir.ClassNode; import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.DebuggerNode; import jdk.nashorn.internal.ir.EmptyNode; import jdk.nashorn.internal.ir.ErrorNode; import jdk.nashorn.internal.ir.Expression; + import jdk.nashorn.internal.ir.ExpressionList; import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.JoinPredecessorExpression; import jdk.nashorn.internal.ir.LabelNode; + import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; + import jdk.nashorn.internal.ir.Module; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyKey; import jdk.nashorn.internal.ir.PropertyNode; import jdk.nashorn.internal.ir.ReturnNode;
*** 108,117 **** --- 134,144 ---- import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; + import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.JSErrorType; import jdk.nashorn.internal.runtime.ParserException; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
*** 252,261 **** --- 279,296 ---- public FunctionNode parse() { return parse(PROGRAM.symbolName(), 0, source.getLength(), false); } /** + * Set up first token. Skips opening EOL. + */ + private void scanFirstToken() { + k = -1; + next(); + } + + /** * Execute parse and return the resulting function node. * Errors will be thrown and the error manager will contain information * if parsing should fail * * This should be used to create one and only one function node
*** 278,290 **** stream = new TokenStream(); lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, env._es6, reparsedFunction != null); lexer.line = lexer.pendingLine = lineOffset + 1; line = lineOffset; ! // Set up first token (skips opening EOL.) ! k = -1; ! next(); // Begin parse. return program(scriptName, allowPropertyFunction); } catch (final Exception e) { handleParseException(e); --- 313,323 ---- stream = new TokenStream(); lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, env._es6, reparsedFunction != null); lexer.line = lexer.pendingLine = lineOffset + 1; line = lineOffset; ! scanFirstToken(); // Begin parse. return program(scriptName, allowPropertyFunction); } catch (final Exception e) { handleParseException(e);
*** 299,308 **** --- 332,379 ---- } } } /** + * Parse and return the resulting module. + * Errors will be thrown and the error manager will contain information + * if parsing should fail + * + * @param moduleName name for the module, given to the parsed FunctionNode + * @param startPos start position in source + * @param len length of parse + * + * @return function node resulting from successful parse + */ + public FunctionNode parseModule(final String moduleName, final int startPos, final int len) { + try { + stream = new TokenStream(); + lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions, env._es6, reparsedFunction != null); + lexer.line = lexer.pendingLine = lineOffset + 1; + line = lineOffset; + + scanFirstToken(); + // Begin parse. + return module(moduleName); + } catch (final Exception e) { + handleParseException(e); + + return null; + } + } + + /** + * Entry point for parsing a module. + * + * @param moduleName the module name + * @return the parsed module + */ + public FunctionNode parseModule(final String moduleName) { + return parseModule(moduleName, 0, source.getLength()); + } + + /** * Parse and return the list of function parameter list. A comma * separated list of function parameter identifiers is expected to be parsed. * Errors will be thrown and the error manager will contain information * if parsing should fail. This method is used to check if parameter Strings * passed to "Function" constructor is a valid or not.
*** 312,326 **** public List<IdentNode> parseFormalParameterList() { try { stream = new TokenStream(); lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions, env._es6); ! // Set up first token (skips opening EOL.) ! k = -1; ! next(); ! return formalParameterList(TokenType.EOF); } catch (final Exception e) { handleParseException(e); return null; } } --- 383,395 ---- public List<IdentNode> parseFormalParameterList() { try { stream = new TokenStream(); lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions, env._es6); ! scanFirstToken(); ! return formalParameterList(TokenType.EOF, false); } catch (final Exception e) { handleParseException(e); return null; } }
*** 337,349 **** try { stream = new TokenStream(); lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions, env._es6); final int functionLine = line; ! // Set up first token (skips opening EOL.) ! k = -1; ! next(); // Make a fake token for the function. final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength()); // Set up the function to append elements. --- 406,416 ---- try { stream = new TokenStream(); lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions, env._es6); final int functionLine = line; ! scanFirstToken(); // Make a fake token for the function. final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength()); // Set up the function to append elements.
*** 430,440 **** e.printStackTrace(env.getErr()); } } // Skip to a recovery point. ! loop: while (true) { switch (type) { case EOF: // Can not go any further. break loop; --- 497,507 ---- e.printStackTrace(env.getErr()); } } // Skip to a recovery point. ! loop: while (true) { switch (type) { case EOF: // Can not go any further. break loop;
*** 472,482 **** assert ident.getName() != null; sb.append(ident.getName()); final String name = namespace.uniqueName(sb.toString()); ! assert parentFunction != null || name.equals(PROGRAM.symbolName()) || name.startsWith(RecompilableScriptFunctionData.RECOMPILATION_PREFIX) : "name = " + name; int flags = 0; if (isStrictMode) { flags |= FunctionNode.IS_STRICT; } --- 539,549 ---- assert ident.getName() != null; sb.append(ident.getName()); final String name = namespace.uniqueName(sb.toString()); ! assert parentFunction != null || name.equals(PROGRAM.symbolName()) : "name = " + name; int flags = 0; if (isStrictMode) { flags |= FunctionNode.IS_STRICT; }
*** 487,497 **** final ParserContextFunctionNode functionNode = new ParserContextFunctionNode(functionToken, ident, name, namespace, functionLine, kind, parameters); functionNode.setFlag(flags); return functionNode; } ! private FunctionNode createFunctionNode(final ParserContextFunctionNode function, final long startToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind, final int functionLine, final Block body){ // Start new block. final FunctionNode functionNode = new FunctionNode( source, functionLine, --- 554,565 ---- final ParserContextFunctionNode functionNode = new ParserContextFunctionNode(functionToken, ident, name, namespace, functionLine, kind, parameters); functionNode.setFlag(flags); return functionNode; } ! private FunctionNode createFunctionNode(final ParserContextFunctionNode function, final long startToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind, final int functionLine, final Block body) { ! // assert body.isFunctionBody() || body.getFlag(Block.IS_PARAMETER_BLOCK) && ((BlockStatement) body.getLastStatement()).getBlock().isFunctionBody(); // Start new block. final FunctionNode functionNode = new FunctionNode( source, functionLine,
*** 504,514 **** function.getName(), parameters, kind, function.getFlags(), body, ! function.getEndParserState()); printAST(functionNode); return functionNode; } --- 572,584 ---- function.getName(), parameters, kind, function.getFlags(), body, ! function.getEndParserState(), ! function.getModule(), ! function.getDebugFlags()); printAST(functionNode); return functionNode; }
*** 542,568 **** // Block closing brace. if (needsBraces) { expect(RBRACE); } ! final int flags = newBlock.getFlags() | (needsBraces? 0 : Block.IS_SYNTHETIC); return new Block(blockToken, finish, flags, newBlock.getStatements()); } /** * Get all the statements generated by a single statement. * @return Statements. */ private Block getStatement() { if (type == LBRACE) { return getBlock(true); } // Set up new block. Captures first token. final ParserContextBlockNode newBlock = newBlock(); try { ! statement(false, false, true); } finally { restoreBlock(newBlock); } return new Block(newBlock.getToken(), finish, newBlock.getFlags() | Block.IS_SYNTHETIC, newBlock.getStatements()); } --- 612,654 ---- // Block closing brace. if (needsBraces) { expect(RBRACE); } ! final int flags = newBlock.getFlags() | (needsBraces ? 0 : Block.IS_SYNTHETIC); return new Block(blockToken, finish, flags, newBlock.getStatements()); } + /** + * Get the statements in a case clause. + */ + private List<Statement> caseStatementList() { + final ParserContextBlockNode newBlock = newBlock(); + try { + statementList(); + } finally { + restoreBlock(newBlock); + } + return newBlock.getStatements(); + } /** * Get all the statements generated by a single statement. * @return Statements. */ private Block getStatement() { + return getStatement(false); + } + + private Block getStatement(boolean labelledStatement) { if (type == LBRACE) { return getBlock(true); } // Set up new block. Captures first token. final ParserContextBlockNode newBlock = newBlock(); try { ! statement(false, false, true, labelledStatement); } finally { restoreBlock(newBlock); } return new Block(newBlock.getToken(), finish, newBlock.getFlags() | Block.IS_SYNTHETIC, newBlock.getStatements()); }
*** 574,605 **** private void detectSpecialFunction(final IdentNode ident) { final String name = ident.getName(); if (EVAL.symbolName().equals(name)) { markEval(lc); } } /** * Detect use of special properties. * @param ident Referenced property. */ private void detectSpecialProperty(final IdentNode ident) { if (isArguments(ident)) { ! lc.getCurrentFunction().setFlag(FunctionNode.USES_ARGUMENTS); } } private boolean useBlockScope() { return env._es6; } private static boolean isArguments(final String name) { return ARGUMENTS_NAME.equals(name); } ! private static boolean isArguments(final IdentNode ident) { return isArguments(ident.getName()); } /** * Tells whether a IdentNode can be used as L-value of an assignment --- 660,699 ---- private void detectSpecialFunction(final IdentNode ident) { final String name = ident.getName(); if (EVAL.symbolName().equals(name)) { markEval(lc); + } else if (SUPER.getName().equals(name)) { + assert ident.isDirectSuper(); + markSuperCall(lc); } } /** * Detect use of special properties. * @param ident Referenced property. */ private void detectSpecialProperty(final IdentNode ident) { if (isArguments(ident)) { ! // skip over arrow functions, e.g. function f() { return (() => arguments.length)(); } ! getCurrentNonArrowFunction().setFlag(FunctionNode.USES_ARGUMENTS); } } private boolean useBlockScope() { return env._es6; } + private boolean isES6() { + return env._es6; + } + private static boolean isArguments(final String name) { return ARGUMENTS_NAME.equals(name); } ! static boolean isArguments(final IdentNode ident) { return isArguments(ident.getName()); } /** * Tells whether a IdentNode can be used as L-value of an assignment
*** 632,655 **** case ASSIGN_MUL: case ASSIGN_SAR: case ASSIGN_SHL: case ASSIGN_SHR: case ASSIGN_SUB: - if (!(lhs instanceof AccessNode || - lhs instanceof IndexNode || - lhs instanceof IdentNode)) { - return referenceError(lhs, rhs, env._early_lvalue_error); - } - if (lhs instanceof IdentNode) { if (!checkIdentLValue((IdentNode)lhs)) { return referenceError(lhs, rhs, false); } ! verifyStrictIdent((IdentNode)lhs, "assignment"); ! } break; ! default: break; } // Build up node. --- 726,749 ---- case ASSIGN_MUL: case ASSIGN_SAR: case ASSIGN_SHL: case ASSIGN_SHR: case ASSIGN_SUB: if (lhs instanceof IdentNode) { if (!checkIdentLValue((IdentNode)lhs)) { return referenceError(lhs, rhs, false); } ! verifyIdent((IdentNode)lhs, "assignment"); break; ! } else if (lhs instanceof AccessNode || lhs instanceof IndexNode) { ! break; ! } else if (opType == ASSIGN && isDestructuringLhs(lhs)) { ! verifyDestructuringAssignmentPattern(lhs, "assignment"); ! break; ! } else { ! return referenceError(lhs, rhs, env._early_lvalue_error); ! } default: break; } // Build up node.
*** 657,666 **** --- 751,868 ---- return new BinaryNode(op, new JoinPredecessorExpression(lhs), new JoinPredecessorExpression(rhs)); } return new BinaryNode(op, lhs, rhs); } + private boolean isDestructuringLhs(Expression lhs) { + if (lhs instanceof ObjectNode || lhs instanceof LiteralNode.ArrayLiteralNode) { + return isES6(); + } + return false; + } + + private void verifyDestructuringAssignmentPattern(Expression pattern, String contextString) { + assert pattern instanceof ObjectNode || pattern instanceof LiteralNode.ArrayLiteralNode; + pattern.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + @Override + public boolean enterLiteralNode(LiteralNode<?> literalNode) { + if (literalNode.isArray()) { + boolean restElement = false; + for (Expression element : literalNode.getElementExpressions()) { + if (element != null) { + if (restElement) { + throw error(String.format("Unexpected element after rest element"), element.getToken()); + } + if (element.isTokenType(SPREAD_ARRAY)) { + restElement = true; + Expression lvalue = ((UnaryNode) element).getExpression(); + if (!checkValidLValue(lvalue, contextString)) { + throw error(AbstractParser.message("invalid.lvalue"), lvalue.getToken()); + } + } + element.accept(this); + } + } + return false; + } else { + return enterDefault(literalNode); + } + } + + @Override + public boolean enterObjectNode(ObjectNode objectNode) { + return true; + } + + @Override + public boolean enterPropertyNode(PropertyNode propertyNode) { + if (propertyNode.getValue() != null) { + propertyNode.getValue().accept(this); + return false; + } else { + return enterDefault(propertyNode); + } + } + + @Override + public boolean enterIdentNode(IdentNode identNode) { + verifyIdent(identNode, contextString); + if (!checkIdentLValue(identNode)) { + referenceError(identNode, null, true); + return false; + } + return false; + } + + @Override + public boolean enterAccessNode(AccessNode accessNode) { + return false; + } + + @Override + public boolean enterIndexNode(IndexNode indexNode) { + return false; + } + + @Override + public boolean enterBinaryNode(BinaryNode binaryNode) { + if (binaryNode.isTokenType(ASSIGN)) { + binaryNode.lhs().accept(this); + // Initializer(rhs) can be any AssignmentExpression + return false; + } else { + return enterDefault(binaryNode); + } + } + + @Override + public boolean enterUnaryNode(UnaryNode unaryNode) { + if (unaryNode.isTokenType(SPREAD_ARRAY)) { + // rest element + return true; + } else { + return enterDefault(unaryNode); + } + } + + @Override + protected boolean enterDefault(Node node) { + throw error(String.format("unexpected node in AssignmentPattern: %s", node)); + } + }); + } + + private static Expression newBinaryExpression(final long op, final Expression lhs, final Expression rhs) { + final TokenType opType = Token.descType(op); + + // Build up node. + if (BinaryNode.isLogical(opType)) { + return new BinaryNode(op, new JoinPredecessorExpression(lhs), new JoinPredecessorExpression(rhs)); + } + return new BinaryNode(op, lhs, rhs); + } + /** * Reduce increment/decrement to simpler operations. * @param firstToken First token. * @param tokenType Operation token (INCPREFIX/DEC.)
*** 715,725 **** addFunctionDeclarations(script); functionDeclarations = null; restoreBlock(body); body.setFlag(Block.NEEDS_SCOPE); ! final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC, body.getStatements()); lc.pop(script); script.setLastToken(token); expect(EOF); --- 917,927 ---- addFunctionDeclarations(script); functionDeclarations = null; restoreBlock(body); body.setFlag(Block.NEEDS_SCOPE); ! final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC | Block.IS_BODY, body.getStatements()); lc.pop(script); script.setLastToken(token); expect(EOF);
*** 774,784 **** break; } try { // Get the next element. ! statement(true, allowPropertyFunction, false); allowPropertyFunction = false; // check for directive prologues if (checkDirective) { // skip any debug statement like line number to get actual first line --- 976,986 ---- break; } try { // Get the next element. ! statement(true, allowPropertyFunction, false, false); allowPropertyFunction = false; // check for directive prologues if (checkDirective) { // skip any debug statement like line number to get actual first line
*** 814,833 **** getValue(statement.getToken()); } // verify that function name as well as parameter names // satisfy strict mode restrictions. ! verifyStrictIdent(function.getIdent(), "function name"); for (final IdentNode param : function.getParameters()) { ! verifyStrictIdent(param, "function parameter"); } } } else if (Context.DEBUG) { ! final int flag = FunctionNode.getDirectiveFlag(directive); ! if (flag != 0) { final ParserContextFunctionNode function = lc.getCurrentFunction(); ! function.setFlag(flag); } } } } } catch (final Exception e) { --- 1016,1035 ---- getValue(statement.getToken()); } // verify that function name as well as parameter names // satisfy strict mode restrictions. ! verifyIdent(function.getIdent(), "function name"); for (final IdentNode param : function.getParameters()) { ! verifyIdent(param, "function parameter"); } } } else if (Context.DEBUG) { ! final int debugFlag = FunctionNode.getDirectiveFlag(directive); ! if (debugFlag != 0) { final ParserContextFunctionNode function = lc.getCurrentFunction(); ! function.setDebugFlag(debugFlag); } } } } } catch (final Exception e) {
*** 847,894 **** isStrictMode = oldStrictMode; } } /** * Statement : ! * Block * VariableStatement * EmptyStatement * ExpressionStatement * IfStatement ! * IterationStatement * ContinueStatement * BreakStatement * ReturnStatement * WithStatement * LabelledStatement - * SwitchStatement * ThrowStatement * TryStatement * DebuggerStatement * ! * see 12 * ! * Parse any of the basic statement types. */ private void statement() { ! statement(false, false, false); } /** * @param topLevel does this statement occur at the "top level" of a script or a function? * @param allowPropertyFunction allow property "get" and "set" functions? * @param singleStatement are we in a single statement context? */ ! private void statement(final boolean topLevel, final boolean allowPropertyFunction, final boolean singleStatement) { ! if (type == FUNCTION) { ! // As per spec (ECMA section 12), function declarations as arbitrary statement ! // is not "portable". Implementation can issue a warning or disallow the same. ! functionExpression(true, topLevel); ! return; ! } ! switch (type) { case LBRACE: block(); break; case VAR: --- 1049,1113 ---- isStrictMode = oldStrictMode; } } /** + * Parse any of the basic statement types. + * * Statement : ! * BlockStatement * VariableStatement * EmptyStatement * ExpressionStatement * IfStatement ! * BreakableStatement * ContinueStatement * BreakStatement * ReturnStatement * WithStatement * LabelledStatement * ThrowStatement * TryStatement * DebuggerStatement * ! * BreakableStatement : ! * IterationStatement ! * SwitchStatement ! * ! * BlockStatement : ! * Block ! * ! * Block : ! * { StatementList opt } * ! * StatementList : ! * StatementListItem ! * StatementList StatementListItem ! * ! * StatementItem : ! * Statement ! * Declaration ! * ! * Declaration : ! * HoistableDeclaration ! * ClassDeclaration ! * LexicalDeclaration ! * ! * HoistableDeclaration : ! * FunctionDeclaration ! * GeneratorDeclaration */ private void statement() { ! statement(false, false, false, false); } /** * @param topLevel does this statement occur at the "top level" of a script or a function? * @param allowPropertyFunction allow property "get" and "set" functions? * @param singleStatement are we in a single statement context? */ ! private void statement(final boolean topLevel, final boolean allowPropertyFunction, final boolean singleStatement, final boolean labelledStatement) { switch (type) { case LBRACE: block(); break; case VAR:
*** 916,928 **** breakStatement(); break; case RETURN: returnStatement(); break; - case YIELD: - yieldStatement(); - break; case WITH: withStatement(); break; case SWITCH: switchStatement(); --- 1135,1144 ----
*** 939,955 **** case RPAREN: case RBRACKET: case EOF: expect(SEMICOLON); break; default: ! if (useBlockScope() && (type == LET || type == CONST)) { if (singleStatement) { throw error(AbstractParser.message("expected.stmt", type.getName() + " declaration"), token); } variableStatement(type); break; } if (env._const_as_var && type == CONST) { variableStatement(TokenType.VAR); break; } --- 1155,1190 ---- case RPAREN: case RBRACKET: case EOF: expect(SEMICOLON); break; + case FUNCTION: + // As per spec (ECMA section 12), function declarations as arbitrary statement + // is not "portable". Implementation can issue a warning or disallow the same. + if (singleStatement) { + // ES6 B.3.2 Labelled Function Declarations + // It is a Syntax Error if any strict mode source code matches this rule: + // LabelledItem : FunctionDeclaration. + if (!labelledStatement || isStrictMode) { + throw error(AbstractParser.message("expected.stmt", "function declaration"), token); + } + } + functionExpression(true, topLevel || labelledStatement); + return; default: ! if (useBlockScope() && (type == LET && lookaheadIsLetDeclaration(false) || type == CONST)) { if (singleStatement) { throw error(AbstractParser.message("expected.stmt", type.getName() + " declaration"), token); } variableStatement(type); break; + } else if (type == CLASS && isES6()) { + if (singleStatement) { + throw error(AbstractParser.message("expected.stmt", "class declaration"), token); + } + classDeclaration(false); + break; } if (env._const_as_var && type == CONST) { variableStatement(TokenType.VAR); break; }
*** 961,975 **** } if(allowPropertyFunction) { final String ident = (String)getValue(); final long propertyToken = token; final int propertyLine = line; ! if("get".equals(ident)) { next(); addPropertyFunctionStatement(propertyGetterFunction(propertyToken, propertyLine)); return; ! } else if("set".equals(ident)) { next(); addPropertyFunctionStatement(propertySetterFunction(propertyToken, propertyLine)); return; } } --- 1196,1210 ---- } if(allowPropertyFunction) { final String ident = (String)getValue(); final long propertyToken = token; final int propertyLine = line; ! if ("get".equals(ident)) { next(); addPropertyFunctionStatement(propertyGetterFunction(propertyToken, propertyLine)); return; ! } else if ("set".equals(ident)) { next(); addPropertyFunctionStatement(propertySetterFunction(propertyToken, propertyLine)); return; } }
*** 984,1134 **** final FunctionNode fn = propertyFunction.functionNode; functionDeclarations.add(new ExpressionStatement(fn.getLineNumber(), fn.getToken(), finish, fn)); } /** ! * block : ! * { StatementList? } ! * ! * see 12.1 ! * ! * Parse a statement block. */ ! private void block() { ! appendStatement(new BlockStatement(line, getBlock(true))); } /** ! * StatementList : ! * Statement ! * StatementList Statement ! * ! * See 12.1 ! * ! * Parse a list of statements. */ ! private void statementList() { ! // Accumulate statements until end of list. */ ! loop: ! while (type != EOF) { ! switch (type) { ! case EOF: ! case CASE: ! case DEFAULT: ! case RBRACE: ! break loop; ! default: ! break; } ! // Get next statement. ! statement(); } } ! /** ! * Make sure that in strict mode, the identifier name used is allowed. ! * ! * @param ident Identifier that is verified ! * @param contextString String used in error message to give context to the user ! */ ! private void verifyStrictIdent(final IdentNode ident, final String contextString) { ! if (isStrictMode) { ! switch (ident.getName()) { ! case "eval": ! case "arguments": ! throw error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken()); ! default: ! break; } ! if (ident.isFutureStrictName()) { ! throw error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken()); } } } ! /* ! * VariableStatement : ! * var VariableDeclarationList ; ! * ! * VariableDeclarationList : ! * VariableDeclaration ! * VariableDeclarationList , VariableDeclaration ! * ! * VariableDeclaration : ! * Identifier Initializer? ! * ! * Initializer : ! * = AssignmentExpression ! * ! * See 12.2 * ! * Parse a VAR statement. ! * @param isStatement True if a statement (not used in a FOR.) */ ! private List<VarNode> variableStatement(final TokenType varType) { ! return variableStatement(varType, true, -1); ! } ! ! private List<VarNode> variableStatement(final TokenType varType, final boolean isStatement, final int sourceOrder) { ! // VAR tested in caller. next(); ! ! final List<VarNode> vars = new ArrayList<>(); ! int varFlags = 0; ! if (varType == LET) { ! varFlags |= VarNode.IS_LET; ! } else if (varType == CONST) { ! varFlags |= VarNode.IS_CONST; } ! while (true) { ! // Get starting token. ! final int varLine = line; ! final long varToken = token; ! // Get name of var. ! final IdentNode name = getIdent(); ! verifyStrictIdent(name, "variable name"); ! ! // Assume no init. ! Expression init = null; ! // Look for initializer assignment. if (type == ASSIGN) { next(); // Get initializer expression. Suppress IN if not statement. ! defaultNames.push(name); try { init = assignmentExpression(!isStatement); } finally { defaultNames.pop(); } ! } else if (varType == CONST && isStatement) { ! throw error(AbstractParser.message("missing.const.assignment", name.getName())); } // Only set declaration flag on lexically scoped let/const as it adds runtime overhead. ! final IdentNode actualName = varType == LET || varType == CONST ? name.setIsDeclaredHere() : name; ! // Allocate var node. ! final VarNode var = new VarNode(varLine, varToken, sourceOrder, finish, actualName, init, varFlags); ! vars.add(var); appendStatement(var); if (type != COMMARIGHT) { break; } next(); } // If is a statement then handle end of line. if (isStatement) { endOfLine(); } ! return vars; } /** * EmptyStatement : * ; --- 1219,1813 ---- final FunctionNode fn = propertyFunction.functionNode; functionDeclarations.add(new ExpressionStatement(fn.getLineNumber(), fn.getToken(), finish, fn)); } /** ! * ClassDeclaration[Yield, Default] : ! * class BindingIdentifier[?Yield] ClassTail[?Yield] ! * [+Default] class ClassTail[?Yield] */ ! private ClassNode classDeclaration(boolean isDefault) { ! int classLineNumber = line; ! ! ClassNode classExpression = classExpression(!isDefault); ! ! if (!isDefault) { ! VarNode classVar = new VarNode(classLineNumber, classExpression.getToken(), classExpression.getIdent().getFinish(), classExpression.getIdent(), classExpression, VarNode.IS_CONST); ! appendStatement(classVar); ! } ! return classExpression; } /** ! * ClassExpression[Yield] : ! * class BindingIdentifier[?Yield]opt ClassTail[?Yield] */ ! private ClassNode classExpression(boolean isStatement) { ! assert type == CLASS; ! int classLineNumber = line; ! long classToken = token; ! next(); ! ! IdentNode className = null; ! if (isStatement || type == IDENT) { ! className = getIdent(); } ! return classTail(classLineNumber, classToken, className); } + + private static final class ClassElementKey { + private final boolean isStatic; + private final String propertyName; + + private ClassElementKey(boolean isStatic, String propertyName) { + this.isStatic = isStatic; + this.propertyName = propertyName; } ! @Override ! public int hashCode() { ! final int prime = 31; ! int result = 1; ! result = prime * result + (isStatic ? 1231 : 1237); ! result = prime * result + ((propertyName == null) ? 0 : propertyName.hashCode()); ! return result; } ! @Override ! public boolean equals(Object obj) { ! if (obj instanceof ClassElementKey) { ! ClassElementKey other = (ClassElementKey) obj; ! return this.isStatic == other.isStatic && Objects.equals(this.propertyName, other.propertyName); } + return false; } } ! /** ! * Parse ClassTail and ClassBody. * ! * ClassTail[Yield] : ! * ClassHeritage[?Yield]opt { ClassBody[?Yield]opt } ! * ClassHeritage[Yield] : ! * extends LeftHandSideExpression[?Yield] ! * ! * ClassBody[Yield] : ! * ClassElementList[?Yield] ! * ClassElementList[Yield] : ! * ClassElement[?Yield] ! * ClassElementList[?Yield] ClassElement[?Yield] ! * ClassElement[Yield] : ! * MethodDefinition[?Yield] ! * static MethodDefinition[?Yield] ! * ; */ ! private ClassNode classTail(final int classLineNumber, final long classToken, final IdentNode className) { ! final boolean oldStrictMode = isStrictMode; ! isStrictMode = true; ! try { ! Expression classHeritage = null; ! if (type == EXTENDS) { next(); ! classHeritage = leftHandSideExpression(); } ! expect(LBRACE); ! PropertyNode constructor = null; ! final ArrayList<PropertyNode> classElements = new ArrayList<>(); ! final Map<ClassElementKey, Integer> keyToIndexMap = new HashMap<>(); ! for (;;) { ! if (type == SEMICOLON) { ! next(); ! continue; ! } ! if (type == RBRACE) { ! break; ! } ! final long classElementToken = token; ! boolean isStatic = false; ! if (type == STATIC) { ! isStatic = true; ! next(); ! } ! boolean generator = false; ! if (isES6() && type == MUL) { ! generator = true; ! next(); ! } ! final PropertyNode classElement = methodDefinition(isStatic, classHeritage != null, generator); ! if (classElement.isComputed()) { ! classElements.add(classElement); ! } else if (!classElement.isStatic() && classElement.getKeyName().equals("constructor")) { ! if (constructor == null) { ! constructor = classElement; ! } else { ! throw error(AbstractParser.message("multiple.constructors"), classElementToken); ! } ! } else { ! // Check for duplicate method definitions and combine accessor methods. ! // In ES6, a duplicate is never an error regardless of strict mode (in consequence of computed property names). ! ! final ClassElementKey key = new ClassElementKey(classElement.isStatic(), classElement.getKeyName()); ! final Integer existing = keyToIndexMap.get(key); ! ! if (existing == null) { ! keyToIndexMap.put(key, classElements.size()); ! classElements.add(classElement); ! } else { ! final PropertyNode existingProperty = classElements.get(existing); ! ! final Expression value = classElement.getValue(); ! final FunctionNode getter = classElement.getGetter(); ! final FunctionNode setter = classElement.getSetter(); ! ! if (value != null || existingProperty.getValue() != null) { ! keyToIndexMap.put(key, classElements.size()); ! classElements.add(classElement); ! } else if (getter != null) { ! assert existingProperty.getGetter() != null || existingProperty.getSetter() != null; ! classElements.set(existing, existingProperty.setGetter(getter)); ! } else if (setter != null) { ! assert existingProperty.getGetter() != null || existingProperty.getSetter() != null; ! classElements.set(existing, existingProperty.setSetter(setter)); ! } ! } ! } ! } ! ! final long lastToken = token; ! expect(RBRACE); ! ! if (constructor == null) { ! constructor = createDefaultClassConstructor(classLineNumber, classToken, lastToken, className, classHeritage != null); ! } ! ! classElements.trimToSize(); ! return new ClassNode(classLineNumber, classToken, finish, className, classHeritage, constructor, classElements); ! } finally { ! isStrictMode = oldStrictMode; ! } ! } ! ! private PropertyNode createDefaultClassConstructor(int classLineNumber, long classToken, long lastToken, IdentNode className, boolean subclass) { ! final int ctorFinish = finish; ! final List<Statement> statements; ! final List<IdentNode> parameters; ! final long identToken = Token.recast(classToken, TokenType.IDENT); ! if (subclass) { ! final IdentNode superIdent = createIdentNode(identToken, ctorFinish, SUPER.getName()).setIsDirectSuper(); ! final IdentNode argsIdent = createIdentNode(identToken, ctorFinish, "args").setIsRestParameter(); ! final Expression spreadArgs = new UnaryNode(Token.recast(classToken, TokenType.SPREAD_ARGUMENT), argsIdent); ! final CallNode superCall = new CallNode(classLineNumber, classToken, ctorFinish, superIdent, Collections.singletonList(spreadArgs), false); ! statements = Collections.singletonList(new ExpressionStatement(classLineNumber, classToken, ctorFinish, superCall)); ! parameters = Collections.singletonList(argsIdent); ! } else { ! statements = Collections.emptyList(); ! parameters = Collections.emptyList(); ! } ! ! final Block body = new Block(classToken, ctorFinish, Block.IS_BODY, statements); ! final IdentNode ctorName = className != null ? className : createIdentNode(identToken, ctorFinish, "constructor"); ! final ParserContextFunctionNode function = createParserContextFunctionNode(ctorName, classToken, FunctionNode.Kind.NORMAL, classLineNumber, parameters); ! function.setLastToken(lastToken); ! ! function.setFlag(FunctionNode.ES6_IS_METHOD); ! function.setFlag(FunctionNode.ES6_IS_CLASS_CONSTRUCTOR); ! if (subclass) { ! function.setFlag(FunctionNode.ES6_IS_SUBCLASS_CONSTRUCTOR); ! function.setFlag(FunctionNode.ES6_HAS_DIRECT_SUPER); ! } ! if (className == null) { ! function.setFlag(FunctionNode.IS_ANONYMOUS); ! } ! ! final PropertyNode constructor = new PropertyNode(classToken, ctorFinish, ctorName, createFunctionNode( ! function, ! classToken, ! ctorName, ! parameters, ! FunctionNode.Kind.NORMAL, ! classLineNumber, ! body ! ), null, null, false, false); ! return constructor; ! } ! ! private PropertyNode methodDefinition(final boolean isStatic, final boolean subclass, final boolean generator) { ! final long methodToken = token; ! final int methodLine = line; ! final boolean computed = type == LBRACKET; ! final boolean isIdent = type == IDENT; ! final Expression propertyName = propertyName(); ! int flags = FunctionNode.ES6_IS_METHOD; ! if (!computed) { ! final String name = ((PropertyKey)propertyName).getPropertyName(); ! if (!generator && isIdent && type != LPAREN && name.equals("get")) { ! final PropertyFunction methodDefinition = propertyGetterFunction(methodToken, methodLine, flags); ! verifyAllowedMethodName(methodDefinition.key, isStatic, methodDefinition.computed, generator, true); ! return new PropertyNode(methodToken, finish, methodDefinition.key, null, methodDefinition.functionNode, null, isStatic, methodDefinition.computed); ! } else if (!generator && isIdent && type != LPAREN && name.equals("set")) { ! final PropertyFunction methodDefinition = propertySetterFunction(methodToken, methodLine, flags); ! verifyAllowedMethodName(methodDefinition.key, isStatic, methodDefinition.computed, generator, true); ! return new PropertyNode(methodToken, finish, methodDefinition.key, null, null, methodDefinition.functionNode, isStatic, methodDefinition.computed); ! } else { ! if (!isStatic && !generator && name.equals("constructor")) { ! flags |= FunctionNode.ES6_IS_CLASS_CONSTRUCTOR; ! if (subclass) { ! flags |= FunctionNode.ES6_IS_SUBCLASS_CONSTRUCTOR; ! } ! } ! verifyAllowedMethodName(propertyName, isStatic, computed, generator, false); ! } ! } ! final PropertyFunction methodDefinition = propertyMethodFunction(propertyName, methodToken, methodLine, generator, flags, computed); ! return new PropertyNode(methodToken, finish, methodDefinition.key, methodDefinition.functionNode, null, null, isStatic, computed); ! } ! ! /** ! * ES6 14.5.1 Static Semantics: Early Errors. ! */ ! private void verifyAllowedMethodName(final Expression key, final boolean isStatic, final boolean computed, final boolean generator, final boolean accessor) { ! if (!computed) { ! if (!isStatic && generator && ((PropertyKey) key).getPropertyName().equals("constructor")) { ! throw error(AbstractParser.message("generator.constructor"), key.getToken()); ! } ! if (!isStatic && accessor && ((PropertyKey) key).getPropertyName().equals("constructor")) { ! throw error(AbstractParser.message("accessor.constructor"), key.getToken()); ! } ! if (isStatic && ((PropertyKey) key).getPropertyName().equals("prototype")) { ! throw error(AbstractParser.message("static.prototype.method"), key.getToken()); ! } ! } ! } ! ! /** ! * block : ! * { StatementList? } ! * ! * see 12.1 ! * ! * Parse a statement block. ! */ ! private void block() { ! appendStatement(new BlockStatement(line, getBlock(true))); ! } ! ! /** ! * StatementList : ! * Statement ! * StatementList Statement ! * ! * See 12.1 ! * ! * Parse a list of statements. ! */ ! private void statementList() { ! // Accumulate statements until end of list. */ ! loop: ! while (type != EOF) { ! switch (type) { ! case EOF: ! case CASE: ! case DEFAULT: ! case RBRACE: ! break loop; ! default: ! break; ! } ! ! // Get next statement. ! statement(); ! } ! } ! ! /** ! * Make sure that the identifier name used is allowed. ! * ! * @param ident Identifier that is verified ! * @param contextString String used in error message to give context to the user ! */ ! private void verifyIdent(final IdentNode ident, final String contextString) { ! verifyStrictIdent(ident, contextString); ! if (isES6()) { ! final TokenType tokenType = TokenLookup.lookupKeyword(ident.getName().toCharArray(), 0, ident.getName().length()); ! if (tokenType != IDENT && tokenType.getKind() != TokenKind.FUTURESTRICT) { ! throw error(expectMessage(IDENT)); ! } ! } ! } ! ! /** ! * Make sure that in strict mode, the identifier name used is allowed. ! * ! * @param ident Identifier that is verified ! * @param contextString String used in error message to give context to the user ! */ ! private void verifyStrictIdent(final IdentNode ident, final String contextString) { ! if (isStrictMode) { ! switch (ident.getName()) { ! case "eval": ! case "arguments": ! throw error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken()); ! default: ! break; ! } ! ! if (ident.isFutureStrictName()) { ! throw error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken()); ! } ! } ! } ! ! /* ! * VariableStatement : ! * var VariableDeclarationList ; ! * ! * VariableDeclarationList : ! * VariableDeclaration ! * VariableDeclarationList , VariableDeclaration ! * ! * VariableDeclaration : ! * Identifier Initializer? ! * ! * Initializer : ! * = AssignmentExpression ! * ! * See 12.2 ! * ! * Parse a VAR statement. ! * @param isStatement True if a statement (not used in a FOR.) ! */ ! private void variableStatement(final TokenType varType) { ! variableDeclarationList(varType, true, -1); ! } ! ! private List<Expression> variableDeclarationList(final TokenType varType, final boolean isStatement, final int sourceOrder) { ! // VAR tested in caller. ! assert varType == VAR || varType == LET || varType == CONST; ! next(); ! ! final List<Expression> bindings = new ArrayList<>(); ! int varFlags = 0; ! if (varType == LET) { ! varFlags |= VarNode.IS_LET; ! } else if (varType == CONST) { ! varFlags |= VarNode.IS_CONST; ! } ! ! Expression missingAssignment = null; ! while (true) { ! // Get starting token. ! final int varLine = line; ! final long varToken = token; ! // Get name of var. ! if (type == YIELD && inGeneratorFunction()) { ! expect(IDENT); ! } ! ! final String contextString = "variable name"; ! Expression binding = bindingIdentifierOrPattern(contextString); ! final boolean isDestructuring = !(binding instanceof IdentNode); ! if (isDestructuring) { ! final int finalVarFlags = varFlags; ! verifyDestructuringBindingPattern(binding, new Consumer<IdentNode>() { ! public void accept(final IdentNode identNode) { ! verifyIdent(identNode, contextString); ! final VarNode var = new VarNode(varLine, varToken, sourceOrder, identNode.getFinish(), identNode.setIsDeclaredHere(), null, finalVarFlags); ! appendStatement(var); ! } ! }); ! } ! ! // Assume no init. ! Expression init = null; ! ! // Look for initializer assignment. if (type == ASSIGN) { next(); // Get initializer expression. Suppress IN if not statement. ! if (!isDestructuring) { ! defaultNames.push(binding); ! } try { init = assignmentExpression(!isStatement); } finally { + if (!isDestructuring) { defaultNames.pop(); } ! } ! } else if (isStatement) { ! if (isDestructuring) { ! throw error(AbstractParser.message("missing.destructuring.assignment"), token); ! } else if (varType == CONST) { ! throw error(AbstractParser.message("missing.const.assignment", ((IdentNode)binding).getName())); ! } ! // else, if we are in a for loop, delay checking until we know the kind of loop } + if (!isDestructuring) { + assert init != null || varType != CONST || !isStatement; + final IdentNode ident = (IdentNode)binding; + if (!isStatement && ident.getName().equals("let")) { + throw error(AbstractParser.message("let.binding.for")); //ES6 13.7.5.1 + } // Only set declaration flag on lexically scoped let/const as it adds runtime overhead. ! final IdentNode name = varType == LET || varType == CONST ? ident.setIsDeclaredHere() : ident; ! binding = name; ! final VarNode var = new VarNode(varLine, varToken, sourceOrder, finish, name, init, varFlags); appendStatement(var); + if (init == null && varType == CONST) { + if (missingAssignment == null) { + missingAssignment = binding; + } + } + } else { + assert init != null || !isStatement; + binding = init == null ? binding : verifyAssignment(Token.recast(varToken, ASSIGN), binding, init); + if (isStatement) { + appendStatement(new ExpressionStatement(varLine, binding.getToken(), finish, binding)); + } else if (init == null) { + if (missingAssignment == null) { + missingAssignment = binding; + } + } + } + bindings.add(binding); if (type != COMMARIGHT) { break; } next(); } // If is a statement then handle end of line. if (isStatement) { endOfLine(); + } else { + if (type == SEMICOLON) { + // late check for missing assignment, now we know it's a for (init; test; modify) loop + if (missingAssignment != null) { + if (missingAssignment instanceof IdentNode) { + throw error(AbstractParser.message("missing.const.assignment", ((IdentNode)missingAssignment).getName())); + } else { + throw error(AbstractParser.message("missing.destructuring.assignment"), missingAssignment.getToken()); + } + } + } + } + + return bindings; + } + + private boolean isBindingIdentifier() { + return type == IDENT || isNonStrictModeIdent(); + } + + private IdentNode bindingIdentifier(final String contextString) { + final IdentNode name = getIdent(); + verifyIdent(name, contextString); + return name; + } + + private Expression bindingPattern() { + if (type == LBRACKET) { + return arrayLiteral(); + } else if (type == LBRACE) { + return objectLiteral(); + } else { + throw error(AbstractParser.message("expected.binding")); + } + } + + private Expression bindingIdentifierOrPattern(final String contextString) { + if (isBindingIdentifier() || !isES6()) { + return bindingIdentifier(contextString); + } else { + return bindingPattern(); + } + } + + /** + * Verify destructuring variable declaration binding pattern and extract bound variable declarations. + */ + private void verifyDestructuringBindingPattern(final Expression pattern, final Consumer<IdentNode> identifierCallback) { + assert pattern instanceof ObjectNode || pattern instanceof LiteralNode.ArrayLiteralNode; + pattern.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + @Override + public boolean enterLiteralNode(final LiteralNode<?> literalNode) { + if (literalNode.isArray()) { + boolean restElement = false; + for (final Expression element : literalNode.getElementExpressions()) { + if (restElement) { + throw error(String.format("Unexpected element after rest element"), element.getToken()); + } + if (element != null) { + if (element.isTokenType(SPREAD_ARRAY)) { + restElement = true; + if (!(((UnaryNode) element).getExpression() instanceof IdentNode)) { + throw error(String.format("Expected a valid binding identifier"), element.getToken()); + + } + } + element.accept(this); + } + } + return false; + } else { + return enterDefault(literalNode); + } + } + + @Override + public boolean enterObjectNode(final ObjectNode objectNode) { + return true; + } + + @Override + public boolean enterPropertyNode(final PropertyNode propertyNode) { + if (propertyNode.getValue() != null) { + propertyNode.getValue().accept(this); + return false; + } else { + return enterDefault(propertyNode); + } + } + + @Override + public boolean enterIdentNode(final IdentNode identNode) { + identifierCallback.accept(identNode); + return false; + } + + @Override + public boolean enterBinaryNode(final BinaryNode binaryNode) { + if (binaryNode.isTokenType(ASSIGN)) { + binaryNode.lhs().accept(this); + // Initializer(rhs) can be any AssignmentExpression + return false; + } else { + return enterDefault(binaryNode); + } + } + + @Override + public boolean enterUnaryNode(final UnaryNode unaryNode) { + if (unaryNode.isTokenType(SPREAD_ARRAY)) { + // rest element + return true; + } else { + return enterDefault(unaryNode); + } } ! @Override ! protected boolean enterDefault(final Node node) { ! throw error(String.format("unexpected node in BindingPattern: %s", node)); ! } ! }); } /** * EmptyStatement : * ;
*** 1228,1238 **** // Create FOR node, capturing FOR token. final ParserContextLoopNode forNode = new ParserContextLoopNode(); lc.push(forNode); Block body = null; ! List<VarNode> vars = null; Expression init = null; JoinPredecessorExpression test = null; JoinPredecessorExpression modify = null; int flags = 0; --- 1907,1917 ---- // Create FOR node, capturing FOR token. final ParserContextLoopNode forNode = new ParserContextLoopNode(); lc.push(forNode); Block body = null; ! List<Expression> vars = null; Expression init = null; JoinPredecessorExpression test = null; JoinPredecessorExpression modify = null; int flags = 0;
*** 1252,1275 **** expect(LPAREN); switch (type) { case VAR: // Var declaration captured in for outer block. ! vars = variableStatement(type, false, forStart); break; case SEMICOLON: break; default: ! if (useBlockScope() && (type == LET || type == CONST)) { flags |= ForNode.PER_ITERATION_SCOPE; // LET/CONST declaration captured in container block created above. ! vars = variableStatement(type, false, forStart); break; } if (env._const_as_var && type == CONST) { // Var declaration captured in for outer block. ! vars = variableStatement(TokenType.VAR, false, forStart); break; } init = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true); break; --- 1931,1954 ---- expect(LPAREN); switch (type) { case VAR: // Var declaration captured in for outer block. ! vars = variableDeclarationList(type, false, forStart); break; case SEMICOLON: break; default: ! if (useBlockScope() && (type == LET && lookaheadIsLetDeclaration(true) || type == CONST)) { flags |= ForNode.PER_ITERATION_SCOPE; // LET/CONST declaration captured in container block created above. ! vars = variableDeclarationList(type, false, forStart); break; } if (env._const_as_var && type == CONST) { // Var declaration captured in for outer block. ! vars = variableDeclarationList(TokenType.VAR, false, forStart); break; } init = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true); break;
*** 1307,1317 **** flags |= isForOf ? ForNode.IS_FOR_OF : ForNode.IS_FOR_IN; test = new JoinPredecessorExpression(); if (vars != null) { // for (var i in obj) if (vars.size() == 1) { ! init = new IdentNode(vars.get(0).getName()); } else { // for (var i, j in obj) is invalid throw error(AbstractParser.message("many.vars.in.for.in.loop", isForOf ? "of" : "in"), vars.get(1).getToken()); } } else { --- 1986,2000 ---- flags |= isForOf ? ForNode.IS_FOR_OF : ForNode.IS_FOR_IN; test = new JoinPredecessorExpression(); if (vars != null) { // for (var i in obj) if (vars.size() == 1) { ! init = new IdentNode((IdentNode)vars.get(0)); ! if (init.isTokenType(ASSIGN)) { ! throw error(AbstractParser.message("for.in.loop.initializer"), init.getToken()); ! } ! assert init instanceof IdentNode || isDestructuringLhs(init); } else { // for (var i, j in obj) is invalid throw error(AbstractParser.message("many.vars.in.for.in.loop", isForOf ? "of" : "in"), vars.get(1).getToken()); } } else {
*** 1349,1363 **** // Set the for body. body = getStatement(); } finally { lc.pop(forNode); ! if (vars != null) { ! for (final VarNode var : vars) { appendStatement(var); } - } if (body != null) { appendStatement(new ForNode(forLine, forToken, body.getFinish(), body, (forNode.getFlags() | flags), init, test, modify)); } if (outer != null) { restoreBlock(outer); --- 2032,2045 ---- // Set the for body. body = getStatement(); } finally { lc.pop(forNode); ! for (final Statement var : forNode.getStatements()) { ! assert var instanceof VarNode; appendStatement(var); } if (body != null) { appendStatement(new ForNode(forLine, forToken, body.getFinish(), body, (forNode.getFlags() | flags), init, test, modify)); } if (outer != null) { restoreBlock(outer);
*** 1369,1378 **** --- 2051,2103 ---- } } } } + private boolean checkValidLValue(final Expression init, final String contextString) { + if (init instanceof IdentNode) { + if (!checkIdentLValue((IdentNode)init)) { + return false; + } + verifyIdent((IdentNode)init, contextString); + return true; + } else if (init instanceof AccessNode || init instanceof IndexNode) { + return true; + } else if (isDestructuringLhs(init)) { + verifyDestructuringAssignmentPattern(init, contextString); + return true; + } else { + return false; + } + } + + private boolean lookaheadIsLetDeclaration(final boolean ofContextualKeyword) { + assert type == LET; + for (int i = 1;; i++) { + TokenType t = T(k + i); + switch (t) { + case EOL: + case COMMENT: + continue; + case IDENT: + if (ofContextualKeyword && isES6() && "of".equals(getValue(getToken(k + i)))) { + return false; + } + // fall through + case LBRACKET: + case LBRACE: + return true; + default: + // accept future strict tokens in non-strict mode (including LET) + if (!isStrictMode && t.getKind() == TokenKind.FUTURESTRICT) { + return true; + } + return false; + } + } + } + /** * ...IterationStatement : * ... * while ( Expression ) Statement * ...
*** 1557,1567 **** * * Parse RETURN statement. */ private void returnStatement() { // check for return outside function ! if (lc.getCurrentFunction().getKind() == FunctionNode.Kind.SCRIPT) { throw error(AbstractParser.message("invalid.return")); } // Capture RETURN token. final int returnLine = line; --- 2282,2292 ---- * * Parse RETURN statement. */ private void returnStatement() { // check for return outside function ! if (lc.getCurrentFunction().getKind() == FunctionNode.Kind.SCRIPT || lc.getCurrentFunction().getKind() == FunctionNode.Kind.MODULE) { throw error(AbstractParser.message("invalid.return")); } // Capture RETURN token. final int returnLine = line;
*** 1589,1631 **** // Construct and add RETURN node. appendStatement(new ReturnNode(returnLine, returnToken, finish, expression)); } /** ! * YieldStatement : ! * yield Expression? ; // [no LineTerminator here] ! * ! * JavaScript 1.8 * ! * Parse YIELD statement. */ ! private void yieldStatement() { // Capture YIELD token. ! final int yieldLine = line; ! final long yieldToken = token; // YIELD tested in caller. nextOrEOL(); Expression expression = null; ! // SEMICOLON or expression. switch (type) { case RBRACE: case SEMICOLON: case EOL: case EOF: break; default: ! expression = expression(); break; } - endOfLine(); - // Construct and add YIELD node. ! appendStatement(new ReturnNode(yieldLine, yieldToken, finish, expression)); } /** * WithStatement : * with ( Expression ) Statement --- 2314,2378 ---- // Construct and add RETURN node. appendStatement(new ReturnNode(returnLine, returnToken, finish, expression)); } /** ! * Parse YieldExpression. * ! * YieldExpression[In] : ! * yield ! * yield [no LineTerminator here] AssignmentExpression[?In, Yield] ! * yield [no LineTerminator here] * AssignmentExpression[?In, Yield] */ ! private Expression yieldExpression(final boolean noIn) { ! assert inGeneratorFunction(); // Capture YIELD token. ! long yieldToken = token; // YIELD tested in caller. + assert type == YIELD; nextOrEOL(); Expression expression = null; ! boolean yieldAsterisk = false; ! if (type == MUL) { ! yieldAsterisk = true; ! yieldToken = Token.recast(yieldToken, YIELD_STAR); ! next(); ! } ! switch (type) { case RBRACE: case SEMICOLON: case EOL: case EOF: + case COMMARIGHT: + case RPAREN: + case RBRACKET: + case COLON: + if (!yieldAsterisk) { + // treat (yield) as (yield void 0) + expression = newUndefinedLiteral(yieldToken, finish); + if (type == EOL) { + next(); + } break; + } else { + // AssignmentExpression required, fall through + } default: ! expression = assignmentExpression(noIn); break; } // Construct and add YIELD node. ! return new UnaryNode(yieldToken, expression); ! } ! ! private static UnaryNode newUndefinedLiteral(final long token, final int finish) { ! return new UnaryNode(Token.recast(token, VOID), LiteralNode.newInstance(token, finish, 0)); } /** * WithStatement : * with ( Expression ) Statement
*** 1677,1691 **** * Parse SWITCH statement. */ private void switchStatement() { final int switchLine = line; final long switchToken = token; // SWITCH tested in caller. next(); // Create and add switch statement. ! final ParserContextSwitchNode switchNode= new ParserContextSwitchNode(); lc.push(switchNode); CaseNode defaultCase = null; // Prepare to accumulate cases. final List<CaseNode> cases = new ArrayList<>(); --- 2424,2442 ---- * Parse SWITCH statement. */ private void switchStatement() { final int switchLine = line; final long switchToken = token; + + // Block to capture variables declared inside the switch statement. + final ParserContextBlockNode switchBlock = newBlock(); + // SWITCH tested in caller. next(); // Create and add switch statement. ! final ParserContextSwitchNode switchNode = new ParserContextSwitchNode(); lc.push(switchNode); CaseNode defaultCase = null; // Prepare to accumulate cases. final List<CaseNode> cases = new ArrayList<>();
*** 1725,1735 **** } expect(COLON); // Get CASE body. ! final Block statements = getBlock(false); final CaseNode caseNode = new CaseNode(caseToken, finish, caseExpression, statements); if (caseExpression == null) { defaultCase = caseNode; } --- 2476,2486 ---- } expect(COLON); // Get CASE body. ! final Block statements = getBlock(false); // TODO: List<Statement> statements = caseStatementList(); final CaseNode caseNode = new CaseNode(caseToken, finish, caseExpression, statements); if (caseExpression == null) { defaultCase = caseNode; }
*** 1738,1750 **** } next(); } finally { lc.pop(switchNode); } ! appendStatement(new SwitchNode(switchLine, switchToken, finish, expression, cases, defaultCase)); } /** * LabelledStatement : * Identifier : Statement --- 2489,2503 ---- } next(); } finally { lc.pop(switchNode); + restoreBlock(switchBlock); } ! final SwitchNode switchStatement = new SwitchNode(switchLine, switchToken, finish, expression, cases, defaultCase); ! appendStatement(new BlockStatement(switchLine, new Block(switchToken, finish, switchBlock.getFlags() | Block.IS_SYNTHETIC | Block.IS_SWITCH_BLOCK, switchStatement))); } /** * LabelledStatement : * Identifier : Statement
*** 1767,1777 **** final ParserContextLabelNode labelNode = new ParserContextLabelNode(ident.getName()); Block body = null; try { lc.push(labelNode); ! body = getStatement(); } finally { assert lc.peek() instanceof ParserContextLabelNode; lc.pop(labelNode); } --- 2520,2530 ---- final ParserContextLabelNode labelNode = new ParserContextLabelNode(ident.getName()); Block body = null; try { lc.push(labelNode); ! body = getStatement(true); } finally { assert lc.peek() instanceof ParserContextLabelNode; lc.pop(labelNode); }
*** 1933,1949 **** } /** * PrimaryExpression : * this ! * Identifier * Literal * ArrayLiteral * ObjectLiteral * RegularExpressionLiteral * TemplateLiteral * ( Expression ) * * Parse primary expression. * @return Expression node. */ @SuppressWarnings("fallthrough") --- 2686,2708 ---- } /** * PrimaryExpression : * this ! * IdentifierReference * Literal * ArrayLiteral * ObjectLiteral * RegularExpressionLiteral * TemplateLiteral + * CoverParenthesizedExpressionAndArrowParameterList + * + * CoverParenthesizedExpressionAndArrowParameterList : * ( Expression ) + * ( ) + * ( ... BindingIdentifier ) + * ( Expression , ... BindingIdentifier ) * * Parse primary expression. * @return Expression node. */ @SuppressWarnings("fallthrough")
*** 1954,1964 **** switch (type) { case THIS: final String name = type.getName(); next(); ! lc.getCurrentFunction().setFlag(FunctionNode.USES_THIS); return new IdentNode(primaryToken, finish, name); case IDENT: final IdentNode ident = getIdent(); if (ident == null) { break; --- 2713,2723 ---- switch (type) { case THIS: final String name = type.getName(); next(); ! markThis(lc); return new IdentNode(primaryToken, finish, name); case IDENT: final IdentNode ident = getIdent(); if (ident == null) { break;
*** 1995,2004 **** --- 2754,2779 ---- case LBRACE: return objectLiteral(); case LPAREN: next(); + if (isES6()) { + if (type == RPAREN) { + // () + nextOrEOL(); + expectDontAdvance(ARROW); + return new ExpressionList(primaryToken, finish, Collections.emptyList()); + } else if (type == ELLIPSIS) { + // (...rest) + final IdentNode restParam = formalParameterList(false).get(0); + expectDontAdvance(RPAREN); + nextOrEOL(); + expectDontAdvance(ARROW); + return new ExpressionList(primaryToken, finish, Collections.singletonList(restParam)); + } + } + final Expression expression = expression(); expect(RPAREN); return expression;
*** 2071,2082 **** // Prepare to accumulate elements. final List<Expression> elements = new ArrayList<>(); // Track elisions. boolean elision = true; ! loop: while (true) { switch (type) { case RBRACKET: next(); break loop; --- 2846,2858 ---- // Prepare to accumulate elements. final List<Expression> elements = new ArrayList<>(); // Track elisions. boolean elision = true; ! loop: while (true) { + long spreadToken = 0; switch (type) { case RBRACKET: next(); break loop;
*** 2091,2108 **** elision = true; break; default: if (!elision) { throw error(AbstractParser.message("expected.comma", type.getNameOrType())); } - // Add expression element. - final Expression expression = assignmentExpression(false); if (expression != null) { elements.add(expression); } else { expect(RBRACKET); } --- 2867,2894 ---- elision = true; break; + case ELLIPSIS: + if (isES6()) { + spreadToken = token; + next(); + } + // fall through + default: if (!elision) { throw error(AbstractParser.message("expected.comma", type.getNameOrType())); } + // Add expression element. + Expression expression = assignmentExpression(false); if (expression != null) { + if (spreadToken != 0) { + expression = new UnaryNode(Token.recast(spreadToken, SPREAD_ARRAY), expression); + } elements.add(expression); } else { expect(RBRACKET); }
*** 2139,2149 **** final List<PropertyNode> elements = new ArrayList<>(); final Map<String, Integer> map = new HashMap<>(); // Create a block for the object literal. boolean commaSeen = true; ! loop: while (true) { switch (type) { case RBRACE: next(); break loop; --- 2925,2935 ---- final List<PropertyNode> elements = new ArrayList<>(); final Map<String, Integer> map = new HashMap<>(); // Create a block for the object literal. boolean commaSeen = true; ! loop: while (true) { switch (type) { case RBRACE: next(); break loop;
*** 2162,2171 **** --- 2948,2963 ---- } commaSeen = false; // Get and add the next property. final PropertyNode property = propertyAssignment(); + + if (property.isComputed()) { + elements.add(property); + break; + } + final String key = property.getKeyName(); final Integer existing = map.get(key); if (existing == null) { map.put(key, elements.size());
*** 2183,2243 **** final Expression prevValue = existingProperty.getValue(); final FunctionNode prevGetter = existingProperty.getGetter(); final FunctionNode prevSetter = existingProperty.getSetter(); // ECMA 11.1.5 strict mode restrictions if (isStrictMode && value != null && prevValue != null) { ! throw error(AbstractParser.message("property.redefinition", key), property.getToken()); } final boolean isPrevAccessor = prevGetter != null || prevSetter != null; final boolean isAccessor = getter != null || setter != null; // data property redefined as accessor property if (prevValue != null && isAccessor) { ! throw error(AbstractParser.message("property.redefinition", key), property.getToken()); } // accessor property redefined as data if (isPrevAccessor && value != null) { ! throw error(AbstractParser.message("property.redefinition", key), property.getToken()); } if (isAccessor && isPrevAccessor) { if (getter != null && prevGetter != null || setter != null && prevSetter != null) { ! throw error(AbstractParser.message("property.redefinition", key), property.getToken()); ! } ! } ! ! if (value != null) { ! elements.add(property); ! } else if (getter != null) { ! elements.set(existing, existingProperty.setGetter(getter)); ! } else if (setter != null) { ! elements.set(existing, existingProperty.setSetter(setter)); ! } ! break; } } - - return new ObjectNode(objectToken, finish, elements); } /** ! * PropertyName : * IdentifierName * StringLiteral * NumericLiteral * - * See 11.1.5 - * * @return PropertyName node */ @SuppressWarnings("fallthrough") ! private PropertyKey propertyName() { switch (type) { case IDENT: return getIdent().setIsPropertyName(); case OCTAL_LEGACY: if (isStrictMode) { --- 2975,3047 ---- final Expression prevValue = existingProperty.getValue(); final FunctionNode prevGetter = existingProperty.getGetter(); final FunctionNode prevSetter = existingProperty.getSetter(); + if (!isES6()) { + checkPropertyRedefinition(property, value, getter, setter, prevValue, prevGetter, prevSetter); + } else { + if (property.getKey() instanceof IdentNode && ((IdentNode)property.getKey()).isProtoPropertyName() && + existingProperty.getKey() instanceof IdentNode && ((IdentNode)existingProperty.getKey()).isProtoPropertyName()) { + throw error(AbstractParser.message("multiple.proto.key"), property.getToken()); + } + } + + if (value != null || prevValue != null) { + map.put(key, elements.size()); + elements.add(property); + } else if (getter != null) { + assert prevGetter != null || prevSetter != null; + elements.set(existing, existingProperty.setGetter(getter)); + } else if (setter != null) { + assert prevGetter != null || prevSetter != null; + elements.set(existing, existingProperty.setSetter(setter)); + } + break; + } + } + + return new ObjectNode(objectToken, finish, elements); + } + + private void checkPropertyRedefinition(final PropertyNode property, final Expression value, final FunctionNode getter, final FunctionNode setter, final Expression prevValue, final FunctionNode prevGetter, final FunctionNode prevSetter) { // ECMA 11.1.5 strict mode restrictions if (isStrictMode && value != null && prevValue != null) { ! throw error(AbstractParser.message("property.redefinition", property.getKeyName()), property.getToken()); } final boolean isPrevAccessor = prevGetter != null || prevSetter != null; final boolean isAccessor = getter != null || setter != null; // data property redefined as accessor property if (prevValue != null && isAccessor) { ! throw error(AbstractParser.message("property.redefinition", property.getKeyName()), property.getToken()); } // accessor property redefined as data if (isPrevAccessor && value != null) { ! throw error(AbstractParser.message("property.redefinition", property.getKeyName()), property.getToken()); } if (isAccessor && isPrevAccessor) { if (getter != null && prevGetter != null || setter != null && prevSetter != null) { ! throw error(AbstractParser.message("property.redefinition", property.getKeyName()), property.getToken()); } } } /** ! * LiteralPropertyName : * IdentifierName * StringLiteral * NumericLiteral * * @return PropertyName node */ @SuppressWarnings("fallthrough") ! private PropertyKey literalPropertyName() { switch (type) { case IDENT: return getIdent().setIsPropertyName(); case OCTAL_LEGACY: if (isStrictMode) {
*** 2255,2264 **** --- 3059,3096 ---- return getIdentifierName().setIsPropertyName(); } } /** + * ComputedPropertyName : + * AssignmentExpression + * + * @return PropertyName node + */ + private Expression computedPropertyName() { + expect(LBRACKET); + Expression expression = assignmentExpression(false); + expect(RBRACKET); + return expression; + } + + /** + * PropertyName : + * LiteralPropertyName + * ComputedPropertyName + * + * @return PropertyName node + */ + private Expression propertyName() { + if (type == LBRACKET && isES6()) { + return computedPropertyName(); + } else { + return (Expression)literalPropertyName(); + } + } + + /** * PropertyAssignment : * PropertyName : AssignmentExpression * get PropertyName ( ) { FunctionBody } * set PropertyName ( PropertySetParameterList ) { FunctionBody } *
*** 2278,2332 **** private PropertyNode propertyAssignment() { // Capture firstToken. final long propertyToken = token; final int functionLine = line; ! PropertyKey propertyName; if (type == IDENT) { // Get IDENT. final String ident = (String)expectValue(IDENT); ! if (type != COLON) { final long getSetToken = propertyToken; switch (ident) { case "get": final PropertyFunction getter = propertyGetterFunction(getSetToken, functionLine); ! return new PropertyNode(propertyToken, finish, getter.ident, null, getter.functionNode, null); case "set": final PropertyFunction setter = propertySetterFunction(getSetToken, functionLine); ! return new PropertyNode(propertyToken, finish, setter.ident, null, null, setter.functionNode); default: break; } } ! propertyName = createIdentNode(propertyToken, finish, ident).setIsPropertyName(); } else { propertyName = propertyName(); } expect(COLON); defaultNames.push(propertyName); try { ! return new PropertyNode(propertyToken, finish, propertyName, assignmentExpression(false), null, null); } finally { defaultNames.pop(); } } private PropertyFunction propertyGetterFunction(final long getSetToken, final int functionLine) { ! final PropertyKey getIdent = propertyName(); ! final String getterName = getIdent.getPropertyName(); ! final IdentNode getNameNode = createIdentNode(((Node)getIdent).getToken(), finish, NameCodec.encode("get " + getterName)); expect(LPAREN); expect(RPAREN); final ParserContextFunctionNode functionNode = createParserContextFunctionNode(getNameNode, getSetToken, FunctionNode.Kind.GETTER, functionLine, Collections.<IdentNode>emptyList()); lc.push(functionNode); Block functionBody; --- 3110,3208 ---- private PropertyNode propertyAssignment() { // Capture firstToken. final long propertyToken = token; final int functionLine = line; ! final Expression propertyName; ! final boolean isIdentifier; ! ! boolean generator = false; ! if (type == MUL && isES6()) { ! generator = true; ! next(); ! } + final boolean computed = type == LBRACKET; if (type == IDENT) { // Get IDENT. final String ident = (String)expectValue(IDENT); ! if (type != COLON && (type != LPAREN || !isES6())) { final long getSetToken = propertyToken; switch (ident) { case "get": final PropertyFunction getter = propertyGetterFunction(getSetToken, functionLine); ! return new PropertyNode(propertyToken, finish, getter.key, null, getter.functionNode, null, false, getter.computed); case "set": final PropertyFunction setter = propertySetterFunction(getSetToken, functionLine); ! return new PropertyNode(propertyToken, finish, setter.key, null, null, setter.functionNode, false, setter.computed); default: break; } } ! isIdentifier = true; ! IdentNode identNode = createIdentNode(propertyToken, finish, ident).setIsPropertyName(); ! if (type == COLON && ident.equals("__proto__")) { ! identNode = identNode.setIsProtoPropertyName(); ! } ! propertyName = identNode; } else { + isIdentifier = isNonStrictModeIdent(); propertyName = propertyName(); } + Expression propertyValue; + + if (generator) { + expectDontAdvance(LPAREN); + } + + if (type == LPAREN && isES6()) { + propertyValue = propertyMethodFunction(propertyName, propertyToken, functionLine, generator, FunctionNode.ES6_IS_METHOD, computed).functionNode; + } else if (isIdentifier && (type == COMMARIGHT || type == RBRACE || type == ASSIGN) && isES6()) { + propertyValue = createIdentNode(propertyToken, finish, ((IdentNode) propertyName).getPropertyName()); + if (type == ASSIGN && isES6()) { + // TODO if not destructuring, this is a SyntaxError + final long assignToken = token; + next(); + final Expression rhs = assignmentExpression(false); + propertyValue = verifyAssignment(assignToken, propertyValue, rhs); + } + } else { expect(COLON); defaultNames.push(propertyName); try { ! propertyValue = assignmentExpression(false); } finally { defaultNames.pop(); } } + return new PropertyNode(propertyToken, finish, propertyName, propertyValue, null, null, false, computed); + } + private PropertyFunction propertyGetterFunction(final long getSetToken, final int functionLine) { ! return propertyGetterFunction(getSetToken, functionLine, FunctionNode.ES6_IS_METHOD); ! } ! ! private PropertyFunction propertyGetterFunction(final long getSetToken, final int functionLine, final int flags) { ! final boolean computed = type == LBRACKET; ! final Expression propertyName = propertyName(); ! final String getterName = propertyName instanceof PropertyKey ? ((PropertyKey) propertyName).getPropertyName() : getDefaultValidFunctionName(functionLine, false); ! final IdentNode getNameNode = createIdentNode((propertyName).getToken(), finish, NameCodec.encode("get " + getterName)); expect(LPAREN); expect(RPAREN); final ParserContextFunctionNode functionNode = createParserContextFunctionNode(getNameNode, getSetToken, FunctionNode.Kind.GETTER, functionLine, Collections.<IdentNode>emptyList()); + functionNode.setFlag(flags); + if (computed) { + functionNode.setFlag(FunctionNode.IS_ANONYMOUS); + } lc.push(functionNode); Block functionBody;
*** 2343,2366 **** Collections.<IdentNode>emptyList(), FunctionNode.Kind.GETTER, functionLine, functionBody); ! return new PropertyFunction(getIdent, function); } private PropertyFunction propertySetterFunction(final long getSetToken, final int functionLine) { ! final PropertyKey setIdent = propertyName(); ! final String setterName = setIdent.getPropertyName(); ! final IdentNode setNameNode = createIdentNode(((Node)setIdent).getToken(), finish, NameCodec.encode("set " + setterName)); expect(LPAREN); // be sloppy and allow missing setter parameter even though // spec does not permit it! final IdentNode argIdent; ! if (type == IDENT || isNonStrictModeIdent()) { argIdent = getIdent(); ! verifyStrictIdent(argIdent, "setter argument"); } else { argIdent = null; } expect(RPAREN); final List<IdentNode> parameters = new ArrayList<>(); --- 3219,3247 ---- Collections.<IdentNode>emptyList(), FunctionNode.Kind.GETTER, functionLine, functionBody); ! return new PropertyFunction(propertyName, function, computed); } private PropertyFunction propertySetterFunction(final long getSetToken, final int functionLine) { ! return propertySetterFunction(getSetToken, functionLine, FunctionNode.ES6_IS_METHOD); ! } ! ! private PropertyFunction propertySetterFunction(final long getSetToken, final int functionLine, final int flags) { ! final boolean computed = type == LBRACKET; ! final Expression propertyName = propertyName(); ! final String setterName = propertyName instanceof PropertyKey ? ((PropertyKey) propertyName).getPropertyName() : getDefaultValidFunctionName(functionLine, false); ! final IdentNode setNameNode = createIdentNode((propertyName).getToken(), finish, NameCodec.encode("set " + setterName)); expect(LPAREN); // be sloppy and allow missing setter parameter even though // spec does not permit it! final IdentNode argIdent; ! if (isBindingIdentifier()) { argIdent = getIdent(); ! verifyIdent(argIdent, "setter argument"); } else { argIdent = null; } expect(RPAREN); final List<IdentNode> parameters = new ArrayList<>();
*** 2368,2377 **** --- 3249,3262 ---- parameters.add(argIdent); } final ParserContextFunctionNode functionNode = createParserContextFunctionNode(setNameNode, getSetToken, FunctionNode.Kind.SETTER, functionLine, parameters); + functionNode.setFlag(flags); + if (computed) { + functionNode.setFlag(FunctionNode.IS_ANONYMOUS); + } lc.push(functionNode); Block functionBody; try { functionBody = functionBody(functionNode);
*** 2387,2423 **** parameters, FunctionNode.Kind.SETTER, functionLine, functionBody); ! return new PropertyFunction(setIdent, function); } private static class PropertyFunction { ! final PropertyKey ident; final FunctionNode functionNode; ! PropertyFunction(final PropertyKey ident, final FunctionNode function) { ! this.ident = ident; this.functionNode = function; } } /** - * Parse left hand side expression. - * * LeftHandSideExpression : * NewExpression * CallExpression * * CallExpression : * MemberExpression Arguments * CallExpression Arguments * CallExpression [ Expression ] * CallExpression . IdentifierName - * CallExpression TemplateLiteral * * @return Expression node. */ private Expression leftHandSideExpression() { int callLine = line; long callToken = token; --- 3272,3356 ---- parameters, FunctionNode.Kind.SETTER, functionLine, functionBody); ! return new PropertyFunction(propertyName, function, computed); ! } ! ! private PropertyFunction propertyMethodFunction(Expression key, final long methodToken, final int methodLine, final boolean generator, final int flags, boolean computed) { ! final String methodName = key instanceof PropertyKey ? ((PropertyKey) key).getPropertyName() : getDefaultValidFunctionName(methodLine, false); ! final IdentNode methodNameNode = createIdentNode(((Node)key).getToken(), finish, methodName); ! ! FunctionNode.Kind functionKind = generator ? FunctionNode.Kind.GENERATOR : FunctionNode.Kind.NORMAL; ! final ParserContextFunctionNode functionNode = createParserContextFunctionNode(methodNameNode, methodToken, functionKind, methodLine, null); ! functionNode.setFlag(flags); ! if (computed) { ! functionNode.setFlag(FunctionNode.IS_ANONYMOUS); ! } ! lc.push(functionNode); ! ! try { ! final ParserContextBlockNode parameterBlock = newBlock(); ! final List<IdentNode> parameters; ! try { ! expect(LPAREN); ! parameters = formalParameterList(generator); ! functionNode.setParameters(parameters); ! expect(RPAREN); ! } finally { ! restoreBlock(parameterBlock); ! } ! ! Block functionBody = functionBody(functionNode); ! ! functionBody = maybeWrapBodyInParameterBlock(functionBody, parameterBlock); ! ! final FunctionNode function = createFunctionNode( ! functionNode, ! methodToken, ! methodNameNode, ! parameters, ! functionKind, ! methodLine, ! functionBody); ! return new PropertyFunction(key, function, computed); ! } finally { ! lc.pop(functionNode); ! } } private static class PropertyFunction { ! final Expression key; final FunctionNode functionNode; + final boolean computed; ! PropertyFunction(final Expression key, final FunctionNode function, final boolean computed) { ! this.key = key; this.functionNode = function; + this.computed = computed; } } /** * LeftHandSideExpression : * NewExpression * CallExpression * * CallExpression : * MemberExpression Arguments + * SuperCall * CallExpression Arguments * CallExpression [ Expression ] * CallExpression . IdentifierName * + * SuperCall : + * super Arguments + * + * See 11.2 + * + * Parse left hand side expression. * @return Expression node. */ private Expression leftHandSideExpression() { int callLine = line; long callToken = token;
*** 2433,2443 **** } lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false); } ! loop: while (true) { // Capture token. callLine = line; callToken = token; --- 3366,3376 ---- } lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false); } ! loop: while (true) { // Capture token. callLine = line; callToken = token;
*** 2477,2486 **** --- 3410,3420 ---- case TEMPLATE: case TEMPLATE_HEAD: { // tagged template literal final List<Expression> arguments = templateLiteralArgumentList(); + // Create call node. lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false); break; } default:
*** 2504,2513 **** --- 3438,3461 ---- private Expression newExpression() { final long newToken = token; // NEW is tested in caller. next(); + if (type == PERIOD && isES6()) { + next(); + if (type == IDENT && "target".equals(getValue())) { + if (lc.getCurrentFunction().isProgram()) { + throw error(AbstractParser.message("new.target.in.function"), token); + } + next(); + markNewTarget(lc); + return new IdentNode(newToken, finish, "new.target"); + } else { + throw error(AbstractParser.message("expected.target"), token); + } + } + // Get function base. final int callLine = line; final Expression constructor = memberExpression(); if (constructor == null) { return null;
*** 2539,2563 **** return new UnaryNode(newToken, callNode); } /** - * Parse member expression. - * * MemberExpression : * PrimaryExpression * FunctionExpression * MemberExpression [ Expression ] * MemberExpression . IdentifierName * MemberExpression TemplateLiteral * new MemberExpression Arguments * * @return Expression node. */ private Expression memberExpression() { // Prepare to build operation. Expression lhs; switch (type) { case NEW: // Get new expression. lhs = newExpression(); --- 3487,3523 ---- return new UnaryNode(newToken, callNode); } /** * MemberExpression : * PrimaryExpression * FunctionExpression + * ClassExpression + * GeneratorExpression * MemberExpression [ Expression ] * MemberExpression . IdentifierName * MemberExpression TemplateLiteral + * SuperProperty + * MetaProperty * new MemberExpression Arguments * + * SuperProperty : + * super [ Expression ] + * super . IdentifierName + * + * MetaProperty : + * NewTarget + * + * Parse member expression. * @return Expression node. */ + @SuppressWarnings("fallthrough") private Expression memberExpression() { // Prepare to build operation. Expression lhs; + boolean isSuper = false; switch (type) { case NEW: // Get new expression. lhs = newExpression();
*** 2566,2582 **** case FUNCTION: // Get function expression. lhs = functionExpression(false, false); break; default: // Get primary expression. lhs = primaryExpression(); break; } ! loop: while (true) { // Capture token. final long callToken = token; switch (type) { --- 3526,3582 ---- case FUNCTION: // Get function expression. lhs = functionExpression(false, false); break; + case CLASS: + if (isES6()) { + lhs = classExpression(false); + break; + } else { + // fall through + } + + case SUPER: + if (isES6()) { + final ParserContextFunctionNode currentFunction = getCurrentNonArrowFunction(); + if (currentFunction.isMethod()) { + long identToken = Token.recast(token, IDENT); + next(); + lhs = createIdentNode(identToken, finish, SUPER.getName()); + + switch (type) { + case LBRACKET: + case PERIOD: + getCurrentNonArrowFunction().setFlag(FunctionNode.ES6_USES_SUPER); + isSuper = true; + break; + case LPAREN: + if (currentFunction.isSubclassConstructor()) { + lhs = ((IdentNode)lhs).setIsDirectSuper(); + break; + } else { + // fall through to throw error + } + default: + throw error(AbstractParser.message("invalid.super"), identToken); + } + break; + } else { + // fall through + } + } else { + // fall through + } + default: // Get primary expression. lhs = primaryExpression(); break; } ! loop: while (true) { // Capture token. final long callToken = token; switch (type) {
*** 2589,2598 **** --- 3589,3603 ---- expect(RBRACKET); // Create indexing node. lhs = new IndexNode(callToken, finish, lhs, index); + if (isSuper) { + isSuper = false; + lhs = ((BaseNode) lhs).setIsSuper(); + } + break; } case PERIOD: { if (lhs == null) { throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
*** 2603,2612 **** --- 3608,3622 ---- final IdentNode property = getIdentifierName(); // Create property access node. lhs = new AccessNode(callToken, finish, lhs, property.getName()); + if (isSuper) { + isSuper = false; + lhs = ((BaseNode) lhs).setIsSuper(); + } + break; } case TEMPLATE: case TEMPLATE_HEAD: { // tagged template literal
*** 2630,2640 **** --- 3640,3652 ---- * ( ) * ( ArgumentList ) * * ArgumentList : * AssignmentExpression + * ... AssignmentExpression * ArgumentList , AssignmentExpression + * ArgumentList , ... AssignmentExpression * * See 11.2 * * Parse function call arguments. * @return Argument list.
*** 2654,2665 **** expect(COMMARIGHT); } else { first = false; } // Get argument expression. ! nodeList.add(assignmentExpression(false)); } expect(RPAREN); return nodeList; } --- 3666,3687 ---- expect(COMMARIGHT); } else { first = false; } + long spreadToken = 0; + if (type == ELLIPSIS && isES6()) { + spreadToken = token; + next(); + } + // Get argument expression. ! Expression expression = assignmentExpression(false); ! if (spreadToken != 0) { ! expression = new UnaryNode(Token.recast(spreadToken, TokenType.SPREAD_ARGUMENT), expression); ! } ! nodeList.add(expression); } expect(RPAREN); return nodeList; }
*** 2695,2709 **** */ private Expression functionExpression(final boolean isStatement, final boolean topLevel) { final long functionToken = token; final int functionLine = line; // FUNCTION is tested in caller. next(); IdentNode name = null; ! if (type == IDENT || isNonStrictModeIdent()) { name = getIdent(); verifyStrictIdent(name, "function name"); } else if (isStatement) { // Nashorn extension: anonymous function statements. // Do not allow anonymous function statement if extensions --- 3717,3744 ---- */ private Expression functionExpression(final boolean isStatement, final boolean topLevel) { final long functionToken = token; final int functionLine = line; // FUNCTION is tested in caller. + assert type == FUNCTION; + next(); + + boolean generator = false; + if (type == MUL && isES6()) { + generator = true; next(); + } IdentNode name = null; ! if (isBindingIdentifier()) { ! if (type == YIELD && ((!isStatement && generator) || (isStatement && inGeneratorFunction()))) { ! // 12.1.1 Early SyntaxError if: ! // GeneratorExpression with BindingIdentifier yield ! // HoistableDeclaration with BindingIdentifier yield in generator function body ! expect(IDENT); ! } name = getIdent(); verifyStrictIdent(name, "function name"); } else if (isStatement) { // Nashorn extension: anonymous function statements. // Do not allow anonymous function statement if extensions
*** 2721,2749 **** final String tmpName = getDefaultValidFunctionName(functionLine, isStatement); name = new IdentNode(functionToken, Token.descPosition(functionToken), tmpName); isAnonymous = true; } ! expect(LPAREN); ! final List<IdentNode> parameters = formalParameterList(); ! expect(RPAREN); ! ! final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, FunctionNode.Kind.NORMAL, functionLine, parameters); lc.push(functionNode); Block functionBody = null; // Hide the current default name across function boundaries. E.g. "x3 = function x1() { function() {}}" // If we didn't hide the current default name, then the innermost anonymous function would receive "x3". hideDefaultName(); ! try{ functionBody = functionBody(functionNode); } finally { defaultNames.pop(); lc.pop(functionNode); } if (isStatement) { ! if (topLevel || useBlockScope()) { functionNode.setFlag(FunctionNode.IS_DECLARED); } else if (isStrictMode) { throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("strict.no.func.decl.here"), functionToken); } else if (env._function_statement == ScriptEnvironment.FunctionStatementBehavior.ERROR) { throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("no.func.decl.here"), functionToken); --- 3756,3795 ---- final String tmpName = getDefaultValidFunctionName(functionLine, isStatement); name = new IdentNode(functionToken, Token.descPosition(functionToken), tmpName); isAnonymous = true; } ! FunctionNode.Kind functionKind = generator ? FunctionNode.Kind.GENERATOR : FunctionNode.Kind.NORMAL; ! List<IdentNode> parameters = Collections.emptyList(); ! final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, functionKind, functionLine, parameters); lc.push(functionNode); + Block functionBody = null; // Hide the current default name across function boundaries. E.g. "x3 = function x1() { function() {}}" // If we didn't hide the current default name, then the innermost anonymous function would receive "x3". hideDefaultName(); ! try { ! final ParserContextBlockNode parameterBlock = newBlock(); ! try { ! expect(LPAREN); ! parameters = formalParameterList(generator); ! functionNode.setParameters(parameters); ! expect(RPAREN); ! } finally { ! restoreBlock(parameterBlock); ! } ! functionBody = functionBody(functionNode); + + functionBody = maybeWrapBodyInParameterBlock(functionBody, parameterBlock); } finally { defaultNames.pop(); lc.pop(functionNode); } if (isStatement) { ! if (topLevel || useBlockScope() || (!isStrictMode && env._function_statement == ScriptEnvironment.FunctionStatementBehavior.ACCEPT)) { functionNode.setFlag(FunctionNode.IS_DECLARED); } else if (isStrictMode) { throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("strict.no.func.decl.here"), functionToken); } else if (env._function_statement == ScriptEnvironment.FunctionStatementBehavior.ERROR) { throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("no.func.decl.here"), functionToken);
*** 2757,2812 **** if (isAnonymous) { functionNode.setFlag(FunctionNode.IS_ANONYMOUS); } ! final int arity = parameters.size(); ! ! final boolean strict = functionNode.isStrict(); ! if (arity > 1) { ! final HashSet<String> parametersSet = new HashSet<>(arity); ! for (int i = arity - 1; i >= 0; i--) { ! final IdentNode parameter = parameters.get(i); ! String parameterName = parameter.getName(); ! if (isArguments(parameterName)) { ! functionNode.setFlag(FunctionNode.DEFINES_ARGUMENTS); ! } ! ! if (parametersSet.contains(parameterName)) { ! // redefinition of parameter name ! if (strict) { ! throw error(AbstractParser.message("strict.param.redefinition", parameterName), parameter.getToken()); ! } ! // rename in non-strict mode ! parameterName = functionNode.uniqueName(parameterName); ! final long parameterToken = parameter.getToken(); ! parameters.set(i, new IdentNode(parameterToken, Token.descPosition(parameterToken), functionNode.uniqueName(parameterName))); ! } ! ! parametersSet.add(parameterName); ! } ! } else if (arity == 1) { ! if (isArguments(parameters.get(0))) { ! functionNode.setFlag(FunctionNode.DEFINES_ARGUMENTS); ! } ! } ! ! final FunctionNode function = createFunctionNode( ! functionNode, ! functionToken, ! name, ! parameters, ! FunctionNode.Kind.NORMAL, ! functionLine, ! functionBody); ! ! if (isStatement) { ! if (isAnonymous) { ! appendStatement(new ExpressionStatement(functionLine, functionToken, finish, function)); ! return function; } // mark ES6 block functions as lexically scoped final int varFlags = (topLevel || !useBlockScope()) ? 0 : VarNode.IS_LET; final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, function, varFlags); --- 3803,3827 ---- if (isAnonymous) { functionNode.setFlag(FunctionNode.IS_ANONYMOUS); } ! verifyParameterList(parameters, functionNode); ! final FunctionNode function = createFunctionNode( ! functionNode, ! functionToken, ! name, ! parameters, ! functionKind, ! functionLine, ! functionBody); ! if (isStatement) { ! if (isAnonymous) { ! appendStatement(new ExpressionStatement(functionLine, functionToken, finish, function)); ! return function; } // mark ES6 block functions as lexically scoped final int varFlags = (topLevel || !useBlockScope()) ? 0 : VarNode.IS_LET; final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, function, varFlags);
*** 2820,2829 **** --- 3835,3878 ---- } return function; } + private void verifyParameterList(final List<IdentNode> parameters, final ParserContextFunctionNode functionNode) { + final IdentNode duplicateParameter = functionNode.getDuplicateParameterBinding(); + if (duplicateParameter != null) { + if (functionNode.isStrict() || functionNode.getKind() == FunctionNode.Kind.ARROW || !functionNode.isSimpleParameterList()) { + throw error(AbstractParser.message("strict.param.redefinition", duplicateParameter.getName()), duplicateParameter.getToken()); + } + + final int arity = parameters.size(); + final HashSet<String> parametersSet = new HashSet<>(arity); + + for (int i = arity - 1; i >= 0; i--) { + final IdentNode parameter = parameters.get(i); + String parameterName = parameter.getName(); + + if (parametersSet.contains(parameterName)) { + // redefinition of parameter name, rename in non-strict mode + parameterName = functionNode.uniqueName(parameterName); + final long parameterToken = parameter.getToken(); + parameters.set(i, new IdentNode(parameterToken, Token.descPosition(parameterToken), functionNode.uniqueName(parameterName))); + } + parametersSet.add(parameterName); + } + } + } + + private static Block maybeWrapBodyInParameterBlock(Block functionBody, ParserContextBlockNode parameterBlock) { + assert functionBody.isFunctionBody(); + if (!parameterBlock.getStatements().isEmpty()) { + parameterBlock.appendStatement(new BlockStatement(functionBody)); + return new Block(parameterBlock.getToken(), functionBody.getFinish(), (functionBody.getFlags() | Block.IS_PARAMETER_BLOCK) & ~Block.IS_BODY, parameterBlock.getStatements()); + } + return functionBody; + } + private String getDefaultValidFunctionName(final int functionLine, final boolean isStatement) { final String defaultFunctionName = getDefaultFunctionName(); if (isValidIdentifier(defaultFunctionName)) { if (isStatement) { // The name will be used as the LHS of a symbol assignment. We add the anonymous function
*** 2834,2864 **** } return ANON_FUNCTION_PREFIX.symbolName() + functionLine; } private static boolean isValidIdentifier(final String name) { ! if(name == null || name.isEmpty()) { return false; } ! if(!Character.isJavaIdentifierStart(name.charAt(0))) { return false; } ! for(int i = 1; i < name.length(); ++i) { ! if(!Character.isJavaIdentifierPart(name.charAt(i))) { return false; } } return true; } private String getDefaultFunctionName() { ! if(!defaultNames.isEmpty()) { final Object nameExpr = defaultNames.peek(); ! if(nameExpr instanceof PropertyKey) { markDefaultNameUsed(); return ((PropertyKey)nameExpr).getPropertyName(); ! } else if(nameExpr instanceof AccessNode) { markDefaultNameUsed(); return ((AccessNode)nameExpr).getProperty(); } } return null; --- 3883,3913 ---- } return ANON_FUNCTION_PREFIX.symbolName() + functionLine; } private static boolean isValidIdentifier(final String name) { ! if (name == null || name.isEmpty()) { return false; } ! if (!Character.isJavaIdentifierStart(name.charAt(0))) { return false; } ! for (int i = 1; i < name.length(); ++i) { ! if (!Character.isJavaIdentifierPart(name.charAt(i))) { return false; } } return true; } private String getDefaultFunctionName() { ! if (!defaultNames.isEmpty()) { final Object nameExpr = defaultNames.peek(); ! if (nameExpr instanceof PropertyKey) { markDefaultNameUsed(); return ((PropertyKey)nameExpr).getPropertyName(); ! } else if (nameExpr instanceof AccessNode) { markDefaultNameUsed(); return ((AccessNode)nameExpr).getProperty(); } } return null;
*** 2883,2894 **** * See 13 * * Parse function parameter list. * @return List of parameter nodes. */ ! private List<IdentNode> formalParameterList() { ! return formalParameterList(RPAREN); } /** * Same as the other method of the same name - except that the end * token type expected is passed as argument to this method. --- 3932,3943 ---- * See 13 * * Parse function parameter list. * @return List of parameter nodes. */ ! private List<IdentNode> formalParameterList(final boolean yield) { ! return formalParameterList(RPAREN, yield); } /** * Same as the other method of the same name - except that the end * token type expected is passed as argument to this method.
*** 2900,2910 **** * See 13 * * Parse function parameter list. * @return List of parameter nodes. */ ! private List<IdentNode> formalParameterList(final TokenType endType) { // Prepare to gather parameters. final ArrayList<IdentNode> parameters = new ArrayList<>(); // Track commas. boolean first = true; --- 3949,3959 ---- * See 13 * * Parse function parameter list. * @return List of parameter nodes. */ ! private List<IdentNode> formalParameterList(final TokenType endType, final boolean yield) { // Prepare to gather parameters. final ArrayList<IdentNode> parameters = new ArrayList<>(); // Track commas. boolean first = true;
*** 2914,2936 **** expect(COMMARIGHT); } else { first = false; } ! // Get and add parameter. ! final IdentNode ident = getIdent(); ! // ECMA 13.1 strict mode restrictions ! verifyStrictIdent(ident, "function parameter"); parameters.add(ident); } parameters.trimToSize(); return parameters; } /** * FunctionBody : * SourceElements? * * See 13 --- 3963,4074 ---- expect(COMMARIGHT); } else { first = false; } ! boolean restParameter = false; ! if (type == ELLIPSIS && isES6()) { ! next(); ! restParameter = true; ! } ! ! if (type == YIELD && yield) { ! expect(IDENT); ! } ! ! final long paramToken = token; ! final int paramLine = line; ! final String contextString = "function parameter"; ! IdentNode ident; ! if (isBindingIdentifier() || restParameter || !isES6()) { ! ident = bindingIdentifier(contextString); ! ! if (restParameter) { ! ident = ident.setIsRestParameter(); ! // rest parameter must be last ! expectDontAdvance(endType); ! parameters.add(ident); ! break; ! } else if (type == ASSIGN && isES6()) { ! next(); ! ident = ident.setIsDefaultParameter(); ! if (type == YIELD && yield) { ! // error: yield in default expression ! expect(IDENT); ! } ! ! // default parameter ! Expression initializer = assignmentExpression(false); ! ! ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); ! if (currentFunction != null) { ! // desugar to: param = (param === undefined) ? initializer : param; ! // possible alternative: if (param === undefined) param = initializer; ! BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); ! TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); ! BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), ident, value); ! lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); ! } ! } ! ! ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); ! if (currentFunction != null) { ! currentFunction.addParameterBinding(ident); ! if (ident.isRestParameter() || ident.isDefaultParameter()) { ! currentFunction.setSimpleParameterList(false); ! } ! } ! } else { ! final Expression pattern = bindingPattern(); ! // Introduce synthetic temporary parameter to capture the object to be destructured. ! ident = createIdentNode(paramToken, pattern.getFinish(), String.format("arguments[%d]", parameters.size())).setIsDestructuredParameter(); ! verifyDestructuringParameterBindingPattern(pattern, paramToken, paramLine, contextString); ! ! Expression value = ident; ! if (type == ASSIGN) { ! next(); ! ident = ident.setIsDefaultParameter(); ! ! // binding pattern with initializer. desugar to: (param === undefined) ? initializer : param ! Expression initializer = assignmentExpression(false); ! // TODO initializer must not contain yield expression if yield=true (i.e. this is generator function's parameter list) ! BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); ! value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); ! } + ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); + if (currentFunction != null) { + // destructuring assignment + BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), pattern, value); + lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + } + } parameters.add(ident); } parameters.trimToSize(); return parameters; } + private void verifyDestructuringParameterBindingPattern(final Expression pattern, final long paramToken, final int paramLine, final String contextString) { + verifyDestructuringBindingPattern(pattern, new Consumer<IdentNode>() { + public void accept(IdentNode identNode) { + verifyIdent(identNode, contextString); + + ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); + if (currentFunction != null) { + // declare function-scope variables for destructuring bindings + lc.getFunctionBody(currentFunction).appendStatement(new VarNode(paramLine, Token.recast(paramToken, VAR), pattern.getFinish(), identNode, null)); + // detect duplicate bounds names in parameter list + currentFunction.addParameterBinding(identNode); + currentFunction.setSimpleParameterList(false); + } + } + }); + } + /** * FunctionBody : * SourceElements? * * See 13
*** 2956,2975 **** } assert functionNode != null; final int functionId = functionNode.getId(); parseBody = reparsedFunction == null || functionId <= reparsedFunction.getFunctionNodeId(); // Nashorn extension: expression closures ! if (!env._no_syntax_extensions && type != LBRACE) { /* * Example: * * function square(x) x * x; * print(square(3)); */ // just expression as function body ! final Expression expr = assignmentExpression(true); lastToken = previousToken; functionNode.setLastToken(previousToken); assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode); // EOL uses length field to store the line number final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken)); --- 4094,4113 ---- } assert functionNode != null; final int functionId = functionNode.getId(); parseBody = reparsedFunction == null || functionId <= reparsedFunction.getFunctionNodeId(); // Nashorn extension: expression closures ! if ((!env._no_syntax_extensions || functionNode.getKind() == FunctionNode.Kind.ARROW) && type != LBRACE) { /* * Example: * * function square(x) x * x; * print(square(3)); */ // just expression as function body ! final Expression expr = assignmentExpression(false); lastToken = previousToken; functionNode.setLastToken(previousToken); assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode); // EOL uses length field to store the line number final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken));
*** 2980,2989 **** --- 4118,4128 ---- // details). if (parseBody) { final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr); appendStatement(returnNode); } + // bodyFinish = finish; } else { expectDontAdvance(LBRACE); if (parseBody || !skipFunctionBody(functionNode)) { next(); // Gather the function elements.
*** 3052,3062 **** assert functionNode.hasScopeBlock(); body.setFlag(Block.NEEDS_SCOPE); } } } ! functionBody = new Block(bodyToken, bodyFinish, body.getFlags(), body.getStatements()); return functionBody; } private boolean skipFunctionBody(final ParserContextFunctionNode functionNode) { if (reparsedFunction == null) { --- 4191,4201 ---- assert functionNode.hasScopeBlock(); body.setFlag(Block.NEEDS_SCOPE); } } } ! functionBody = new Block(bodyToken, bodyFinish, body.getFlags() | Block.IS_BODY, body.getStatements()); return functionBody; } private boolean skipFunctionBody(final ParserContextFunctionNode functionNode) { if (reparsedFunction == null) {
*** 3093,3104 **** line = parserState.line; linePosition = parserState.linePosition; // Doesn't really matter, but it's safe to treat it as if there were a semicolon before // the RBRACE. type = SEMICOLON; ! k = -1; ! next(); return true; } /** --- 4232,4242 ---- line = parserState.line; linePosition = parserState.linePosition; // Doesn't really matter, but it's safe to treat it as if there were a semicolon before // the RBRACE. type = SEMICOLON; ! scanFirstToken(); return true; } /**
*** 3124,3138 **** return newLexer; } } private void printAST(final FunctionNode functionNode) { ! if (functionNode.getFlag(FunctionNode.IS_PRINT_AST)) { env.getErr().println(new ASTWriter(functionNode)); } ! if (functionNode.getFlag(FunctionNode.IS_PRINT_PARSE)) { env.getErr().println(new PrintVisitor(functionNode, true, false)); } } private void addFunctionDeclarations(final ParserContextFunctionNode functionNode) { --- 4262,4276 ---- return newLexer; } } private void printAST(final FunctionNode functionNode) { ! if (functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_AST)) { env.getErr().println(new ASTWriter(functionNode)); } ! if (functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_PARSE)) { env.getErr().println(new PrintVisitor(functionNode, true, false)); } } private void addFunctionDeclarations(final ParserContextFunctionNode functionNode) {
*** 3220,3243 **** // ++, -- without operand.. if (lhs == null) { throw error(AbstractParser.message("expected.lvalue", type.getNameOrType())); } ! if (!(lhs instanceof AccessNode || ! lhs instanceof IndexNode || ! lhs instanceof IdentNode)) { ! return referenceError(lhs, null, env._early_lvalue_error); ! } ! ! if (lhs instanceof IdentNode) { ! if (!checkIdentLValue((IdentNode)lhs)) { ! return referenceError(lhs, null, false); ! } ! verifyStrictIdent((IdentNode)lhs, "operand for " + opType.getName() + " operator"); ! } ! ! return incDecExpression(unaryToken, opType, lhs, false); default: break; } --- 4358,4368 ---- // ++, -- without operand.. if (lhs == null) { throw error(AbstractParser.message("expected.lvalue", type.getNameOrType())); } ! return verifyIncDecExpression(unaryToken, opType, lhs, false); default: break; }
*** 3245,3287 **** if (last != EOL) { switch (type) { case INCPREFIX: case DECPREFIX: final TokenType opType = type; final Expression lhs = expression; // ++, -- without operand.. if (lhs == null) { throw error(AbstractParser.message("expected.lvalue", type.getNameOrType())); } if (!(lhs instanceof AccessNode || lhs instanceof IndexNode || lhs instanceof IdentNode)) { - next(); return referenceError(lhs, null, env._early_lvalue_error); } if (lhs instanceof IdentNode) { if (!checkIdentLValue((IdentNode)lhs)) { - next(); return referenceError(lhs, null, false); } ! verifyStrictIdent((IdentNode)lhs, "operand for " + opType.getName() + " operator"); ! } ! expression = incDecExpression(token, type, expression, true); ! next(); ! break; ! default: ! break; ! } } ! if (expression == null) { ! throw error(AbstractParser.message("expected.operand", type.getNameOrType())); ! } ! ! return expression; } /** * {@code * MultiplicativeExpression : --- 4370,4418 ---- if (last != EOL) { switch (type) { case INCPREFIX: case DECPREFIX: + final long opToken = token; final TokenType opType = type; final Expression lhs = expression; // ++, -- without operand.. if (lhs == null) { throw error(AbstractParser.message("expected.lvalue", type.getNameOrType())); } + next(); + + return verifyIncDecExpression(opToken, opType, lhs, true); + default: + break; + } + } + + if (expression == null) { + throw error(AbstractParser.message("expected.operand", type.getNameOrType())); + } + + return expression; + } + + private Expression verifyIncDecExpression(final long unaryToken, final TokenType opType, final Expression lhs, final boolean isPostfix) { + assert lhs != null; if (!(lhs instanceof AccessNode || lhs instanceof IndexNode || lhs instanceof IdentNode)) { return referenceError(lhs, null, env._early_lvalue_error); } + if (lhs instanceof IdentNode) { if (!checkIdentLValue((IdentNode)lhs)) { return referenceError(lhs, null, false); } ! verifyIdent((IdentNode)lhs, "operand for " + opType.getName() + " operator"); } ! return incDecExpression(unaryToken, opType, lhs, isPostfix); } /** * {@code * MultiplicativeExpression :
*** 3378,3388 **** protected Expression expression() { // This method is protected so that subclass can get details // at expression start point! // Include commas in expression parsing. ! return expression(unaryExpression(), COMMARIGHT.getPrecedence(), false); } private JoinPredecessorExpression joinPredecessorExpression() { return new JoinPredecessorExpression(expression()); } --- 4509,4554 ---- protected Expression expression() { // This method is protected so that subclass can get details // at expression start point! // Include commas in expression parsing. ! return expression(false); ! } ! ! private Expression expression(final boolean noIn) { ! Expression assignmentExpression = assignmentExpression(noIn); ! while (type == COMMARIGHT) { ! long commaToken = token; ! next(); ! ! boolean rhsRestParameter = false; ! if (type == ELLIPSIS && isES6()) { ! // (a, b, ...rest) is not a valid expression, unless we're parsing the parameter list of an arrow function (we need to throw the right error). ! // But since the rest parameter is always last, at least we know that the expression has to end here and be followed by RPAREN and ARROW, so peek ahead. ! if (isRestParameterEndOfArrowFunctionParameterList()) { ! next(); ! rhsRestParameter = true; ! } ! } ! ! Expression rhs = assignmentExpression(noIn); ! ! if (rhsRestParameter) { ! rhs = ((IdentNode)rhs).setIsRestParameter(); ! // Our only valid move is to end Expression here and continue with ArrowFunction. ! // We've already checked that this is the parameter list of an arrow function (see above). ! // RPAREN is next, so we'll finish the binary expression and drop out of the loop. ! assert type == RPAREN; ! } ! ! assignmentExpression = new BinaryNode(commaToken, assignmentExpression, rhs); ! } ! return assignmentExpression; ! } ! ! private Expression expression(final int minPrecedence, final boolean noIn) { ! return expression(unaryExpression(), minPrecedence, noIn); } private JoinPredecessorExpression joinPredecessorExpression() { return new JoinPredecessorExpression(expression()); }
*** 3446,3461 **** } return lhs; } protected Expression assignmentExpression(final boolean noIn) { // This method is protected so that subclass can get details // at assignment expression start point! ! // Exclude commas in expression parsing. ! return expression(unaryExpression(), ASSIGN.getPrecedence(), noIn); } /** * Parse an end of line. */ --- 4612,4931 ---- } return lhs; } + /** + * AssignmentExpression. + * + * AssignmentExpression[In, Yield] : + * ConditionalExpression[?In, ?Yield] + * [+Yield] YieldExpression[?In] + * ArrowFunction[?In, ?Yield] + * LeftHandSideExpression[?Yield] = AssignmentExpression[?In, ?Yield] + * LeftHandSideExpression[?Yield] AssignmentOperator AssignmentExpression[?In, ?Yield] + */ protected Expression assignmentExpression(final boolean noIn) { // This method is protected so that subclass can get details // at assignment expression start point! ! if (type == YIELD && inGeneratorFunction() && isES6()) { ! return yieldExpression(noIn); ! } ! ! final long startToken = token; ! final int startLine = line; ! final Expression exprLhs = conditionalExpression(noIn); ! ! if (type == ARROW && isES6()) { ! if (checkNoLineTerminator()) { ! final Expression paramListExpr; ! if (exprLhs instanceof ExpressionList) { ! paramListExpr = (((ExpressionList)exprLhs).getExpressions().isEmpty() ? null : ((ExpressionList)exprLhs).getExpressions().get(0)); ! } else { ! paramListExpr = exprLhs; ! } ! return arrowFunction(startToken, startLine, paramListExpr); ! } ! } ! assert !(exprLhs instanceof ExpressionList); ! ! if (isAssignmentOperator(type)) { ! final boolean isAssign = type == ASSIGN; ! if (isAssign) { ! defaultNames.push(exprLhs); ! } ! try { ! final long assignToken = token; ! next(); ! final Expression exprRhs = assignmentExpression(noIn); ! return verifyAssignment(assignToken, exprLhs, exprRhs); ! } finally { ! if (isAssign) { ! defaultNames.pop(); ! } ! } ! } else { ! return exprLhs; ! } ! } ! ! /** ! * Is type one of {@code = *= /= %= += -= <<= >>= >>>= &= ^= |=}? ! */ ! private static boolean isAssignmentOperator(TokenType type) { ! switch (type) { ! case ASSIGN: ! case ASSIGN_ADD: ! case ASSIGN_BIT_AND: ! case ASSIGN_BIT_OR: ! case ASSIGN_BIT_XOR: ! case ASSIGN_DIV: ! case ASSIGN_MOD: ! case ASSIGN_MUL: ! case ASSIGN_SAR: ! case ASSIGN_SHL: ! case ASSIGN_SHR: ! case ASSIGN_SUB: ! return true; ! } ! return false; ! } ! ! /** ! * ConditionalExpression. ! */ ! private Expression conditionalExpression(boolean noIn) { ! return expression(TERNARY.getPrecedence(), noIn); ! } ! ! /** ! * ArrowFunction. ! * ! * @param startToken start token of the ArrowParameters expression ! * @param functionLine start line of the arrow function ! * @param paramListExpr ArrowParameters expression or {@code null} for {@code ()} (empty list) ! */ ! private Expression arrowFunction(final long startToken, final int functionLine, final Expression paramListExpr) { ! // caller needs to check that there's no LineTerminator between parameter list and arrow ! assert type != ARROW || checkNoLineTerminator(); ! expect(ARROW); ! ! final long functionToken = Token.recast(startToken, ARROW); ! final IdentNode name = new IdentNode(functionToken, Token.descPosition(functionToken), "=>:" + functionLine); ! final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, FunctionNode.Kind.ARROW, functionLine, null); ! functionNode.setFlag(FunctionNode.IS_ANONYMOUS); ! ! lc.push(functionNode); ! try { ! ParserContextBlockNode parameterBlock = newBlock(); ! final List<IdentNode> parameters; ! try { ! parameters = convertArrowFunctionParameterList(paramListExpr, functionLine); ! functionNode.setParameters(parameters); ! ! if (!functionNode.isSimpleParameterList()) { ! markEvalInArrowParameterList(parameterBlock); ! } ! } finally { ! restoreBlock(parameterBlock); ! } ! Block functionBody = functionBody(functionNode); ! ! functionBody = maybeWrapBodyInParameterBlock(functionBody, parameterBlock); ! ! verifyParameterList(parameters, functionNode); ! ! final FunctionNode function = createFunctionNode( ! functionNode, ! functionToken, ! name, ! parameters, ! FunctionNode.Kind.ARROW, ! functionLine, ! functionBody); ! return function; ! } finally { ! lc.pop(functionNode); ! } ! } ! ! private void markEvalInArrowParameterList(final ParserContextBlockNode parameterBlock) { ! final Iterator<ParserContextFunctionNode> iter = lc.getFunctions(); ! final ParserContextFunctionNode current = iter.next(); ! final ParserContextFunctionNode parent = iter.next(); ! ! if (parent.getFlag(FunctionNode.HAS_EVAL) != 0) { ! // we might have flagged has-eval in the parent function during parsing the parameter list, ! // if the parameter list contains eval; must tag arrow function as has-eval. ! for (final Statement st : parameterBlock.getStatements()) { ! st.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { ! @Override ! public boolean enterCallNode(final CallNode callNode) { ! if (callNode.getFunction() instanceof IdentNode && ((IdentNode) callNode.getFunction()).getName().equals("eval")) { ! current.setFlag(FunctionNode.HAS_EVAL); ! } ! return true; ! } ! }); ! } ! // TODO: function containing the arrow function should not be flagged has-eval ! } ! } ! ! private List<IdentNode> convertArrowFunctionParameterList(final Expression paramListExpr, final int functionLine) { ! final List<IdentNode> parameters; ! if (paramListExpr == null) { ! // empty parameter list, i.e. () => ! parameters = Collections.emptyList(); ! } else if (paramListExpr instanceof IdentNode || paramListExpr.isTokenType(ASSIGN) || isDestructuringLhs(paramListExpr)) { ! parameters = Collections.singletonList(verifyArrowParameter(paramListExpr, 0, functionLine)); ! } else if (paramListExpr instanceof BinaryNode && Token.descType(paramListExpr.getToken()) == COMMARIGHT) { ! parameters = new ArrayList<>(); ! Expression car = paramListExpr; ! do { ! final Expression cdr = ((BinaryNode) car).rhs(); ! parameters.add(0, verifyArrowParameter(cdr, parameters.size(), functionLine)); ! car = ((BinaryNode) car).lhs(); ! } while (car instanceof BinaryNode && Token.descType(car.getToken()) == COMMARIGHT); ! parameters.add(0, verifyArrowParameter(car, parameters.size(), functionLine)); ! } else { ! throw error(AbstractParser.message("expected.arrow.parameter"), paramListExpr.getToken()); ! } ! return parameters; ! } ! ! private IdentNode verifyArrowParameter(Expression param, int index, int paramLine) { ! final String contextString = "function parameter"; ! if (param instanceof IdentNode) { ! IdentNode ident = (IdentNode)param; ! verifyStrictIdent(ident, contextString); ! ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); ! if (currentFunction != null) { ! currentFunction.addParameterBinding(ident); ! } ! return ident; ! } ! ! if (param.isTokenType(ASSIGN)) { ! Expression lhs = ((BinaryNode) param).lhs(); ! long paramToken = lhs.getToken(); ! Expression initializer = ((BinaryNode) param).rhs(); ! if (lhs instanceof IdentNode) { ! // default parameter ! IdentNode ident = (IdentNode) lhs; ! ! ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); ! if (currentFunction != null) { ! BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); ! TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); ! BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), ident, value); ! lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); ! ! currentFunction.addParameterBinding(ident); ! currentFunction.setSimpleParameterList(false); ! } ! return ident; ! } else if (isDestructuringLhs(lhs)) { ! // binding pattern with initializer ! // Introduce synthetic temporary parameter to capture the object to be destructured. ! IdentNode ident = createIdentNode(paramToken, param.getFinish(), String.format("arguments[%d]", index)).setIsDestructuredParameter().setIsDefaultParameter(); ! verifyDestructuringParameterBindingPattern(param, paramToken, paramLine, contextString); ! ! ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); ! if (currentFunction != null) { ! BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); ! TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); ! BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), param, value); ! lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); ! } ! return ident; ! } ! } else if (isDestructuringLhs(param)) { ! // binding pattern ! long paramToken = param.getToken(); ! ! // Introduce synthetic temporary parameter to capture the object to be destructured. ! IdentNode ident = createIdentNode(paramToken, param.getFinish(), String.format("arguments[%d]", index)).setIsDestructuredParameter(); ! verifyDestructuringParameterBindingPattern(param, paramToken, paramLine, contextString); ! ! ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); ! if (currentFunction != null) { ! BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), param, ident); ! lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); ! } ! return ident; ! } ! throw error(AbstractParser.message("invalid.arrow.parameter"), param.getToken()); ! } ! ! private boolean checkNoLineTerminator() { ! assert type == ARROW; ! if (last == RPAREN) { ! return true; ! } else if (last == IDENT) { ! return true; ! } ! for (int i = k - 1; i >= 0; i--) { ! TokenType t = T(i); ! switch (t) { ! case RPAREN: ! case IDENT: ! return true; ! case EOL: ! return false; ! case COMMENT: ! continue; ! default: ! if (t.getKind() == TokenKind.FUTURESTRICT) { ! return true; ! } ! return false; ! } ! } ! return false; ! } ! ! /** ! * Peek ahead to see if what follows after the ellipsis is a rest parameter ! * at the end of an arrow function parameter list. ! */ ! private boolean isRestParameterEndOfArrowFunctionParameterList() { ! assert type == ELLIPSIS; ! // find IDENT, RPAREN, ARROW, in that order, skipping over EOL (where allowed) and COMMENT ! int i = 1; ! for (;;) { ! TokenType t = T(k + i++); ! if (t == IDENT) { ! break; ! } else if (t == EOL || t == COMMENT) { ! continue; ! } else { ! return false; ! } ! } ! for (;;) { ! TokenType t = T(k + i++); ! if (t == RPAREN) { ! break; ! } else if (t == EOL || t == COMMENT) { ! continue; ! } else { ! return false; ! } ! } ! for (;;) { ! TokenType t = T(k + i++); ! if (t == ARROW) { ! break; ! } else if (t == COMMENT) { ! continue; ! } else { ! return false; ! } ! } ! return true; } /** * Parse an end of line. */
*** 3549,3558 **** --- 5019,5402 ---- next(); rawStrings.add(LiteralNode.newInstance(stringToken, finish, rawString)); cookedStrings.add(LiteralNode.newInstance(stringToken, finish, cookedString)); } + + /** + * Parse a module. + * + * Module : + * ModuleBody? + * + * ModuleBody : + * ModuleItemList + */ + private FunctionNode module(final String moduleName) { + boolean oldStrictMode = isStrictMode; + try { + isStrictMode = true; // Module code is always strict mode code. (ES6 10.2.1) + + // Make a pseudo-token for the script holding its start and length. + int functionStart = Math.min(Token.descPosition(Token.withDelimiter(token)), finish); + final long functionToken = Token.toDesc(FUNCTION, functionStart, source.getLength() - functionStart); + final int functionLine = line; + + final IdentNode ident = new IdentNode(functionToken, Token.descPosition(functionToken), moduleName); + final ParserContextFunctionNode script = createParserContextFunctionNode( + ident, + functionToken, + FunctionNode.Kind.MODULE, + functionLine, + Collections.<IdentNode>emptyList()); + lc.push(script); + + final ParserContextModuleNode module = new ParserContextModuleNode(moduleName); + lc.push(module); + + final ParserContextBlockNode body = newBlock(); + + functionDeclarations = new ArrayList<>(); + moduleBody(); + addFunctionDeclarations(script); + functionDeclarations = null; + + restoreBlock(body); + body.setFlag(Block.NEEDS_SCOPE); + final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC | Block.IS_BODY, body.getStatements()); + lc.pop(module); + lc.pop(script); + script.setLastToken(token); + + expect(EOF); + + script.setModule(module.createModule()); + return createFunctionNode(script, functionToken, ident, Collections.<IdentNode>emptyList(), FunctionNode.Kind.MODULE, functionLine, programBody); + } finally { + isStrictMode = oldStrictMode; + } + } + + /** + * Parse module body. + * + * ModuleBody : + * ModuleItemList + * + * ModuleItemList : + * ModuleItem + * ModuleItemList ModuleItem + * + * ModuleItem : + * ImportDeclaration + * ExportDeclaration + * StatementListItem + */ + private void moduleBody() { + loop: + while (type != EOF) { + switch (type) { + case EOF: + break loop; + case IMPORT: + importDeclaration(); + break; + case EXPORT: + exportDeclaration(); + break; + default: + // StatementListItem + statement(true, false, false, false); + break; + } + } + } + + + /** + * Parse import declaration. + * + * ImportDeclaration : + * import ImportClause FromClause ; + * import ModuleSpecifier ; + * ImportClause : + * ImportedDefaultBinding + * NameSpaceImport + * NamedImports + * ImportedDefaultBinding , NameSpaceImport + * ImportedDefaultBinding , NamedImports + * ImportedDefaultBinding : + * ImportedBinding + * ModuleSpecifier : + * StringLiteral + * ImportedBinding : + * BindingIdentifier + */ + private void importDeclaration() { + expect(IMPORT); + final ParserContextModuleNode module = lc.getCurrentModule(); + if (type == STRING || type == ESCSTRING) { + // import ModuleSpecifier ; + final String moduleSpecifier = (String) getValue(); + next(); + module.addModuleRequest(moduleSpecifier); + } else { + // import ImportClause FromClause ; + List<Module.ImportEntry> importEntries; + if (type == MUL) { + importEntries = Collections.singletonList(nameSpaceImport()); + } else if (type == LBRACE) { + importEntries = namedImports(); + } else if (isBindingIdentifier()) { + // ImportedDefaultBinding + final IdentNode importedDefaultBinding = bindingIdentifier("ImportedBinding"); + Module.ImportEntry defaultImport = Module.ImportEntry.importDefault(importedDefaultBinding.getName()); + + if (type == COMMARIGHT) { + next(); + importEntries = new ArrayList<>(); + if (type == MUL) { + importEntries.add(nameSpaceImport()); + } else if (type == LBRACE) { + importEntries.addAll(namedImports()); + } else { + throw error(AbstractParser.message("expected.named.import")); + } + } else { + importEntries = Collections.singletonList(defaultImport); + } + } else { + throw error(AbstractParser.message("expected.import")); + } + + final String moduleSpecifier = fromClause(); + module.addModuleRequest(moduleSpecifier); + for (int i = 0; i < importEntries.size(); i++) { + module.addImportEntry(importEntries.get(i).withFrom(moduleSpecifier)); + } + } + expect(SEMICOLON); + } + + /** + * NameSpaceImport : + * * as ImportedBinding + * + * @return imported binding identifier + */ + private Module.ImportEntry nameSpaceImport() { + assert type == MUL; + next(); + final long asToken = token; + final String as = (String) expectValue(IDENT); + if (!"as".equals(as)) { + throw error(AbstractParser.message("expected.as"), asToken); + } + final IdentNode localNameSpace = bindingIdentifier("ImportedBinding"); + return Module.ImportEntry.importStarAsNameSpaceFrom(localNameSpace.getName()); + } + + /** + * NamedImports : + * { } + * { ImportsList } + * { ImportsList , } + * ImportsList : + * ImportSpecifier + * ImportsList , ImportSpecifier + * ImportSpecifier : + * ImportedBinding + * IdentifierName as ImportedBinding + * ImportedBinding : + * BindingIdentifier + */ + private List<Module.ImportEntry> namedImports() { + assert type == LBRACE; + next(); + List<Module.ImportEntry> importEntries = new ArrayList<>(); + while (type != RBRACE) { + final boolean bindingIdentifier = isBindingIdentifier(); + final long nameToken = token; + final IdentNode importName = getIdentifierName(); + if (type == IDENT && "as".equals(getValue())) { + next(); + final IdentNode localName = bindingIdentifier("ImportedBinding"); + importEntries.add(Module.ImportEntry.importSpecifier(importName.getName(), localName.getName())); + } else if (!bindingIdentifier) { + throw error(AbstractParser.message("expected.binding.identifier"), nameToken); + } else { + importEntries.add(Module.ImportEntry.importSpecifier(importName.getName())); + } + if (type == COMMARIGHT) { + next(); + } else { + break; + } + } + expect(RBRACE); + return importEntries; + } + + /** + * FromClause : + * from ModuleSpecifier + */ + private String fromClause() { + final long fromToken = token; + final String name = (String) expectValue(IDENT); + if (!"from".equals(name)) { + throw error(AbstractParser.message("expected.from"), fromToken); + } + if (type == STRING || type == ESCSTRING) { + final String moduleSpecifier = (String) getValue(); + next(); + return moduleSpecifier; + } else { + throw error(expectMessage(STRING)); + } + } + + /** + * Parse export declaration. + * + * ExportDeclaration : + * export * FromClause ; + * export ExportClause FromClause ; + * export ExportClause ; + * export VariableStatement + * export Declaration + * export default HoistableDeclaration[Default] + * export default ClassDeclaration[Default] + * export default [lookahead !in {function, class}] AssignmentExpression[In] ; + */ + private void exportDeclaration() { + expect(EXPORT); + final ParserContextModuleNode module = lc.getCurrentModule(); + switch (type) { + case MUL: { + next(); + final String moduleRequest = fromClause(); + expect(SEMICOLON); + module.addModuleRequest(moduleRequest); + module.addStarExportEntry(Module.ExportEntry.exportStarFrom(moduleRequest)); + break; + } + case LBRACE: { + final List<Module.ExportEntry> exportEntries = exportClause(); + if (type == IDENT && "from".equals(getValue())) { + final String moduleRequest = fromClause(); + module.addModuleRequest(moduleRequest); + for (Module.ExportEntry exportEntry : exportEntries) { + module.addIndirectExportEntry(exportEntry.withFrom(moduleRequest)); + } + } else { + for (Module.ExportEntry exportEntry : exportEntries) { + module.addLocalExportEntry(exportEntry); + } + } + expect(SEMICOLON); + break; + } + case DEFAULT: + next(); + final Expression assignmentExpression; + IdentNode ident; + final int lineNumber = line; + final long rhsToken = token; + final boolean declaration; + switch (type) { + case FUNCTION: + assignmentExpression = functionExpression(false, true); + ident = ((FunctionNode) assignmentExpression).getIdent(); + declaration = true; + break; + case CLASS: + assignmentExpression = classDeclaration(true); + ident = ((ClassNode) assignmentExpression).getIdent(); + declaration = true; + break; + default: + assignmentExpression = assignmentExpression(false); + ident = null; + declaration = false; + break; + } + if (ident != null) { + module.addLocalExportEntry(Module.ExportEntry.exportDefault(ident.getName())); + } else { + ident = createIdentNode(Token.recast(rhsToken, IDENT), finish, Module.DEFAULT_EXPORT_BINDING_NAME); + lc.appendStatementToCurrentNode(new VarNode(lineNumber, Token.recast(rhsToken, LET), finish, ident, assignmentExpression)); + if (!declaration) { + expect(SEMICOLON); + } + module.addLocalExportEntry(Module.ExportEntry.exportDefault()); + } + break; + case VAR: + case LET: + case CONST: + final List<Statement> statements = lc.getCurrentBlock().getStatements(); + final int previousEnd = statements.size(); + variableStatement(type); + for (final Statement statement : statements.subList(previousEnd, statements.size())) { + if (statement instanceof VarNode) { + module.addLocalExportEntry(Module.ExportEntry.exportSpecifier(((VarNode) statement).getName().getName())); + } + } + break; + case CLASS: { + final ClassNode classDeclaration = classDeclaration(false); + module.addLocalExportEntry(Module.ExportEntry.exportSpecifier(classDeclaration.getIdent().getName())); + break; + } + case FUNCTION: { + final FunctionNode functionDeclaration = (FunctionNode) functionExpression(true, true); + module.addLocalExportEntry(Module.ExportEntry.exportSpecifier(functionDeclaration.getIdent().getName())); + break; + } + default: + throw error(AbstractParser.message("invalid.export"), token); + } + } + + /** + * ExportClause : + * { } + * { ExportsList } + * { ExportsList , } + * ExportsList : + * ExportSpecifier + * ExportsList , ExportSpecifier + * ExportSpecifier : + * IdentifierName + * IdentifierName as IdentifierName + * + * @return a list of ExportSpecifiers + */ + private List<Module.ExportEntry> exportClause() { + assert type == LBRACE; + next(); + List<Module.ExportEntry> exports = new ArrayList<>(); + while (type != RBRACE) { + final IdentNode localName = getIdentifierName(); + if (type == IDENT && "as".equals(getValue())) { + next(); + final IdentNode exportName = getIdentifierName(); + exports.add(Module.ExportEntry.exportSpecifier(exportName.getName(), localName.getName())); + } else { + exports.add(Module.ExportEntry.exportSpecifier(localName.getName())); + } + if (type == COMMARIGHT) { + next(); + } else { + break; + } + } + expect(RBRACE); + return exports; + } + @Override public String toString() { return "'JavaScript Parsing'"; }
*** 3562,3571 **** --- 5406,5421 ---- while (iter.hasNext()) { final ParserContextFunctionNode fn = iter.next(); if (!flaggedCurrentFn) { fn.setFlag(FunctionNode.HAS_EVAL); flaggedCurrentFn = true; + if (fn.getKind() == FunctionNode.Kind.ARROW) { + // possible use of this in an eval that's nested in an arrow function, e.g.: + // function fun(){ return (() => eval("this"))(); }; + markThis(lc); + markNewTarget(lc); + } } else { fn.setFlag(FunctionNode.HAS_NESTED_EVAL); } final ParserContextBlockNode body = lc.getFunctionBody(fn); // NOTE: it is crucial to mark the body of the outer function as needing scope even when we skip
*** 3581,3586 **** --- 5431,5487 ---- } private void appendStatement(final Statement statement) { lc.appendStatementToCurrentNode(statement); } + + private static void markSuperCall(final ParserContext lc) { + final Iterator<ParserContextFunctionNode> iter = lc.getFunctions(); + while (iter.hasNext()) { + final ParserContextFunctionNode fn = iter.next(); + if (fn.getKind() != FunctionNode.Kind.ARROW) { + assert fn.isSubclassConstructor(); + fn.setFlag(FunctionNode.ES6_HAS_DIRECT_SUPER); + break; + } + } + } + + private ParserContextFunctionNode getCurrentNonArrowFunction() { + final Iterator<ParserContextFunctionNode> iter = lc.getFunctions(); + while (iter.hasNext()) { + final ParserContextFunctionNode fn = iter.next(); + if (fn.getKind() != FunctionNode.Kind.ARROW) { + return fn; + } + } + return null; + } + + private static void markThis(final ParserContext lc) { + final Iterator<ParserContextFunctionNode> iter = lc.getFunctions(); + while (iter.hasNext()) { + final ParserContextFunctionNode fn = iter.next(); + fn.setFlag(FunctionNode.USES_THIS); + if (fn.getKind() != FunctionNode.Kind.ARROW) { + break; + } + } + } + + private static void markNewTarget(final ParserContext lc) { + final Iterator<ParserContextFunctionNode> iter = lc.getFunctions(); + while (iter.hasNext()) { + final ParserContextFunctionNode fn = iter.next(); + if (fn.getKind() != FunctionNode.Kind.ARROW) { + if (!fn.isProgram()) { + fn.setFlag(FunctionNode.ES6_USES_NEW_TARGET); + } + break; + } + } + } + + private boolean inGeneratorFunction() { + return lc.getCurrentFunction().getKind() == FunctionNode.Kind.GENERATOR; + } }
< prev index next >