< 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 >