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;
import java.util.Deque;
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;
import jdk.nashorn.internal.ir.Block;
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;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.TemplateLiteral;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.ThrowNode;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.UnaryNode;
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;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptingFunctions;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.Timing;
import jdk.nashorn.internal.runtime.linker.NameCodec;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
@Logger(name="parser")
public class Parser extends AbstractParser implements Loggable {
private static final String ARGUMENTS_NAME = CompilerConstants.ARGUMENTS_VAR.symbolName();
private static final String CONSTRUCTOR_NAME = "constructor";
private static final String GET_NAME = "get";
private static final String SET_NAME = "set";
private final ScriptEnvironment env;
private final boolean scripting;
private List<Statement> functionDeclarations;
private final ParserContext lc;
private final Deque<Object> defaultNames;
private final Namespace namespace;
private final DebugLogger log;
protected final Lexer.LineInfoReceiver lineInfoReceiver;
private RecompilableScriptFunctionData reparsedFunction;
public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors) {
this(env, source, errors, env._strict, null);
}
public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final DebugLogger log) {
this(env, source, errors, strict, 0, log);
}
public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int lineOffset, final DebugLogger log) {
super(source, errors, strict, lineOffset);
this.lc = new ParserContext();
this.defaultNames = new ArrayDeque<>();
this.env = env;
this.namespace = new Namespace(env.getNamespace());
this.scripting = env._scripting;
if (this.scripting) {
this.lineInfoReceiver = new Lexer.LineInfoReceiver() {
@Override
public void lineInfo(final int receiverLine, final int receiverLinePosition) {
Parser.this.line = receiverLine;
Parser.this.linePosition = receiverLinePosition;
}
};
} else {
this.lineInfoReceiver = null;
}
this.log = log == null ? DebugLogger.DISABLED_LOGGER : log;
}
@Override
public DebugLogger getLogger() {
return log;
}
@Override
public DebugLogger initLogger(final Context context) {
return context.getLogger(this.getClass());
}
public void setFunctionName(final String name) {
defaultNames.push(createIdentNode(0, 0, name));
}
public void setReparsedFunction(final RecompilableScriptFunctionData reparsedFunction) {
this.reparsedFunction = reparsedFunction;
}
public FunctionNode parse() {
return parse(PROGRAM.symbolName(), 0, source.getLength(), 0);
}
private void scanFirstToken() {
k = -1;
next();
}
public FunctionNode parse(final String scriptName, final int startPos, final int len, final int reparseFlags) {
final boolean isTimingEnabled = env.isTimingEnabled();
final long t0 = isTimingEnabled ? System.nanoTime() : 0L;
log.info(this, " begin for '", scriptName, "'");
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();
return program(scriptName, reparseFlags);
} catch (final Exception e) {
handleParseException(e);
return null;
} finally {
final String end = this + " end '" + scriptName + "'";
if (isTimingEnabled) {
env._timing.accumulateTime(toString(), System.nanoTime() - t0);
log.info(end, "' in ", Timing.toMillisPrint(System.nanoTime() - t0), " ms");
} else {
log.info(end);
}
}
}
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();
return module(moduleName);
} catch (final Exception e) {
handleParseException(e);
return null;
}
}
public FunctionNode parseModule(final String moduleName) {
return parseModule(moduleName, 0, source.getLength());
}
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;
}
}
public FunctionNode parseFunctionBody() {
try {
stream = new TokenStream();
lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions, env._es6);
final int functionLine = line;
scanFirstToken();
final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength());
final IdentNode ident = new IdentNode(functionToken, Token.descPosition(functionToken), PROGRAM.symbolName());
final ParserContextFunctionNode function = createParserContextFunctionNode(ident, functionToken, FunctionNode.Kind.NORMAL, functionLine, Collections.<IdentNode>emptyList());
lc.push(function);
final ParserContextBlockNode body = newBlock();
functionDeclarations = new ArrayList<>();
sourceElements(0);
addFunctionDeclarations(function);
functionDeclarations = null;
restoreBlock(body);
body.setFlag(Block.NEEDS_SCOPE);
final Block functionBody = new Block(functionToken, source.getLength() - 1,
body.getFlags() | Block.IS_SYNTHETIC, body.getStatements());
lc.pop(function);
expect(EOF);
final FunctionNode functionNode = createFunctionNode(
function,
functionToken,
ident,
Collections.<IdentNode>emptyList(),
FunctionNode.Kind.NORMAL,
functionLine,
functionBody);
printAST(functionNode);
return functionNode;
} catch (final Exception e) {
handleParseException(e);
return null;
}
}
private void handleParseException(final Exception e) {
String message = e.getMessage();
if (message == null) {
message = e.toString();
}
if (e instanceof ParserException) {
errors.error((ParserException)e);
} else {
errors.error(message);
}
if (env._dump_on_error) {
e.printStackTrace(env.getErr());
}
}
private void recover(final Exception e) {
if (e != null) {
String message = e.getMessage();
if (message == null) {
message = e.toString();
}
if (e instanceof ParserException) {
errors.error((ParserException)e);
} else {
errors.error(message);
}
if (env._dump_on_error) {
e.printStackTrace(env.getErr());
}
}
loop:
while (true) {
switch (type) {
case EOF:
break loop;
case EOL:
case SEMICOLON:
case RBRACE:
next();
break loop;
default:
nextOrEOL();
break;
}
}
}
private ParserContextBlockNode newBlock() {
return lc.push(new ParserContextBlockNode(token));
}
private ParserContextFunctionNode createParserContextFunctionNode(final IdentNode ident, final long functionToken, final FunctionNode.Kind kind, final int functionLine, final List<IdentNode> parameters) {
final StringBuilder sb = new StringBuilder();
final ParserContextFunctionNode parentFunction = lc.getCurrentFunction();
if (parentFunction != null && !parentFunction.isProgram()) {
sb.append(parentFunction.getName()).append(CompilerConstants.NESTED_FUNCTION_SEPARATOR.symbolName());
}
assert ident.getName() != null;
sb.append(ident.getName());
final String name = namespace.uniqueName(sb.toString());
assert parentFunction != null || kind == FunctionNode.Kind.MODULE || name.equals(PROGRAM.symbolName()) : "name = " + name;
int flags = 0;
if (isStrictMode) {
flags |= FunctionNode.IS_STRICT;
}
if (parentFunction == null) {
flags |= FunctionNode.IS_PROGRAM;
}
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) {
final FunctionNode functionNode =
new FunctionNode(
source,
functionLine,
body.getToken(),
Token.descPosition(body.getToken()),
startToken,
function.getLastToken(),
namespace,
ident,
function.getName(),
parameters,
function.getParameterExpressions(),
kind,
function.getFlags(),
body,
function.getEndParserState(),
function.getModule(),
function.getDebugFlags());
printAST(functionNode);
return functionNode;
}
private ParserContextBlockNode restoreBlock(final ParserContextBlockNode block) {
return lc.pop(block);
}
private Block getBlock(final boolean needsBraces) {
final long blockToken = token;
final ParserContextBlockNode newBlock = newBlock();
try {
if (needsBraces) {
expect(LBRACE);
}
statementList();
} finally {
restoreBlock(newBlock);
}
if (needsBraces) {
expect(RBRACE);
}
final int flags = newBlock.getFlags() | (needsBraces ? 0 : Block.IS_SYNTHETIC);
return new Block(blockToken, finish, flags, newBlock.getStatements());
}
private Block getStatement() {
return getStatement(false);
}
private Block getStatement(final boolean labelledStatement) {
if (type == LBRACE) {
return getBlock(true);
}
final ParserContextBlockNode newBlock = newBlock();
try {
statement(false, 0, true, labelledStatement);
} finally {
restoreBlock(newBlock);
}
return new Block(newBlock.getToken(), finish, newBlock.getFlags() | Block.IS_SYNTHETIC, newBlock.getStatements());
}
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);
}
}
private void detectSpecialProperty(final IdentNode ident) {
if (isArguments(ident)) {
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());
}
private static boolean checkIdentLValue(final IdentNode ident) {
return ident.tokenType().getKind() != TokenKind.KEYWORD;
}
private Expression verifyAssignment(final long op, final Expression lhs, final Expression rhs) {
final TokenType opType = Token.descType(op);
switch (opType) {
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:
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;
}
if(BinaryNode.isLogical(opType)) {
return new BinaryNode(op, new JoinPredecessorExpression(lhs), new JoinPredecessorExpression(rhs));
}
return new BinaryNode(op, lhs, rhs);
}
private boolean isDestructuringLhs(final Expression lhs) {
if (lhs instanceof ObjectNode || lhs instanceof LiteralNode.ArrayLiteralNode) {
return isES6();
}
return false;
}
private void verifyDestructuringAssignmentPattern(final Expression pattern, final String contextString) {
assert pattern instanceof ObjectNode || pattern instanceof LiteralNode.ArrayLiteralNode;
pattern.accept(new VerifyDestructuringPatternNodeVisitor(new LexicalContext()) {
@Override
protected void verifySpreadElement(final Expression lvalue) {
if (!checkValidLValue(lvalue, contextString)) {
throw error(AbstractParser.message("invalid.lvalue"), lvalue.getToken());
}
}
@Override
public boolean enterIdentNode(final IdentNode identNode) {
verifyIdent(identNode, contextString);
if (!checkIdentLValue(identNode)) {
referenceError(identNode, null, true);
return false;
}
return false;
}
@Override
public boolean enterAccessNode(final AccessNode accessNode) {
return false;
}
@Override
public boolean enterIndexNode(final IndexNode indexNode) {
return false;
}
@Override
protected boolean enterDefault(final Node node) {
throw error(String.format("unexpected node in AssignmentPattern: %s", node));
}
});
}
private static UnaryNode incDecExpression(final long firstToken, final TokenType tokenType, final Expression expression, final boolean isPostfix) {
if (isPostfix) {
return new UnaryNode(Token.recast(firstToken, tokenType == DECPREFIX ? DECPOSTFIX : INCPOSTFIX), expression.getStart(), Token.descPosition(firstToken) + Token.descLength(firstToken), expression);
}
return new UnaryNode(firstToken, expression);
}
private FunctionNode program(final String scriptName, final int reparseFlags) {
final long functionToken = Token.toDesc(FUNCTION, Token.descPosition(Token.withDelimiter(token)), source.getLength());
final int functionLine = line;
final IdentNode ident = new IdentNode(functionToken, Token.descPosition(functionToken), scriptName);
final ParserContextFunctionNode script = createParserContextFunctionNode(
ident,
functionToken,
FunctionNode.Kind.SCRIPT,
functionLine,
Collections.<IdentNode>emptyList());
lc.push(script);
final ParserContextBlockNode body = newBlock();
functionDeclarations = new ArrayList<>();
sourceElements(reparseFlags);
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);
return createFunctionNode(script, functionToken, ident, Collections.<IdentNode>emptyList(), FunctionNode.Kind.SCRIPT, functionLine, programBody);
}
private String getDirective(final Node stmt) {
if (stmt instanceof ExpressionStatement) {
final Node expr = ((ExpressionStatement)stmt).getExpression();
if (expr instanceof LiteralNode) {
final LiteralNode<?> lit = (LiteralNode<?>)expr;
final long litToken = lit.getToken();
final TokenType tt = Token.descType(litToken);
if (tt == TokenType.STRING || tt == TokenType.ESCSTRING) {
return source.getString(lit.getStart(), Token.descLength(litToken));
}
}
}
return null;
}
private void sourceElements(final int reparseFlags) {
List<Node> directiveStmts = null;
boolean checkDirective = true;
int functionFlags = reparseFlags;
final boolean oldStrictMode = isStrictMode;
try {
while (type != EOF) {
if (type == RBRACE) {
break;
}
try {
statement(true, functionFlags, false, false);
functionFlags = 0;
if (checkDirective) {
final Statement lastStatement = lc.getLastStatement();
final String directive = getDirective(lastStatement);
checkDirective = directive != null;
if (checkDirective) {
if (!oldStrictMode) {
if (directiveStmts == null) {
directiveStmts = new ArrayList<>();
}
directiveStmts.add(lastStatement);
}
if ("use strict".equals(directive)) {
isStrictMode = true;
final ParserContextFunctionNode function = lc.getCurrentFunction();
function.setFlag(FunctionNode.IS_STRICT);
if (!oldStrictMode && directiveStmts != null) {
for (final Node statement : directiveStmts) {
getValue(statement.getToken());
}
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) {
final int errorLine = line;
final long errorToken = token;
recover(e);
final ErrorNode errorExpr = new ErrorNode(errorToken, finish);
final ExpressionStatement expressionStatement = new ExpressionStatement(errorLine, errorToken, finish, errorExpr);
appendStatement(expressionStatement);
}
stream.commit(k);
}
} finally {
isStrictMode = oldStrictMode;
}
}
private void statement() {
statement(false, 0, false, false);
}
private void statement(final boolean topLevel, final int reparseFlags, final boolean singleStatement, final boolean labelledStatement) {
switch (type) {
case LBRACE:
block();
break;
case VAR:
variableStatement(type);
break;
case SEMICOLON:
emptyStatement();
break;
case IF:
ifStatement();
break;
case FOR:
forStatement();
break;
case WHILE:
whileStatement();
break;
case DO:
doStatement();
break;
case CONTINUE:
continueStatement();
break;
case BREAK:
breakStatement();
break;
case RETURN:
returnStatement();
break;
case WITH:
withStatement();
break;
case SWITCH:
switchStatement();
break;
case THROW:
throwStatement();
break;
case TRY:
tryStatement();
break;
case DEBUGGER:
debuggerStatement();
break;
case RPAREN:
case RBRACKET:
case EOF:
expect(SEMICOLON);
break;
case FUNCTION:
if (singleStatement) {
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;
}
if (type == IDENT || isNonStrictModeIdent()) {
if (T(k + 1) == COLON) {
labelStatement();
return;
}
if ((reparseFlags & ScriptFunctionData.IS_PROPERTY_ACCESSOR) != 0) {
final String ident = (String) getValue();
final long propertyToken = token;
final int propertyLine = line;
if (GET_NAME.equals(ident)) {
next();
addPropertyFunctionStatement(propertyGetterFunction(propertyToken, propertyLine));
return;
} else if (SET_NAME.equals(ident)) {
next();
addPropertyFunctionStatement(propertySetterFunction(propertyToken, propertyLine));
return;
}
}
}
if ((reparseFlags & ScriptFunctionData.IS_ES6_METHOD) != 0
&& (type == IDENT || type == LBRACKET || isNonStrictModeIdent())) {
final String ident = (String)getValue();
final long propertyToken = token;
final int propertyLine = line;
final Expression propertyKey = propertyName();
final int flags = CONSTRUCTOR_NAME.equals(ident) ? FunctionNode.ES6_IS_CLASS_CONSTRUCTOR : FunctionNode.ES6_IS_METHOD;
addPropertyFunctionStatement(propertyMethodFunction(propertyKey, propertyToken, propertyLine, false, flags, false));
return;
}
expressionStatement();
break;
}
}
private void addPropertyFunctionStatement(final PropertyFunction propertyFunction) {
final FunctionNode fn = propertyFunction.functionNode;
functionDeclarations.add(new ExpressionStatement(fn.getLineNumber(), fn.getToken(), finish, fn));
}
private ClassNode classDeclaration(final boolean isDefault) {
final int classLineNumber = line;
final ClassNode classExpression = classExpression(!isDefault);
if (!isDefault) {
final VarNode classVar = new VarNode(classLineNumber, classExpression.getToken(), classExpression.getIdent().getFinish(), classExpression.getIdent(), classExpression, VarNode.IS_CONST);
appendStatement(classVar);
}
return classExpression;
}
private ClassNode classExpression(final boolean isStatement) {
assert type == CLASS;
final int classLineNumber = line;
final long classToken = token;
next();
IdentNode className = null;
if (isStatement || type == IDENT) {
className = getIdent();
}
return classTail(classLineNumber, classToken, className, isStatement);
}
private static final class ClassElementKey {
private final boolean isStatic;
private final String propertyName;
private ClassElementKey(final boolean isStatic, final 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(final Object obj) {
if (obj instanceof ClassElementKey) {
final ClassElementKey other = (ClassElementKey) obj;
return this.isStatic == other.isStatic && Objects.equals(this.propertyName, other.propertyName);
}
return false;
}
}
private ClassNode classTail(final int classLineNumber, final long classToken,
final IdentNode className, final boolean isStatement) {
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_NAME)) {
if (constructor == null) {
constructor = classElement;
} else {
throw error(AbstractParser.message("multiple.constructors"), classElementToken);
}
} else {
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, isStatement);
} finally {
isStrictMode = oldStrictMode;
}
}
private PropertyNode createDefaultClassConstructor(final int classLineNumber, final long classToken, final long lastToken, final IdentNode className, final 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_NAME);
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_NAME)) {
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_NAME)) {
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_NAME)) {
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);
}
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_NAME)) {
throw error(AbstractParser.message("generator.constructor"), key.getToken());
}
if (!isStatic && accessor && ((PropertyKey) key).getPropertyName().equals(CONSTRUCTOR_NAME)) {
throw error(AbstractParser.message("accessor.constructor"), key.getToken());
}
if (isStatic && ((PropertyKey) key).getPropertyName().equals("prototype")) {
throw error(AbstractParser.message("static.prototype.method"), key.getToken());
}
}
}
private void block() {
appendStatement(new BlockStatement(line, getBlock(true)));
}
private void statementList() {
loop:
while (type != EOF) {
switch (type) {
case EOF:
case CASE:
case DEFAULT:
case RBRACE:
break loop;
default:
break;
}
statement();
}
}
private void verifyIdent(final IdentNode ident, final String contextString) {
verifyStrictIdent(ident, contextString);
checkEscapedKeyword(ident);
}
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());
}
}
}
private void checkEscapedKeyword(final IdentNode ident) {
if (isES6() && ident.containsEscapes()) {
final TokenType tokenType = TokenLookup.lookupKeyword(ident.getName().toCharArray(), 0, ident.getName().length());
if (tokenType != IDENT && !(tokenType.getKind() == TokenKind.FUTURESTRICT && !isStrictMode)) {
throw error(AbstractParser.message("keyword.escaped.character"), ident.getToken());
}
}
}
private void variableStatement(final TokenType varType) {
variableDeclarationList(varType, true, -1);
}
private static final class ForVariableDeclarationListResult {
Expression missingAssignment;
long declarationWithInitializerToken;
Expression init;
Expression firstBinding;
Expression secondBinding;
void recordMissingAssignment(final Expression binding) {
if (missingAssignment == null) {
missingAssignment = binding;
}
}
void recordDeclarationWithInitializer(final long token) {
if (declarationWithInitializerToken == 0L) {
declarationWithInitializerToken = token;
}
}
void addBinding(final Expression binding) {
if (firstBinding == null) {
firstBinding = binding;
} else if (secondBinding == null) {
secondBinding = binding;
}
}
void addAssignment(final Expression assignment) {
if (init == null) {
init = assignment;
} else {
init = new BinaryNode(Token.recast(init.getToken(), COMMARIGHT), init, assignment);
}
}
}
private ForVariableDeclarationListResult variableDeclarationList(final TokenType varType, final boolean isStatement, final int sourceOrder) {
assert varType == VAR || varType == LET || varType == CONST;
final int varLine = line;
final long varToken = token;
next();
int varFlags = 0;
if (varType == LET) {
varFlags |= VarNode.IS_LET;
} else if (varType == CONST) {
varFlags |= VarNode.IS_CONST;
}
final ForVariableDeclarationListResult forResult = isStatement ? null : new ForVariableDeclarationListResult();
while (true) {
if (type == YIELD && inGeneratorFunction()) {
expect(IDENT);
}
final String contextString = "variable name";
final Expression binding = bindingIdentifierOrPattern(contextString);
final boolean isDestructuring = !(binding instanceof IdentNode);
if (isDestructuring) {
final int finalVarFlags = varFlags;
verifyDestructuringBindingPattern(binding, new Consumer<IdentNode>() {
@Override
public void accept(final IdentNode identNode) {
verifyIdent(identNode, contextString);
if (!env._parse_only) {
final VarNode var = new VarNode(varLine, varToken, sourceOrder, identNode.getFinish(), identNode.setIsDeclaredHere(), null, finalVarFlags);
appendStatement(var);
}
}
});
}
Expression init = null;
if (type == ASSIGN) {
if (!isStatement) {
forResult.recordDeclarationWithInitializer(varToken);
}
next();
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()));
}
}
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"));
}
final IdentNode name = varType == LET || varType == CONST ? ident.setIsDeclaredHere() : ident;
if (!isStatement) {
if (init == null && varType == CONST) {
forResult.recordMissingAssignment(name);
}
forResult.addBinding(new IdentNode(name));
}
final VarNode var = new VarNode(varLine, varToken, sourceOrder, finish, name, init, varFlags);
appendStatement(var);
} else {
assert init != null || !isStatement;
if (init != null) {
final Expression assignment = verifyAssignment(Token.recast(varToken, ASSIGN), binding, init);
if (isStatement) {
appendStatement(new ExpressionStatement(varLine, assignment.getToken(), finish, assignment, varType));
} else {
forResult.addAssignment(assignment);
forResult.addBinding(assignment);
}
} else if (!isStatement) {
forResult.recordMissingAssignment(binding);
forResult.addBinding(binding);
}
}
if (type != COMMARIGHT) {
break;
}
next();
}
if (isStatement) {
endOfLine();
}
return forResult;
}
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();
}
}
private abstract class VerifyDestructuringPatternNodeVisitor extends NodeVisitor<LexicalContext> {
VerifyDestructuringPatternNodeVisitor(final LexicalContext lc) {
super(lc);
}
@Override
public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
if (literalNode.isArray()) {
if (((LiteralNode.ArrayLiteralNode)literalNode).hasSpread() && ((LiteralNode.ArrayLiteralNode)literalNode).hasTrailingComma()) {
throw error("Rest element must be last", literalNode.getElementExpressions().get(literalNode.getElementExpressions().size() - 1).getToken());
}
boolean restElement = false;
for (final Expression element : literalNode.getElementExpressions()) {
if (element != null) {
if (restElement) {
throw error("Unexpected element after rest element", element.getToken());
}
if (element.isTokenType(SPREAD_ARRAY)) {
restElement = true;
final Expression lvalue = ((UnaryNode) element).getExpression();
verifySpreadElement(lvalue);
}
element.accept(this);
}
}
return false;
} else {
return enterDefault(literalNode);
}
}
protected abstract void verifySpreadElement(Expression lvalue);
@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 enterBinaryNode(final BinaryNode binaryNode) {
if (binaryNode.isTokenType(ASSIGN)) {
binaryNode.lhs().accept(this);
return false;
} else {
return enterDefault(binaryNode);
}
}
@Override
public boolean enterUnaryNode(final UnaryNode unaryNode) {
if (unaryNode.isTokenType(SPREAD_ARRAY)) {
return true;
} else {
return enterDefault(unaryNode);
}
}
}
private void verifyDestructuringBindingPattern(final Expression pattern, final Consumer<IdentNode> identifierCallback) {
assert (pattern instanceof BinaryNode && pattern.isTokenType(ASSIGN)) ||
pattern instanceof ObjectNode || pattern instanceof LiteralNode.ArrayLiteralNode;
pattern.accept(new VerifyDestructuringPatternNodeVisitor(new LexicalContext()) {
@Override
protected void verifySpreadElement(final Expression lvalue) {
if (lvalue instanceof IdentNode) {
} else if (isDestructuringLhs(lvalue)) {
verifyDestructuringBindingPattern(lvalue, identifierCallback);
} else {
throw error("Expected a valid binding identifier", lvalue.getToken());
}
}
@Override
public boolean enterIdentNode(final IdentNode identNode) {
identifierCallback.accept(identNode);
return false;
}
@Override
protected boolean enterDefault(final Node node) {
throw error(String.format("unexpected node in BindingPattern: %s", node));
}
});
}
private void emptyStatement() {
if (env._empty_statements) {
appendStatement(new EmptyNode(line, token, Token.descPosition(token) + Token.descLength(token)));
}
next();
}
private void expressionStatement() {
final int expressionLine = line;
final long expressionToken = token;
final Expression expression = expression();
if (expression != null) {
final ExpressionStatement expressionStatement = new ExpressionStatement(expressionLine, expressionToken, finish, expression);
appendStatement(expressionStatement);
} else {
expect(null);
}
endOfLine();
}
private void ifStatement() {
final int ifLine = line;
final long ifToken = token;
next();
expect(LPAREN);
final Expression test = expression();
expect(RPAREN);
final Block pass = getStatement();
Block fail = null;
if (type == ELSE) {
next();
fail = getStatement();
}
appendStatement(new IfNode(ifLine, ifToken, fail != null ? fail.getFinish() : pass.getFinish(), test, pass, fail));
}
@SuppressWarnings("fallthrough")
private void forStatement() {
final long forToken = token;
final int forLine = line;
final int forStart = Token.descPosition(forToken);
final ParserContextBlockNode outer = useBlockScope() ? newBlock() : null;
final ParserContextLoopNode forNode = new ParserContextLoopNode();
lc.push(forNode);
Block body = null;
Expression init = null;
JoinPredecessorExpression test = null;
JoinPredecessorExpression modify = null;
ForVariableDeclarationListResult varDeclList = null;
int flags = 0;
boolean isForOf = false;
try {
next();
if (!env._no_syntax_extensions && type == IDENT && "each".equals(getValue())) {
flags |= ForNode.IS_FOR_EACH;
next();
}
expect(LPAREN);
TokenType varType = null;
switch (type) {
case VAR:
varDeclList = variableDeclarationList(varType = type, false, forStart);
break;
case SEMICOLON:
break;
default:
if (useBlockScope() && (type == LET && lookaheadIsLetDeclaration(true) || type == CONST)) {
flags |= ForNode.PER_ITERATION_SCOPE;
varDeclList = variableDeclarationList(varType = type, false, forStart);
break;
}
if (env._const_as_var && type == CONST) {
varDeclList = variableDeclarationList(varType = TokenType.VAR, false, forStart);
break;
}
init = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true);
break;
}
switch (type) {
case SEMICOLON:
if (varDeclList != null) {
assert init == null;
init = varDeclList.init;
if (varDeclList.missingAssignment != null) {
if (varDeclList.missingAssignment instanceof IdentNode) {
throw error(AbstractParser.message("missing.const.assignment", ((IdentNode)varDeclList.missingAssignment).getName()));
} else {
throw error(AbstractParser.message("missing.destructuring.assignment"), varDeclList.missingAssignment.getToken());
}
}
}
if ((flags & ForNode.IS_FOR_EACH) != 0) {
throw error(AbstractParser.message("for.each.without.in"), token);
}
expect(SEMICOLON);
if (type != SEMICOLON) {
test = joinPredecessorExpression();
}
expect(SEMICOLON);
if (type != RPAREN) {
modify = joinPredecessorExpression();
}
break;
case IDENT:
if (env._es6 && "of".equals(getValue())) {
isForOf = true;
} else {
expect(SEMICOLON);
break;
}
case IN:
flags |= isForOf ? ForNode.IS_FOR_OF : ForNode.IS_FOR_IN;
test = new JoinPredecessorExpression();
if (varDeclList != null) {
if (varDeclList.secondBinding != null) {
throw error(AbstractParser.message("many.vars.in.for.in.loop", isForOf ? "of" : "in"), varDeclList.secondBinding.getToken());
}
if (varDeclList.declarationWithInitializerToken != 0 && (isStrictMode || type != TokenType.IN || varType != VAR || varDeclList.init != null)) {
throw error(AbstractParser.message("for.in.loop.initializer", isForOf ? "of" : "in"), varDeclList.declarationWithInitializerToken);
}
init = varDeclList.firstBinding;
assert init instanceof IdentNode || isDestructuringLhs(init);
} else {
assert init != null : "for..in/of init expression can not be null here";
if (!checkValidLValue(init, isForOf ? "for-of iterator" : "for-in iterator")) {
throw error(AbstractParser.message("not.lvalue.for.in.loop", isForOf ? "of" : "in"), init.getToken());
}
}
next();
modify = isForOf ? new JoinPredecessorExpression(assignmentExpression(false)) : joinPredecessorExpression();
break;
default:
expect(SEMICOLON);
break;
}
expect(RPAREN);
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);
if (body != null) {
List<Statement> statements = new ArrayList<>();
for (final Statement var : outer.getStatements()) {
if(var instanceof VarNode && !((VarNode)var).isBlockScoped()) {
appendStatement(var);
}else {
statements.add(var);
}
}
appendStatement(new BlockStatement(forLine, new Block(
outer.getToken(),
body.getFinish(),
statements)));
}
}
}
}
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;
}
}
@SuppressWarnings("fallthrough")
private boolean lookaheadIsLetDeclaration(final boolean ofContextualKeyword) {
assert type == LET;
for (int i = 1;; i++) {
final 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;
}
case LBRACKET:
case LBRACE:
return true;
default:
if (!isStrictMode && t.getKind() == TokenKind.FUTURESTRICT) {
return true;
}
return false;
}
}
}
private void whileStatement() {
final long whileToken = token;
final int whileLine = line;
next();
final ParserContextLoopNode whileNode = new ParserContextLoopNode();
lc.push(whileNode);
JoinPredecessorExpression test = null;
Block body = null;
try {
expect(LPAREN);
test = joinPredecessorExpression();
expect(RPAREN);
body = getStatement();
} finally {
lc.pop(whileNode);
}
if (body != null) {
appendStatement(new WhileNode(whileLine, whileToken, body.getFinish(), false, test, body));
}
}
private void doStatement() {
final long doToken = token;
int doLine = 0;
next();
final ParserContextLoopNode doWhileNode = new ParserContextLoopNode();
lc.push(doWhileNode);
Block body = null;
JoinPredecessorExpression test = null;
try {
body = getStatement();
expect(WHILE);
expect(LPAREN);
doLine = line;
test = joinPredecessorExpression();
expect(RPAREN);
if (type == SEMICOLON) {
endOfLine();
}
} finally {
lc.pop(doWhileNode);
}
appendStatement(new WhileNode(doLine, doToken, finish, true, test, body));
}
private void continueStatement() {
final int continueLine = line;
final long continueToken = token;
nextOrEOL();
ParserContextLabelNode labelNode = null;
switch (type) {
case RBRACE:
case SEMICOLON:
case EOL:
case EOF:
break;
default:
final IdentNode ident = getIdent();
labelNode = lc.findLabel(ident.getName());
if (labelNode == null) {
throw error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
}
break;
}
final String labelName = labelNode == null ? null : labelNode.getLabelName();
final ParserContextLoopNode targetNode = lc.getContinueTo(labelName);
if (targetNode == null) {
throw error(AbstractParser.message("illegal.continue.stmt"), continueToken);
}
endOfLine();
appendStatement(new ContinueNode(continueLine, continueToken, finish, labelName));
}
private void breakStatement() {
final int breakLine = line;
final long breakToken = token;
nextOrEOL();
ParserContextLabelNode labelNode = null;
switch (type) {
case RBRACE:
case SEMICOLON:
case EOL:
case EOF:
break;
default:
final IdentNode ident = getIdent();
labelNode = lc.findLabel(ident.getName());
if (labelNode == null) {
throw error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
}
break;
}
final String labelName = labelNode == null ? null : labelNode.getLabelName();
final ParserContextBreakableNode targetNode = lc.getBreakable(labelName);
if( targetNode instanceof ParserContextBlockNode) {
targetNode.setFlag(Block.IS_BREAKABLE);
}
if (targetNode == null) {
throw error(AbstractParser.message("illegal.break.stmt"), breakToken);
}
endOfLine();
appendStatement(new BreakNode(breakLine, breakToken, finish, labelName));
}
private void returnStatement() {
if (lc.getCurrentFunction().getKind() == FunctionNode.Kind.SCRIPT || lc.getCurrentFunction().getKind() == FunctionNode.Kind.MODULE) {
throw error(AbstractParser.message("invalid.return"));
}
final int returnLine = line;
final long returnToken = token;
nextOrEOL();
Expression expression = null;
switch (type) {
case RBRACE:
case SEMICOLON:
case EOL:
case EOF:
break;
default:
expression = expression();
break;
}
endOfLine();
appendStatement(new ReturnNode(returnLine, returnToken, finish, expression));
}
@SuppressWarnings("fallthrough")
private Expression yieldExpression(final boolean noIn) {
assert inGeneratorFunction();
long yieldToken = token;
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) {
expression = newUndefinedLiteral(yieldToken, finish);
if (type == EOL) {
next();
}
break;
} else {
}
default:
expression = assignmentExpression(noIn);
break;
}
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));
}
private void withStatement() {
final int withLine = line;
final long withToken = token;
next();
if (isStrictMode) {
throw error(AbstractParser.message("strict.no.with"), withToken);
}
expect(LPAREN);
final Expression expression = expression();
expect(RPAREN);
final Block body = getStatement();
appendStatement(new WithNode(withLine, withToken, finish, expression, body));
}
private void switchStatement() {
final int switchLine = line;
final long switchToken = token;
final ParserContextBlockNode switchBlock = newBlock();
next();
final ParserContextSwitchNode switchNode = new ParserContextSwitchNode();
lc.push(switchNode);
CaseNode defaultCase = null;
final List<CaseNode> cases = new ArrayList<>();
Expression expression = null;
try {
expect(LPAREN);
expression = expression();
expect(RPAREN);
expect(LBRACE);
while (type != RBRACE) {
Expression caseExpression = null;
final long caseToken = token;
switch (type) {
case CASE:
next();
caseExpression = expression();
break;
case DEFAULT:
if (defaultCase != null) {
throw error(AbstractParser.message("duplicate.default.in.switch"));
}
next();
break;
default:
expect(CASE);
break;
}
expect(COLON);
final Block statements = getBlock(false);
final CaseNode caseNode = new CaseNode(caseToken, finish, caseExpression, statements);
if (caseExpression == null) {
defaultCase = caseNode;
}
cases.add(caseNode);
}
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)));
}
private void labelStatement() {
final long labelToken = token;
final IdentNode ident = getIdent();
expect(COLON);
if (lc.findLabel(ident.getName()) != null) {
throw error(AbstractParser.message("duplicate.label", ident.getName()), labelToken);
}
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);
}
appendStatement(new LabelNode(line, labelToken, finish, ident.getName(), body));
}
private void throwStatement() {
final int throwLine = line;
final long throwToken = token;
nextOrEOL();
Expression expression = null;
switch (type) {
case RBRACE:
case SEMICOLON:
case EOL:
break;
default:
expression = expression();
break;
}
if (expression == null) {
throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
}
endOfLine();
appendStatement(new ThrowNode(throwLine, throwToken, finish, expression, false));
}
private void tryStatement() {
final int tryLine = line;
final long tryToken = token;
next();
final int startLine = line;
final ParserContextBlockNode outer = newBlock();
try {
final Block tryBody = getBlock(true);
final List<Block> catchBlocks = new ArrayList<>();
while (type == CATCH) {
final int catchLine = line;
final long catchToken = token;
next();
expect(LPAREN);
final String contextString = "catch argument";
final Expression exception = bindingIdentifierOrPattern(contextString);
final boolean isDestructuring = !(exception instanceof IdentNode);
if (isDestructuring) {
verifyDestructuringBindingPattern(exception, new Consumer<IdentNode>() {
@Override
public void accept(final IdentNode identNode) {
verifyIdent(identNode, contextString);
}
});
} else {
verifyIdent((IdentNode) exception, "catch argument");
}
final Expression ifExpression;
if (!env._no_syntax_extensions && type == IF) {
next();
ifExpression = expression();
} else {
ifExpression = null;
}
expect(RPAREN);
final ParserContextBlockNode catchBlock = newBlock();
try {
final Block catchBody = getBlock(true);
final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, ifExpression, catchBody, false);
appendStatement(catchNode);
} finally {
restoreBlock(catchBlock);
catchBlocks.add(new Block(catchBlock.getToken(), finish, catchBlock.getFlags() | Block.IS_SYNTHETIC, catchBlock.getStatements()));
}
if (ifExpression == null) {
break;
}
}
Block finallyStatements = null;
if (type == FINALLY) {
next();
finallyStatements = getBlock(true);
}
if (catchBlocks.isEmpty() && finallyStatements == null) {
throw error(AbstractParser.message("missing.catch.or.finally"), tryToken);
}
final TryNode tryNode = new TryNode(tryLine, tryToken, finish, tryBody, catchBlocks, finallyStatements);
assert lc.peek() == outer;
appendStatement(tryNode);
} finally {
restoreBlock(outer);
}
appendStatement(new BlockStatement(startLine, new Block(tryToken, finish, outer.getFlags() | Block.IS_SYNTHETIC, outer.getStatements())));
}
private void debuggerStatement() {
final int debuggerLine = line;
final long debuggerToken = token;
next();
endOfLine();
appendStatement(new DebuggerNode(debuggerLine, debuggerToken, finish));
}
@SuppressWarnings("fallthrough")
private Expression primaryExpression() {
final int primaryLine = line;
final long primaryToken = token;
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;
}
detectSpecialProperty(ident);
checkEscapedKeyword(ident);
return ident;
case OCTAL_LEGACY:
if (isStrictMode) {
throw error(AbstractParser.message("strict.no.octal"), token);
}
case STRING:
case ESCSTRING:
case DECIMAL:
case HEXADECIMAL:
case OCTAL:
case BINARY_NUMBER:
case FLOATING:
case REGEX:
case XML:
return getLiteral();
case EXECSTRING:
return execString(primaryLine, primaryToken);
case FALSE:
next();
return LiteralNode.newInstance(primaryToken, finish, false);
case TRUE:
next();
return LiteralNode.newInstance(primaryToken, finish, true);
case NULL:
next();
return LiteralNode.newInstance(primaryToken, finish);
case LBRACKET:
return arrayLiteral();
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) {
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;
case TEMPLATE:
case TEMPLATE_HEAD:
return templateLiteral();
default:
if (lexer.scanLiteral(primaryToken, type, lineInfoReceiver)) {
next();
return getLiteral();
}
if (isNonStrictModeIdent()) {
return getIdent();
}
break;
}
return null;
}
CallNode execString(final int primaryLine, final long primaryToken) {
final IdentNode execIdent = new IdentNode(primaryToken, finish, ScriptingFunctions.EXEC_NAME);
next();
expect(LBRACE);
final List<Expression> arguments = Collections.singletonList(expression());
expect(RBRACE);
return new CallNode(primaryLine, primaryToken, finish, execIdent, arguments, false);
}
@SuppressWarnings("fallthrough")
private LiteralNode<Expression[]> arrayLiteral() {
final long arrayToken = token;
next();
final List<Expression> elements = new ArrayList<>();
boolean elision = true;
boolean hasSpread = false;
loop:
while (true) {
long spreadToken = 0;
switch (type) {
case RBRACKET:
next();
break loop;
case COMMARIGHT:
next();
if (elision) {
elements.add(null);
}
elision = true;
break;
case ELLIPSIS:
if (isES6()) {
hasSpread = true;
spreadToken = token;
next();
}
default:
if (!elision) {
throw error(AbstractParser.message("expected.comma", type.getNameOrType()));
}
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);
}
elision = false;
break;
}
}
return LiteralNode.newInstance(arrayToken, finish, elements, hasSpread, elision);
}
private ObjectNode objectLiteral() {
final long objectToken = token;
next();
final List<PropertyNode> elements = new ArrayList<>();
final Map<String, Integer> map = new HashMap<>();
boolean commaSeen = true;
loop:
while (true) {
switch (type) {
case RBRACE:
next();
break loop;
case COMMARIGHT:
if (commaSeen) {
throw error(AbstractParser.message("expected.property.id", type.getNameOrType()));
}
next();
commaSeen = true;
break;
default:
if (!commaSeen) {
throw error(AbstractParser.message("expected.comma", type.getNameOrType()));
}
commaSeen = false;
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());
elements.add(property);
break;
}
final PropertyNode existingProperty = elements.get(existing);
final Expression value = property.getValue();
final FunctionNode getter = property.getGetter();
final FunctionNode setter = property.getSetter();
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) {
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;
if (prevValue != null && isAccessor) {
throw error(AbstractParser.message("property.redefinition", property.getKeyName()), property.getToken());
}
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());
}
}
}
@SuppressWarnings("fallthrough")
private PropertyKey literalPropertyName() {
switch (type) {
case IDENT:
return getIdent().setIsPropertyName();
case OCTAL_LEGACY:
if (isStrictMode) {
throw error(AbstractParser.message("strict.no.octal"), token);
}
case STRING:
case ESCSTRING:
case DECIMAL:
case HEXADECIMAL:
case OCTAL:
case BINARY_NUMBER:
case FLOATING:
return getLiteral();
default:
return getIdentifierName().setIsPropertyName();
}
}
private Expression computedPropertyName() {
expect(LBRACKET);
final Expression expression = assignmentExpression(false);
expect(RBRACKET);
return expression;
}
private Expression propertyName() {
if (type == LBRACKET && isES6()) {
return computedPropertyName();
} else {
return (Expression)literalPropertyName();
}
}
private PropertyNode propertyAssignment() {
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) {
final String ident = (String)expectValue(IDENT);
if (type != COLON && (type != LPAREN || !isES6())) {
final long getSetToken = propertyToken;
switch (ident) {
case GET_NAME:
final PropertyFunction getter = propertyGetterFunction(getSetToken, functionLine);
return new PropertyNode(propertyToken, finish, getter.key, null, getter.functionNode, null, false, getter.computed);
case SET_NAME:
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()) {
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;
try {
functionBody = functionBody(functionNode);
} finally {
lc.pop(functionNode);
}
final FunctionNode function = createFunctionNode(
functionNode,
getSetToken,
getNameNode,
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);
final IdentNode argIdent;
if (isBindingIdentifier()) {
argIdent = getIdent();
verifyIdent(argIdent, "setter argument");
} else {
argIdent = null;
}
expect(RPAREN);
final List<IdentNode> parameters = new ArrayList<>();
if (argIdent != null) {
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);
} finally {
lc.pop(functionNode);
}
final FunctionNode function = createFunctionNode(
functionNode,
getSetToken,
setNameNode,
parameters,
FunctionNode.Kind.SETTER,
functionLine,
functionBody);
return new PropertyFunction(propertyName, function, computed);
}
private PropertyFunction propertyMethodFunction(final Expression key, final long methodToken, final int methodLine, final boolean generator, final int flags, final boolean computed) {
final String methodName = key instanceof PropertyKey ? ((PropertyKey) key).getPropertyName() : getDefaultValidFunctionName(methodLine, false);
final IdentNode methodNameNode = createIdentNode(((Node)key).getToken(), finish, methodName);
final 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;
}
}
private Expression leftHandSideExpression() {
int callLine = line;
long callToken = token;
Expression lhs = memberExpression();
if (type == LPAREN) {
final List<Expression> arguments = optimizeList(argumentList());
if (lhs instanceof IdentNode) {
detectSpecialFunction((IdentNode)lhs);
checkEscapedKeyword((IdentNode)lhs);
}
lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false);
}
loop:
while (true) {
callLine = line;
callToken = token;
switch (type) {
case LPAREN: {
final List<Expression> arguments = optimizeList(argumentList());
lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false);
break;
}
case LBRACKET: {
next();
final Expression rhs = expression();
expect(RBRACKET);
lhs = new IndexNode(callToken, finish, lhs, rhs);
break;
}
case PERIOD: {
next();
final IdentNode property = getIdentifierName();
lhs = new AccessNode(callToken, finish, lhs, property.getName());
break;
}
case TEMPLATE:
case TEMPLATE_HEAD: {
final List<Expression> arguments = templateLiteralArgumentList();
lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false);
break;
}
default:
break loop;
}
}
return lhs;
}
private Expression newExpression() {
final long newToken = token;
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);
}
}
final int callLine = line;
final Expression constructor = memberExpression();
if (constructor == null) {
return null;
}
ArrayList<Expression> arguments;
if (type == LPAREN) {
arguments = argumentList();
} else {
arguments = new ArrayList<>();
}
if (!env._no_syntax_extensions && type == LBRACE) {
arguments.add(objectLiteral());
}
final CallNode callNode = new CallNode(callLine, constructor.getToken(), finish, constructor, optimizeList(arguments), true);
return new UnaryNode(newToken, callNode);
}
@SuppressWarnings("fallthrough")
private Expression memberExpression() {
Expression lhs;
boolean isSuper = false;
switch (type) {
case NEW:
lhs = newExpression();
break;
case FUNCTION:
lhs = functionExpression(false, false);
break;
case CLASS:
if (isES6()) {
lhs = classExpression(false);
break;
} else {
}
case SUPER:
if (isES6()) {
final ParserContextFunctionNode currentFunction = getCurrentNonArrowFunction();
if (currentFunction.isMethod()) {
final 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 {
}
default:
throw error(AbstractParser.message("invalid.super"), identToken);
}
break;
} else {
}
} else {
}
default:
lhs = primaryExpression();
break;
}
loop:
while (true) {
final long callToken = token;
switch (type) {
case LBRACKET: {
next();
final Expression index = expression();
expect(RBRACKET);
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()));
}
next();
final IdentNode property = getIdentifierName();
lhs = new AccessNode(callToken, finish, lhs, property.getName());
if (isSuper) {
isSuper = false;
lhs = ((BaseNode) lhs).setIsSuper();
}
break;
}
case TEMPLATE:
case TEMPLATE_HEAD: {
final int callLine = line;
final List<Expression> arguments = templateLiteralArgumentList();
lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false);
break;
}
default:
break loop;
}
}
return lhs;
}
private ArrayList<Expression> argumentList() {
final ArrayList<Expression> nodeList = new ArrayList<>();
next();
boolean first = true;
while (type != RPAREN) {
if (!first) {
expect(COMMARIGHT);
} else {
first = false;
}
long spreadToken = 0;
if (type == ELLIPSIS && isES6()) {
spreadToken = token;
next();
}
Expression expression = assignmentExpression(false);
if (spreadToken != 0) {
expression = new UnaryNode(Token.recast(spreadToken, TokenType.SPREAD_ARGUMENT), expression);
}
nodeList.add(expression);
}
expect(RPAREN);
return nodeList;
}
private static <T> List<T> optimizeList(final ArrayList<T> list) {
switch(list.size()) {
case 0: {
return Collections.emptyList();
}
case 1: {
return Collections.singletonList(list.get(0));
}
default: {
list.trimToSize();
return list;
}
}
}
private Expression functionExpression(final boolean isStatement, final boolean topLevel) {
final long functionToken = token;
final int functionLine = line;
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()))) {
expect(IDENT);
}
name = getIdent();
verifyIdent(name, "function name");
} else if (isStatement) {
if (env._no_syntax_extensions && reparsedFunction == null) {
expect(IDENT);
}
}
boolean isAnonymous = false;
if (name == null) {
final String tmpName = getDefaultValidFunctionName(functionLine, isStatement);
name = new IdentNode(functionToken, Token.descPosition(functionToken), tmpName);
isAnonymous = true;
}
final 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;
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);
} else if (env._function_statement == ScriptEnvironment.FunctionStatementBehavior.WARNING) {
warning(JSErrorType.SYNTAX_ERROR, AbstractParser.message("no.func.decl.here.warn"), functionToken);
}
if (isArguments(name)) {
lc.getCurrentFunction().setFlag(FunctionNode.DEFINES_ARGUMENTS);
}
}
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;
}
final int varFlags = (topLevel || !useBlockScope()) ? 0 : VarNode.IS_LET;
final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, function, varFlags);
if (topLevel) {
functionDeclarations.add(varNode);
} else if (useBlockScope()) {
prependStatement(varNode);
} else {
appendStatement(varNode);
}
}
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)) {
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(final Block functionBody, final 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) {
return ANON_FUNCTION_PREFIX.symbolName() + defaultFunctionName;
}
return defaultFunctionName;
}
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;
}
private void markDefaultNameUsed() {
defaultNames.pop();
hideDefaultName();
}
private void hideDefaultName() {
defaultNames.push("");
}
private List<IdentNode> formalParameterList(final boolean yield) {
return formalParameterList(RPAREN, yield);
}
private List<IdentNode> formalParameterList(final TokenType endType, final boolean yield) {
final ArrayList<IdentNode> parameters = new ArrayList<>();
boolean first = true;
while (type != endType) {
if (!first) {
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();
expectDontAdvance(endType);
parameters.add(ident);
break;
} else if (type == ASSIGN && isES6()) {
next();
ident = ident.setIsDefaultParameter();
if (type == YIELD && yield) {
expect(IDENT);
}
final Expression initializer = assignmentExpression(false);
final ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
if (currentFunction != null) {
if (env._parse_only) {
final BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), ident, initializer);
currentFunction.addParameterExpression(ident, assignment);
} else {
final BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish));
final TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident));
final BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), ident, value);
lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment));
}
}
}
final ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
if (currentFunction != null) {
currentFunction.addParameterBinding(ident);
if (ident.isRestParameter() || ident.isDefaultParameter()) {
currentFunction.setSimpleParameterList(false);
}
}
} else {
final Expression pattern = bindingPattern();
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();
final Expression initializer = assignmentExpression(false);
if (env._parse_only) {
value = initializer;
} else {
final 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));
}
}
final ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
if (currentFunction != null) {
final BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), pattern, value);
if (env._parse_only) {
if (ident.isDefaultParameter()) {
currentFunction.addParameterExpression(ident, assignment);
} else {
currentFunction.addParameterExpression(ident, pattern);
}
} else {
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(final IdentNode identNode) {
verifyIdent(identNode, contextString);
final ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
if (currentFunction != null) {
if (!env._parse_only) {
lc.getFunctionBody(currentFunction).appendStatement(new VarNode(paramLine, Token.recast(paramToken, VAR), pattern.getFinish(), identNode, null));
}
currentFunction.addParameterBinding(identNode);
currentFunction.setSimpleParameterList(false);
}
}
});
}
private Block functionBody(final ParserContextFunctionNode functionNode) {
long lastToken = 0L;
ParserContextBlockNode body = null;
final long bodyToken = token;
Block functionBody;
int bodyFinish = 0;
final boolean parseBody;
Object endParserState = null;
try {
body = newBlock();
if (env._debug_scopes) {
markEval(lc);
}
assert functionNode != null;
final int functionId = functionNode.getId();
parseBody = reparsedFunction == null || functionId <= reparsedFunction.getFunctionNodeId();
if ((!env._no_syntax_extensions || functionNode.getKind() == FunctionNode.Kind.ARROW) && type != LBRACE) {
final Expression expr = assignmentExpression(false);
lastToken = previousToken;
functionNode.setLastToken(previousToken);
assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode);
final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken));
if (parseBody) {
functionNode.setFlag(FunctionNode.HAS_EXPRESSION_BODY);
final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr);
appendStatement(returnNode);
}
} else {
expectDontAdvance(LBRACE);
if (parseBody || !skipFunctionBody(functionNode)) {
next();
final List<Statement> prevFunctionDecls = functionDeclarations;
functionDeclarations = new ArrayList<>();
try {
sourceElements(0);
addFunctionDeclarations(functionNode);
} finally {
functionDeclarations = prevFunctionDecls;
}
lastToken = token;
if (parseBody) {
endParserState = new ParserState(Token.descPosition(token), line, linePosition);
}
}
bodyFinish = finish;
functionNode.setLastToken(token);
expect(RBRACE);
}
} finally {
restoreBlock(body);
}
if (parseBody) {
functionNode.setEndParserState(endParserState);
} else if (!body.getStatements().isEmpty()){
body.setStatements(Collections.<Statement>emptyList());
}
if (reparsedFunction != null) {
final RecompilableScriptFunctionData data = reparsedFunction.getScriptFunctionData(functionNode.getId());
if (data != null) {
functionNode.setFlag(data.getFunctionFlags());
if (functionNode.hasNestedEval()) {
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) {
return false;
}
final RecompilableScriptFunctionData data = reparsedFunction.getScriptFunctionData(functionNode.getId());
if (data == null) {
return false;
}
final ParserState parserState = (ParserState)data.getEndParserState();
assert parserState != null;
if (k < stream.last() && start < parserState.position && parserState.position <= Token.descPosition(stream.get(stream.last()))) {
for (; k < stream.last(); k++) {
final long nextToken = stream.get(k + 1);
if (Token.descPosition(nextToken) == parserState.position && Token.descType(nextToken) == RBRACE) {
token = stream.get(k);
type = Token.descType(token);
next();
assert type == RBRACE && start == parserState.position;
return true;
}
}
}
stream.reset();
lexer = parserState.createLexer(source, lexer, stream, scripting && !env._no_syntax_extensions, env._es6);
line = parserState.line;
linePosition = parserState.linePosition;
type = SEMICOLON;
scanFirstToken();
return true;
}
private static class ParserState implements Serializable {
private final int position;
private final int line;
private final int linePosition;
private static final long serialVersionUID = -2382565130754093694L;
ParserState(final int position, final int line, final int linePosition) {
this.position = position;
this.line = line;
this.linePosition = linePosition;
}
Lexer createLexer(final Source source, final Lexer lexer, final TokenStream stream, final boolean scripting, final boolean es6) {
final Lexer newLexer = new Lexer(source, position, lexer.limit - position, stream, scripting, es6, true);
newLexer.restoreState(new Lexer.State(position, Integer.MAX_VALUE, line, -1, linePosition, SEMICOLON));
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) {
VarNode lastDecl = null;
for (int i = functionDeclarations.size() - 1; i >= 0; i--) {
Statement decl = functionDeclarations.get(i);
if (lastDecl == null && decl instanceof VarNode) {
decl = lastDecl = ((VarNode)decl).setFlag(VarNode.IS_LAST_FUNCTION_DECLARATION);
functionNode.setFlag(FunctionNode.HAS_FUNCTION_DECLARATIONS);
}
prependStatement(decl);
}
}
private RuntimeNode referenceError(final Expression lhs, final Expression rhs, final boolean earlyError) {
if (env._parse_only || earlyError) {
throw error(JSErrorType.REFERENCE_ERROR, AbstractParser.message("invalid.lvalue"), lhs.getToken());
}
final ArrayList<Expression> args = new ArrayList<>();
args.add(lhs);
if (rhs == null) {
args.add(LiteralNode.newInstance(lhs.getToken(), lhs.getFinish()));
} else {
args.add(rhs);
}
args.add(LiteralNode.newInstance(lhs.getToken(), lhs.getFinish(), lhs.toString()));
return new RuntimeNode(lhs.getToken(), lhs.getFinish(), RuntimeNode.Request.REFERENCE_ERROR, args);
}
private Expression unaryExpression() {
final int unaryLine = line;
final long unaryToken = token;
switch (type) {
case ADD:
case SUB: {
final TokenType opType = type;
next();
final Expression expr = unaryExpression();
return new UnaryNode(Token.recast(unaryToken, (opType == TokenType.ADD) ? TokenType.POS : TokenType.NEG), expr);
}
case DELETE:
case VOID:
case TYPEOF:
case BIT_NOT:
case NOT:
next();
final Expression expr = unaryExpression();
return new UnaryNode(unaryToken, expr);
case INCPREFIX:
case DECPREFIX:
final TokenType opType = type;
next();
final Expression lhs = leftHandSideExpression();
if (lhs == null) {
throw error(AbstractParser.message("expected.lvalue", type.getNameOrType()));
}
return verifyIncDecExpression(unaryToken, opType, lhs, false);
default:
break;
}
final Expression expression = leftHandSideExpression();
if (last != EOL) {
switch (type) {
case INCPREFIX:
case DECPREFIX:
final long opToken = token;
final TokenType opType = type;
final Expression lhs = expression;
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);
}
protected Expression expression() {
return expression(false);
}
private Expression expression(final boolean noIn) {
Expression assignmentExpression = assignmentExpression(noIn);
while (type == COMMARIGHT) {
final long commaToken = token;
next();
boolean rhsRestParameter = false;
if (type == ELLIPSIS && isES6()) {
if (isRestParameterEndOfArrowFunctionParameterList()) {
next();
rhsRestParameter = true;
}
}
Expression rhs = assignmentExpression(noIn);
if (rhsRestParameter) {
rhs = ((IdentNode)rhs).setIsRestParameter();
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());
}
private Expression expression(final Expression exprLhs, final int minPrecedence, final boolean noIn) {
int precedence = type.getPrecedence();
Expression lhs = exprLhs;
while (type.isOperator(noIn) && precedence >= minPrecedence) {
final long op = token;
if (type == TERNARY) {
next();
final Expression trueExpr = expression(unaryExpression(), ASSIGN.getPrecedence(), false);
expect(COLON);
final Expression falseExpr = expression(unaryExpression(), ASSIGN.getPrecedence(), noIn);
lhs = new TernaryNode(op, lhs, new JoinPredecessorExpression(trueExpr), new JoinPredecessorExpression(falseExpr));
} else {
next();
Expression rhs;
final boolean isAssign = Token.descType(op) == ASSIGN;
if(isAssign) {
defaultNames.push(lhs);
}
try {
rhs = unaryExpression();
int nextPrecedence = type.getPrecedence();
while (type.isOperator(noIn) &&
(nextPrecedence > precedence ||
nextPrecedence == precedence && !type.isLeftAssociative())) {
rhs = expression(rhs, nextPrecedence, noIn);
nextPrecedence = type.getPrecedence();
}
} finally {
if(isAssign) {
defaultNames.pop();
}
}
lhs = verifyAssignment(op, lhs, rhs);
}
precedence = type.getPrecedence();
}
return lhs;
}
protected Expression assignmentExpression(final boolean noIn) {
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;
}
}
private static boolean isAssignmentOperator(final 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;
}
private Expression conditionalExpression(final boolean noIn) {
return expression(TERNARY.getPrecedence(), noIn);
}
private Expression arrowFunction(final long startToken, final int functionLine, final Expression paramListExpr) {
assert type != ARROW || checkNoLineTerminator();
expect(ARROW);
final long functionToken = Token.recast(startToken, ARROW);
final IdentNode name = new IdentNode(functionToken, Token.descPosition(functionToken), NameCodec.encode("=>:") + functionLine);
final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, FunctionNode.Kind.ARROW, functionLine, null);
functionNode.setFlag(FunctionNode.IS_ANONYMOUS);
lc.push(functionNode);
try {
final 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) {
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;
}
});
}
}
}
private List<IdentNode> convertArrowFunctionParameterList(final Expression paramListExpr, final int functionLine) {
final List<IdentNode> parameters;
if (paramListExpr == null) {
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(final Expression param, final int index, final int paramLine) {
final String contextString = "function parameter";
if (param instanceof IdentNode) {
final IdentNode ident = (IdentNode)param;
verifyIdent(ident, contextString);
final ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
if (currentFunction != null) {
currentFunction.addParameterBinding(ident);
}
return ident;
}
if (param.isTokenType(ASSIGN)) {
final Expression lhs = ((BinaryNode) param).lhs();
final long paramToken = lhs.getToken();
final Expression initializer = ((BinaryNode) param).rhs();
if (lhs instanceof IdentNode) {
final IdentNode ident = (IdentNode) lhs;
final ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
if (currentFunction != null) {
if (env._parse_only) {
currentFunction.addParameterExpression(ident, param);
} else {
final BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish));
final TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident));
final 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)) {
final IdentNode ident = createIdentNode(paramToken, param.getFinish(), String.format("arguments[%d]", index)).setIsDestructuredParameter().setIsDefaultParameter();
verifyDestructuringParameterBindingPattern(param, paramToken, paramLine, contextString);
final ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
if (currentFunction != null) {
if (env._parse_only) {
currentFunction.addParameterExpression(ident, param);
} else {
final BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish));
final TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident));
final 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)) {
final long paramToken = param.getToken();
final IdentNode ident = createIdentNode(paramToken, param.getFinish(), String.format("arguments[%d]", index)).setIsDestructuredParameter();
verifyDestructuringParameterBindingPattern(param, paramToken, paramLine, contextString);
final ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
if (currentFunction != null) {
if (env._parse_only) {
currentFunction.addParameterExpression(ident, param);
} else {
final 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--) {
final 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;
}
private boolean isRestParameterEndOfArrowFunctionParameterList() {
assert type == ELLIPSIS;
int i = 1;
for (;;) {
final TokenType t = T(k + i++);
if (t == IDENT) {
break;
} else if (t == EOL || t == COMMENT) {
continue;
} else {
return false;
}
}
for (;;) {
final TokenType t = T(k + i++);
if (t == RPAREN) {
break;
} else if (t == EOL || t == COMMENT) {
continue;
} else {
return false;
}
}
for (;;) {
final TokenType t = T(k + i++);
if (t == ARROW) {
break;
} else if (t == COMMENT) {
continue;
} else {
return false;
}
}
return true;
}
private void endOfLine() {
switch (type) {
case SEMICOLON:
case EOL:
next();
break;
case RPAREN:
case RBRACKET:
case RBRACE:
case EOF:
break;
default:
if (last != EOL) {
expect(SEMICOLON);
}
break;
}
}
private Expression templateLiteral() {
assert type == TEMPLATE || type == TEMPLATE_HEAD;
final boolean noSubstitutionTemplate = type == TEMPLATE;
long lastLiteralToken = token;
LiteralNode<?> literal = getLiteral();
if (noSubstitutionTemplate) {
return literal;
}
if (env._parse_only) {
final List<Expression> exprs = new ArrayList<>();
exprs.add(literal);
TokenType lastLiteralType;
do {
final Expression expression = expression();
if (type != TEMPLATE_MIDDLE && type != TEMPLATE_TAIL) {
throw error(AbstractParser.message("unterminated.template.expression"), token);
}
exprs.add(expression);
lastLiteralType = type;
literal = getLiteral();
exprs.add(literal);
} while (lastLiteralType == TEMPLATE_MIDDLE);
return new TemplateLiteral(exprs);
} else {
Expression concat = literal;
TokenType lastLiteralType;
do {
final Expression expression = expression();
if (type != TEMPLATE_MIDDLE && type != TEMPLATE_TAIL) {
throw error(AbstractParser.message("unterminated.template.expression"), token);
}
concat = new BinaryNode(Token.recast(lastLiteralToken, TokenType.ADD), concat, expression);
lastLiteralType = type;
lastLiteralToken = token;
literal = getLiteral();
concat = new BinaryNode(Token.recast(lastLiteralToken, TokenType.ADD), concat, literal);
} while (lastLiteralType == TEMPLATE_MIDDLE);
return concat;
}
}
private List<Expression> templateLiteralArgumentList() {
assert type == TEMPLATE || type == TEMPLATE_HEAD;
final ArrayList<Expression> argumentList = new ArrayList<>();
final ArrayList<Expression> rawStrings = new ArrayList<>();
final ArrayList<Expression> cookedStrings = new ArrayList<>();
argumentList.add(null);
final long templateToken = token;
final boolean hasSubstitutions = type == TEMPLATE_HEAD;
addTemplateLiteralString(rawStrings, cookedStrings);
if (hasSubstitutions) {
TokenType lastLiteralType;
do {
final Expression expression = expression();
if (type != TEMPLATE_MIDDLE && type != TEMPLATE_TAIL) {
throw error(AbstractParser.message("unterminated.template.expression"), token);
}
argumentList.add(expression);
lastLiteralType = type;
addTemplateLiteralString(rawStrings, cookedStrings);
} while (lastLiteralType == TEMPLATE_MIDDLE);
}
final LiteralNode<Expression[]> rawStringArray = LiteralNode.newInstance(templateToken, finish, rawStrings);
final LiteralNode<Expression[]> cookedStringArray = LiteralNode.newInstance(templateToken, finish, cookedStrings);
if (!env._parse_only) {
final RuntimeNode templateObject = new RuntimeNode(templateToken, finish, RuntimeNode.Request.GET_TEMPLATE_OBJECT, rawStringArray, cookedStringArray);
argumentList.set(0, templateObject);
} else {
argumentList.set(0, rawStringArray);
}
return optimizeList(argumentList);
}
private void addTemplateLiteralString(final ArrayList<Expression> rawStrings, final ArrayList<Expression> cookedStrings) {
final long stringToken = token;
final String rawString = lexer.valueOfRawString(stringToken);
final String cookedString = (String) getValue();
next();
rawStrings.add(LiteralNode.newInstance(stringToken, finish, rawString));
cookedStrings.add(LiteralNode.newInstance(stringToken, finish, cookedString));
}
private FunctionNode module(final String moduleName) {
final boolean oldStrictMode = isStrictMode;
try {
isStrictMode = true;
final 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;
}
}
private void moduleBody() {
loop:
while (type != EOF) {
switch (type) {
case EOF:
break loop;
case IMPORT:
importDeclaration();
break;
case EXPORT:
exportDeclaration();
break;
default:
statement(true, 0, false, false);
break;
}
}
}
private void importDeclaration() {
final int startPosition = start;
expect(IMPORT);
final ParserContextModuleNode module = lc.getCurrentModule();
if (type == STRING || type == ESCSTRING) {
final IdentNode moduleSpecifier = createIdentNode(token, finish, (String) getValue());
next();
module.addModuleRequest(moduleSpecifier);
} else {
List<Module.ImportEntry> importEntries;
if (type == MUL) {
importEntries = Collections.singletonList(nameSpaceImport(startPosition));
} else if (type == LBRACE) {
importEntries = namedImports(startPosition);
} else if (isBindingIdentifier()) {
final IdentNode importedDefaultBinding = bindingIdentifier("ImportedBinding");
final Module.ImportEntry defaultImport = Module.ImportEntry.importSpecifier(importedDefaultBinding, startPosition, finish);
if (type == COMMARIGHT) {
next();
importEntries = new ArrayList<>();
if (type == MUL) {
importEntries.add(nameSpaceImport(startPosition));
} else if (type == LBRACE) {
importEntries.addAll(namedImports(startPosition));
} else {
throw error(AbstractParser.message("expected.named.import"));
}
} else {
importEntries = Collections.singletonList(defaultImport);
}
} else {
throw error(AbstractParser.message("expected.import"));
}
final IdentNode moduleSpecifier = fromClause();
module.addModuleRequest(moduleSpecifier);
for (int i = 0; i < importEntries.size(); i++) {
module.addImportEntry(importEntries.get(i).withFrom(moduleSpecifier, finish));
}
}
expect(SEMICOLON);
}
private Module.ImportEntry nameSpaceImport(final int startPosition) {
assert type == MUL;
final IdentNode starName = createIdentNode(Token.recast(token, IDENT), finish, Module.STAR_NAME);
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.importSpecifier(starName, localNameSpace, startPosition, finish);
}
private List<Module.ImportEntry> namedImports(final int startPosition) {
assert type == LBRACE;
next();
final 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, localName, startPosition, finish));
} else if (!bindingIdentifier) {
throw error(AbstractParser.message("expected.binding.identifier"), nameToken);
} else {
importEntries.add(Module.ImportEntry.importSpecifier(importName, startPosition, finish));
}
if (type == COMMARIGHT) {
next();
} else {
break;
}
}
expect(RBRACE);
return importEntries;
}
private IdentNode 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 IdentNode moduleSpecifier = createIdentNode(Token.recast(token, IDENT), finish, (String) getValue());
next();
return moduleSpecifier;
} else {
throw error(expectMessage(STRING));
}
}
private void exportDeclaration() {
expect(EXPORT);
final int startPosition = start;
final ParserContextModuleNode module = lc.getCurrentModule();
switch (type) {
case MUL: {
final IdentNode starName = createIdentNode(Token.recast(token, IDENT), finish, Module.STAR_NAME);
next();
final IdentNode moduleRequest = fromClause();
expect(SEMICOLON);
module.addModuleRequest(moduleRequest);
module.addStarExportEntry(Module.ExportEntry.exportStarFrom(starName, moduleRequest, startPosition, finish));
break;
}
case LBRACE: {
final List<Module.ExportEntry> exportEntries = exportClause(startPosition);
if (type == IDENT && "from".equals(getValue())) {
final IdentNode moduleRequest = fromClause();
module.addModuleRequest(moduleRequest);
for (final Module.ExportEntry exportEntry : exportEntries) {
module.addIndirectExportEntry(exportEntry.withFrom(moduleRequest, finish));
}
} else {
for (final Module.ExportEntry exportEntry : exportEntries) {
module.addLocalExportEntry(exportEntry);
}
}
expect(SEMICOLON);
break;
}
case DEFAULT:
final IdentNode defaultName = createIdentNode(Token.recast(token, IDENT), finish, Module.DEFAULT_NAME);
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(defaultName, ident, startPosition, finish));
} 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(defaultName, ident, startPosition, finish));
}
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(), startPosition, finish));
}
}
break;
case CLASS: {
final ClassNode classDeclaration = classDeclaration(false);
module.addLocalExportEntry(Module.ExportEntry.exportSpecifier(classDeclaration.getIdent(), startPosition, finish));
break;
}
case FUNCTION: {
final FunctionNode functionDeclaration = (FunctionNode) functionExpression(true, true);
module.addLocalExportEntry(Module.ExportEntry.exportSpecifier(functionDeclaration.getIdent(), startPosition, finish));
break;
}
default:
throw error(AbstractParser.message("invalid.export"), token);
}
}
private List<Module.ExportEntry> exportClause(final int startPosition) {
assert type == LBRACE;
next();
final 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, localName, startPosition, finish));
} else {
exports.add(Module.ExportEntry.exportSpecifier(localName, startPosition, finish));
}
if (type == COMMARIGHT) {
next();
} else {
break;
}
}
expect(RBRACE);
return exports;
}
@Override
public String toString() {
return "'JavaScript Parsing'";
}
private static void markEval(final ParserContext lc) {
final Iterator<ParserContextFunctionNode> iter = lc.getFunctions();
boolean flaggedCurrentFn = false;
while (iter.hasNext()) {
final ParserContextFunctionNode fn = iter.next();
if (!flaggedCurrentFn) {
fn.setFlag(FunctionNode.HAS_EVAL);
flaggedCurrentFn = true;
if (fn.getKind() == FunctionNode.Kind.ARROW) {
markThis(lc);
markNewTarget(lc);
}
} else {
fn.setFlag(FunctionNode.HAS_NESTED_EVAL);
}
final ParserContextBlockNode body = lc.getFunctionBody(fn);
body.setFlag(Block.NEEDS_SCOPE);
fn.setFlag(FunctionNode.HAS_SCOPE_BLOCK);
}
}
private void prependStatement(final Statement statement) {
lc.prependStatementToCurrentNode(statement);
}
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;
}
}