package com.oracle.js.parser;
import static com.oracle.js.parser.TokenType.ARROW;
import static com.oracle.js.parser.TokenType.AS;
import static com.oracle.js.parser.TokenType.ASSIGN;
import static com.oracle.js.parser.TokenType.ASSIGN_INIT;
import static com.oracle.js.parser.TokenType.ASYNC;
import static com.oracle.js.parser.TokenType.AWAIT;
import static com.oracle.js.parser.TokenType.CASE;
import static com.oracle.js.parser.TokenType.CATCH;
import static com.oracle.js.parser.TokenType.CLASS;
import static com.oracle.js.parser.TokenType.COLON;
import static com.oracle.js.parser.TokenType.COMMARIGHT;
import static com.oracle.js.parser.TokenType.COMMENT;
import static com.oracle.js.parser.TokenType.CONST;
import static com.oracle.js.parser.TokenType.DECPOSTFIX;
import static com.oracle.js.parser.TokenType.DECPREFIX;
import static com.oracle.js.parser.TokenType.ELLIPSIS;
import static com.oracle.js.parser.TokenType.ELSE;
import static com.oracle.js.parser.TokenType.EOF;
import static com.oracle.js.parser.TokenType.EOL;
import static com.oracle.js.parser.TokenType.EQ_STRICT;
import static com.oracle.js.parser.TokenType.ESCSTRING;
import static com.oracle.js.parser.TokenType.EXPORT;
import static com.oracle.js.parser.TokenType.EXTENDS;
import static com.oracle.js.parser.TokenType.FINALLY;
import static com.oracle.js.parser.TokenType.FROM;
import static com.oracle.js.parser.TokenType.FUNCTION;
import static com.oracle.js.parser.TokenType.GET;
import static com.oracle.js.parser.TokenType.IDENT;
import static com.oracle.js.parser.TokenType.IF;
import static com.oracle.js.parser.TokenType.IMPORT;
import static com.oracle.js.parser.TokenType.INCPOSTFIX;
import static com.oracle.js.parser.TokenType.INCPREFIX;
import static com.oracle.js.parser.TokenType.LBRACE;
import static com.oracle.js.parser.TokenType.LBRACKET;
import static com.oracle.js.parser.TokenType.LET;
import static com.oracle.js.parser.TokenType.LPAREN;
import static com.oracle.js.parser.TokenType.MUL;
import static com.oracle.js.parser.TokenType.OF;
import static com.oracle.js.parser.TokenType.PERIOD;
import static com.oracle.js.parser.TokenType.RBRACE;
import static com.oracle.js.parser.TokenType.RBRACKET;
import static com.oracle.js.parser.TokenType.RPAREN;
import static com.oracle.js.parser.TokenType.SEMICOLON;
import static com.oracle.js.parser.TokenType.SET;
import static com.oracle.js.parser.TokenType.SPREAD_ARGUMENT;
import static com.oracle.js.parser.TokenType.SPREAD_ARRAY;
import static com.oracle.js.parser.TokenType.SPREAD_OBJECT;
import static com.oracle.js.parser.TokenType.STATIC;
import static com.oracle.js.parser.TokenType.STRING;
import static com.oracle.js.parser.TokenType.SUPER;
import static com.oracle.js.parser.TokenType.TEMPLATE;
import static com.oracle.js.parser.TokenType.TEMPLATE_HEAD;
import static com.oracle.js.parser.TokenType.TEMPLATE_MIDDLE;
import static com.oracle.js.parser.TokenType.TEMPLATE_TAIL;
import static com.oracle.js.parser.TokenType.TERNARY;
import static com.oracle.js.parser.TokenType.VAR;
import static com.oracle.js.parser.TokenType.VOID;
import static com.oracle.js.parser.TokenType.WHILE;
import static com.oracle.js.parser.TokenType.YIELD;
import static com.oracle.js.parser.TokenType.YIELD_STAR;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import org.graalvm.collections.Pair;
import com.oracle.js.parser.ir.AccessNode;
import com.oracle.js.parser.ir.BaseNode;
import com.oracle.js.parser.ir.BinaryNode;
import com.oracle.js.parser.ir.Block;
import com.oracle.js.parser.ir.BlockStatement;
import com.oracle.js.parser.ir.BreakNode;
import com.oracle.js.parser.ir.CallNode;
import com.oracle.js.parser.ir.CaseNode;
import com.oracle.js.parser.ir.CatchNode;
import com.oracle.js.parser.ir.ClassNode;
import com.oracle.js.parser.ir.ContinueNode;
import com.oracle.js.parser.ir.DebuggerNode;
import com.oracle.js.parser.ir.EmptyNode;
import com.oracle.js.parser.ir.ErrorNode;
import com.oracle.js.parser.ir.ExportNode;
import com.oracle.js.parser.ir.ExportSpecifierNode;
import com.oracle.js.parser.ir.Expression;
import com.oracle.js.parser.ir.ExpressionList;
import com.oracle.js.parser.ir.ExpressionStatement;
import com.oracle.js.parser.ir.ForNode;
import com.oracle.js.parser.ir.FromNode;
import com.oracle.js.parser.ir.FunctionNode;
import com.oracle.js.parser.ir.IdentNode;
import com.oracle.js.parser.ir.IfNode;
import com.oracle.js.parser.ir.ImportClauseNode;
import com.oracle.js.parser.ir.ImportNode;
import com.oracle.js.parser.ir.ImportSpecifierNode;
import com.oracle.js.parser.ir.IndexNode;
import com.oracle.js.parser.ir.JoinPredecessorExpression;
import com.oracle.js.parser.ir.LabelNode;
import com.oracle.js.parser.ir.LexicalContext;
import com.oracle.js.parser.ir.LiteralNode;
import com.oracle.js.parser.ir.LiteralNode.ArrayLiteralNode;
import com.oracle.js.parser.ir.Module;
import com.oracle.js.parser.ir.Module.ImportEntry;
import com.oracle.js.parser.ir.NameSpaceImportNode;
import com.oracle.js.parser.ir.NamedExportsNode;
import com.oracle.js.parser.ir.NamedImportsNode;
import com.oracle.js.parser.ir.Node;
import com.oracle.js.parser.ir.ObjectNode;
import com.oracle.js.parser.ir.ParameterNode;
import com.oracle.js.parser.ir.PropertyKey;
import com.oracle.js.parser.ir.PropertyNode;
import com.oracle.js.parser.ir.ReturnNode;
import com.oracle.js.parser.ir.RuntimeNode;
import com.oracle.js.parser.ir.Scope;
import com.oracle.js.parser.ir.Statement;
import com.oracle.js.parser.ir.SwitchNode;
import com.oracle.js.parser.ir.Symbol;
import com.oracle.js.parser.ir.TernaryNode;
import com.oracle.js.parser.ir.ThrowNode;
import com.oracle.js.parser.ir.TryNode;
import com.oracle.js.parser.ir.UnaryNode;
import com.oracle.js.parser.ir.VarNode;
import com.oracle.js.parser.ir.WhileNode;
import com.oracle.js.parser.ir.WithNode;
import com.oracle.js.parser.ir.visitor.NodeVisitor;
@SuppressWarnings("fallthrough")
public class Parser extends AbstractParser {
private static final String ARGUMENTS_NAME = "arguments";
private static final String EVAL_NAME = "eval";
private static final String CONSTRUCTOR_NAME = "constructor";
private static final String PRIVATE_CONSTRUCTOR_NAME = "#constructor";
private static final String PROTO_NAME = "__proto__";
private static final String NEW_TARGET_NAME = "new.target";
private static final String IMPORT_META_NAME = "import.meta";
private static final String PROTOTYPE_NAME = "prototype";
private static final String APPLY_NAME = "apply";
private static final String EXEC_NAME = "$EXEC";
private static final String ANONYMOUS_FUNCTION_NAME = ":anonymous";
private static final String PROGRAM_NAME = ":program";
private static final String ERROR_BINDING_NAME = ":error";
private static final String SWITCH_BINDING_NAME = ":switch";
private static final String ARROW_FUNCTION_NAME = ":=>";
private static final String INITIALIZER_FUNCTION_NAME = ":initializer";
private static final String FUNCTION_PARAMETER_CONTEXT = "function parameter";
private static final String CATCH_PARAMETER_CONTEXT = "catch parameter";
private static final String IMPORTED_BINDING_CONTEXT = "imported binding";
private static final String CLASS_NAME_CONTEXT = "class name";
private static final String VARIABLE_NAME_CONTEXT = "variable name";
private static final String ASSIGNMENT_TARGET_CONTEXT = "assignment target";
private static final boolean ES6_FOR_OF = Options.getBooleanProperty("parser.for.of", true);
private static final boolean ES6_CLASS = Options.getBooleanProperty("parser.class", true);
private static final boolean ES6_ARROW_FUNCTION = Options.getBooleanProperty("parser.arrow.function", true);
private static final boolean ES6_REST_PARAMETER = Options.getBooleanProperty("parser.rest.parameter", true);
private static final boolean ES6_SPREAD_ARGUMENT = Options.getBooleanProperty("parser.spread.argument", true);
private static final boolean ES6_GENERATOR_FUNCTION = Options.getBooleanProperty("parser.generator.function", true);
private static final boolean ES6_DESTRUCTURING = Options.getBooleanProperty("parser.destructuring", true);
private static final boolean ES6_SPREAD_ARRAY = Options.getBooleanProperty("parser.spread.array", true);
private static final boolean ES6_COMPUTED_PROPERTY_NAME = Options.getBooleanProperty("parser.computed.property.name", true);
private static final boolean ES6_DEFAULT_PARAMETER = Options.getBooleanProperty("parser.default.parameter", true);
private static final boolean ES6_NEW_TARGET = Options.getBooleanProperty("parser.new.target", true);
private static final boolean ES8_TRAILING_COMMA = Options.getBooleanProperty("parser.trailing.comma", true);
private static final boolean ES8_ASYNC_FUNCTION = Options.getBooleanProperty("parser.async.function", true);
private static final boolean ES8_REST_SPREAD_PROPERTY = Options.getBooleanProperty("parser.rest.spread.property", true);
private static final boolean ES8_FOR_AWAIT_OF = Options.getBooleanProperty("parser.for.await.of", true);
private static final boolean ES2019_OPTIONAL_CATCH_BINDING = Options.getBooleanProperty("parser.optional.catch.binding", true);
private static final boolean ES2020_CLASS_FIELDS = Options.getBooleanProperty("parser.class.fields", true);
private static final boolean ES2021_TOP_LEVEL_AWAIT = Options.getBooleanProperty("parser.top.level.await", true);
private static final int REPARSE_IS_PROPERTY_ACCESSOR = 1 << 0;
private static final int REPARSE_IS_METHOD = 1 << 1;
private static final int PARSE_EVAL = 1 << 2;
private static final int PARSE_FUNCTION_CONTEXT_EVAL = 1 << 3;
private static final String MESSAGE_INVALID_LVALUE = "invalid.lvalue";
private static final String MESSAGE_EXPECTED_STMT = "expected.stmt";
private static final String MESSAGE_ESCAPED_KEYWORD = "escaped.keyword";
private static final String MESSAGE_INVALID_PROPERTY_INITIALIZER = "invalid.property.initializer";
private static final String MESSAGE_INVALID_ARROW_PARAMETER = "invalid.arrow.parameter";
private static final String MESSAGE_EXPECTED_OPERAND = "expected.operand";
private static final String MESSAGE_PROPERTY_REDEFINITON = "property.redefinition";
private final ScriptEnvironment env;
private final boolean scripting;
private final boolean shebang;
private final boolean allowBigInt;
private List<Statement> functionDeclarations;
private final ParserContext lc;
private final List<Object> defaultNames;
private final Namespace namespace;
protected final Lexer.LineInfoReceiver lineInfoReceiver;
private RecompilableScriptFunctionData reparsedFunction;
private boolean isModule;
public static final boolean PROFILE_PARSING = Options.getBooleanProperty("parser.profiling", false);
public static final boolean PROFILE_PARSING_PRINT = Options.getBooleanProperty("parser.profiling.print", true);
public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors) {
this(env, source, errors, env.strict);
}
public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict) {
this(env, source, errors, strict, 0);
}
public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int lineOffset) {
super(source, errors, strict, lineOffset);
this.lc = new ParserContext();
this.defaultNames = new ArrayList<>();
this.env = env;
this.namespace = new Namespace(env.getNamespace());
this.scripting = env.scripting && env.syntaxExtensions;
this.shebang = env.shebang || scripting;
this.allowBigInt = env.allowBigInt;
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;
}
}
public FunctionNode parse() {
return parse(PROGRAM_NAME, 0, source.getLength(), 0, null, null);
}
public void setReparsedFunction(final RecompilableScriptFunctionData reparsedFunction) {
this.reparsedFunction = reparsedFunction;
}
private void scanFirstToken() {
k = -1;
next();
}
private void prepareLexer(final int startPos, final int len) {
stream = new TokenStream();
lexer = new Lexer(source, startPos, len, stream, scripting, env.ecmaScriptVersion, shebang, isModule, reparsedFunction != null, allowBigInt);
lexer.line = lexer.pendingLine = lineOffset + 1;
line = lineOffset;
}
private TokenType lookahead() {
for (int i = 1;; i++) {
TokenType t = T(k + i);
if (t == EOL || t == COMMENT) {
continue;
} else {
return t;
}
}
}
public FunctionNode parse(final String scriptName, final int startPos, final int len, final int reparseFlags, Scope parentScope, String[] argumentNames) {
long startTime = PROFILE_PARSING ? System.nanoTime() : 0L;
try {
prepareLexer(startPos, len);
scanFirstToken();
return program(scriptName, reparseFlags, parentScope, argumentNames);
} catch (final Exception e) {
handleParseException(e);
return null;
} finally {
if (PROFILE_PARSING) {
long duration = (System.nanoTime() - startTime);
if (PROFILE_PARSING_PRINT) {
System.out.println("Parsing: " + duration / 1_000_000);
}
}
}
}
public FunctionNode parseModule(final String moduleName, final int startPos, final int len) {
boolean oldModule = isModule;
boolean oldStrictMode = isStrictMode;
try {
isModule = true;
isStrictMode = true;
prepareLexer(startPos, len);
scanFirstToken();
return module(moduleName);
} catch (final Exception e) {
handleParseException(e);
return null;
} finally {
isStrictMode = oldStrictMode;
isModule = oldModule;
}
}
public FunctionNode parseModule(final String moduleName) {
return parseModule(moduleName, 0, source.getLength());
}
public FunctionNode parseEval(boolean functionContext, Scope parentScope) {
return parse(PROGRAM_NAME, 0, source.getLength(), PARSE_EVAL | (functionContext ? PARSE_FUNCTION_CONTEXT_EVAL : 0), parentScope, null);
}
public FunctionNode parseWithArguments(String[] argumentNames) {
return parse(PROGRAM_NAME, 0, source.getLength(), 0, null, argumentNames);
}
public void parseFormalParameterList() {
try {
stream = new TokenStream();
lexer = new Lexer(source, stream, scripting, env.ecmaScriptVersion, shebang, isModule, allowBigInt);
scanFirstToken();
formalParameterList(TokenType.EOF, false, false);
} catch (final Exception e) {
handleParseException(e);
}
}
public FunctionNode parseFunctionBody(boolean generator, boolean async) {
try {
stream = new TokenStream();
lexer = new Lexer(source, stream, scripting, env.ecmaScriptVersion, shebang, isModule, allowBigInt);
final int functionLine = line;
scanFirstToken();
final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength());
final IdentNode ident = new IdentNode(functionToken, Token.descPosition(functionToken), PROGRAM_NAME);
final int functionFlags = (generator ? FunctionNode.IS_GENERATOR : 0) | (async ? FunctionNode.IS_ASYNC : 0);
final ParserContextFunctionNode function = createParserContextFunctionNode(ident, functionToken, functionFlags, functionLine, Collections.<IdentNode> emptyList(), 0);
function.clearFlag(FunctionNode.IS_PROGRAM);
assert lc.getCurrentScope() == null;
lc.push(function);
final ParserContextBlockNode body = newBlock(function.createBodyScope());
functionDeclarations = new ArrayList<>();
try {
sourceElements(0);
addFunctionDeclarations(function);
} finally {
functionDeclarations = null;
restoreBlock(body);
lc.pop(function);
}
body.setFlag(Block.NEEDS_SCOPE);
final Block functionBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC | Block.IS_BODY, body.getScope(), body.getStatements());
expect(EOF);
final FunctionNode functionNode = createFunctionNode(
function,
functionToken,
ident,
functionLine,
functionBody);
return functionNode;
} catch (final Exception e) {
handleParseException(e);
return null;
}
}
private void handleParseException(final Exception e) {
if (e instanceof ParserException) {
errors.error((ParserException) e);
} else {
String message = e.getMessage();
if (message == null) {
message = e.toString();
}
errors.error(message);
}
if (env.dumpOnError) {
e.printStackTrace(env.getErr());
}
}
private void recover(final Exception e) {
if (e != null) {
if (e instanceof ParserException) {
errors.error((ParserException) e);
} else {
String message = e.getMessage();
if (message == null) {
message = e.toString();
}
errors.error(message);
}
if (env.dumpOnError) {
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() {
Scope scope = Scope.createBlock(lc.getCurrentScope());
return newBlock(scope);
}
private ParserContextBlockNode newBlock(Scope scope) {
return lc.push(new ParserContextBlockNode(token, scope));
}
private ParserContextFunctionNode createParserContextFunctionNode(final IdentNode ident, final long functionToken, final int functionFlags, final int functionLine) {
return createParserContextFunctionNode(ident, functionToken, functionFlags, functionLine, null, 0);
}
private ParserContextFunctionNode createParserContextFunctionNode(final IdentNode ident, final long functionToken, final int functionFlags, final int functionLine,
final List<IdentNode> parameters, int functionLength) {
return createParserContextFunctionNode(ident, functionToken, functionFlags, functionLine, parameters, functionLength, null);
}
private ParserContextFunctionNode createParserContextFunctionNode(final IdentNode ident, final long functionToken, final int functionFlags, final int functionLine,
final List<IdentNode> parameters, int functionLength, Scope functionTopScope) {
final ParserContextFunctionNode parentFunction = lc.getCurrentFunction();
final String name = ident == null ? "" : ident.getName();
int flags = functionFlags;
if (isStrictMode) {
flags |= FunctionNode.IS_STRICT;
}
if (parentFunction == null) {
flags |= FunctionNode.IS_PROGRAM;
flags |= FunctionNode.IS_ANONYMOUS;
}
final Scope parentScope = lc.getCurrentScope();
return new ParserContextFunctionNode(functionToken, ident, name, namespace, functionLine, flags, parameters, functionLength, parentScope, functionTopScope);
}
private FunctionNode createFunctionNode(final ParserContextFunctionNode function, final long startToken, final IdentNode ident,
final int functionLine, final Block body) {
assert body.isFunctionBody() || (body.isParameterBlock() && ((BlockStatement) body.getLastStatement()).getBlock().isFunctionBody());
VarNode varNode = function.getBodyScope().verifyHoistedVarDeclarations();
if (varNode != null) {
throw error(ECMAErrors.getMessage("syntax.error.redeclare.variable", varNode.getName().getName()), varNode.getToken());
}
long lastTokenWithDelimiter = Token.withDelimiter(function.getLastToken());
int lastTokenFinish = Token.descPosition(lastTokenWithDelimiter) + (Token.descType(lastTokenWithDelimiter) == TokenType.EOL ? 0 : Token.descLength(lastTokenWithDelimiter));
final FunctionNode functionNode = new FunctionNode(
source,
functionLine,
body.getToken(),
lastTokenFinish,
startToken,
function.getLastToken(),
ident,
function.getName(),
function.getLength(),
function.getParameterCount(),
optimizeList(function.getParameters()),
function.getFlags(),
body,
function.getEndParserState(),
function.getModule(),
function.getInternalName());
return functionNode;
}
private ParserContextBlockNode restoreBlock(final ParserContextBlockNode block) {
block.getScope().close();
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);
}
int realFinish;
if (needsBraces) {
expectDontAdvance(RBRACE);
realFinish = Token.descPosition(token) + Token.descLength(token);
expect(RBRACE);
} else {
realFinish = finish;
}
final int flags = newBlock.getFlags() | (needsBraces ? 0 : Block.IS_SYNTHETIC);
return new Block(blockToken, Math.max(realFinish, Token.descPosition(blockToken)), flags, newBlock.getScope(), newBlock.getStatements());
}
private List<Statement> caseStatementList() {
final ParserContextBlockNode newBlock = newBlock(lc.getCurrentScope());
try {
statementList();
} finally {
lc.pop(newBlock);
}
return newBlock.getStatements();
}
private Block getStatement() {
return getStatement(false, false);
}
private Block getStatement(boolean labelledStatement, boolean mayBeFunctionDeclaration) {
return getStatement(labelledStatement, mayBeFunctionDeclaration, mayBeFunctionDeclaration);
}
private Block getStatement(boolean labelledStatement, boolean mayBeFunctionDeclaration, boolean maybeLabeledFunctionDeclaration) {
if (type == LBRACE) {
return getBlock(true);
}
final ParserContextBlockNode newBlock = newBlock();
try {
statement(false, 0, true, labelledStatement, mayBeFunctionDeclaration, maybeLabeledFunctionDeclaration);
} finally {
restoreBlock(newBlock);
}
return new Block(newBlock.getToken(), finish, newBlock.getFlags() | Block.IS_SYNTHETIC, newBlock.getScope(), newBlock.getStatements());
}
private IdentNode detectSpecialProperty(final IdentNode ident) {
if (isArguments(ident)) {
return markArguments(ident);
}
return ident;
}
private IdentNode markArguments(final IdentNode ident) {
if (lc.getCurrentScope().inClassFieldInitializer()) {
throw error(AbstractParser.message("arguments.in.field.initializer"), ident.getToken());
}
lc.getCurrentNonArrowFunction().setFlag(FunctionNode.USES_ARGUMENTS);
return ident.setIsArguments();
}
private boolean useBlockScope() {
return isES6();
}
private boolean isES6() {
return env.ecmaScriptVersion >= 6;
}
private boolean isES2017() {
return env.ecmaScriptVersion >= 8;
}
private boolean isES2020() {
return env.ecmaScriptVersion >= 11;
}
private boolean isES2021() {
return env.ecmaScriptVersion >= 12;
}
private boolean isClassFields() {
return ES2020_CLASS_FIELDS && env.classFields;
}
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, boolean inPatternPosition) {
final TokenType opType = Token.descType(op);
Expression rhsExpr = rhs;
switch (opType) {
case ASSIGN:
case ASSIGN_INIT:
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_EXP:
case ASSIGN_SAR:
case ASSIGN_SHL:
case ASSIGN_SHR:
case ASSIGN_SUB:
case ASSIGN_AND:
case ASSIGN_OR:
case ASSIGN_NULLCOAL:
if (lhs instanceof IdentNode) {
IdentNode ident = (IdentNode) lhs;
if (!checkIdentLValue(ident) || ident.isMetaProperty()) {
throw invalidLHSError(lhs);
}
verifyStrictIdent(ident, ASSIGNMENT_TARGET_CONTEXT);
if (!lhs.isParenthesized() && isAnonymousFunctionDefinition(rhsExpr)) {
rhsExpr = setAnonymousFunctionName(rhsExpr, ident.getName());
}
break;
} else if (lhs instanceof AccessNode || lhs instanceof IndexNode) {
if (((BaseNode) lhs).isOptional()) {
throw invalidLHSError(lhs);
}
break;
} else if ((opType == ASSIGN || opType == ASSIGN_INIT) && isDestructuringLhs(lhs) && (inPatternPosition || !lhs.isParenthesized())) {
verifyDestructuringAssignmentPattern(lhs, ASSIGNMENT_TARGET_CONTEXT);
break;
} else {
throw invalidLHSError(lhs);
}
default:
break;
}
assert !BinaryNode.isLogical(opType);
return new BinaryNode(op, lhs, rhsExpr);
}
private boolean isDestructuringLhs(Expression lhs) {
if (lhs instanceof ObjectNode || lhs instanceof ArrayLiteralNode) {
return ES6_DESTRUCTURING && isES6();
}
return false;
}
private void verifyDestructuringAssignmentPattern(Expression pattern, String contextString) {
assert pattern instanceof ObjectNode || pattern instanceof ArrayLiteralNode;
pattern.accept(new VerifyDestructuringPatternNodeVisitor(new LexicalContext()) {
@Override
protected void verifySpreadElement(Expression lvalue) {
if (!checkValidLValue(lvalue, contextString)) {
throw error(AbstractParser.message(MESSAGE_INVALID_LVALUE), lvalue.getToken());
}
lvalue.accept(this);
}
@Override
public boolean enterIdentNode(IdentNode identNode) {
if (!checkIdentLValue(identNode) || identNode.isMetaProperty()) {
throw error(AbstractParser.message(MESSAGE_INVALID_LVALUE), identNode.getToken());
}
verifyStrictIdent(identNode, contextString);
return false;
}
@Override
public boolean enterAccessNode(AccessNode accessNode) {
if (accessNode.isOptional()) {
throw error(AbstractParser.message(MESSAGE_INVALID_LVALUE), accessNode.getToken());
}
return false;
}
@Override
public boolean enterIndexNode(IndexNode indexNode) {
if (indexNode.isOptional()) {
throw error(AbstractParser.message(MESSAGE_INVALID_LVALUE), indexNode.getToken());
}
return false;
}
@Override
protected boolean enterDefault(Node node) {
throw error(String.format("unexpected node in AssignmentPattern: %s", node));
}
});
}
private Expression newBinaryExpression(final long op, final Expression lhs, final Expression rhs) {
final TokenType opType = Token.descType(op);
if (BinaryNode.isLogical(opType)) {
if (forbiddenNullishCoalescingUsage(opType, lhs, rhs)) {
throw error(String.format("nullish coalescing operator cannot immediately contain, or be contained within, an && or || operation"));
}
return new BinaryNode(op, new JoinPredecessorExpression(lhs), new JoinPredecessorExpression(rhs));
}
return new BinaryNode(op, lhs, rhs);
}
private static boolean forbiddenNullishCoalescingUsage(TokenType opType, Expression lhs, Expression rhs) {
if (opType == TokenType.NULLISHCOALESC) {
return forbiddenNullishCoalescingChaining(lhs) || forbiddenNullishCoalescingChaining(rhs);
} else {
assert opType == TokenType.AND || opType == TokenType.OR;
return (!lhs.isParenthesized() && lhs.isTokenType(TokenType.NULLISHCOALESC)) || (!rhs.isParenthesized() && rhs.isTokenType(TokenType.NULLISHCOALESC));
}
}
private static boolean forbiddenNullishCoalescingChaining(Expression expression) {
return !expression.isParenthesized() && (expression.isTokenType(TokenType.AND) || expression.isTokenType(TokenType.OR));
}
private static UnaryNode incDecExpression(final long firstToken, final TokenType tokenType, final Expression expression, final boolean isPostfix) {
assert tokenType == INCPREFIX || tokenType == DECPREFIX;
if (isPostfix) {
long postfixToken = Token.recast(firstToken, tokenType == DECPREFIX ? DECPOSTFIX : INCPOSTFIX);
return new UnaryNode(postfixToken, expression.getStart(), Token.descPosition(firstToken) + Token.descLength(firstToken), expression);
}
return new UnaryNode(firstToken, expression);
}
private FunctionNode program(final String scriptName, final int parseFlags, final Scope parentScope, final String[] argumentNames) {
int functionStart = Math.min(Token.descPosition(Token.withDelimiter(token)), finish);
final long functionToken = Token.toDesc(FUNCTION, functionStart, source.getLength() - functionStart);
final int functionLine = line;
Scope topScope = (parseFlags & PARSE_EVAL) != 0 ? createEvalScope(parseFlags, parentScope) : Scope.createGlobal();
topScope = applyArgumentsToScope(topScope, argumentNames);
final IdentNode ident = null;
final List<IdentNode> parameters = createFunctionNodeParameters(argumentNames);
final ParserContextFunctionNode script = createParserContextFunctionNode(
ident,
functionToken,
FunctionNode.IS_SCRIPT,
functionLine,
parameters,
parameters.size(),
topScope);
script.setInternalName(scriptName);
lc.push(script);
final ParserContextBlockNode body = newBlock(topScope);
functionDeclarations = new ArrayList<>();
try {
sourceElements(parseFlags);
addFunctionDeclarations(script);
} finally {
functionDeclarations = null;
restoreBlock(body);
lc.pop(script);
}
body.setFlag(Block.NEEDS_SCOPE);
final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC | Block.IS_BODY, body.getScope(), body.getStatements());
script.setLastToken(token);
expect(EOF);
return createFunctionNode(script, functionToken, ident, functionLine, programBody);
}
private static Scope applyArgumentsToScope(Scope scope, String[] argumentNames) {
if (argumentNames == null) {
return scope;
}
Scope body = Scope.createFunctionBody(scope, 0);
for (String argument : argumentNames) {
body.putSymbol(new Symbol(argument, Symbol.IS_VAR | Symbol.IS_PARAM));
}
return body;
}
private static List<IdentNode> createFunctionNodeParameters(String[] argumentNames) {
if (argumentNames == null) {
return Collections.emptyList();
}
ArrayList<IdentNode> list = new ArrayList<>();
for (String argumentName : argumentNames) {
list.add(new IdentNode(0, 0, argumentName));
}
return list;
}
private Scope createEvalScope(final int parseFlags, Scope parentScope) {
assert (parseFlags & PARSE_EVAL) != 0;
if ((isStrictMode || (parseFlags & PARSE_FUNCTION_CONTEXT_EVAL) != 0)) {
return Scope.createEval(parentScope, isStrictMode);
} else {
return Scope.createGlobal();
}
}
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() + 1, Token.descLength(litToken) - 2);
}
}
}
return null;
}
private void sourceElements(final int parseFlags) {
boolean checkDirective = true;
int functionFlags = parseFlags;
final boolean oldStrictMode = isStrictMode;
try {
while (type != EOF) {
final TokenType elementType = type;
if (elementType == RBRACE) {
break;
}
try {
statement(true, functionFlags, false, false, true);
functionFlags = 0;
if (checkDirective) {
final Statement lastStatement = (elementType == STRING || elementType == ESCSTRING) ? lc.getLastStatement() : null;
final String directive = getDirective(lastStatement);
checkDirective = directive != null;
if (checkDirective) {
if (elementType == STRING && "use strict".equals(directive)) {
final ParserContextFunctionNode function = lc.getCurrentFunction();
if (!function.isSimpleParameterList()) {
throw error(AbstractParser.message("use.strict.non.simple.param"), lastStatement.getToken());
}
if (!oldStrictMode) {
function.setFlag(FunctionNode.IS_STRICT);
isStrictMode = true;
verifyUseStrict(function, parseFlags);
} else {
assert function.isStrict();
}
}
}
}
} 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 verifyUseStrict(final ParserContextFunctionNode function, final int parseFlags) {
for (final Node statement : lc.peek().getStatements()) {
getValue(statement.getToken());
}
if (function.getIdent() != null) {
verifyStrictIdent(function.getIdent(), "function name");
}
for (final IdentNode param : function.getParameters()) {
verifyStrictIdent(param, FUNCTION_PARAMETER_CONTEXT);
}
if ((parseFlags & PARSE_EVAL) != 0) {
setupStrictEvalScope();
}
}
private void setupStrictEvalScope() {
ParserContextBlockNode body = lc.getCurrentBlock();
assert body.getScope().getSymbolCount() == 0;
if (body.getScope().isGlobalScope()) {
Scope evalScope = Scope.createEval(body.getScope(), true);
body.setScope(evalScope);
ParserContextFunctionNode function = lc.getCurrentFunction();
function.replaceBodyScope(evalScope);
assert function.getBodyScope() == evalScope;
}
}
private void statement() {
statement(false, 0, false, false, false);
}
private void statement(final boolean topLevel, final int reparseFlags, final boolean singleStatement, final boolean labelledStatement, final boolean mayBeFunctionDeclaration) {
statement(topLevel, reparseFlags, singleStatement, labelledStatement, mayBeFunctionDeclaration, mayBeFunctionDeclaration);
}
private void statement(final boolean topLevel, final int reparseFlags, final boolean singleStatement, final boolean labelledStatement, final boolean mayBeFunctionDeclaration,
final boolean maybeLabeledFunctionDeclaration) {
switch (type) {
case LBRACE:
block();
return;
case VAR:
variableStatement(type);
return;
case SEMICOLON:
emptyStatement();
return;
case IF:
ifStatement();
return;
case FOR:
forStatement();
return;
case WHILE:
whileStatement();
return;
case DO:
doStatement();
return;
case CONTINUE:
continueStatement();
return;
case BREAK:
breakStatement();
return;
case RETURN:
returnStatement();
return;
case WITH:
withStatement();
return;
case SWITCH:
switchStatement();
return;
case THROW:
throwStatement();
return;
case TRY:
tryStatement();
return;
case DEBUGGER:
debuggerStatement();
return;
case RPAREN:
case RBRACKET:
case EOF:
expect(SEMICOLON);
return;
case FUNCTION:
if (singleStatement) {
if (isStrictMode || !mayBeFunctionDeclaration) {
throw error(AbstractParser.message(MESSAGE_EXPECTED_STMT, "function declaration"), token);
}
}
functionExpression(true, topLevel || labelledStatement, singleStatement);
return;
case LET:
if (useBlockScope()) {
TokenType lookahead = lookaheadOfLetDeclaration();
if (lookahead != null) {
if (singleStatement) {
if (lookahead == LBRACKET || T(k + 1) == IDENT) {
throw error(AbstractParser.message(MESSAGE_EXPECTED_STMT, "let declaration"), token);
}
} else {
variableStatement(type);
return;
}
}
}
break;
case CONST:
if (useBlockScope()) {
if (singleStatement) {
throw error(AbstractParser.message(MESSAGE_EXPECTED_STMT, "const declaration"), token);
}
variableStatement(type);
return;
} else if (env.constAsVar) {
variableStatement(TokenType.VAR);
return;
}
break;
case CLASS:
if (ES6_CLASS && isES6()) {
if (singleStatement) {
throw error(AbstractParser.message(MESSAGE_EXPECTED_STMT, "class declaration"), token);
}
classDeclaration(inGeneratorFunction(), inAsyncFunction(), false);
return;
}
break;
case ASYNC:
if (isAsync() && lookaheadIsAsyncFunction()) {
if (singleStatement) {
throw error(AbstractParser.message(MESSAGE_EXPECTED_STMT, "async function declaration"), token);
}
asyncFunctionExpression(true, topLevel || labelledStatement);
return;
}
break;
default:
break;
}
if (isBindingIdentifier()) {
if (T(k + 1) == COLON && (type != YIELD || !inGeneratorFunction()) && (!isAwait() || !inAsyncFunction())) {
labelStatement(maybeLabeledFunctionDeclaration);
return;
}
if (reparseFlags != 0 && reparseFunctionStatement(reparseFlags)) {
return;
}
}
expressionStatement();
}
private boolean reparseFunctionStatement(final int reparseFlags) {
final boolean allowPropertyFunction = (reparseFlags & REPARSE_IS_PROPERTY_ACCESSOR) != 0;
final boolean isES6Method = (reparseFlags & REPARSE_IS_METHOD) != 0;
if (allowPropertyFunction) {
final long propertyToken = token;
final int propertyLine = line;
if (type == GET) {
next();
addPropertyFunctionStatement(propertyGetterFunction(propertyToken, propertyLine, false, false, false));
return true;
} else if (type == SET) {
next();
addPropertyFunctionStatement(propertySetterFunction(propertyToken, propertyLine, false, false, false));
return true;
}
} else if (isES6Method) {
final String ident = (String) getValue();
IdentNode identNode = createIdentNode(token, finish, ident).setIsPropertyName();
final long propertyToken = token;
final int propertyLine = line;
next();
final int flags = CONSTRUCTOR_NAME.equals(ident) ? FunctionNode.IS_CLASS_CONSTRUCTOR : FunctionNode.IS_METHOD;
addPropertyFunctionStatement(propertyMethodFunction(identNode, propertyToken, propertyLine, false, flags, false, false));
return true;
}
return false;
}
private void addPropertyFunctionStatement(final PropertyFunction propertyFunction) {
final FunctionNode fn = propertyFunction.functionNode;
functionDeclarations.add(new ExpressionStatement(fn.getLineNumber(), fn.getToken(), finish, fn));
}
private ClassNode classDeclaration(boolean yield, boolean await, boolean defaultExport) {
assert type == CLASS;
int classLineNumber = line;
long classToken = token;
next();
boolean oldStrictMode = isStrictMode;
isStrictMode = true;
try {
IdentNode className = null;
if (!defaultExport || isBindingIdentifier()) {
className = bindingIdentifier(yield, await, CLASS_NAME_CONTEXT);
}
ClassNode classExpression = classTail(classLineNumber, classToken, className, yield, await);
if (!defaultExport) {
VarNode classVar = new VarNode(classLineNumber, Token.recast(classExpression.getToken(), LET), classExpression.getFinish(), className, classExpression, VarNode.IS_LET);
appendStatement(classVar);
declareVar(lc.getCurrentScope(), classVar);
}
return classExpression;
} finally {
isStrictMode = oldStrictMode;
}
}
private ClassNode classExpression(boolean yield, boolean await) {
assert type == CLASS;
int classLineNumber = line;
long classToken = token;
next();
boolean oldStrictMode = isStrictMode;
isStrictMode = true;
try {
IdentNode className = null;
if (isBindingIdentifier()) {
className = bindingIdentifier(yield, await, CLASS_NAME_CONTEXT);
}
return classTail(classLineNumber, classToken, className, yield, await);
} finally {
isStrictMode = oldStrictMode;
}
}
private ClassNode classTail(int classLineNumber, long classToken, IdentNode className, boolean yield, boolean await) {
assert isStrictMode;
Scope classScope = Scope.createClass(lc.getCurrentScope());
if (className != null) {
classScope.putSymbol(new Symbol(className.getName(), Symbol.IS_CONST));
}
ParserContextClassNode classNode = new ParserContextClassNode(classScope);
lc.push(classNode);
try {
Expression classHeritage = null;
if (type == EXTENDS) {
next();
classHeritage = leftHandSideExpression(yield, await);
IdentNode invalidPrivateIdent = classNode.verifyAllPrivateIdentifiersValid(lc);
if (invalidPrivateIdent != null) {
throw error(AbstractParser.message("invalid.private.ident"), invalidPrivateIdent.getToken());
}
}
expect(LBRACE);
PropertyNode constructor = null;
ArrayList<PropertyNode> classElements = new ArrayList<>();
Map<String, Integer> privateNameToAccessorIndexMap = new HashMap<>();
int instanceFieldCount = 0;
int staticFieldCount = 0;
boolean hasPrivateMethods = false;
boolean hasPrivateInstanceMethods = false;
for (;;) {
if (type == SEMICOLON) {
next();
continue;
}
if (type == RBRACE) {
break;
}
boolean isStatic = false;
if (type == STATIC) {
TokenType nextToken = lookahead();
if (nextToken != LPAREN && nextToken != ASSIGN && nextToken != SEMICOLON && nextToken != RBRACE) {
isStatic = true;
next();
}
}
long classElementToken = token;
int classElementLine = line;
boolean async = false;
if (isAsync() && lookaheadIsAsyncMethod(true)) {
async = true;
next();
}
boolean generator = false;
if (type == MUL && ES6_GENERATOR_FUNCTION && isES6()) {
generator = true;
next();
}
final TokenType nameTokenType = type;
final boolean computed = nameTokenType == LBRACKET;
final Expression classElementName = classElementName(yield, await, true);
PropertyNode classElement;
if (!generator && !async && isClassFieldDefinition(nameTokenType)) {
classElement = fieldDefinition(classElementName, isStatic, classElementToken, computed);
if (isStatic) {
staticFieldCount++;
} else {
instanceFieldCount++;
}
} else {
classElement = methodDefinition(classElementName, isStatic, classHeritage != null, generator, async, classElementToken, classElementLine, yield, await, nameTokenType, computed);
if (!classElement.isComputed() && classElement.isAccessor()) {
if (classElement.isPrivate()) {
String privateName = classElement.getPrivateName();
Integer existing = privateNameToAccessorIndexMap.get(privateName);
if (existing == null) {
privateNameToAccessorIndexMap.put(privateName, classElements.size());
} else {
PropertyNode otherAccessor = classElements.get(existing);
if (isStatic == otherAccessor.isStatic()) {
if (otherAccessor.getGetter() == null && classElement.getGetter() != null) {
classElements.set(existing, otherAccessor.setGetter(classElement.getGetter()));
continue;
} else if (otherAccessor.getSetter() == null && classElement.getSetter() != null) {
classElements.set(existing, otherAccessor.setSetter(classElement.getSetter()));
continue;
}
}
}
} else if (!classElements.isEmpty()) {
PropertyNode lastElement = classElements.get(classElements.size() - 1);
if (!lastElement.isComputed() && lastElement.isAccessor() && isStatic == lastElement.isStatic() &&
!lastElement.isPrivate() && classElement.getKeyName().equals(lastElement.getKeyName())) {
PropertyNode merged = classElement.getGetter() != null ? lastElement.setGetter(classElement.getGetter()) : lastElement.setSetter(classElement.getSetter());
classElements.set(classElements.size() - 1, merged);
continue;
}
}
}
}
if (classElement.isPrivate()) {
hasPrivateMethods = hasPrivateMethods || !classElement.isClassField();
hasPrivateInstanceMethods = hasPrivateInstanceMethods || (!classElement.isClassField() && !classElement.isStatic());
declarePrivateName(classScope, classElement);
}
if (!classElement.isStatic() && !classElement.isComputed() && classElement.getKeyName().equals(CONSTRUCTOR_NAME)) {
assert !classElement.isClassField();
if (constructor == null) {
constructor = classElement;
} else {
throw error(AbstractParser.message("multiple.constructors"), classElementToken);
}
} else {
classElements.add(classElement);
}
}
long lastToken = token;
expect(RBRACE);
classElements.trimToSize();
int classFinish = Token.descPosition(lastToken) + Token.descLength(lastToken);
if (constructor == null) {
constructor = createDefaultClassConstructor(classLineNumber, classToken, lastToken, className, classHeritage != null);
} else {
FunctionNode ctor = (FunctionNode) constructor.getValue();
int flags = ctor.getFlags();
if (className == null) {
flags |= FunctionNode.IS_ANONYMOUS;
}
constructor = constructor.setValue(new FunctionNode(ctor.getSource(), ctor.getLineNumber(), ctor.getToken(), classFinish, classToken, lastToken, className,
className == null ? "" : className.getName(),
ctor.getLength(), ctor.getNumOfParams(), ctor.getParameters(), flags, ctor.getBody(), ctor.getEndParserState(), ctor.getModule(), ctor.getInternalName()));
}
IdentNode invalidPrivateIdent = classNode.verifyAllPrivateIdentifiersValid(lc);
if (invalidPrivateIdent != null) {
throw error(AbstractParser.message("invalid.private.ident"), invalidPrivateIdent.getToken());
}
if (hasPrivateMethods) {
classScope.putSymbol(new Symbol(ClassNode.PRIVATE_CONSTRUCTOR_BINDING_NAME, Symbol.IS_CONST | Symbol.HAS_BEEN_DECLARED));
}
classScope.close();
return new ClassNode(classToken, classFinish, className, classHeritage, constructor, classElements, classScope,
instanceFieldCount, staticFieldCount, hasPrivateMethods, hasPrivateInstanceMethods);
} finally {
lc.pop(classNode);
}
}
private Expression classElementName(boolean yield, boolean await, boolean allowPrivate) {
if (allowPrivate && type == TokenType.PRIVATE_IDENT) {
return privateIdentifierDeclaration();
}
return propertyName(yield, await);
}
private IdentNode parsePrivateIdentifier() {
assert type == TokenType.PRIVATE_IDENT;
if (!isClassFields() && !isES2021()) {
throw error(AbstractParser.message("unexpected.token", type.getNameOrType()));
}
final long identToken = token;
final String name = (String) getValue(identToken);
next();
return createIdentNode(identToken, finish, name).setIsPrivate();
}
private IdentNode privateIdentifierDeclaration() {
IdentNode privateIdent = parsePrivateIdentifier();
ParserContextClassNode currentClass = lc.getCurrentClass();
if (currentClass == null) {
throw error(AbstractParser.message("invalid.private.ident"), privateIdent.getToken());
}
return privateIdent;
}
private void declarePrivateName(Scope classScope, PropertyNode classElement) {
int privateFlags = (classElement.isStatic() ? Symbol.IS_PRIVATE_NAME_STATIC : 0);
if (!classElement.isClassField()) {
privateFlags |= classElement.isAccessor() ? Symbol.IS_PRIVATE_NAME_ACCESSOR : Symbol.IS_PRIVATE_NAME_METHOD;
}
if (!classScope.addPrivateName(classElement.getPrivateName(), privateFlags)) {
throw error(ECMAErrors.getMessage("syntax.error.redeclare.variable", classElement.getPrivateName()), classElement.getKey().getToken());
}
}
private IdentNode privateIdentifierUse() {
IdentNode privateIdent = parsePrivateIdentifier();
ParserContextClassNode currentClass = lc.getCurrentClass();
if (currentClass != null) {
currentClass.usePrivateName(privateIdent);
} else {
if (!lc.getCurrentScope().findPrivateName(privateIdent.getName())) {
throw error(AbstractParser.message("invalid.private.ident"), privateIdent.getToken());
}
}
return privateIdent;
}
private boolean isClassFieldDefinition(final TokenType nameTokenType) {
if (!isClassFields()) {
return false;
}
switch (type) {
case ASSIGN:
case SEMICOLON:
case RBRACE:
return true;
case LPAREN:
return false;
default:
if (nameTokenType == GET || nameTokenType == SET) {
return false;
}
if (last == EOL) {
return true;
} else {
return false;
}
}
}
private PropertyNode createDefaultClassConstructor(int classLineNumber, long classToken, long lastToken, IdentNode className, boolean derived) {
final int ctorFinish = finish;
final List<Statement> statements;
final List<IdentNode> parameters;
final long identToken = Token.recast(classToken, TokenType.IDENT);
if (derived) {
IdentNode superIdent = new IdentNode(identToken, ctorFinish, SUPER.getName()).setIsDirectSuper();
IdentNode argsIdent = new IdentNode(identToken, ctorFinish, "args").setIsRestParameter();
Expression spreadArgs = new UnaryNode(Token.recast(classToken, TokenType.SPREAD_ARGUMENT), argsIdent);
Expression superCall = CallNode.forCall(classLineNumber, classToken, Token.descPosition(classToken), ctorFinish, superIdent, Collections.singletonList(spreadArgs));
statements = Collections.singletonList(new ExpressionStatement(classLineNumber, classToken, ctorFinish, superCall));
parameters = Collections.singletonList(argsIdent);
} else {
statements = Collections.emptyList();
parameters = Collections.emptyList();
}
int functionFlags = FunctionNode.IS_METHOD | FunctionNode.IS_CLASS_CONSTRUCTOR;
ParserContextFunctionNode function = createParserContextFunctionNode(className, classToken, functionFlags, classLineNumber, parameters, 0);
function.setLastToken(lastToken);
Scope scope = function.createBodyScope();
scope.close();
Block body = new Block(classToken, ctorFinish, Block.IS_BODY, scope, statements);
if (derived) {
function.setFlag(FunctionNode.IS_DERIVED_CONSTRUCTOR);
function.setFlag(FunctionNode.HAS_DIRECT_SUPER);
}
if (className == null) {
function.setFlag(FunctionNode.IS_ANONYMOUS);
function.setInternalName(CONSTRUCTOR_NAME);
}
PropertyNode constructor = new PropertyNode(classToken, ctorFinish, new IdentNode(identToken, ctorFinish, CONSTRUCTOR_NAME),
createFunctionNode(function, classToken, className, classLineNumber, body),
null, null, false, false, false, false);
return constructor;
}
private PropertyNode methodDefinition(Expression propertyName, boolean isStatic, boolean derived, boolean generator, boolean async, long startToken, int methodLine, boolean yield,
boolean await, TokenType nameTokenType, boolean computed) {
int flags = FunctionNode.IS_METHOD;
if (!computed) {
final String name = ((PropertyKey) propertyName).getPropertyName();
if (!generator && nameTokenType == GET && type != LPAREN) {
PropertyFunction methodDefinition = propertyGetterFunction(startToken, methodLine, yield, await, true);
verifyAllowedMethodName(methodDefinition.key, isStatic, methodDefinition.computed, generator, true, async);
return new PropertyNode(startToken, finish, methodDefinition.key, null, methodDefinition.functionNode, null, isStatic, methodDefinition.computed, false, false);
} else if (!generator && nameTokenType == SET && type != LPAREN) {
PropertyFunction methodDefinition = propertySetterFunction(startToken, methodLine, yield, await, true);
verifyAllowedMethodName(methodDefinition.key, isStatic, methodDefinition.computed, generator, true, async);
return new PropertyNode(startToken, finish, methodDefinition.key, null, null, methodDefinition.functionNode, isStatic, methodDefinition.computed, false, false);
} else {
if (!isStatic && !generator && name.equals(CONSTRUCTOR_NAME)) {
flags |= FunctionNode.IS_CLASS_CONSTRUCTOR;
if (derived) {
flags |= FunctionNode.IS_DERIVED_CONSTRUCTOR;
}
}
verifyAllowedMethodName(propertyName, isStatic, computed, generator, false, async);
}
}
PropertyFunction methodDefinition = propertyMethodFunction(propertyName, startToken, methodLine, generator, flags, computed, async);
return new PropertyNode(startToken, finish, methodDefinition.key, methodDefinition.functionNode, null, null, isStatic, computed, false, false);
}
private void verifyAllowedMethodName(Expression key, boolean isStatic, boolean computed, boolean generator, boolean accessor, boolean async) {
if (!computed) {
final String name = ((PropertyKey) key).getPropertyName();
if (!isStatic && generator && name.equals(CONSTRUCTOR_NAME)) {
throw error(AbstractParser.message("generator.constructor"), key.getToken());
}
if (!isStatic && accessor && name.equals(CONSTRUCTOR_NAME)) {
throw error(AbstractParser.message("accessor.constructor"), key.getToken());
}
if (!isStatic && async && name.equals(CONSTRUCTOR_NAME)) {
throw error(AbstractParser.message("async.constructor"), key.getToken());
}
if (isStatic && name.equals(PROTOTYPE_NAME)) {
throw error(AbstractParser.message("static.prototype.method"), key.getToken());
}
if (name.equals(PRIVATE_CONSTRUCTOR_NAME)) {
throw error(AbstractParser.message("private.constructor.method"), key.getToken());
}
}
}
private PropertyNode fieldDefinition(Expression propertyName, boolean isStatic, long startToken, boolean computed) {
if (!computed && propertyName instanceof PropertyKey) {
String name = ((PropertyKey) propertyName).getPropertyName();
if (CONSTRUCTOR_NAME.equals(name) || PRIVATE_CONSTRUCTOR_NAME.equals(name)) {
throw error(AbstractParser.message("constructor.field"), startToken);
}
if (isStatic && PROTOTYPE_NAME.equals(name)) {
throw error(AbstractParser.message("static.prototype.field"), startToken);
}
}
FunctionNode initializer = null;
boolean isAnonymousFunctionDefinition = false;
if (type == ASSIGN) {
next();
Pair<FunctionNode, Boolean> pair = fieldInitializer(line, startToken, propertyName, computed);
initializer = pair.getLeft();
isAnonymousFunctionDefinition = pair.getRight();
endOfLine();
}
return new PropertyNode(startToken, finish, propertyName, initializer, null, null, isStatic, computed, false, false, true, isAnonymousFunctionDefinition);
}
private Pair<FunctionNode, Boolean> fieldInitializer(int lineNumber, long fieldToken, Expression propertyName, boolean computed) {
int functionFlags = FunctionNode.IS_METHOD | FunctionNode.IS_CLASS_FIELD_INITIALIZER | FunctionNode.IS_ANONYMOUS;
ParserContextFunctionNode function = createParserContextFunctionNode(null, fieldToken, functionFlags, lineNumber, Collections.emptyList(), 0);
function.setInternalName(INITIALIZER_FUNCTION_NAME);
lc.push(function);
ParserContextBlockNode body = newBlock(function.createBodyScope());
Expression initializer;
try {
initializer = assignmentExpression(true, false, false);
} finally {
restoreBlock(body);
lc.pop(function);
}
assert function.getFlag(FunctionNode.USES_ARGUMENTS) == 0;
function.setLastToken(token);
boolean isAnonymousFunctionDefinition = false;
if (isAnonymousFunctionDefinition(initializer)) {
if (!computed && propertyName instanceof PropertyKey) {
initializer = setAnonymousFunctionName(initializer, ((PropertyKey) propertyName).getPropertyName());
} else {
isAnonymousFunctionDefinition = true;
}
}
final List<Statement> statements = Collections.singletonList(new ReturnNode(lineNumber, fieldToken, finish, initializer));
Block bodyBlock = new Block(fieldToken, finish, Block.IS_BODY | Block.IS_SYNTHETIC, body.getScope(), statements);
return Pair.create(createFunctionNode(function, fieldToken, null, lineNumber, bodyBlock), isAnonymousFunctionDefinition);
}
private boolean isPropertyName(long currentToken) {
TokenType currentType = Token.descType(currentToken);
if (ES6_COMPUTED_PROPERTY_NAME && currentType == LBRACKET && isES6()) {
return true;
}
switch (currentType) {
case IDENT:
return true;
case NON_OCTAL_DECIMAL:
case OCTAL_LEGACY:
if (isStrictMode) {
return false;
}
case STRING:
case ESCSTRING:
case DECIMAL:
case HEXADECIMAL:
case OCTAL:
case BINARY_NUMBER:
case BIGINT:
case FLOATING:
return true;
default:
return isIdentifierName(currentToken);
}
}
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 boolean yield, final boolean await) {
if (isES6()) {
if (isEscapedIdent(ident) && isReservedWordSequence(ident.getName())) {
throw error(AbstractParser.message(MESSAGE_ESCAPED_KEYWORD, ident.getName()), ident.getToken());
} else {
assert !isReservedWordSequence(ident.getName()) : ident.getName();
}
}
if (yield) {
if (ident.isTokenType(YIELD)) {
throw error(expectMessage(IDENT, ident.getToken()), ident.getToken());
} else if (isEscapedIdent(ident) && YIELD.getName().equals(ident.getName())) {
throw error(AbstractParser.message(MESSAGE_ESCAPED_KEYWORD, ident.getName()), ident.getToken());
} else {
assert !YIELD.getName().equals(ident.getName());
}
}
if (await || isModule) {
if (ident.isTokenType(AWAIT)) {
throw error(expectMessage(IDENT, ident.getToken()), ident.getToken());
} else if (isEscapedIdent(ident) && AWAIT.getName().equals(ident.getName())) {
throw error(AbstractParser.message(MESSAGE_ESCAPED_KEYWORD, ident.getName()), ident.getToken());
} else {
assert !AWAIT.getName().equals(ident.getName());
}
}
}
private static boolean isEscapedIdent(final IdentNode ident) {
return ident.getName().length() != Token.descLength(ident.getToken());
}
private static boolean isReservedWordSequence(final String name) {
TokenType tokenType = TokenLookup.lookupKeyword(name.toCharArray(), 0, name.length());
return (tokenType != IDENT && !tokenType.isContextualKeyword() && !tokenType.isFutureStrict());
}
private void verifyStrictIdent(final IdentNode ident, final String contextString, final boolean bindingIdentifier) {
if (isStrictMode) {
if (!isValidStrictIdent(ident, bindingIdentifier)) {
throw error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken());
}
}
}
private void verifyStrictIdent(final IdentNode ident, final String contextString) {
verifyStrictIdent(ident, contextString, true);
}
private static boolean isValidStrictIdent(final IdentNode ident, final boolean bindingIdentifier) {
if (bindingIdentifier) {
switch (ident.getName()) {
case EVAL_NAME:
case ARGUMENTS_NAME:
return false;
default:
break;
}
}
if (isFutureStrictName(ident)) {
return false;
}
return true;
}
private static boolean isFutureStrictName(final IdentNode ident) {
if (ident.tokenType().isFutureStrict()) {
return true;
} else if (isEscapedIdent(ident)) {
TokenType tokenType = TokenLookup.lookupKeyword(ident.getName().toCharArray(), 0, ident.getName().length());
return (tokenType != IDENT && tokenType.isFutureStrict());
}
return false;
}
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(Expression binding) {
if (missingAssignment == null) {
missingAssignment = binding;
}
}
void recordDeclarationWithInitializer(long token) {
if (declarationWithInitializerToken == 0L) {
declarationWithInitializerToken = token;
}
}
void addBinding(Expression binding) {
if (firstBinding == null) {
firstBinding = binding;
} else if (secondBinding == null) {
secondBinding = binding;
}
}
void addAssignment(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) {
int varStart = Token.descPosition(token);
assert varType == VAR || varType == LET || varType == CONST;
next();
int varFlags = 0;
if (varType == LET) {
varFlags |= VarNode.IS_LET;
} else if (varType == CONST) {
varFlags |= VarNode.IS_CONST;
}
ForVariableDeclarationListResult forResult = isStatement ? null : new ForVariableDeclarationListResult();
Scope scope = lc.getCurrentScope();
while (true) {
final int varLine = line;
final long varToken = Token.recast(token, varType);
final Expression binding = bindingIdentifierOrPattern(VARIABLE_NAME_CONTEXT);
final boolean isDestructuring = !(binding instanceof IdentNode);
if (isDestructuring) {
final int finalVarFlags = varFlags | VarNode.IS_DESTRUCTURING;
verifyDestructuringBindingPattern(binding, new Consumer<IdentNode>() {
@Override
public void accept(IdentNode identNode) {
verifyStrictIdent(identNode, VARIABLE_NAME_CONTEXT);
if (varType != VAR && identNode.getName().equals(LET.getName())) {
throw error(AbstractParser.message("let.lexical.binding"));
}
final VarNode var = new VarNode(varLine, varToken, sourceOrder, identNode.getFinish(), identNode.setIsDeclaredHere(), null, finalVarFlags);
appendStatement(var);
declareVar(scope, var);
}
});
}
Expression init = null;
if (type == ASSIGN) {
if (!isStatement) {
forResult.recordDeclarationWithInitializer(varToken);
}
next();
if (!isDestructuring) {
pushDefaultName(binding);
}
try {
init = assignmentExpression(isStatement);
} finally {
if (!isDestructuring) {
popDefaultName();
}
}
} 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 (varType != VAR && ident.getName().equals(LET.getName())) {
throw error(AbstractParser.message("let.lexical.binding"));
}
if (!isStatement) {
if (init == null && varType == CONST) {
forResult.recordMissingAssignment(binding);
}
forResult.addBinding(binding);
}
if (isAnonymousFunctionDefinition(init)) {
init = setAnonymousFunctionName(init, ident.getName());
}
final VarNode var = new VarNode(varLine, varToken, sourceOrder, varStart, finish, ident.setIsDeclaredHere(), init, varFlags);
appendStatement(var);
declareVar(scope, var);
} else {
assert init != null || !isStatement;
if (init != null) {
final Expression assignment = verifyAssignment(Token.recast(varToken, ASSIGN_INIT), binding, init, true);
if (isStatement) {
appendStatement(new ExpressionStatement(varLine, assignment.getToken(), finish, assignment));
} 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 void declareVar(Scope scope, VarNode varNode) {
String name = varNode.getName().getName();
if (detectVarNameConflict(scope, varNode)) {
throw error(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), varNode.getToken());
}
if (varNode.isBlockScoped()) {
int symbolFlags = varNode.getSymbolFlags() |
(scope.isSwitchBlockScope() ? Symbol.IS_DECLARED_IN_SWITCH_BLOCK : 0) |
(varNode.isFunctionDeclaration() ? Symbol.IS_BLOCK_FUNCTION_DECLARATION : 0);
scope.putSymbol(new Symbol(name, symbolFlags));
if (varNode.isFunctionDeclaration() && isAnnexB()) {
ParserContextFunctionNode function = lc.getCurrentFunction();
Scope varScope = function.getBodyScope();
if (!function.isStrict() && scope != varScope && (varScope.isGlobalScope() || !name.equals(ARGUMENTS_NAME))) {
assert !scope.isFunctionBodyScope() && !scope.isFunctionParameterScope();
if (varScope.getExistingSymbol(name) == null && !scope.getParent().isLexicallyDeclaredName(name, true, true)) {
varScope.recordHoistableBlockFunctionDeclaration(varNode, scope);
}
}
}
} else {
ParserContextFunctionNode function = lc.getCurrentFunction();
Scope varScope = function.getBodyScope();
int symbolFlags = varNode.getSymbolFlags() |
(varNode.isHoistableDeclaration() ? Symbol.IS_HOISTABLE_DECLARATION : 0) |
(varScope.isGlobalScope() ? Symbol.IS_GLOBAL : 0);
if (function.hasParameterExpressions() && function.getParameterBlock().getScope().hasSymbol(name)) {
symbolFlags |= Symbol.IS_VAR_REDECLARED_HERE;
}
varScope.putSymbol(new Symbol(name, symbolFlags));
if (scope != varScope) {
assert scope.isBlockScope();
varScope.recordHoistedVarDeclaration(varNode, scope);
}
}
}
private boolean detectVarNameConflict(Scope scope, VarNode varNode) {
String varName = varNode.getName().getName();
if (varNode.isBlockScoped()) {
Scope currentScope = scope;
Symbol existingSymbol = currentScope.getExistingSymbol(varName);
if (existingSymbol != null) {
if (existingSymbol.isBlockFunctionDeclaration() && !isStrictMode && isAnnexB() && varNode.isFunctionDeclaration()) {
return false;
} else {
return true;
}
} else {
Scope parentScope = scope.getParent();
if (parentScope != null && (parentScope.isCatchParameterScope() || parentScope.isFunctionParameterScope())) {
if (parentScope.getExistingSymbol(varName) != null) {
return true;
}
}
return false;
}
} else {
return scope.isLexicallyDeclaredName(varName, isAnnexB(), false);
}
}
private boolean isAnnexB() {
return env.annexB;
}
private boolean isIdentifier() {
return type == IDENT || type.isContextualKeyword() || isNonStrictModeIdent();
}
private IdentNode identifier(boolean yield, boolean await, String contextString, boolean bindingIdentifier) {
final IdentNode ident = getIdent();
verifyIdent(ident, yield, await);
verifyStrictIdent(ident, contextString, bindingIdentifier);
return ident;
}
private IdentNode identifierReference(boolean yield, boolean await) {
return identifier(yield, await, "IdentifierReference", false);
}
private IdentNode labelIdentifier() {
return identifier(inGeneratorFunction(), inAsyncFunction(), "LabelIdentifier", false);
}
private boolean isBindingIdentifier() {
return type == IDENT || type.isContextualKeyword() || isNonStrictModeIdent();
}
private IdentNode bindingIdentifier(boolean yield, boolean await, String contextString) {
return identifier(yield, await, contextString, true);
}
private Expression bindingPattern(boolean yield, boolean await) {
if (type == LBRACKET) {
return arrayLiteral(yield, await);
} else if (type == LBRACE) {
return objectLiteral(yield, await);
} else {
throw error(AbstractParser.message("expected.binding"));
}
}
private Expression bindingIdentifierOrPattern(boolean yield, boolean await, String contextString) {
if (isBindingIdentifier() || !(ES6_DESTRUCTURING && isES6())) {
return bindingIdentifier(yield, await, contextString);
} else {
return bindingPattern(yield, await);
}
}
private Expression bindingIdentifierOrPattern(String contextString) {
return bindingIdentifierOrPattern(inGeneratorFunction(), inAsyncFunction(), contextString);
}
private abstract class VerifyDestructuringPatternNodeVisitor extends NodeVisitor<LexicalContext> {
VerifyDestructuringPatternNodeVisitor(LexicalContext lc) {
super(lc);
}
@Override
public boolean enterLiteralNode(LiteralNode<?> literalNode) {
if (literalNode.isArray()) {
if (literalNode.isParenthesized()) {
throw error(AbstractParser.message(MESSAGE_INVALID_LVALUE), literalNode.getToken());
}
if (((ArrayLiteralNode) literalNode).hasSpread() && ((ArrayLiteralNode) literalNode).hasTrailingComma()) {
throw error("Rest element must be last", literalNode.getElementExpressions().get(literalNode.getElementExpressions().size() - 1).getToken());
}
boolean restElement = false;
for (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;
Expression lvalue = ((UnaryNode) element).getExpression();
verifySpreadElement(lvalue);
} else {
element.accept(this);
}
}
}
return false;
} else {
return enterDefault(literalNode);
}
}
protected abstract void verifySpreadElement(Expression lvalue);
@Override
public boolean enterObjectNode(ObjectNode objectNode) {
if (objectNode.isParenthesized()) {
throw error(AbstractParser.message(MESSAGE_INVALID_LVALUE), objectNode.getToken());
}
boolean restElement = false;
for (PropertyNode property : objectNode.getElements()) {
if (property != null) {
if (restElement) {
throw error("Unexpected element after rest element", property.getToken());
}
Expression key = property.getKey();
if (key.isTokenType(SPREAD_OBJECT)) {
restElement = true;
Expression lvalue = ((UnaryNode) key).getExpression();
verifySpreadElement(lvalue);
} else {
property.accept(this);
}
}
}
return false;
}
@Override
public boolean enterPropertyNode(PropertyNode propertyNode) {
if (propertyNode.getValue() != null) {
propertyNode.getValue().accept(this);
return false;
} else {
return enterDefault(propertyNode);
}
}
@Override
public boolean enterBinaryNode(BinaryNode binaryNode) {
if (binaryNode.isTokenType(ASSIGN)) {
binaryNode.getLhs().accept(this);
return false;
} else {
return enterDefault(binaryNode);
}
}
}
private void verifyDestructuringBindingPattern(Expression pattern, Consumer<IdentNode> identifierCallback) {
assert pattern instanceof ObjectNode || pattern instanceof ArrayLiteralNode;
pattern.accept(new VerifyDestructuringPatternNodeVisitor(new LexicalContext()) {
@Override
protected void verifySpreadElement(Expression lvalue) {
if (lvalue instanceof IdentNode) {
enterIdentNode((IdentNode) lvalue);
} else if (isDestructuringLhs(lvalue)) {
verifyDestructuringBindingPattern(lvalue, identifierCallback);
} else {
throw error("Expected a valid binding identifier", lvalue.getToken());
}
}
@Override
public boolean enterIdentNode(IdentNode identNode) {
if (identNode.isParenthesized()) {
throw error("Expected a valid binding identifier", identNode.getToken());
}
identifierCallback.accept(identNode);
return false;
}
@Override
protected boolean enterDefault(Node node) {
throw error(String.format("unexpected node in BindingPattern: %s", node));
}
});
}
private void emptyStatement() {
if (env.emptyStatements) {
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) {
endOfLine();
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(false, true, false);
Block fail = null;
if (type == ELSE) {
next();
fail = getStatement(false, true, false);
}
appendStatement(new IfNode(ifLine, ifToken, fail != null ? fail.getFinish() : pass.getFinish(), test, pass, fail));
}
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;
boolean isForAwaitOf = false;
boolean initStartsWithLet = false;
try {
next();
if (env.syntaxExtensions && type == IDENT && lexer.checkIdentForKeyword(token, "each")) {
flags |= ForNode.IS_FOR_EACH;
next();
} else if (ES8_FOR_AWAIT_OF && type == AWAIT) {
if (!inAsyncFunction()) {
throw error(AbstractParser.message("invalid.for.await.of"), token);
}
isForAwaitOf = true;
next();
}
expect(LPAREN);
TokenType varType = null;
switch (type) {
case VAR:
varType = type;
varDeclList = variableDeclarationList(varType, false, forStart);
break;
case SEMICOLON:
break;
default:
if (useBlockScope() && (type == LET && lookaheadIsLetDeclaration() || type == CONST)) {
varType = type;
varDeclList = variableDeclarationList(varType, false, forStart);
if (varType == LET) {
if (!forNode.getStatements().isEmpty()) {
flags |= ForNode.PER_ITERATION_SCOPE;
}
}
break;
}
if (env.constAsVar && type == CONST) {
varType = TokenType.VAR;
varDeclList = variableDeclarationList(varType, false, forStart);
break;
}
initStartsWithLet = (type == LET);
init = expression(false, inGeneratorFunction(), inAsyncFunction(), 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());
}
}
} else {
if (hasCoverInitializedName(init)) {
throw error(AbstractParser.message(MESSAGE_INVALID_PROPERTY_INITIALIZER));
}
}
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 OF:
if (ES8_FOR_AWAIT_OF && isForAwaitOf && !initStartsWithLet) {
} else if (ES6_FOR_OF && !initStartsWithLet) {
isForOf = true;
} else {
expect(SEMICOLON);
break;
}
case IN:
if (isForAwaitOf) {
expectDontAdvance(OF);
flags |= ForNode.IS_FOR_AWAIT_OF;
} else {
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 || isForAwaitOf ? "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 || isForAwaitOf ? "of" : "in"), varDeclList.declarationWithInitializerToken);
}
init = varDeclList.firstBinding;
assert init instanceof IdentNode || isDestructuringLhs(init);
if (varType == CONST || varType == LET) {
flags |= ForNode.PER_ITERATION_SCOPE;
}
} else {
assert init != null : "for..in/of init expression can not be null here";
if (!checkValidLValue(init, isForOf || isForAwaitOf ? "for-of iterator" : "for-in iterator")) {
throw error(AbstractParser.message("not.lvalue.for.in.loop", isForOf || isForAwaitOf ? "of" : "in"), init.getToken());
}
}
next();
modify = isForOf || isForAwaitOf ? new JoinPredecessorExpression(assignmentExpression(true)) : joinPredecessorExpression();
break;
default:
expect(SEMICOLON);
break;
}
expect(RPAREN);
body = getStatement();
} finally {
lc.pop(forNode);
boolean skipVars = (flags & ForNode.PER_ITERATION_SCOPE) != 0 && (isForOf || isForAwaitOf || (flags & ForNode.IS_FOR_IN) != 0);
if (!skipVars) {
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) {
appendStatement(new BlockStatement(forLine, new Block(outer.getToken(), body.getFinish(), 0, outer.getScope(), outer.getStatements())));
}
}
}
}
private boolean checkValidLValue(Expression init, String contextString) {
if (init instanceof IdentNode) {
IdentNode ident = (IdentNode) init;
if (!checkIdentLValue(ident)) {
return false;
}
if (ident.isMetaProperty()) {
return false;
}
verifyStrictIdent(ident, contextString);
return true;
} else if (init instanceof AccessNode || init instanceof IndexNode) {
if (((BaseNode) init).isOptional()) {
return false;
}
return true;
} else if (isDestructuringLhs(init)) {
verifyDestructuringAssignmentPattern(init, contextString);
return true;
} else {
return false;
}
}
private boolean lookaheadIsLetDeclaration() {
return lookaheadOfLetDeclaration() != null;
}
private TokenType lookaheadOfLetDeclaration() {
assert type == LET;
for (int i = 1;; i++) {
TokenType t = T(k + i);
switch (t) {
case EOL:
case COMMENT:
continue;
case OF:
case IDENT:
case LBRACKET:
case LBRACE:
return t;
default:
if (t.isContextualKeyword() || (!isStrictMode && t.isFutureStrict())) {
return t;
}
return null;
}
}
}
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();
boolean seenEOL = (type == EOL);
if (seenEOL) {
next();
}
ParserContextLabelNode labelNode = null;
switch (type) {
case RBRACE:
case SEMICOLON:
case EOF:
break;
default:
if (seenEOL) {
break;
}
final IdentNode ident = labelIdentifier();
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();
boolean seenEOL = (type == EOL);
if (seenEOL) {
next();
}
ParserContextLabelNode labelNode = null;
switch (type) {
case RBRACE:
case SEMICOLON:
case EOF:
break;
default:
if (seenEOL) {
break;
}
final IdentNode ident = labelIdentifier();
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 == null) {
throw error(AbstractParser.message("illegal.break.stmt"), breakToken);
}
endOfLine();
appendStatement(new BreakNode(breakLine, breakToken, finish, labelName));
}
private void returnStatement() {
if (lc.getCurrentFunction().isScriptOrModule()) {
throw error(AbstractParser.message("invalid.return"));
}
final int returnLine = line;
final long returnToken = token;
nextOrEOL();
boolean seenEOL = (type == EOL);
if (seenEOL) {
next();
}
Expression expression = null;
switch (type) {
case RBRACE:
case SEMICOLON:
case EOF:
break;
default:
if (seenEOL) {
break;
}
expression = expression();
break;
}
endOfLine();
appendStatement(new ReturnNode(returnLine, returnToken, finish, expression));
}
private Expression yieldExpression(boolean in, boolean await) {
assert inGeneratorFunction() && isES6();
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(in, true, await);
break;
}
return new UnaryNode(yieldToken, expression);
}
private Expression awaitExpression(boolean yield) {
assert inAsyncFunction();
long awaitToken = token;
nextOrEOL();
Expression expression = unaryExpression(yield, true);
if (lc.getCurrentFunction().isModule()) {
lc.getCurrentFunction().setFlag(FunctionNode.IS_ASYNC);
}
return new UnaryNode(Token.recast(awaitToken, AWAIT), expression);
}
private static UnaryNode newUndefinedLiteral(long token, 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 outerBlock = useBlockScope() ? newBlock() : null;
final ParserContextBlockNode switchBlock = newBlock(Scope.createSwitchBlock(lc.getCurrentScope()));
next();
final ParserContextSwitchNode switchNode = new ParserContextSwitchNode();
lc.push(switchNode);
int defaultCaseIndex = -1;
final ArrayList<CaseNode> cases = new ArrayList<>();
SwitchNode switchStatement = null;
try {
expect(LPAREN);
int expressionLine = line;
Expression expression = expression();
expect(RPAREN);
expect(LBRACE);
if (useBlockScope()) {
IdentNode switchExprName = new IdentNode(Token.recast(expression.getToken(), IDENT), expression.getFinish(), SWITCH_BINDING_NAME);
VarNode varNode = new VarNode(expressionLine, Token.recast(expression.getToken(), LET), expression.getFinish(), switchExprName, expression, VarNode.IS_LET);
outerBlock.appendStatement(varNode);
declareVar(outerBlock.getScope(), varNode);
expression = switchExprName;
}
while (type != RBRACE) {
Expression caseExpression = null;
final long caseToken = token;
switch (type) {
case CASE:
next();
caseExpression = expression();
break;
case DEFAULT:
if (defaultCaseIndex != -1) {
throw error(AbstractParser.message("duplicate.default.in.switch"));
}
next();
break;
default:
expect(CASE);
break;
}
expect(COLON);
List<Statement> statements = caseStatementList();
final CaseNode caseNode = new CaseNode(caseToken, finish, caseExpression, statements);
if (caseExpression == null) {
assert defaultCaseIndex == -1;
defaultCaseIndex = cases.size();
}
cases.add(caseNode);
}
next();
switchStatement = new SwitchNode(switchLine, switchToken, finish, expression, optimizeList(cases), defaultCaseIndex);
} finally {
lc.pop(switchNode);
restoreBlock(switchBlock);
if (switchStatement != null) {
appendStatement(new BlockStatement(switchLine,
new Block(switchToken, switchStatement.getFinish(), switchBlock.getFlags() | Block.IS_SYNTHETIC | Block.IS_SWITCH_BLOCK, switchBlock.getScope(), switchStatement)));
}
if (outerBlock != null) {
restoreBlock(outerBlock);
if (switchStatement != null) {
appendStatement(new BlockStatement(switchLine,
new Block(switchToken, switchStatement.getFinish(), outerBlock.getFlags() | Block.IS_SYNTHETIC, outerBlock.getScope(), outerBlock.getStatements())));
}
}
}
}
private void labelStatement(final boolean mayBeFunctionDeclaration) {
final long labelToken = token;
final IdentNode ident = labelIdentifier();
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, mayBeFunctionDeclaration);
} finally {
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(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 ArrayList<Block> catchBlocks = new ArrayList<>();
while (type == CATCH) {
final int catchLine = line;
final long catchToken = token;
next();
if (type == LBRACE && ES2019_OPTIONAL_CATCH_BINDING) {
catchBlocks.add(catchBlock(catchToken, catchLine, null, null, null));
break;
}
expect(LPAREN);
final Expression catchParameter = bindingIdentifierOrPattern(CATCH_PARAMETER_CONTEXT);
final IdentNode exception;
final Expression pattern;
if (catchParameter instanceof IdentNode) {
exception = ((IdentNode) catchParameter).setIsCatchParameter();
pattern = null;
} else {
exception = new IdentNode(Token.recast(catchParameter.getToken(), IDENT), catchParameter.getFinish(), ERROR_BINDING_NAME).setIsCatchParameter();
pattern = catchParameter;
}
final Expression ifExpression;
if (env.syntaxExtensions && type == IF) {
next();
ifExpression = expression();
} else {
ifExpression = null;
}
expect(RPAREN);
catchBlocks.add(catchBlock(catchToken, catchLine, exception, pattern, ifExpression));
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, optimizeList(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.getScope(), outer.getStatements())));
}
private Block catchBlock(final long catchToken, final int catchLine, final IdentNode exception, final Expression pattern, final Expression ifExpression) {
final ParserContextBlockNode catchBlock = newBlock(Scope.createCatch(lc.getCurrentScope()));
try {
if (exception != null) {
VarNode exceptionVar = new VarNode(catchLine, Token.recast(exception.getToken(), LET), exception.getFinish(), exception.setIsDeclaredHere(), null, VarNode.IS_LET);
appendStatement(exceptionVar);
declareVar(catchBlock.getScope(), exceptionVar);
if (pattern != null) {
verifyDestructuringBindingPattern(pattern, new Consumer<IdentNode>() {
@Override
public void accept(IdentNode identNode) {
verifyStrictIdent(identNode, CATCH_PARAMETER_CONTEXT);
final int varFlags = VarNode.IS_LET | VarNode.IS_DESTRUCTURING;
final VarNode var = new VarNode(catchLine, Token.recast(identNode.getToken(), LET), identNode.getFinish(), identNode.setIsDeclaredHere(), null, varFlags);
appendStatement(var);
declareVar(catchBlock.getScope(), var);
}
});
}
}
final Block catchBody = getBlock(true);
final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, pattern, ifExpression, catchBody, false);
appendStatement(catchNode);
} finally {
restoreBlock(catchBlock);
}
int catchFinish = Math.max(finish, Token.descPosition(catchBlock.getToken()));
return new Block(catchBlock.getToken(), catchFinish, catchBlock.getFlags() | Block.IS_SYNTHETIC, catchBlock.getScope(), catchBlock.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(boolean yield, boolean await) {
final int primaryLine = line;
final long primaryToken = token;
switch (type) {
case THIS:
final String name = type.getName();
next();
markThis();
return new IdentNode(primaryToken, finish, name).setIsThis();
case IDENT:
final IdentNode ident = identifierReference(yield, await);
if (ident == null) {
break;
}
return detectSpecialProperty(ident);
case NON_OCTAL_DECIMAL:
if (isStrictMode) {
throw error(AbstractParser.message("strict.no.nonoctaldecimal"), token);
}
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 BIGINT:
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(yield, await);
case LBRACE:
return objectLiteral(yield, await);
case LPAREN:
return parenthesizedExpressionAndArrowParameterList(yield, await);
case TEMPLATE:
case TEMPLATE_HEAD:
return templateLiteral(yield, await);
default:
if (lexer.scanLiteral(primaryToken, type, lineInfoReceiver)) {
next();
return getLiteral();
}
if (type.isContextualKeyword() || isNonStrictModeIdent()) {
return identifierReference(yield, await);
}
break;
}
throw error(AbstractParser.message(MESSAGE_EXPECTED_OPERAND, type.getNameOrType()));
}
private Expression execString(final int primaryLine, final long primaryToken) {
final IdentNode execIdent = new IdentNode(primaryToken, finish, EXEC_NAME);
next();
expect(LBRACE);
final List<Expression> arguments = Collections.singletonList(expression());
expect(RBRACE);
long tokenWithDelimiter = Token.withDelimiter(primaryToken);
return CallNode.forCall(primaryLine, tokenWithDelimiter, Token.descPosition(tokenWithDelimiter), finish, execIdent, arguments);
}
private LiteralNode<Expression[]> arrayLiteral(boolean yield, boolean await) {
final long arrayToken = token;
next();
final ArrayList<Expression> elements = new ArrayList<>();
boolean elision = true;
boolean hasSpread = false;
boolean hasCoverInitializedName = 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 (ES6_SPREAD_ARRAY) {
hasSpread = true;
spreadToken = token;
next();
}
default:
if (!elision) {
throw error(AbstractParser.message("expected.comma", type.getNameOrType()));
}
Expression expression = assignmentExpression(true, yield, await, true);
if (expression != null) {
if (spreadToken != 0) {
expression = new UnaryNode(Token.recast(spreadToken, SPREAD_ARRAY), expression);
}
elements.add(expression);
hasCoverInitializedName = hasCoverInitializedName || hasCoverInitializedName(expression);
} else {
expect(RBRACKET);
}
elision = false;
break;
}
}
return LiteralNode.newInstance(arrayToken, finish, optimizeList(elements), hasSpread, elision, hasCoverInitializedName);
}
private ObjectNode objectLiteral(boolean yield, boolean await) {
final long objectToken = token;
next();
final ArrayList<PropertyNode> elements = new ArrayList<>();
final Map<String, PropertyNode> map = new HashMap<>();
boolean commaSeen = true;
boolean hasCoverInitializedName = false;
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 = propertyDefinition(yield, await);
elements.add(property);
hasCoverInitializedName = hasCoverInitializedName || property.isCoverInitializedName() || hasCoverInitializedName(property.getValue());
if (property.isComputed() || property.getKey().isTokenType(SPREAD_OBJECT)) {
break;
}
final String key = property.getKeyName();
final PropertyNode existingProperty = map.get(key);
if (existingProperty == null) {
map.put(key, property);
break;
}
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.isProto() && existingProperty.isProto()) {
throw error(AbstractParser.message("multiple.proto.key"), property.getToken());
}
}
if (!isES6() && value == null && prevValue == null) {
if (getter != null) {
assert prevGetter != null || prevSetter != null;
map.put(key, existingProperty.setGetter(getter));
} else if (setter != null) {
assert prevGetter != null || prevSetter != null;
map.put(key, existingProperty.setSetter(setter));
}
}
break;
}
}
return new ObjectNode(objectToken, finish, optimizeList(elements), hasCoverInitializedName);
}
private static boolean hasCoverInitializedName(Expression value) {
return (value != null && ((value instanceof ObjectNode && ((ObjectNode) value).hasCoverInitializedName()) ||
(value instanceof ArrayLiteralNode && ((ArrayLiteralNode) value).hasCoverInitializedName())));
}
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(MESSAGE_PROPERTY_REDEFINITON, 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(MESSAGE_PROPERTY_REDEFINITON, property.getKeyName()), property.getToken());
}
if (isPrevAccessor && value != null) {
throw error(AbstractParser.message(MESSAGE_PROPERTY_REDEFINITON, property.getKeyName()), property.getToken());
}
if (isAccessor && isPrevAccessor) {
if (getter != null && prevGetter != null || setter != null && prevSetter != null) {
throw error(AbstractParser.message(MESSAGE_PROPERTY_REDEFINITON, property.getKeyName()), property.getToken());
}
}
}
@SuppressWarnings("fallthrough")
private PropertyKey literalPropertyName() {
switch (type) {
case IDENT:
return getIdent().setIsPropertyName();
case NON_OCTAL_DECIMAL:
if (isStrictMode) {
throw error(AbstractParser.message("strict.no.nonoctaldecimal"), token);
}
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 BIGINT:
case FLOATING:
return (PropertyKey) getLiteral();
default:
return getIdentifierName().setIsPropertyName();
}
}
private Expression computedPropertyName(boolean yield, boolean await) {
expect(LBRACKET);
Expression expression = assignmentExpression(true, yield, await);
expect(RBRACKET);
return expression;
}
private Expression propertyName(boolean yield, boolean await) {
if (ES6_COMPUTED_PROPERTY_NAME && type == LBRACKET && isES6()) {
return computedPropertyName(yield, await);
} else {
return (Expression) literalPropertyName();
}
}
private PropertyNode propertyDefinition(boolean yield, boolean await) {
final long propertyToken = token;
final int functionLine = line;
final Expression propertyName;
final boolean isIdentifier;
boolean async = false;
if (isAsync() && lookaheadIsAsyncMethod(false)) {
async = true;
next();
}
boolean generator = false;
if (type == MUL && ES6_GENERATOR_FUNCTION && isES6()) {
generator = true;
next();
}
final boolean computed = type == LBRACKET;
if (type == IDENT || (isIdentifier() && !(type == GET || type == SET))) {
isIdentifier = true;
propertyName = getIdent().setIsPropertyName();
} else if (type == GET || type == SET) {
final TokenType getOrSet = type;
next();
if (type != COLON && type != COMMARIGHT && type != RBRACE && ((type != ASSIGN && type != LPAREN) || !isES6())) {
final long getOrSetToken = propertyToken;
if (getOrSet == GET) {
final PropertyFunction getter = propertyGetterFunction(getOrSetToken, functionLine, yield, await, false);
return new PropertyNode(propertyToken, finish, getter.key, null, getter.functionNode, null, false, getter.computed, false, false);
} else if (getOrSet == SET) {
final PropertyFunction setter = propertySetterFunction(getOrSetToken, functionLine, yield, await, false);
return new PropertyNode(propertyToken, finish, setter.key, null, null, setter.functionNode, false, setter.computed, false, false);
}
}
isIdentifier = true;
propertyName = new IdentNode(propertyToken, finish, getOrSet.getName()).setIsPropertyName();
} else if (type == ELLIPSIS && ES8_REST_SPREAD_PROPERTY && isES2017() && !(generator || async)) {
long spreadToken = Token.recast(propertyToken, TokenType.SPREAD_OBJECT);
next();
Expression assignmentExpression = assignmentExpression(true, yield, await);
Expression spread = new UnaryNode(spreadToken, assignmentExpression);
return new PropertyNode(propertyToken, finish, spread, null, null, null, false, false, false, false);
} else {
isIdentifier = false;
propertyName = propertyName(yield, await);
}
Expression propertyValue;
if (generator || async) {
expectDontAdvance(LPAREN);
}
boolean coverInitializedName = false;
boolean proto = false;
boolean isAnonymousFunctionDefinition = false;
if (type == LPAREN && isES6()) {
propertyValue = propertyMethodFunction(propertyName, propertyToken, functionLine, generator, FunctionNode.IS_METHOD, computed, async).functionNode;
} else if (isIdentifier && (type == COMMARIGHT || type == RBRACE || type == ASSIGN) && isES6()) {
IdentNode ident = (IdentNode) propertyName;
verifyIdent(ident, yield, await);
propertyValue = createIdentNode(propertyToken, finish, ident.getPropertyName());
if (type == ASSIGN && ES6_DESTRUCTURING) {
long assignToken = token;
coverInitializedName = true;
next();
Expression rhs = assignmentExpression(true, yield, await);
propertyValue = verifyAssignment(assignToken, propertyValue, rhs, true);
}
} else {
expect(COLON);
if (!computed && PROTO_NAME.equals(((PropertyKey) propertyName).getPropertyName())) {
proto = true;
}
pushDefaultName(propertyName);
try {
propertyValue = assignmentExpression(true, yield, await, true);
} finally {
popDefaultName();
}
if (!proto) {
if (isAnonymousFunctionDefinition(propertyValue)) {
if (!computed && propertyName instanceof PropertyKey) {
propertyValue = setAnonymousFunctionName(propertyValue, ((PropertyKey) propertyName).getPropertyName());
} else {
isAnonymousFunctionDefinition = true;
}
}
}
}
return new PropertyNode(propertyToken, finish, propertyName, propertyValue, null, null, false, computed, coverInitializedName, proto, false, isAnonymousFunctionDefinition);
}
private PropertyFunction propertyGetterFunction(long getSetToken, int functionLine, boolean yield, boolean await, boolean allowPrivate) {
final boolean computed = type == LBRACKET;
final Expression propertyName = classElementName(yield, await, allowPrivate);
final IdentNode getterName = computed ? null : createMethodNameIdent(propertyName, "get ");
expect(LPAREN);
expect(RPAREN);
int functionFlags = FunctionNode.IS_GETTER | FunctionNode.IS_METHOD |
(computed ? FunctionNode.IS_ANONYMOUS : 0);
final ParserContextFunctionNode functionNode = createParserContextFunctionNode(getterName, getSetToken, functionFlags, functionLine, Collections.<IdentNode> emptyList(), 0);
lc.push(functionNode);
Block functionBody;
try {
functionBody = functionBody(functionNode);
} finally {
lc.pop(functionNode);
}
final FunctionNode function = createFunctionNode(
functionNode,
getSetToken,
getterName,
functionLine,
functionBody);
return new PropertyFunction(propertyName, function, computed);
}
private PropertyFunction propertySetterFunction(long getSetToken, int functionLine, boolean yield, boolean await, boolean allowPrivate) {
final boolean computed = type == LBRACKET;
final Expression propertyName = classElementName(yield, await, allowPrivate);
final IdentNode setterName = computed ? null : createMethodNameIdent(propertyName, "set ");
expect(LPAREN);
int functionFlags = FunctionNode.IS_SETTER | FunctionNode.IS_METHOD |
(computed ? FunctionNode.IS_ANONYMOUS : 0);
final ParserContextFunctionNode functionNode = createParserContextFunctionNode(setterName, getSetToken, functionFlags, functionLine);
lc.push(functionNode);
Block functionBody;
try {
final ParserContextBlockNode parameterBlock = functionNode.createParameterBlock();
lc.push(parameterBlock);
try {
if (!env.syntaxExtensions || type != RPAREN) {
formalParameter(yield, await);
}
expect(RPAREN);
functionBody = functionBody(functionNode);
} finally {
restoreBlock(parameterBlock);
}
if (parameterBlock != null) {
functionBody = wrapParameterBlock(parameterBlock, functionBody);
}
} finally {
lc.pop(functionNode);
}
final FunctionNode function = createFunctionNode(
functionNode,
getSetToken,
setterName,
functionLine,
functionBody);
return new PropertyFunction(propertyName, function, computed);
}
private PropertyFunction propertyMethodFunction(Expression key, final long methodToken, final int methodLine, final boolean generator, final int flags, boolean computed, boolean async) {
final IdentNode methodNameNode = computed ? null : createMethodNameIdent(key, "");
expect(LPAREN);
int functionFlags = flags |
(computed ? FunctionNode.IS_ANONYMOUS : 0) |
(generator ? FunctionNode.IS_GENERATOR : 0) |
(async ? FunctionNode.IS_ASYNC : 0);
final ParserContextFunctionNode functionNode = createParserContextFunctionNode(methodNameNode, methodToken, functionFlags, methodLine);
lc.push(functionNode);
try {
ParserContextBlockNode parameterBlock = functionNode.createParameterBlock();
lc.push(parameterBlock);
Block functionBody;
try {
formalParameterList(generator, async);
expect(RPAREN);
functionBody = functionBody(functionNode);
} finally {
restoreBlock(parameterBlock);
}
verifyParameterList(functionNode);
if (parameterBlock != null) {
functionBody = wrapParameterBlock(parameterBlock, functionBody);
}
final FunctionNode function = createFunctionNode(
functionNode,
methodToken,
methodNameNode,
methodLine,
functionBody);
return new PropertyFunction(key, function, computed);
} finally {
lc.pop(functionNode);
}
}
private IdentNode createMethodNameIdent(Expression propertyKey, String prefix) {
String methodName;
boolean intern = false;
if (propertyKey instanceof IdentNode) {
methodName = ((IdentNode) propertyKey).getPropertyName();
} else if (propertyKey instanceof PropertyKey) {
methodName = ((PropertyKey) propertyKey).getPropertyName();
intern = true;
} else {
return null;
}
if (!prefix.isEmpty()) {
methodName = prefix.concat(methodName);
intern = true;
}
if (intern) {
methodName = lexer.stringIntern(methodName);
}
return createIdentNode(propertyKey.getToken(), propertyKey.getFinish(), methodName);
}
private static boolean isAnonymousFunctionDefinition(Expression expression) {
if (expression instanceof FunctionNode && ((FunctionNode) expression).isAnonymous()) {
return true;
} else if (expression instanceof ClassNode && ((ClassNode) expression).isAnonymous()) {
return true;
} else {
return false;
}
}
private Expression setAnonymousFunctionName(Expression expression, String functionName) {
if (!isES6()) {
return expression;
}
if (expression instanceof FunctionNode && ((FunctionNode) expression).isAnonymous()) {
return ((FunctionNode) expression).setName(null, functionName);
} else if (expression instanceof ClassNode && ((ClassNode) expression).isAnonymous()) {
ClassNode classNode = (ClassNode) expression;
FunctionNode constructorFunction = (FunctionNode) classNode.getConstructor().getValue();
return classNode.setConstructor(classNode.getConstructor().setValue(constructorFunction.setName(null, functionName)));
}
return expression;
}
private static final 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(boolean yield, boolean await) {
int callLine = line;
long callToken = token;
Expression lhs = memberExpression(yield, await);
if (type == LPAREN) {
boolean async = ES8_ASYNC_FUNCTION && isES2017() && lhs.isTokenType(ASYNC);
final List<Expression> arguments = argumentList(yield, await, async);
if (async) {
if (type == ARROW && checkNoLineTerminator()) {
return new ExpressionList(callToken, callLine, optimizeList(arguments));
} else {
for (Expression argument : arguments) {
if (hasCoverInitializedName(argument)) {
throw error(AbstractParser.message(MESSAGE_INVALID_PROPERTY_INITIALIZER));
}
}
}
}
boolean eval = false;
boolean applyArguments = false;
if (lhs instanceof IdentNode) {
final IdentNode ident = (IdentNode) lhs;
final String name = ident.getName();
if (EVAL_NAME.equals(name)) {
markEval();
eval = true;
} else if (SUPER.getName().equals(name)) {
assert ident.isDirectSuper();
markSuperCall();
}
} else if (lhs instanceof AccessNode && !((AccessNode) lhs).isPrivate() && arguments.size() == 2 && arguments.get(1) instanceof IdentNode &&
((IdentNode) arguments.get(1)).isArguments() && APPLY_NAME.equals(((AccessNode) lhs).getProperty())) {
if (markApplyArgumentsCall(lc, arguments)) {
applyArguments = true;
}
}
lhs = CallNode.forCall(callLine, callToken, lhs.getStart(), finish, lhs, optimizeList(arguments), false, false, eval, applyArguments);
}
boolean optionalChain = false;
loop: while (true) {
callLine = line;
callToken = token;
switch (type) {
case LPAREN: {
final List<Expression> arguments = optimizeList(argumentList(yield, await));
lhs = CallNode.forCall(callLine, callToken, lhs.getStart(), finish, lhs, arguments, false, optionalChain);
break;
}
case LBRACKET: {
next();
final Expression rhs = expression(true, yield, await);
expect(RBRACKET);
lhs = new IndexNode(callToken, finish, lhs, rhs, false, false, optionalChain);
break;
}
case PERIOD: {
next();
final boolean isPrivate = type == TokenType.PRIVATE_IDENT;
final IdentNode property;
if (isPrivate) {
property = privateIdentifierUse();
} else {
property = getIdentifierName();
}
lhs = new AccessNode(callToken, finish, lhs, property.getName(), false, isPrivate, false, optionalChain);
break;
}
case TEMPLATE:
case TEMPLATE_HEAD: {
if (optionalChain) {
throw error(AbstractParser.message("optional.chain.template"));
}
final List<Expression> arguments = templateLiteralArgumentList(yield, await);
lhs = CallNode.forCall(callLine, callToken, lhs.getStart(), finish, lhs, arguments, false, optionalChain);
break;
}
case OPTIONAL_CHAIN: {
next();
optionalChain = true;
switch (type) {
case LPAREN: {
final List<Expression> arguments = optimizeList(argumentList(yield, await));
lhs = CallNode.forCall(callLine, callToken, lhs.getStart(), finish, lhs, arguments, true, optionalChain);
break;
}
case LBRACKET: {
next();
final Expression rhs = expression(true, yield, await);
expect(RBRACKET);
lhs = new IndexNode(callToken, finish, lhs, rhs, false, true, optionalChain);
break;
}
default: {
final boolean isPrivate = type == TokenType.PRIVATE_IDENT;
final IdentNode property;
if (isPrivate) {
property = privateIdentifierUse();
} else {
property = getIdentifierName();
}
lhs = new AccessNode(callToken, finish, lhs, property.getName(), false, isPrivate, true, optionalChain);
break;
}
}
break;
}
default:
break loop;
}
}
return lhs;
}
private Expression newExpression(boolean yield, boolean await) {
final long newToken = token;
assert type == TokenType.NEW;
next();
if (ES6_NEW_TARGET && type == PERIOD && isES6()) {
next();
if (type == IDENT && "target".equals(getValueNoEscape())) {
next();
markNewTarget();
return new IdentNode(newToken, finish, NEW_TARGET_NAME).setIsNewTarget();
} else {
throw error(AbstractParser.message("expected.target"), token);
}
} else if (type == IMPORT && isES2020() && lookahead() == LPAREN) {
throw error(AbstractParser.message(MESSAGE_EXPECTED_OPERAND, IMPORT.getName()), token);
}
final int callLine = line;
final Expression constructor = memberExpression(yield, await);
ArrayList<Expression> arguments;
if (type == LPAREN) {
arguments = argumentList(yield, await);
} else {
arguments = new ArrayList<>();
if (type == TokenType.OPTIONAL_CHAIN) {
throw error(AbstractParser.message("unexpected.token", type.getNameOrType()));
}
}
if (env.syntaxExtensions && type == LBRACE) {
arguments.add(objectLiteral(yield, await));
}
final Expression callNode = CallNode.forNew(callLine, newToken, Token.descPosition(newToken), finish, constructor, optimizeList(arguments));
return new UnaryNode(newToken, callNode);
}
private Expression memberExpression(boolean yield, boolean await) {
Expression lhs;
boolean isSuper = false;
switch (type) {
case NEW:
lhs = newExpression(yield, await);
break;
case FUNCTION:
lhs = functionExpression(false, false);
break;
case CLASS:
if (ES6_CLASS && isES6()) {
lhs = classExpression(yield, await);
break;
}
case SUPER:
if (ES6_CLASS && isES6()) {
Scope scope = lc.getCurrentScope();
if (scope.inMethod()) {
long identToken = Token.recast(token, IDENT);
next();
lhs = new IdentNode(identToken, finish, SUPER.getName()).setIsSuper();
switch (type) {
case LBRACKET:
case PERIOD:
ParserContextFunctionNode currentFunction = lc.getCurrentNonArrowFunction();
if (currentFunction.isMethod()) {
currentFunction.setFlag(FunctionNode.USES_SUPER);
}
isSuper = true;
break;
case LPAREN:
if (scope.inDerivedConstructor()) {
lhs = ((IdentNode) lhs).setIsDirectSuper();
break;
} else {
}
default:
throw error(AbstractParser.message("invalid.super"), identToken);
}
break;
}
}
case ASYNC:
if (isAsync() && lookaheadIsAsyncFunction()) {
lhs = asyncFunctionExpression(false, false);
break;
}
case IMPORT:
if (isES2020() && type == IMPORT) {
lhs = importExpression(yield, await);
break;
}
default:
lhs = primaryExpression(yield, await);
break;
}
loop: while (true) {
final long callToken = token;
switch (type) {
case LBRACKET: {
next();
final Expression index = expression(true, yield, await);
expect(RBRACKET);
lhs = new IndexNode(callToken, finish, lhs, index, isSuper, false, false);
if (isSuper) {
isSuper = false;
}
break;
}
case PERIOD: {
next();
final boolean isPrivate = type == TokenType.PRIVATE_IDENT;
final IdentNode property;
if (!isSuper && isPrivate) {
property = privateIdentifierUse();
} else {
property = getIdentifierName();
}
lhs = new AccessNode(callToken, finish, lhs, property.getName(), isSuper, isPrivate, false, false);
if (isSuper) {
isSuper = false;
}
break;
}
case TEMPLATE:
case TEMPLATE_HEAD: {
final int callLine = line;
final List<Expression> arguments = templateLiteralArgumentList(yield, await);
lhs = CallNode.forCall(callLine, callToken, lhs.getStart(), finish, lhs, arguments, false, false);
break;
}
default:
break loop;
}
}
return lhs;
}
private Expression importExpression(boolean yield, boolean await) {
final long importToken = token;
final int importLine = line;
final int importStart = start;
assert type == IMPORT;
next();
if (type == PERIOD) {
next();
expectDontAdvance(IDENT);
String meta = (String) getValueNoEscape();
if ("meta".equals(meta)) {
if (!isModule) {
throw error(AbstractParser.message("unexpected.import.meta"), importToken);
}
next();
return new IdentNode(importToken, finish, IMPORT_META_NAME).setIsImportMeta();
} else {
throw error(AbstractParser.message("unexpected.ident", meta), token);
}
} else if (type == LPAREN) {
next();
Expression argument = assignmentExpression(true, yield, await);
expect(RPAREN);
IdentNode importIdent = new IdentNode(importToken, Token.descPosition(importToken) + Token.descLength(importToken), IMPORT.getName());
return CallNode.forImport(importLine, importToken, importStart, finish, importIdent, Collections.singletonList(argument));
} else {
throw error(AbstractParser.message(MESSAGE_EXPECTED_OPERAND, IMPORT.getName()), importToken);
}
}
private ArrayList<Expression> argumentList(boolean yield, boolean await) {
return argumentList(yield, await, false);
}
private ArrayList<Expression> argumentList(boolean yield, boolean await, boolean inPatternPosition) {
final ArrayList<Expression> nodeList = new ArrayList<>();
next();
boolean first = true;
while (type != RPAREN) {
if (!first) {
expect(COMMARIGHT);
if (ES8_TRAILING_COMMA && isES2017() && type == RPAREN) {
break;
}
} else {
first = false;
}
long spreadToken = 0;
if (ES6_SPREAD_ARGUMENT && type == ELLIPSIS && isES6()) {
spreadToken = token;
next();
}
Expression expression = assignmentExpression(true, yield, await, inPatternPosition);
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 static <T> List<T> optimizeList(final List<T> list) {
switch (list.size()) {
case 0:
return Collections.emptyList();
case 1:
return Collections.singletonList(list.get(0));
default:
((ArrayList<T>) list).trimToSize();
return list;
}
}
private Expression asyncFunctionExpression(final boolean isStatement, final boolean topLevel) {
assert isAsync() && lookaheadIsAsyncFunction();
long asyncToken = token;
nextOrEOL();
return functionExpression(isStatement, topLevel, true, Token.recast(asyncToken, FUNCTION), false);
}
private Expression functionExpression(final boolean isStatement, final boolean topLevel) {
return functionExpression(isStatement, topLevel, false, token, false);
}
private Expression functionExpression(final boolean isStatement, final boolean topLevel, final boolean expressionStatement) {
return functionExpression(isStatement, topLevel, false, token, expressionStatement);
}
private Expression functionExpression(final boolean isStatement, final boolean topLevel, final boolean async, final long functionToken, final boolean expressionStatement) {
final int functionLine = line;
assert type == FUNCTION;
next();
boolean generator = false;
if (type == MUL && ES6_GENERATOR_FUNCTION && isES6()) {
if (expressionStatement) {
throw error(AbstractParser.message(MESSAGE_EXPECTED_STMT, "generator function declaration"), token);
}
generator = true;
next();
}
IdentNode name = null;
if (isBindingIdentifier()) {
boolean yield = (!isStatement && generator) || (isStatement && inGeneratorFunction());
boolean await = (!isStatement && async) || (isStatement && inAsyncFunction());
name = bindingIdentifier(yield, await, "function name");
} else if (isStatement) {
if (!env.syntaxExtensions && reparsedFunction == null) {
expect(IDENT);
}
}
expect(LPAREN);
boolean isAnonymous = name == null;
int functionFlags = (generator ? FunctionNode.IS_GENERATOR : 0) |
(async ? FunctionNode.IS_ASYNC : 0) |
(isAnonymous ? FunctionNode.IS_ANONYMOUS : 0);
final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, functionFlags, functionLine);
if (isAnonymous) {
functionNode.setInternalName(getDefaultFunctionName());
}
lc.push(functionNode);
Block functionBody;
hideDefaultName();
try {
final ParserContextBlockNode parameterBlock = functionNode.createParameterBlock();
lc.push(parameterBlock);
try {
formalParameterList(generator, async);
expect(RPAREN);
functionBody = functionBody(functionNode);
} finally {
restoreBlock(parameterBlock);
}
if (parameterBlock != null) {
functionBody = wrapParameterBlock(parameterBlock, functionBody);
}
} finally {
popDefaultName();
lc.pop(functionNode);
}
if (isStatement && !isAnonymous) {
functionNode.setFlag(FunctionNode.IS_STATEMENT);
if (topLevel || useBlockScope() || (!isStrictMode && env.functionStatement == ScriptEnvironment.FunctionStatementBehavior.ACCEPT)) {
functionNode.setFlag(FunctionNode.IS_DECLARED);
} else if (isStrictMode) {
throw error(JSErrorType.SyntaxError, AbstractParser.message("strict.no.func.decl.here"), functionToken);
} else if (env.functionStatement == ScriptEnvironment.FunctionStatementBehavior.ERROR) {
throw error(JSErrorType.SyntaxError, AbstractParser.message("no.func.decl.here"), functionToken);
} else if (env.functionStatement == ScriptEnvironment.FunctionStatementBehavior.WARNING) {
warning(JSErrorType.SyntaxError, AbstractParser.message("no.func.decl.here.warn"), functionToken);
}
}
verifyParameterList(functionNode);
final FunctionNode function = createFunctionNode(
functionNode,
functionToken,
name,
functionLine,
functionBody);
if (isStatement) {
if (isAnonymous) {
appendStatement(new ExpressionStatement(functionLine, functionToken, finish, function));
return function;
}
final int varFlags = ((topLevel && !isModule) || !useBlockScope()) ? 0 : VarNode.IS_LET;
final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, function, varFlags);
declareVar(lc.getCurrentScope(), varNode);
if (topLevel) {
functionDeclarations.add(varNode);
} else if (useBlockScope()) {
prependStatement(varNode);
} else {
appendStatement(varNode);
}
}
return function;
}
private static Block wrapParameterBlock(ParserContextBlockNode parameterBlock, Block functionBody) {
assert parameterBlock.getFlag(Block.IS_PARAMETER_BLOCK) != 0 && functionBody.isFunctionBody();
if (parameterBlock.getStatements().isEmpty()) {
return functionBody;
} else {
parameterBlock.getStatements().add(new BlockStatement(functionBody.getFirstStatementLineNumber(), functionBody));
return new Block(parameterBlock.getToken(), functionBody.getFinish(), parameterBlock.getFlags(), parameterBlock.getScope(), parameterBlock.getStatements());
}
}
private void verifyParameterList(final ParserContextFunctionNode functionNode) {
IdentNode duplicateParameter = functionNode.getDuplicateParameterBinding();
if (duplicateParameter != null) {
if (functionNode.isStrict() || functionNode.isMethod() || functionNode.isArrow() || !functionNode.isSimpleParameterList()) {
throw error(AbstractParser.message("strict.param.redefinition", duplicateParameter.getName()), duplicateParameter.getToken());
}
final List<IdentNode> parameters = functionNode.getParameters();
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 void pushDefaultName(final Expression nameExpr) {
defaultNames.add(nameExpr);
}
private Object popDefaultName() {
return defaultNames.remove(defaultNames.size() - 1);
}
private String getDefaultFunctionName() {
if (!defaultNames.isEmpty()) {
final Object nameExpr = defaultNames.get(defaultNames.size() - 1);
if (nameExpr instanceof PropertyKey) {
markDefaultNameUsed();
return ((PropertyKey) nameExpr).getPropertyName();
} else if (nameExpr instanceof AccessNode) {
AccessNode accessNode = (AccessNode) nameExpr;
markDefaultNameUsed();
if (accessNode.getBase() instanceof AccessNode) {
AccessNode base = (AccessNode) accessNode.getBase();
if (base.getBase() instanceof IdentNode && !base.isPrivate() && base.getProperty().equals(PROTOTYPE_NAME)) {
return ((IdentNode) base.getBase()).getName() + "." + accessNode.getProperty();
}
} else if (accessNode.getBase() instanceof IdentNode) {
return ((IdentNode) accessNode.getBase()).getName() + "." + accessNode.getProperty();
}
return accessNode.getProperty();
}
}
return ANONYMOUS_FUNCTION_NAME;
}
private void markDefaultNameUsed() {
popDefaultName();
hideDefaultName();
}
private void hideDefaultName() {
defaultNames.add("");
}
private void formalParameterList(final boolean yield, final boolean async) {
formalParameterList(RPAREN, yield, async);
}
private void formalParameter(final boolean yield, final boolean await) {
if (type == YIELD && yield || isAwait() && await) {
throw error(expectMessage(IDENT));
}
final ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
final long paramToken = token;
final int paramLine = line;
IdentNode ident;
if (isBindingIdentifier() || !(ES6_DESTRUCTURING && isES6())) {
ident = bindingIdentifier(yield, await, FUNCTION_PARAMETER_CONTEXT);
if (type == ASSIGN && (ES6_DEFAULT_PARAMETER && isES6())) {
next();
if (type == YIELD && yield || isAwait() && await) {
throw error(expectMessage(IDENT));
}
Expression initializer = assignmentExpression(true, yield, await);
if (isAnonymousFunctionDefinition(initializer)) {
initializer = setAnonymousFunctionName(initializer, ident.getName());
}
if (currentFunction != null) {
addDefaultParameter(paramToken, finish, paramLine, ident, initializer, currentFunction);
}
} else {
if (currentFunction != null) {
currentFunction.addParameter(ident);
}
}
} else {
final Expression pattern = bindingPattern(yield, await);
verifyDestructuringParameterBindingPattern(pattern, paramToken, paramLine);
Expression initializer = null;
if (type == ASSIGN) {
next();
initializer = assignmentExpression(true, yield, await);
}
if (currentFunction != null) {
addDestructuringParameter(paramToken, finish, paramLine, pattern, initializer, currentFunction, false);
}
}
}
private void functionRestParameter(final TokenType endType, final boolean yield, final boolean await) {
final long paramToken = token;
final int paramLine = line;
final ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
final Expression pattern = bindingIdentifierOrPattern(yield, await, FUNCTION_PARAMETER_CONTEXT);
if (pattern instanceof IdentNode) {
IdentNode ident = ((IdentNode) pattern).setIsRestParameter();
if (currentFunction != null) {
currentFunction.addParameter(ident);
}
} else {
verifyDestructuringParameterBindingPattern(pattern, paramToken, paramLine);
if (currentFunction != null) {
addDestructuringParameter(paramToken, finish, paramLine, pattern, null, currentFunction, true);
}
}
expectDontAdvance(endType);
}
private void formalParameterList(final TokenType endType, final boolean yield, final boolean await) {
boolean first = true;
while (type != endType) {
if (!first) {
expect(COMMARIGHT);
if (ES8_TRAILING_COMMA && isES2017() && type == endType) {
break;
}
} else {
first = false;
}
if (ES6_REST_PARAMETER && type == ELLIPSIS && isES6()) {
next();
functionRestParameter(endType, yield, await);
break;
}
formalParameter(yield, await);
}
}
private static void addDefaultParameter(long paramToken, int paramFinish, int paramLine, IdentNode target, Expression initializer, ParserContextFunctionNode function) {
assert target != null && initializer != null;
final int paramIndex = function.getParameterCount();
final ParameterNode param = new ParameterNode(paramToken, paramFinish, paramIndex);
final BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), param, newUndefinedLiteral(paramToken, paramFinish));
final Expression value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(param));
final VarNode varNode = new VarNode(paramLine, Token.recast(paramToken, LET), paramFinish, target, value, VarNode.IS_LET);
function.addDefaultParameter(varNode);
}
private void addDestructuringParameter(long paramToken, int paramFinish, int paramLine, Expression target, Expression initializer, ParserContextFunctionNode function, boolean isRest) {
assert isDestructuringLhs(target);
final int paramIndex = function.getParameterCount();
final ParameterNode param = new ParameterNode(paramToken, paramFinish, paramIndex, isRest);
final Expression value;
if (initializer == null) {
value = param;
} else {
BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), param, newUndefinedLiteral(paramToken, paramFinish));
value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(param));
}
BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN_INIT), target, value);
function.addParameterInitialization(paramLine, assignment, initializer != null, isRest);
}
private void verifyDestructuringParameterBindingPattern(final Expression pattern, final long paramToken, final int paramLine) {
verifyDestructuringBindingPattern(pattern, new Consumer<IdentNode>() {
@Override
public void accept(IdentNode identNode) {
verifyStrictIdent(identNode, FUNCTION_PARAMETER_CONTEXT);
ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
if (currentFunction != null) {
VarNode declaration = new VarNode(paramLine, Token.recast(paramToken, LET), pattern.getFinish(), identNode, null, VarNode.IS_LET | VarNode.IS_DESTRUCTURING);
currentFunction.addParameterBindingDeclaration(declaration);
}
}
});
}
private Block functionBody(final ParserContextFunctionNode functionNode) {
final long bodyToken = token;
final int bodyFinish;
final boolean parseBody;
Object endParserState = null;
ParserContextBlockNode body = newBlock(functionNode.createBodyScope());
try {
final int functionId = functionNode.getId();
parseBody = reparsedFunction == null || functionId <= reparsedFunction.getFunctionNodeId();
if ((env.syntaxExtensions || functionNode.isArrow()) && type != LBRACE) {
final Expression expr = assignmentExpression(true);
long lastToken = previousToken;
functionNode.setLastToken(previousToken);
assert lc.getCurrentBlock().getScope().isFunctionBodyScope();
final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken));
if (parseBody) {
final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr);
appendStatement(returnNode);
}
bodyFinish = finish;
} else {
expectDontAdvance(LBRACE);
if (parseBody || !skipFunctionBody(functionNode)) {
next();
final List<Statement> prevFunctionDecls = functionDeclarations;
functionDeclarations = new ArrayList<>();
try {
sourceElements(0);
addFunctionDeclarations(functionNode);
} finally {
functionDeclarations = prevFunctionDecls;
}
if (parseBody) {
endParserState = new ParserState(Token.descPosition(token), line, linePosition);
}
}
bodyFinish = Token.descPosition(token) + Token.descLength(token);
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);
}
}
}
return new Block(bodyToken, bodyFinish, body.getFlags() | Block.IS_BODY, body.getScope(), body.getStatements());
}
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++) {
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.ecmaScriptVersion, shebang, isModule, allowBigInt);
line = parserState.line;
linePosition = parserState.linePosition;
type = SEMICOLON;
scanFirstToken();
return true;
}
private static class ParserState {
private final int position;
private final int line;
private final int linePosition;
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 int ecmaScriptVersion, final boolean shebang, final boolean isModule, final boolean allowBigInt) {
final Lexer newLexer = new Lexer(source, position, lexer.limit - position, stream, scripting, ecmaScriptVersion, shebang, isModule, true, allowBigInt);
newLexer.restoreState(new Lexer.State(position, Integer.MAX_VALUE, line, -1, linePosition, SEMICOLON));
return newLexer;
}
}
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 ParserException invalidLHSError(final Expression lhs) {
JSErrorType errorType = isES2020() ? JSErrorType.SyntaxError : JSErrorType.ReferenceError;
return error(errorType, AbstractParser.message(MESSAGE_INVALID_LVALUE), lhs.getToken());
}
private Expression unaryExpression(boolean yield, boolean await) {
final int unaryLine = line;
final long unaryToken = token;
switch (type) {
case DELETE: {
next();
final Expression expr = unaryExpression(yield, await);
if (type == TokenType.EXP) {
throw error(AbstractParser.message("unexpected.token", type.getNameOrType()));
}
return verifyDeleteExpression(unaryLine, unaryToken, expr);
}
case VOID:
case TYPEOF:
case ADD:
case SUB:
case BIT_NOT:
case NOT:
next();
final Expression expr = unaryExpression(yield, await);
if (type == TokenType.EXP) {
throw error(AbstractParser.message("unexpected.token", type.getNameOrType()));
}
return new UnaryNode(unaryToken, expr);
case INCPREFIX:
case DECPREFIX:
final TokenType opType = type;
next();
final Expression lhs = unaryExpression(yield, await);
return verifyIncDecExpression(unaryToken, opType, lhs, false);
default:
if (isAwait() && await) {
return awaitExpression(yield);
}
break;
}
Expression expression = leftHandSideExpression(yield, await);
if (last != EOL) {
switch (type) {
case INCPREFIX:
case DECPREFIX:
final long opToken = token;
final TokenType opType = type;
final Expression lhs = expression;
next();
return verifyIncDecExpression(opToken, opType, lhs, true);
default:
break;
}
}
return expression;
}
private Expression verifyDeleteExpression(final int unaryLine, final long unaryToken, final Expression expr) {
if (expr instanceof BaseNode || expr instanceof IdentNode) {
if (isStrictMode) {
if (expr instanceof IdentNode) {
IdentNode ident = (IdentNode) expr;
if (!ident.isThis() && !ident.isMetaProperty()) {
throw error(AbstractParser.message("strict.cant.delete.ident", ident.getName()), unaryToken);
}
} else if (expr instanceof AccessNode && ((AccessNode) expr).isPrivate()) {
throw error(AbstractParser.message("strict.cant.delete.private"), unaryToken);
}
}
return new UnaryNode(unaryToken, expr);
}
appendStatement(new ExpressionStatement(unaryLine, unaryToken, finish, expr));
return LiteralNode.newInstance(unaryToken, finish, true);
}
private Expression verifyIncDecExpression(final long unaryToken, final TokenType opType, final Expression lhs, final boolean isPostfix) {
assert lhs != null;
if (lhs instanceof IdentNode) {
IdentNode ident = (IdentNode) lhs;
if (!checkIdentLValue(ident) || ident.isMetaProperty()) {
throw invalidLHSError(lhs);
}
assert opType == TokenType.INCPREFIX || opType == TokenType.DECPREFIX;
String contextString = opType == TokenType.INCPREFIX ? "operand for ++ operator" : "operand for -- operator";
verifyStrictIdent((IdentNode) lhs, contextString);
} else if (!(lhs instanceof AccessNode || lhs instanceof IndexNode) || ((BaseNode) lhs).isOptional()) {
throw invalidLHSError(lhs);
}
return incDecExpression(unaryToken, opType, lhs, isPostfix);
}
private Expression expression() {
return expression(true, inGeneratorFunction(), inAsyncFunction());
}
private Expression expression(boolean in, boolean yield, boolean await) {
return expression(in, yield, await, false);
}
private Expression expression(boolean in, boolean yield, boolean await, boolean inPatternPosition) {
Expression assignmentExpression = assignmentExpression(in, yield, await, inPatternPosition);
while (type == COMMARIGHT) {
long commaToken = token;
next();
Expression rhs = assignmentExpression(in, yield, await);
assignmentExpression = new BinaryNode(commaToken, assignmentExpression, rhs);
}
return assignmentExpression;
}
private Expression parenthesizedExpressionAndArrowParameterList(boolean yield, boolean await) {
long primaryToken = token;
assert type == LPAREN;
next();
if (ES6_ARROW_FUNCTION && isES6() && type == RPAREN) {
nextOrEOL();
expectDontAdvance(ARROW);
return new ExpressionList(primaryToken, finish, Collections.emptyList());
}
Expression assignmentExpression = null;
boolean hasCoverInitializedName = false;
long commaToken = 0L;
while (true) {
if (ES6_ARROW_FUNCTION && ES6_REST_PARAMETER && isES6() && type == ELLIPSIS) {
return arrowFunctionRestParameter(assignmentExpression, commaToken, yield, await);
} else if (ES6_ARROW_FUNCTION && ES8_TRAILING_COMMA && isES2017() && type == RPAREN && lookaheadIsArrow()) {
break;
}
Expression rhs = assignmentExpression(true, yield, await, true);
hasCoverInitializedName = hasCoverInitializedName || hasCoverInitializedName(rhs);
if (assignmentExpression == null) {
assignmentExpression = rhs;
} else {
assert Token.descType(commaToken) == COMMARIGHT;
assignmentExpression = new BinaryNode(commaToken, assignmentExpression, rhs);
}
if (type != COMMARIGHT) {
break;
}
commaToken = token;
next();
}
boolean arrowAhead = lookaheadIsArrow();
if (hasCoverInitializedName && !(type == RPAREN && arrowAhead)) {
throw error(AbstractParser.message(MESSAGE_INVALID_PROPERTY_INITIALIZER));
}
expect(RPAREN);
if (!arrowAhead) {
assignmentExpression.makeParenthesized(Token.descPosition(primaryToken), finish);
}
return assignmentExpression;
}
private Expression arrowFunctionRestParameter(Expression paramListExpr, long commaToken, final boolean yield, final boolean await) {
final long ellipsisToken = token;
assert type == ELLIPSIS;
next();
final Expression pattern = bindingIdentifierOrPattern(yield, await, FUNCTION_PARAMETER_CONTEXT);
final Expression restParam;
if (pattern instanceof IdentNode) {
restParam = ((IdentNode) pattern).setIsRestParameter();
} else {
restParam = new UnaryNode(Token.recast(ellipsisToken, SPREAD_ARGUMENT), pattern);
}
expectDontAdvance(RPAREN);
nextOrEOL();
expectDontAdvance(ARROW);
if (paramListExpr == null) {
return restParam;
} else {
assert Token.descType(commaToken) == COMMARIGHT;
return new BinaryNode(commaToken, paramListExpr, restParam);
}
}
private Expression expression(int minPrecedence, boolean in, boolean yield, boolean await) {
return expression(unaryExpression(yield, await), minPrecedence, in, yield, await);
}
private JoinPredecessorExpression joinPredecessorExpression() {
return new JoinPredecessorExpression(expression());
}
private Expression expression(Expression exprLhs, int minPrecedence, boolean in, boolean yield, boolean await) {
int precedence = type.getPrecedence();
Expression lhs = exprLhs;
while (type.isOperator(in) && precedence >= minPrecedence) {
final long op = token;
if (type == TERNARY) {
next();
final Expression trueExpr = assignmentExpression(true, yield, await);
expect(COLON);
final Expression falseExpr = assignmentExpression(in, yield, await);
lhs = new TernaryNode(op, lhs, new JoinPredecessorExpression(trueExpr), new JoinPredecessorExpression(falseExpr));
} else {
next();
assert !Token.descType(op).isAssignment();
Expression rhs = unaryExpression(yield, await);
int nextPrecedence = type.getPrecedence();
while (type.isOperator(in) && (nextPrecedence > precedence || (nextPrecedence == precedence && !type.isLeftAssociative()))) {
rhs = expression(rhs, nextPrecedence, in, yield, await);
nextPrecedence = type.getPrecedence();
}
lhs = newBinaryExpression(op, lhs, rhs);
}
precedence = type.getPrecedence();
}
return lhs;
}
private Expression assignmentExpression(boolean in) {
return assignmentExpression(in, inGeneratorFunction(), inAsyncFunction(), false);
}
private Expression assignmentExpression(boolean in, boolean yield, boolean await) {
return assignmentExpression(in, yield, await, false);
}
private Expression assignmentExpression(boolean in, boolean yield, boolean await, boolean inPatternPosition) {
if (type == YIELD && yield) {
return yieldExpression(in, await);
}
boolean asyncArrow = isAsync() && lookaheadIsAsyncArrowParameterListStart();
final long startToken = token;
final int startLine = line;
Expression exprLhs = conditionalExpression(in, yield, await);
if (asyncArrow && exprLhs instanceof IdentNode && isBindingIdentifier() && lookaheadIsArrow()) {
exprLhs = primaryExpression(yield, await);
}
if (ES6_ARROW_FUNCTION && type == ARROW && isES6()) {
if (checkNoLineTerminator()) {
return arrowFunction(startToken, startLine, exprLhs, asyncArrow);
}
}
assert !(exprLhs instanceof ExpressionList);
if (type.isAssignment()) {
final boolean isAssign = type == ASSIGN;
if (isAssign) {
pushDefaultName(exprLhs);
}
try {
long assignToken = token;
next();
Expression exprRhs = assignmentExpression(in, yield, await);
return verifyAssignment(assignToken, exprLhs, exprRhs, inPatternPosition);
} finally {
if (isAssign) {
popDefaultName();
}
}
} else {
if (!inPatternPosition && hasCoverInitializedName(exprLhs)) {
throw error(AbstractParser.message(MESSAGE_INVALID_PROPERTY_INITIALIZER));
}
return exprLhs;
}
}
private Expression conditionalExpression(boolean in, boolean yield, boolean await) {
return expression(TERNARY.getPrecedence(), in, yield, await);
}
private Expression arrowFunction(final long startToken, final int functionLine, final Expression paramListExpr, boolean async) {
assert type != ARROW || checkNoLineTerminator();
expect(ARROW);
final long functionToken = Token.recast(startToken, ARROW);
final IdentNode name = null;
final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, FunctionNode.IS_ARROW, functionLine);
functionNode.setInternalName(ARROW_FUNCTION_NAME);
functionNode.setFlag(FunctionNode.IS_ANONYMOUS);
if (async) {
functionNode.setFlag(FunctionNode.IS_ASYNC);
}
lc.push(functionNode);
try {
final ParserContextBlockNode parameterBlock = functionNode.createParameterBlock();
lc.push(parameterBlock);
Block functionBody;
try {
convertArrowFunctionParameterList(paramListExpr, functionNode);
functionBody = functionBody(functionNode);
} finally {
restoreBlock(parameterBlock);
}
verifyParameterList(functionNode);
if (parameterBlock != null) {
markEvalInArrowParameterList(parameterBlock);
functionBody = wrapParameterBlock(parameterBlock, functionBody);
}
final FunctionNode function = createFunctionNode(
functionNode,
functionToken,
name,
functionLine,
functionBody);
return function;
} finally {
lc.pop(functionNode);
}
}
private void markEvalInArrowParameterList(ParserContextBlockNode parameterBlock) {
Iterator<ParserContextFunctionNode> iter = lc.getFunctions();
ParserContextFunctionNode current = iter.next();
ParserContextFunctionNode parent = iter.next();
int flagsToPropagate = parent.getFlag(FunctionNode.HAS_EVAL | FunctionNode.HAS_ARROW_EVAL);
if (flagsToPropagate != 0) {
for (Statement st : parameterBlock.getStatements()) {
st.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public boolean enterCallNode(CallNode callNode) {
if (callNode.isEval()) {
current.setFlag(flagsToPropagate);
}
return true;
}
});
}
}
}
private static Expression convertExpressionListToExpression(ExpressionList exprList) {
if (exprList.getExpressions().isEmpty()) {
return null;
} else if (exprList.getExpressions().size() == 1) {
return exprList.getExpressions().get(0);
} else {
long recastToken = Token.recast(exprList.getToken(), COMMARIGHT);
Expression result = null;
for (Expression expression : exprList.getExpressions()) {
result = result == null ? expression : new BinaryNode(recastToken, result, expression);
}
return result;
}
}
private void verifyContainsNeitherYieldNorAwaitExpression(Expression expression) {
expression.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public boolean enterUnaryNode(UnaryNode unaryNode) {
switch (unaryNode.tokenType()) {
case AWAIT:
case YIELD:
case YIELD_STAR:
throw error(AbstractParser.message(MESSAGE_INVALID_ARROW_PARAMETER), unaryNode.getToken());
default:
return super.enterUnaryNode(unaryNode);
}
}
});
}
private void convertArrowFunctionParameterList(Expression paramList, ParserContextFunctionNode function) {
Expression paramListExpr = paramList;
if (paramListExpr instanceof ExpressionList) {
paramListExpr = convertExpressionListToExpression((ExpressionList) paramListExpr);
}
if (paramListExpr == null) {
return;
}
verifyContainsNeitherYieldNorAwaitExpression(paramListExpr);
final int functionLine = function.getLineNumber();
if (paramListExpr instanceof IdentNode || paramListExpr.isTokenType(ASSIGN) || isDestructuringLhs(paramListExpr) || paramListExpr.isTokenType(SPREAD_ARGUMENT)) {
convertArrowParameter(paramListExpr, 0, functionLine, function);
} else if (paramListExpr instanceof BinaryNode && Token.descType(paramListExpr.getToken()) == COMMARIGHT) {
ArrayList<Expression> params = new ArrayList<>();
Expression car = paramListExpr;
do {
Expression cdr = ((BinaryNode) car).getRhs();
params.add(cdr);
car = ((BinaryNode) car).getLhs();
} while (car instanceof BinaryNode && Token.descType(car.getToken()) == COMMARIGHT);
params.add(car);
for (int i = params.size() - 1, pos = 0; i >= 0; i--, pos++) {
Expression param = params.get(i);
if (i != 0 && param.isTokenType(SPREAD_ARGUMENT)) {
throw error(AbstractParser.message(MESSAGE_INVALID_ARROW_PARAMETER), param.getToken());
} else {
convertArrowParameter(param, pos, functionLine, function);
}
}
} else {
throw error(AbstractParser.message("expected.arrow.parameter"), paramListExpr.getToken());
}
}
private void convertArrowParameter(Expression param, int index, int paramLine, ParserContextFunctionNode currentFunction) {
assert index == currentFunction.getParameterCount();
if (param instanceof IdentNode) {
IdentNode ident = (IdentNode) param;
verifyStrictIdent(ident, FUNCTION_PARAMETER_CONTEXT);
if (ident.isParenthesized() || currentFunction.isAsync() && AWAIT.getName().equals(ident.getName())) {
throw error(AbstractParser.message(MESSAGE_INVALID_ARROW_PARAMETER), param.getToken());
}
currentFunction.addParameter(ident);
return;
}
if (param.isTokenType(ASSIGN)) {
Expression lhs = ((BinaryNode) param).getLhs();
long paramToken = lhs.getToken();
Expression initializer = ((BinaryNode) param).getRhs();
if (initializer instanceof IdentNode) {
if (((IdentNode) initializer).getName().equals(AWAIT.getName())) {
throw error(AbstractParser.message(MESSAGE_INVALID_ARROW_PARAMETER), param.getToken());
}
}
if (lc.getCurrentNonArrowFunction().getFlag(FunctionNode.USES_THIS) != 0) {
currentFunction.setFlag(FunctionNode.USES_THIS);
}
if (lhs instanceof IdentNode && !lhs.isParenthesized()) {
IdentNode ident = (IdentNode) lhs;
if (isAnonymousFunctionDefinition(initializer)) {
initializer = setAnonymousFunctionName(initializer, ident.getName());
}
addDefaultParameter(paramToken, param.getFinish(), paramLine, ident, initializer, currentFunction);
return;
} else if (isDestructuringLhs(lhs)) {
verifyDestructuringParameterBindingPattern(lhs, paramToken, paramLine);
addDestructuringParameter(paramToken, param.getFinish(), paramLine, lhs, initializer, currentFunction, false);
} else {
throw error(AbstractParser.message(MESSAGE_INVALID_ARROW_PARAMETER), paramToken);
}
} else if (isDestructuringLhs(param)) {
long paramToken = param.getToken();
verifyDestructuringParameterBindingPattern(param, paramToken, paramLine);
addDestructuringParameter(paramToken, param.getFinish(), paramLine, param, null, currentFunction, false);
} else if (param.isTokenType(SPREAD_ARGUMENT)) {
Expression restParam = ((UnaryNode) param).getExpression();
if (restParam instanceof IdentNode && identAtTheEndOfArrowParamList()) {
IdentNode ident = ((IdentNode) restParam).setIsRestParameter();
convertArrowParameter(ident, index, paramLine, currentFunction);
} else if (isDestructuringLhs(restParam)) {
verifyDestructuringParameterBindingPattern(restParam, restParam.getToken(), paramLine);
addDestructuringParameter(restParam.getToken(), restParam.getFinish(), paramLine, restParam, null, currentFunction, true);
} else {
throw error(AbstractParser.message(MESSAGE_INVALID_ARROW_PARAMETER), param.getToken());
}
} else {
throw error(AbstractParser.message(MESSAGE_INVALID_ARROW_PARAMETER), param.getToken());
}
}
private boolean identAtTheEndOfArrowParamList() {
int idx = k - 1;
assert T(idx) == ARROW;
while (true) {
idx--;
TokenType t = T(idx);
if (t == COMMENT) {
continue;
} else if (t == RPAREN) {
break;
} else {
return false;
}
}
while (true) {
idx--;
TokenType t = T(idx);
if (t == COMMENT) {
continue;
} else if (t == IDENT) {
break;
} else {
return false;
}
}
return true;
}
private boolean checkNoLineTerminator() {
assert type == ARROW;
if (last == RPAREN) {
return true;
} else if (last == IDENT) {
return true;
}
for (int i = k - 1; i >= 0; i--) {
TokenType t = T(i);
switch (t) {
case RPAREN:
case IDENT:
return true;
case EOL:
return false;
case COMMENT:
continue;
default:
return (t.isContextualKeyword() || t.isFutureStrict());
}
}
return false;
}
private boolean lookaheadIsArrow() {
int i = 1;
for (;;) {
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(boolean yield, boolean await) {
assert type == TEMPLATE || type == TEMPLATE_HEAD;
final boolean noSubstitutionTemplate = type == TEMPLATE;
long lastLiteralToken = token;
boolean previousPauseOnRightBrace = lexer.pauseOnRightBrace;
try {
lexer.pauseOnRightBrace = true;
LiteralNode<?> literal = getLiteral();
if (noSubstitutionTemplate) {
return literal;
}
Expression concat = literal;
TokenType lastLiteralType;
do {
Expression expression = templateLiteralExpression(yield, await);
expression = new RuntimeNode(Token.recast(expression.getToken(), VOID), expression.getFinish(), RuntimeNode.Request.TO_STRING, expression);
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;
} finally {
lexer.pauseOnRightBrace = previousPauseOnRightBrace;
}
}
private Expression templateLiteralExpression(boolean yield, boolean await) {
assert lexer.pauseOnRightBrace;
Expression expression = expression(true, yield, await);
if (type != RBRACE) {
throw error(AbstractParser.message("unterminated.template.expression"), token);
}
lexer.scanTemplateSpan();
next();
assert type == TEMPLATE_MIDDLE || type == TEMPLATE_TAIL;
return expression;
}
private List<Expression> templateLiteralArgumentList(boolean yield, boolean await) {
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;
boolean previousPauseOnRightBrace = lexer.pauseOnRightBrace;
try {
lexer.pauseOnRightBrace = true;
addTemplateLiteralString(rawStrings, cookedStrings);
if (hasSubstitutions) {
TokenType lastLiteralType;
do {
Expression expression = templateLiteralExpression(yield, await);
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);
final RuntimeNode templateObject = new RuntimeNode(templateToken, finish, RuntimeNode.Request.GET_TEMPLATE_OBJECT, rawStringArray, cookedStringArray);
argumentList.set(0, templateObject);
return optimizeList(argumentList);
} finally {
lexer.pauseOnRightBrace = previousPauseOnRightBrace;
}
}
private void addTemplateLiteralString(final ArrayList<Expression> rawStrings, final ArrayList<Expression> cookedStrings) {
final long stringToken = token;
final String rawString = lexer.valueOfRawString(stringToken);
final String cookedString = lexer.valueOfTaggedTemplateString(stringToken);
next();
Expression cookedExpression;
if (cookedString == null) {
cookedExpression = newUndefinedLiteral(stringToken, finish);
} else {
cookedExpression = LiteralNode.newInstance(stringToken, cookedString);
}
rawStrings.add(LiteralNode.newInstance(stringToken, rawString));
cookedStrings.add(cookedExpression);
}
private FunctionNode module(final String moduleName) {
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 Scope moduleScope = Scope.createModule();
final IdentNode ident = null;
final ParserContextFunctionNode script = createParserContextFunctionNode(
ident,
functionToken,
FunctionNode.IS_MODULE,
functionLine,
Collections.<IdentNode> emptyList(), 0, moduleScope);
script.setInternalName(moduleName);
lc.push(script);
final ParserContextModuleNode module = new ParserContextModuleNode(moduleName, moduleScope, this);
final ParserContextBlockNode body = newBlock(moduleScope);
functionDeclarations = new ArrayList<>();
try {
moduleBody(module);
long yieldToken = Token.toDesc(YIELD, functionStart, 0);
prependStatement(new ExpressionStatement(functionLine, yieldToken, functionLine, new UnaryNode(yieldToken, newUndefinedLiteral(yieldToken, finish))));
script.setFlag(FunctionNode.IS_GENERATOR);
addFunctionDeclarations(script);
} finally {
functionDeclarations = null;
restoreBlock(body);
lc.pop(script);
}
body.setFlag(Block.NEEDS_SCOPE);
final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC | Block.IS_BODY | Block.IS_MODULE_BODY, body.getScope(), body.getStatements());
script.setLastToken(token);
expect(EOF);
script.setModule(module.createModule());
return createFunctionNode(script, functionToken, ident, functionLine, programBody);
}
private void moduleBody(ParserContextModuleNode module) {
loop: while (type != EOF) {
switch (type) {
case EOF:
break loop;
case EXPORT:
exportDeclaration(module);
break;
case IMPORT:
if (!isImportExpression()) {
importDeclaration(module);
break;
}
default:
statement(true, 0, false, false, false);
break;
}
}
}
private boolean isImportExpression() {
assert type == IMPORT;
if (!isES2020()) {
return false;
}
TokenType la = lookahead();
return la == PERIOD || la == LPAREN;
}
private void declareImportBinding(IdentNode ident, boolean star) {
Scope moduleScope = lc.getCurrentBlock().getScope();
assert moduleScope.isModuleScope();
if (moduleScope.hasSymbol(ident.getName())) {
throw error(ECMAErrors.getMessage("syntax.error.redeclare.variable", ident.getName()), ident.getToken());
}
moduleScope.putSymbol(new Symbol(ident.getName(), Symbol.IS_CONST | Symbol.HAS_BEEN_DECLARED | (star ? 0 : Symbol.IS_IMPORT_BINDING)));
}
private void declareImportBinding(IdentNode ident) {
declareImportBinding(ident, false);
}
private void declareImportStarBinding(IdentNode ident) {
declareImportBinding(ident, true);
}
private void importDeclaration(ParserContextModuleNode module) {
final long importToken = token;
expect(IMPORT);
if (type == STRING || type == ESCSTRING) {
String moduleSpecifier = (String) getValue();
long specifierToken = token;
next();
LiteralNode<String> specifier = LiteralNode.newInstance(specifierToken, moduleSpecifier);
module.addModuleRequest(moduleSpecifier);
module.addImport(new ImportNode(importToken, Token.descPosition(importToken), finish, specifier));
} else {
List<ImportEntry> importEntries;
ImportClauseNode importClause;
final long startToken = token;
if (type == MUL) {
NameSpaceImportNode namespaceNode = nameSpaceImport();
importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, namespaceNode);
importEntries = Collections.singletonList(
ImportEntry.importStarAsNameSpaceFrom(namespaceNode.getBindingIdentifier().getName()));
} else if (type == LBRACE) {
NamedImportsNode namedImportsNode = namedImports();
importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, namedImportsNode);
importEntries = convert(namedImportsNode);
} else if (isBindingIdentifier()) {
IdentNode importedDefaultBinding = bindingIdentifier(false, false, IMPORTED_BINDING_CONTEXT);
declareImportBinding(importedDefaultBinding);
ImportEntry defaultImport = ImportEntry.importDefault(importedDefaultBinding.getName());
if (type == COMMARIGHT) {
next();
if (type == MUL) {
NameSpaceImportNode namespaceNode = nameSpaceImport();
importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, importedDefaultBinding, namespaceNode);
importEntries = new ArrayList<>(2);
importEntries.add(defaultImport);
importEntries.add(ImportEntry.importStarAsNameSpaceFrom(namespaceNode.getBindingIdentifier().getName()));
} else if (type == LBRACE) {
NamedImportsNode namedImportsNode = namedImports();
importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, importedDefaultBinding, namedImportsNode);
importEntries = convert(namedImportsNode);
importEntries.add(0, defaultImport);
} else {
throw error(AbstractParser.message("expected.named.import"));
}
} else {
importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, importedDefaultBinding);
importEntries = Collections.singletonList(defaultImport);
}
} else {
throw error(AbstractParser.message("expected.import"));
}
FromNode fromNode = fromClause();
module.addImport(new ImportNode(importToken, Token.descPosition(importToken), finish, importClause, fromNode));
String moduleSpecifier = fromNode.getModuleSpecifier().getValue();
module.addModuleRequest(moduleSpecifier);
for (int i = 0; i < importEntries.size(); i++) {
module.addImportEntry(importEntries.get(i).withFrom(moduleSpecifier));
}
}
endOfLine();
}
private NameSpaceImportNode nameSpaceImport() {
final long startToken = token;
assert type == MUL;
next();
expect(AS);
IdentNode localNameSpace = bindingIdentifier(false, false, IMPORTED_BINDING_CONTEXT);
declareImportStarBinding(localNameSpace);
return new NameSpaceImportNode(startToken, Token.descPosition(startToken), finish, localNameSpace);
}
private NamedImportsNode namedImports() {
final long startToken = token;
assert type == LBRACE;
next();
ArrayList<ImportSpecifierNode> importSpecifiers = new ArrayList<>();
while (type != RBRACE) {
boolean bindingIdentifier = isBindingIdentifier();
long nameToken = token;
IdentNode importName = getIdentifierName();
if (type == AS) {
next();
IdentNode localName = bindingIdentifier(false, false, IMPORTED_BINDING_CONTEXT);
importSpecifiers.add(new ImportSpecifierNode(nameToken, Token.descPosition(nameToken), finish, localName, importName));
declareImportBinding(localName);
} else if (bindingIdentifier) {
verifyIdent(importName, false, false);
verifyStrictIdent(importName, IMPORTED_BINDING_CONTEXT);
importSpecifiers.add(new ImportSpecifierNode(nameToken, Token.descPosition(nameToken), finish, importName, null));
declareImportBinding(importName);
} else {
throw error(AbstractParser.message("expected.binding.identifier"), nameToken);
}
if (type == COMMARIGHT) {
next();
} else {
break;
}
}
expect(RBRACE);
return new NamedImportsNode(startToken, Token.descPosition(startToken), finish, optimizeList(importSpecifiers));
}
private FromNode fromClause() {
int fromStart = start;
long fromToken = token;
expect(FROM);
if (type == STRING || type == ESCSTRING) {
String moduleSpecifier = (String) getValue();
long specifierToken = token;
next();
LiteralNode<String> specifier = LiteralNode.newInstance(specifierToken, moduleSpecifier);
return new FromNode(fromToken, fromStart, finish, specifier);
} else {
throw error(expectMessage(STRING));
}
}
private void exportDeclaration(ParserContextModuleNode module) {
final long exportToken = token;
expect(EXPORT);
switch (type) {
case MUL: {
next();
IdentNode exportName = null;
if (type == AS && isES2020()) {
next();
exportName = getIdentifierName();
}
FromNode from = fromClause();
String moduleRequest = from.getModuleSpecifier().getValue();
module.addModuleRequest(moduleRequest);
module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, exportName, from));
endOfLine();
break;
}
case LBRACE: {
NamedExportsNode exportClause = namedExports();
FromNode from = null;
if (type == FROM) {
from = fromClause();
String moduleRequest = from.getModuleSpecifier().getValue();
module.addModuleRequest(moduleRequest);
}
module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, exportClause, from));
endOfLine();
break;
}
case DEFAULT: {
next();
Expression assignmentExpression;
IdentNode ident = null;
int lineNumber = line;
long rhsToken = token;
boolean hoistableDeclaration = false;
switch (type) {
case FUNCTION:
assignmentExpression = functionExpression(false, true);
hoistableDeclaration = true;
break;
case CLASS:
assignmentExpression = classDeclaration(false, isES2021() && ES2021_TOP_LEVEL_AWAIT, true);
ident = ((ClassNode) assignmentExpression).getIdent();
break;
default:
if (isAsync() && lookaheadIsAsyncFunction()) {
assignmentExpression = asyncFunctionExpression(false, true);
hoistableDeclaration = true;
break;
}
assignmentExpression = assignmentExpression(true, false, isES2021() && ES2021_TOP_LEVEL_AWAIT);
endOfLine();
break;
}
if (hoistableDeclaration) {
FunctionNode functionNode = (FunctionNode) assignmentExpression;
if (!functionNode.isAnonymous()) {
ident = functionNode.getIdent();
assignmentExpression = functionNode.setFlag(null, FunctionNode.IS_DECLARED);
}
}
if (ident == null) {
ident = new IdentNode(Token.recast(rhsToken, IDENT), finish, Module.DEFAULT_EXPORT_BINDING_NAME);
if (isAnonymousFunctionDefinition(assignmentExpression)) {
assignmentExpression = setAnonymousFunctionName(assignmentExpression, Module.DEFAULT_NAME);
}
}
VarNode varNode = new VarNode(lineNumber, Token.recast(rhsToken, hoistableDeclaration ? VAR : LET), finish, ident, assignmentExpression,
(hoistableDeclaration ? 0 : VarNode.IS_LET) | VarNode.IS_EXPORT);
declareVar(lc.getCurrentScope(), varNode);
if (hoistableDeclaration) {
functionDeclarations.add(varNode);
} else {
lc.appendStatementToCurrentNode(varNode);
}
module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, ident, assignmentExpression, true));
break;
}
case VAR:
case LET:
case CONST: {
List<Statement> statements = lc.getCurrentBlock().getStatements();
int previousEnd = statements.size();
variableStatement(type);
for (Statement statement : statements.subList(previousEnd, statements.size())) {
if (statement instanceof VarNode) {
VarNode varNode = (VarNode) statement;
module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, varNode.getName(), varNode));
}
}
break;
}
case CLASS: {
ClassNode classDeclaration = classDeclaration(false, isES2021() && ES2021_TOP_LEVEL_AWAIT, false);
module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, classDeclaration.getIdent(), classDeclaration, false));
break;
}
case FUNCTION: {
FunctionNode functionDeclaration = (FunctionNode) functionExpression(true, true);
module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, functionDeclaration.getIdent(), functionDeclaration, false));
break;
}
default:
if (isAsync() && lookaheadIsAsyncFunction()) {
FunctionNode functionDeclaration = (FunctionNode) asyncFunctionExpression(true, true);
module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, functionDeclaration.getIdent(), functionDeclaration, false));
break;
}
throw error(AbstractParser.message("invalid.export"), token);
}
}
private NamedExportsNode namedExports() {
final long startToken = token;
assert type == LBRACE;
next();
ArrayList<ExportSpecifierNode> exports = new ArrayList<>();
long reservedWordToken = 0L;
while (type != RBRACE) {
long nameToken = token;
TokenType nameType = type;
IdentNode localName = getIdentifierName();
if (isReservedWord(nameType) || (isEscapedIdent(localName) && (isReservedWordSequence(localName.getName()) || isFutureStrictName(localName)))) {
if (reservedWordToken == 0L) {
reservedWordToken = nameToken;
}
}
if (type == AS) {
next();
IdentNode exportName = getIdentifierName();
exports.add(new ExportSpecifierNode(nameToken, Token.descPosition(nameToken), finish, localName, exportName));
} else {
exports.add(new ExportSpecifierNode(nameToken, Token.descPosition(nameToken), finish, localName, null));
}
if (type == COMMARIGHT) {
next();
} else {
break;
}
}
expect(RBRACE);
if (reservedWordToken != 0L && type != FROM) {
throw error(expectMessage(IDENT, reservedWordToken), reservedWordToken);
}
return new NamedExportsNode(startToken, Token.descPosition(startToken), finish, optimizeList(exports));
}
private static boolean isReservedWord(TokenType type) {
return type.getKind() == TokenKind.KEYWORD || type.getKind() == TokenKind.FUTURE || type.getKind() == TokenKind.FUTURESTRICT;
}
@Override
public String toString() {
return "'JavaScript Parsing'";
}
private void markEval() {
final Iterator<ParserContextFunctionNode> iter = lc.getFunctions();
boolean flaggedCurrentFn = false;
boolean flagArrowParentFn = false;
while (iter.hasNext()) {
final ParserContextFunctionNode fn = iter.next();
if (!flaggedCurrentFn) {
flaggedCurrentFn = true;
fn.setFlag(FunctionNode.HAS_EVAL);
if (fn.isArrow()) {
flagArrowParentFn = true;
}
} else {
fn.setFlag(FunctionNode.HAS_NESTED_EVAL);
if (flagArrowParentFn) {
fn.setFlag(FunctionNode.HAS_ARROW_EVAL);
if (!fn.isArrow()) {
flagArrowParentFn = false;
}
}
}
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 void markSuperCall() {
final Iterator<ParserContextFunctionNode> iter = lc.getFunctions();
while (iter.hasNext()) {
final ParserContextFunctionNode fn = iter.next();
if (!fn.isArrow()) {
if (!fn.isProgram()) {
assert fn.isDerivedConstructor();
fn.setFlag(FunctionNode.HAS_DIRECT_SUPER);
}
break;
}
}
}
private void markThis() {
final Iterator<ParserContextFunctionNode> iter = lc.getFunctions();
while (iter.hasNext()) {
final ParserContextFunctionNode fn = iter.next();
fn.setFlag(FunctionNode.USES_THIS);
if (!fn.isArrow()) {
break;
}
}
}
private void markNewTarget() {
if (!lc.getCurrentScope().inFunction()) {
throw error(AbstractParser.message("new.target.in.function"), token);
}
final Iterator<ParserContextFunctionNode> iter = lc.getFunctions();
while (iter.hasNext()) {
final ParserContextFunctionNode fn = iter.next();
if (!fn.isArrow()) {
if (!fn.isProgram()) {
fn.setFlag(FunctionNode.USES_NEW_TARGET);
}
break;
}
}
}
private static boolean markApplyArgumentsCall(final ParserContext lc, List<Expression> arguments) {
assert arguments.size() == 2 && arguments.get(1) instanceof IdentNode && ((IdentNode) arguments.get(1)).isArguments();
ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
if (!currentFunction.isArrow()) {
currentFunction.setFlag(FunctionNode.HAS_APPLY_ARGUMENTS_CALL);
arguments.set(1, ((IdentNode) arguments.get(1)).setIsApplyArguments());
return true;
}
return false;
}
private boolean inGeneratorFunction() {
if (!ES6_GENERATOR_FUNCTION) {
return false;
}
ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
return currentFunction != null && currentFunction.isGenerator();
}
private boolean inAsyncFunction() {
if (!ES8_ASYNC_FUNCTION) {
return false;
}
ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
if (currentFunction == null) {
return false;
}
boolean isAsync = currentFunction.isAsync();
if (isAsync) {
return true;
} else if (ES2021_TOP_LEVEL_AWAIT && isES2021()) {
return isModule && currentFunction.isModule();
}
return isAsync;
}
private boolean isAwait() {
return ES8_ASYNC_FUNCTION && isES2017() && type == AWAIT;
}
private boolean isAsync() {
return ES8_ASYNC_FUNCTION && isES2017() && type == ASYNC;
}
private boolean lookaheadIsAsyncArrowParameterListStart() {
assert isAsync();
int i = 1;
for (;;) {
TokenType t = T(k + i++);
if (t == LPAREN) {
break;
} else if (t == IDENT) {
break;
} else if (t.isContextualKeyword()) {
break;
} else if (t == COMMENT) {
continue;
} else {
return false;
}
}
return true;
}
private boolean lookaheadIsAsyncFunction() {
assert isAsync();
for (int i = 1;; i++) {
long currentToken = getToken(k + i);
TokenType t = Token.descType(currentToken);
switch (t) {
case COMMENT:
continue;
case FUNCTION:
return true;
default:
return false;
}
}
}
private boolean lookaheadIsAsyncMethod(boolean allowPrivate) {
assert isAsync();
for (int i = 1;; i++) {
long currentToken = getToken(k + i);
TokenType t = Token.descType(currentToken);
if (t == COMMENT) {
continue;
} else {
return isPropertyName(currentToken) || t == MUL || (allowPrivate && t == TokenType.PRIVATE_IDENT);
}
}
}
public Expression parseExpression() {
try {
prepareLexer(0, source.getLength());
scanFirstToken();
return expression();
} catch (final Exception e) {
handleParseException(e);
return null;
}
}
private static List<ImportEntry> convert(NamedImportsNode namedImportsNode) {
List<ImportEntry> importEntries = new ArrayList<>(namedImportsNode.getImportSpecifiers().size());
for (ImportSpecifierNode s : namedImportsNode.getImportSpecifiers()) {
if (s.getIdentifier() != null) {
importEntries.add(ImportEntry.importSpecifier(s.getIdentifier().getName(), s.getBindingIdentifier().getName()));
} else {
importEntries.add(ImportEntry.importSpecifier(s.getBindingIdentifier().getName()));
}
}
return importEntries;
}
}