/**
* Copyright (c) 2012-2016 André Bargull
* Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms.
*
* <https://github.com/anba/es6draft>
*/
package com.github.anba.es6draft.parser;
import static com.github.anba.es6draft.semantics.StaticSemantics.*;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import java.util.*;
import java.util.function.BiFunction;
import com.github.anba.es6draft.ast.*;
import com.github.anba.es6draft.ast.AbruptNode.Abrupt;
import com.github.anba.es6draft.ast.GeneratorDefinition.GeneratorKind;
import com.github.anba.es6draft.ast.MethodDefinition.MethodAllocation;
import com.github.anba.es6draft.ast.MethodDefinition.MethodType;
import com.github.anba.es6draft.ast.scope.BlockScope;
import com.github.anba.es6draft.ast.scope.FunctionScope;
import com.github.anba.es6draft.ast.scope.ModuleScope;
import com.github.anba.es6draft.ast.scope.Name;
import com.github.anba.es6draft.ast.scope.Scope;
import com.github.anba.es6draft.ast.scope.ScriptScope;
import com.github.anba.es6draft.ast.scope.TopLevelScope;
import com.github.anba.es6draft.ast.scope.WithScope;
import com.github.anba.es6draft.parser.ParserException.ExceptionType;
import com.github.anba.es6draft.regexp.RegExpParser;
import com.github.anba.es6draft.runtime.internal.CompatibilityOption;
import com.github.anba.es6draft.runtime.internal.InlineArrayList;
import com.github.anba.es6draft.runtime.internal.Messages;
import com.github.anba.es6draft.runtime.internal.RuntimeContext;
import com.github.anba.es6draft.runtime.internal.Source;
/**
* Parser for ECMAScript6 source code
* <ul>
* <li>12 ECMAScript Language: Expressions
* <li>13 ECMAScript Language: Statements and Declarations
* <li>14 ECMAScript Language: Functions and Classes
* <li>15 ECMAScript Language: Scripts and Modules
* </ul>
*/
public final class Parser {
private static final boolean DEBUG = false;
private static final int MAX_ARGUMENTS = 0x4000;
private static final int NATIVE_CALL_MAX_ARGUMENTS = 250;
private static final String DEFAULT_EXPORT_BINDING_NAME = "*default*";
private static final String DEFAULT_EXPORT_NAME = "default";
private static final List<Binding> NO_INHERITED_BINDING = Collections.emptyList();
private static final List<Expression> NO_DECORATORS = Collections.emptyList();
private static final Set<String> EMPTY_LABEL_SET = Collections.emptySet();
private final Source source;
private final EnumSet<CompatibilityOption> options;
private final EnumSet<Option> parserOptions;
private TokenStream ts;
private ParseContext context;
private boolean moduleCode;
private enum StrictMode {
Unknown, Strict, NonStrict
}
private enum StatementType {
Iteration, Breakable, Statement
}
private enum ContextKind {
Script, Module, Function, Method, Generator, GeneratorMethod, AsyncFunction, AsyncMethod, AsyncGenerator,
AsyncGeneratorMethod, ArrowFunction, GeneratorComprehension, AsyncArrowFunction;
final boolean isScript() {
return this == Script;
}
final boolean isModule() {
return this == Module;
}
final boolean isFunction() {
switch (this) {
case ArrowFunction:
case AsyncArrowFunction:
case AsyncFunction:
case AsyncGenerator:
case AsyncGeneratorMethod:
case AsyncMethod:
case Function:
case Generator:
case GeneratorComprehension:
case GeneratorMethod:
case Method:
return true;
default:
return false;
}
}
final boolean isGenerator() {
switch (this) {
case AsyncGenerator:
case AsyncGeneratorMethod:
case Generator:
case GeneratorMethod:
return true;
default:
return false;
}
}
final boolean isAsync() {
switch (this) {
case AsyncArrowFunction:
case AsyncFunction:
case AsyncGenerator:
case AsyncGeneratorMethod:
case AsyncMethod:
return true;
default:
return false;
}
}
final boolean isLexical() {
switch (this) {
case ArrowFunction:
case AsyncArrowFunction:
case GeneratorComprehension:
return true;
default:
return false;
}
}
public boolean isMethod() {
switch (this) {
case AsyncGeneratorMethod:
case AsyncMethod:
case GeneratorMethod:
case Method:
return true;
default:
return false;
}
}
}
private static final class ParseContext {
final ParseContext parent;
final ContextKind kind;
boolean yieldAllowed = false;
boolean awaitAllowed = false;
boolean returnAllowed = false;
boolean legacyGenerator = false;
boolean explicitStrict = false;
boolean isDerivedClassConstructor = false;
boolean yieldOrAwaitExpression = false;
StrictMode strictMode = StrictMode.Unknown;
ParserException strictError;
InlineArrayList<FunctionNode> deferred;
ArrayDeque<ObjectLiteral> objectLiterals;
ScopeWithNames illegalLexNames = new ScopeWithNames(null, null);
HashMap<String, LabelContext> labelSet;
LabelContext labels;
ScopeContext scopeContext;
final TopContext topContext;
final ScriptContext scriptContext;
final ModuleContext modContext;
final FunctionContext funContext;
ParseContext() {
this.parent = null;
this.kind = null;
this.topContext = null;
this.scriptContext = null;
this.modContext = null;
this.funContext = null;
}
ParseContext(ParseContext parent, ContextKind kind) {
this.parent = parent;
this.kind = kind;
if (kind.isScript()) {
this.scriptContext = new ScriptContext(parent.scopeContext);
this.modContext = null;
this.funContext = null;
this.topContext = scriptContext;
} else if (kind.isModule()) {
this.scriptContext = null;
this.modContext = new ModuleContext(parent.scopeContext);
this.funContext = null;
this.topContext = modContext;
} else {
assert kind.isFunction();
this.scriptContext = null;
this.modContext = null;
this.funContext = new FunctionContext(parent.scopeContext, kind.isLexical());
this.topContext = funContext;
}
this.scopeContext = topContext;
if (parent.strictMode == StrictMode.Strict) {
this.strictMode = parent.strictMode;
}
}
ParseContext findSuperContext() {
for (ParseContext cx = this;; cx = cx.parent) {
switch (cx.kind) {
case ArrowFunction:
case AsyncArrowFunction:
case GeneratorComprehension:
continue;
default:
return cx;
}
}
}
void setHasEval() {
if (funContext != null) {
funContext.directEval = true;
}
scopeContext.setHasEval();
}
void setNeedsSuperBinding() {
if (funContext != null) {
funContext.superReference = true;
}
}
int countLiterals() {
return objectLiterals != null ? objectLiterals.size() : 0;
}
void addLiteral(ObjectLiteral object) {
if (objectLiterals == null) {
objectLiterals = new ArrayDeque<>(4);
}
objectLiterals.push(object);
}
void removeLiteral(ObjectLiteral object) {
boolean removed = objectLiterals.removeFirstOccurrence(object);
assert removed;
}
boolean assertLiteralsUnchecked(int expected) {
int count = countLiterals();
assert count == expected : String.format(
"%d unchecked object literals, but expected %d", count, expected);
return count == expected;
}
ScopeWithNames setIllegalNames(NameSet names) {
ScopeWithNames previous = illegalLexNames;
illegalLexNames = new ScopeWithNames(scopeContext, names);
return previous;
}
void restoreIllegalNames(ScopeWithNames illegalLexNames) {
this.illegalLexNames = illegalLexNames;
}
boolean isIllegalName(Name name) {
return illegalLexNames.scope == scopeContext && illegalLexNames.names.contains(name);
}
}
private static final class ScopeWithNames {
final ScopeContext scope;
final NameSet names;
ScopeWithNames(ScopeContext scope, NameSet names) {
this.scope = scope;
this.names = names;
}
}
private static final class NameSet extends AbstractSet<Name> implements Set<Name> {
private final LinkedHashMap<String, Name> map;
NameSet() {
map = new LinkedHashMap<>();
}
NameSet(NameSet set) {
map = new LinkedHashMap<>(set.map);
}
NameSet(Collection<Name> c) {
map = new LinkedHashMap<>(Math.max((int) (c.size() / 0.75f) + 1, 16));
addAll(c);
}
void addAll(NameSet set) {
map.putAll(set.map);
}
boolean remove(Name o) {
return map.remove(o.getIdentifier()) != null;
}
boolean contains(Name o) {
return map.containsKey(o.getIdentifier());
}
Name get(Name o) {
return map.get(o.getIdentifier());
}
@Override
public Iterator<Name> iterator() {
return map.values().iterator();
}
@Override
public int size() {
return map.size();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public boolean add(Name e) {
return map.put(e.getIdentifier(), e) == null;
}
@Override
public void clear() {
map.clear();
}
@Override
public boolean contains(Object o) {
if (o == null || o.getClass() != Name.class) {
return false;
}
return map.containsKey(((Name) o).getIdentifier());
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
}
private static final class FunctionContext extends TopContext implements FunctionScope {
FunctionNode node;
ScopeContext variableScope = this;
ScopeContext lexicalScope = this;
Name arguments;
NameSet parameterNames;
NameSet blockFunctionNames;
final boolean isLexical;
boolean needsArguments;
boolean directEval;
boolean superReference;
FunctionContext(ScopeContext enclosing, boolean isLexical) {
super(enclosing);
this.isLexical = isLexical;
}
void setBlockFunctions(InlineArrayList<FunctionDeclaration> blockFunctions) {
this.blockFunctions = blockFunctions;
this.blockFunctionNames = new NameSet();
for (FunctionDeclaration f : blockFunctions) {
Name fname = f.getIdentifier().getName();
Name name = variableScope.getDeclaredName(fname);
if (name == null) {
// Create a new name binding, need to use clone() to create a distinct binding.
name = fname.clone();
}
blockFunctionNames.add(name);
}
}
void setParameterNames(List<Name> names) {
this.parameterNames = new NameSet(names);
}
void needsArguments(boolean lookupByName) {
this.needsArguments = true;
}
void setNode(FunctionNode node) {
this.node = node;
ScopeContext varScope = variableScope, lexScope = lexicalScope;
assert (varScope != this) == node.getParameters().containsExpression();
if (lexScope != this) {
// Copy all lexically declared names into the function scope.
assert lexDeclaredNames == null && lexScopedDeclarations == null;
if (lexScope.lexDeclaredNames != null) {
lexDeclaredNames = new NameSet(lexScope.lexDeclaredNames);
}
lexScopedDeclarations = lexScope.lexScopedDeclarations;
lexScope.lexScopedDeclarations = null;
((FunctionBodyContext) lexScope).node = node;
}
if (varScope != this) {
// Copy all variable declared names into varScope.
assert lexScope != this;
if (varDeclaredNames != null) {
if (varScope.lexDeclaredNames == null) {
varScope.lexDeclaredNames = new NameSet(varDeclaredNames);
} else {
assert varScope == lexScope;
varScope.lexDeclaredNames.addAll(varDeclaredNames);
}
}
((FunctionBodyContext) varScope).node = node;
}
assert varScope == this || varScope.lexScopedDeclarations == null;
assert lexScope == this || lexScope.lexScopedDeclarations == null;
if (needsArguments()) {
if (isLexical) {
propagateNeedsArguments();
} else {
setImplicitArguments();
}
}
}
private void setImplicitArguments() {
assert node.getThisMode() != FunctionNode.ThisMode.Lexical;
Name arguments = new Name("arguments");
if (parameterNames().contains(arguments)) {
return;
}
if (!node.getParameters().containsExpression()) {
if (lexicallyDeclaredNames().contains(arguments)) {
return;
}
if (varDeclaredNames().contains(arguments)) {
for (StatementListItem item : node.getStatements()) {
if (item instanceof HoistableDeclaration) {
if (((HoistableDeclaration) item).getName().equals(arguments)) {
return;
}
}
}
}
}
this.arguments = arguments;
}
private void propagateNeedsArguments() {
for (TopContext t = this; t instanceof FunctionContext; t = t.enclosing.top) {
FunctionContext fc = (FunctionContext) t;
if (!fc.isLexical) {
fc.needsArguments(directEval);
break;
}
}
}
Name blockFunctionName(Name name) {
if (blockFunctionNames != null) {
return blockFunctionNames.get(name);
}
return null;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
sb.append("\tparams: ").append(parameterNames != null ? parameterNames : "<null>");
return sb.toString();
}
@Override
public FunctionNode getNode() {
return node;
}
@Override
public Scope variableScope() {
return variableScope;
}
@Override
public Scope lexicalScope() {
return lexicalScope;
}
@Override
public Set<Name> parameterNames() {
return parameterNames;
}
@Override
public Name arguments() {
return arguments;
}
@Override
public Set<Name> blockFunctionNames() {
return emptyIfNull(blockFunctionNames);
}
@Override
public boolean isDynamic() {
return directEval && !IsStrict(node);
}
@Override
public boolean hasEval() {
return directEval;
}
@Override
public boolean hasSuperReference() {
return superReference;
}
@Override
public boolean needsArguments() {
return needsArguments || directEval;
}
@Override
protected Name getDeclaredName(Name name) {
if (arguments != null && arguments.equals(name)) {
return arguments;
}
Name parameter = parameterNames.get(name);
if (parameter != null) {
return parameter;
}
if (variableScope == this && varDeclaredNames != null) {
Name varName = varDeclaredNames.get(name);
if (varName != null) {
return varName;
}
}
if (variableScope == this) {
Name varName = blockFunctionName(name);
if (varName != null) {
return varName;
}
}
if (lexicalScope == this && lexDeclaredNames != null) {
Name lexName = lexDeclaredNames.get(name);
if (lexName != null) {
return lexName;
}
}
return null;
}
@Override
public List<FunctionDeclaration> blockFunctions() {
return emptyIfNull(blockFunctions);
}
}
private static final class FunctionBodyContext extends ScopeContext {
FunctionNode node;
FunctionBodyContext(ScopeContext parent) {
super(parent);
}
private FunctionContext functionContext() {
return (FunctionContext) node.getScope();
}
private boolean isVarScope() {
// NB: (node == null) handles the case when isVarScope() is called in FunctionContext#setBlockFunctions().
return node == null || functionContext().variableScope == this;
}
private Name blockFunctionName(Name name) {
// See the comment in isVarScope() for (node == null).
assert node == null || functionContext() == parent;
return ((FunctionContext) parent).blockFunctionName(name);
}
@Override
public FunctionNode getNode() {
return node;
}
@Override
public boolean isDynamic() {
return isVarScope() && functionContext().isDynamic();
}
@Override
public boolean isPresent() {
return super.isPresent() || isVarScope();
}
@Override
protected Name getDeclaredName(Name name) {
Name declaredName = super.getDeclaredName(name);
if (declaredName == null && isVarScope()) {
return blockFunctionName(name);
}
return declaredName;
}
}
private static final class ScriptContext extends TopContext implements ScriptScope {
NameSet varForOfDeclaredNames;
Script node;
ScriptContext(ScopeContext enclosing) {
super(enclosing);
}
void addVarForOfDeclaredName(Name name) {
if (varForOfDeclaredNames == null) {
varForOfDeclaredNames = new NameSet();
}
varForOfDeclaredNames.add(name);
}
void setBlockFunctions(InlineArrayList<FunctionDeclaration> blockFunctions) {
this.blockFunctions = blockFunctions;
}
@Override
protected Name getDeclaredName(Name name) {
if (node.isEvalScript() && node.isStrict() && varDeclaredNames != null) {
Name varName = varDeclaredNames.get(name);
if (varName != null) {
return varName;
}
}
return super.getDeclaredName(name);
}
@Override
public Script getNode() {
return node;
}
@Override
public Set<Name> varForOfDeclaredNames() {
return emptyIfNull(varForOfDeclaredNames);
}
@Override
public List<FunctionDeclaration> blockFunctions() {
return emptyIfNull(blockFunctions);
}
}
private static final class ModuleContext extends TopContext implements ModuleScope {
HashSet<String> exportNames = new HashSet<>();
HashSet<Name> importBindings = new HashSet<>();
LinkedHashMap<Name, Long> undeclaredExportBindings = new LinkedHashMap<>();
Module node;
ModuleContext(ScopeContext enclosing) {
super(enclosing);
}
void addUndeclaredExportBinding(long position, Name name) {
if (!undeclaredExportBindings.containsKey(name)) {
undeclaredExportBindings.put(name, position);
}
}
boolean addExportName(String exportName) {
return exportNames.add(exportName);
}
boolean addImportBinding(Name name) {
return importBindings.add(name);
}
@Override
protected Name getDeclaredName(Name name) {
if (varDeclaredNames != null) {
Name varName = varDeclaredNames.get(name);
if (varName != null) {
return varName;
}
}
return super.getDeclaredName(name);
}
@Override
public Module getNode() {
return node;
}
@Override
public void addImplicitBinding(Name name) {
addLexDeclaredName(name);
}
}
private static abstract class TopContext extends ScopeContext implements TopLevelScope {
final ScopeContext enclosing;
InlineArrayList<StatementListItem> varScopedDeclarations;
InlineArrayList<FunctionDeclaration> blockFunctions;
TopContext(ScopeContext enclosing) {
super();
this.enclosing = enclosing;
}
final void addVarScopedDeclaration(StatementListItem decl) {
if (varScopedDeclarations == null) {
varScopedDeclarations = newList();
}
varScopedDeclarations.add(decl);
}
final void addBlockFunction(FunctionDeclaration function) {
if (blockFunctions == null) {
blockFunctions = newList();
}
blockFunctions.add(function);
}
@Override
public final ScopeContext getEnclosingScope() {
return enclosing;
}
@Override
public final Set<Name> lexicallyDeclaredNames() {
return emptyIfNull(lexDeclaredNames);
}
@Override
public final List<Declaration> lexicallyScopedDeclarations() {
return emptyIfNull(lexScopedDeclarations);
}
@Override
public final Set<Name> varDeclaredNames() {
return emptyIfNull(varDeclaredNames);
}
@Override
public final List<StatementListItem> varScopedDeclarations() {
return emptyIfNull(varScopedDeclarations);
}
@Override
public final boolean isPresent() {
return true;
}
}
private static final class BlockContext extends ScopeContext implements BlockScope {
ScopedNode node;
BlockContext(ScopeContext parent) {
super(parent);
}
@Override
public ScopedNode getNode() {
return node;
}
@Override
public Set<Name> lexicallyDeclaredNames() {
return emptyIfNull(lexDeclaredNames);
}
@Override
public List<Declaration> lexicallyScopedDeclarations() {
return emptyIfNull(lexScopedDeclarations);
}
}
private static final class CatchContext extends ScopeContext implements BlockScope {
ScopedNode node;
CatchContext(ScopeContext parent) {
super(parent);
}
@Override
public ScopedNode getNode() {
return node;
}
@Override
public boolean isPresent() {
return true;
}
@Override
public Set<Name> lexicallyDeclaredNames() {
return emptyIfNull(lexDeclaredNames);
}
@Override
public List<Declaration> lexicallyScopedDeclarations() {
return emptyIfNull(lexScopedDeclarations);
}
}
private static final class WithContext extends ScopeContext implements WithScope {
WithStatement node;
WithContext(ScopeContext parent) {
super(parent);
}
@Override
public WithStatement getNode() {
return node;
}
@Override
public boolean isPresent() {
return true;
}
@Override
public boolean isDynamic() {
return true;
}
}
private static final class FormalParameterContext extends ScopeContext {
FormalParameter node;
FormalParameterContext(ScopeContext parent) {
super(parent);
}
@Override
public FormalParameter getNode() {
return node;
}
@Override
public boolean isPresent() {
return hasDirectEval();
}
}
private static abstract class ScopeContext implements Scope {
final ScopeContext parent;
final TopContext top;
private boolean directEval;
NameSet varDeclaredNames;
NameSet lexDeclaredNames;
InlineArrayList<Declaration> lexScopedDeclarations;
ScopeContext() {
this.parent = null;
this.top = (TopContext) this;
}
ScopeContext(ScopeContext parent) {
this.parent = parent;
this.top = parent.top;
}
protected Name getDeclaredName(Name name) {
return lexDeclaredNames != null ? lexDeclaredNames.get(name) : null;
}
@Override
public final Scope getParent() {
return parent;
}
@Override
public final TopLevelScope getTop() {
return top;
}
@Override
public final boolean isDeclared(Name name) {
return getDeclaredName(name) != null;
}
@Override
public final Name resolveName(Name name, boolean lookupByName) {
Name declaredName = getDeclaredName(name);
declaredName.resolve(this, lookupByName);
return declaredName;
}
@Override
public boolean isPresent() {
return lexDeclaredNames != null;
}
@Override
public boolean isDynamic() {
return false;
}
@Override
public final Iterator<Scope> iterator() {
return new ScopeIterator(this);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName()).append('@').append(depth());
sb.append("\tvar: ").append(varDeclaredNames != null ? varDeclaredNames : "<null>");
sb.append("\tlex: ").append(lexDeclaredNames != null ? lexDeclaredNames : "<null>");
return sb.toString();
}
private int depth() {
int depth = 0;
for (ScopeContext p = this.parent; p != null; p = p.parent) {
depth += 1;
}
return depth;
}
final void setHasEval() {
for (ScopeContext scope = this; scope != null && !scope.directEval;) {
scope.directEval = true;
ScopeContext parent = scope.parent;
if (parent == null) {
parent = ((TopContext) scope).enclosing;
}
scope = parent;
}
}
final boolean hasDirectEval() {
return directEval;
}
final boolean allowVarDeclaredName(Name name) {
return lexDeclaredNames == null || !lexDeclaredNames.contains(name);
}
final void addVarDeclaredNames(NameSet names) {
if (varDeclaredNames == null) {
varDeclaredNames = names;
} else {
varDeclaredNames.addAll(names);
}
}
final boolean addVarDeclaredName(Name name) {
if (varDeclaredNames == null) {
varDeclaredNames = new NameSet();
}
varDeclaredNames.add(name);
return lexDeclaredNames == null || !lexDeclaredNames.contains(name);
}
final boolean addLexDeclaredName(Name name) {
if (lexDeclaredNames == null) {
lexDeclaredNames = new NameSet();
}
return lexDeclaredNames.add(name)
&& (varDeclaredNames == null || !varDeclaredNames.contains(name));
}
final void addLexScopedDeclaration(Declaration decl) {
if (lexScopedDeclarations == null) {
lexScopedDeclarations = newList();
}
lexScopedDeclarations.add(decl);
}
protected static final <T> Set<T> emptyIfNull(Set<T> list) {
return list != null ? list : Collections.<T> emptySet();
}
protected static final <T> List<T> emptyIfNull(List<T> list) {
return list != null ? list : Collections.<T> emptyList();
}
}
private static final class ScopeIterator implements Iterator<Scope> {
private ScopeContext scope;
ScopeIterator(ScopeContext scope) {
this.scope = scope;
}
@Override
public boolean hasNext() {
return scope != null;
}
@Override
public Scope next() {
ScopeContext s = scope;
if (s == null) {
throw new NoSuchElementException();
}
ScopeContext p = s.parent;
if (p == null) {
p = ((TopContext) s).enclosing;
}
scope = p;
return s;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
private static final class LabelContext {
final LabelContext parent;
final StatementType type;
final Set<String> labelSet;
final EnumSet<Abrupt> abrupts = EnumSet.noneOf(Abrupt.class);
LabelContext(LabelContext parent, StatementType type, Set<String> labelSet) {
this.parent = parent;
this.type = type;
this.labelSet = labelSet;
}
void mark(Abrupt abrupt) {
abrupts.add(abrupt);
}
}
@SuppressWarnings("serial")
private static final class RetryGenerator extends RuntimeException {
public RetryGenerator() {
super("RetryGenerator", null, false, false);
}
}
public enum Option {
/**
* Strictness for source code.
*/
Strict,
/**
* Source code is not global code.
*/
FunctionCode,
/**
* Source code does not receive global object as this-binding.
*/
FunctionThis,
/**
* Source code is not in global scope.
*/
LocalScope,
/**
* Source code is direct eval code.
*/
DirectEval,
/**
* Source code is eval code.
*/
EvalScript,
/**
* Source code is nested in with-statement context.
*/
EnclosedByWithStatement,
/**
* Source code is nested in lexical declaration context.
*/
EnclosedByLexicalDeclaration,
/**
* Source is JSR-223 scripting.
*/
Scripting,
/**
* Allow native call syntax.
*/
NativeCall,
/**
* Parse functions as native.
*/
NativeFunction,
}
public Parser(RuntimeContext context, Source source) {
this(source, context.getOptions(), context.getParserOptions());
}
public Parser(Source source, EnumSet<CompatibilityOption> options, EnumSet<Option> parserOptions) {
this.source = source;
this.options = EnumSet.copyOf(options);
this.parserOptions = EnumSet.copyOf(parserOptions);
context = new ParseContext();
context.strictMode = this.parserOptions.contains(Option.Strict) ? StrictMode.Strict
: StrictMode.NonStrict;
// eval-script option must be set if one of the following options is used
assert !(parserOptions.contains(Option.FunctionCode)
|| parserOptions.contains(Option.FunctionThis)
|| parserOptions.contains(Option.LocalScope)
|| parserOptions.contains(Option.DirectEval)
|| parserOptions.contains(Option.EnclosedByWithStatement) || parserOptions
.contains(Option.EnclosedByLexicalDeclaration))
|| (parserOptions.contains(Option.EvalScript)) : "Illegal option: " + parserOptions;
// eval-script and scripting are mutually exclusive
assert !(parserOptions.contains(Option.Scripting) && parserOptions
.contains(Option.EvalScript)) : "Illegal option: " + parserOptions;
}
String getSourceName() {
return source.getName();
}
int getSourceLine() {
return source.getLine();
}
boolean isEnabled(CompatibilityOption option) {
return options.contains(option);
}
boolean isEnabled(Option option) {
return parserOptions.contains(option);
}
boolean isModule() {
return moduleCode;
}
private ParseContext newContext(ContextKind kind) {
return context = new ParseContext(context, kind);
}
private ParseContext restoreContext() {
if (context.parent.strictError == null) {
context.parent.strictError = context.strictError;
}
return context = context.parent;
}
private WithContext enterWithContext() {
return enterScopeContext(new WithContext(context.scopeContext));
}
private ScopeContext exitWithContext() {
return exitScopeContext();
}
private BlockContext enterBlockContext() {
return enterScopeContext(new BlockContext(context.scopeContext));
}
private BlockContext enterBlockContext(Binding binding) {
BlockContext cx = enterBlockContext();
addLexDeclaredName(binding);
return cx;
}
private BlockContext enterBlockContext(List<Binding> bindings) {
BlockContext cx = enterBlockContext();
addLexDeclaredNames(bindings);
return cx;
}
private ScopeContext exitBlockContext() {
return exitScopeContext();
}
private CatchContext enterCatchContext() {
return enterScopeContext(new CatchContext(context.scopeContext));
}
private ScopeContext exitCatchContext() {
return exitScopeContext();
}
private FunctionBodyContext enterFunctionBodyContext() {
return enterScopeContext(new FunctionBodyContext(context.scopeContext));
}
private ScopeContext exitFunctionBodyContext() {
return exitScopeContext();
}
private FormalParameterContext enterFormalParameterContext() {
return enterScopeContext(new FormalParameterContext(context.scopeContext));
}
private ScopeContext exitFormalParameterContext() {
return exitScopeContext();
}
private <SCOPE extends ScopeContext> SCOPE enterScopeContext(SCOPE scope) {
context.scopeContext = scope;
return scope;
}
private ScopeContext exitScopeContext() {
ScopeContext scope = context.scopeContext;
ScopeContext parent = scope.parent;
assert parent != null : "exitScopeContext() on top-level";
NameSet varDeclaredNames = scope.varDeclaredNames;
if (varDeclaredNames != null) {
parent.addVarDeclaredNames(varDeclaredNames);
scope.varDeclaredNames = null;
}
return context.scopeContext = parent;
}
private void addFunctionDeclaration(FunctionDeclaration decl, boolean isNamedDefault) {
addDeclaration(decl, isNamedDefault);
if (isBlockScopedFunction()) {
context.parent.topContext.addBlockFunction(decl);
}
}
private void addDeclaration(HoistableDeclaration decl, boolean isNamedDefault) {
ParseContext parentContext = context.parent;
ScopeContext parentScope = parentContext.scopeContext;
if (parentContext.kind.isScript() && parentScope == parentContext.scriptContext) {
// top-level function declaration in script context
addVarDeclaredName(decl, parentContext, BoundName(decl));
parentContext.scriptContext.addVarScopedDeclaration(decl);
} else if (parentContext.kind.isFunction()
&& parentScope == parentContext.funContext.lexicalScope) {
// top-level function declaration in function context
addVarDeclaredName(decl, parentContext, BoundName(decl));
parentContext.funContext.addVarScopedDeclaration(decl);
} else {
// lexical-scoped function declaration in module/block context
addLexDeclaredName(decl, parentContext, BoundName(decl));
if (isNamedDefault) {
// TODO: Better error message
addLexDeclaredName(decl, parentContext, new Name(DEFAULT_EXPORT_BINDING_NAME));
}
parentScope.addLexScopedDeclaration(decl);
}
}
private boolean isBlockScopedFunction() {
ParseContext parentContext = context.parent;
if (parentContext.strictMode == StrictMode.Strict || !isEnabled(CompatibilityOption.BlockFunctionDeclaration)) {
return false;
}
if (parentContext.kind.isFunction()) {
return parentContext.funContext.lexicalScope != parentContext.scopeContext;
}
assert parentContext.kind.isScript();
return parentContext.scriptContext != parentContext.scopeContext;
}
private void addDeclaration(ClassDeclaration decl, boolean isNamedDefault) {
addLexDeclaredName(decl, context, BoundName(decl));
if (isNamedDefault) {
// TODO: Better error message
addLexDeclaredName(decl, context, new Name(DEFAULT_EXPORT_BINDING_NAME));
}
context.scopeContext.addLexScopedDeclaration(decl);
}
private void addDeclaration(ExportDefaultExpression defaultExpression) {
assert context.scopeContext == context.modContext : "not in module scope";
// TODO: Better error message
addLexDeclaredName(defaultExpression, context, BoundName(defaultExpression.getBinding()));
context.scopeContext.addLexScopedDeclaration(defaultExpression);
}
private void addLexScopedDeclaration(LexicalDeclaration decl) {
context.scopeContext.addLexScopedDeclaration(decl);
}
private void addVarScopedDeclaration(VariableStatement decl) {
context.topContext.addVarScopedDeclaration(decl);
}
private void addVarDeclaredName(BindingIdentifier bindingIdentifier) {
Name name = BoundName(bindingIdentifier);
addVarDeclaredName(bindingIdentifier, name, Parser::redeclarationNode);
}
private void addVarDeclaredName(BindingPattern bindingPattern) {
for (Name name : BoundNames(bindingPattern)) {
addVarDeclaredName(bindingPattern, name, Parser::redeclarationBindingPattern);
}
}
/**
* <strong>[13.2] Block</strong>
* <p>
* Static Semantics: Early Errors<br>
* <ul>
* <li>It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList also
* occurs in the VarDeclaredNames of StatementList.
* </ul>
*
* @param binding
* the binding to add to the variable scope
* @param name
* the var declared name
*/
private <NODE extends Binding> void addVarDeclaredName(NODE binding, Name name, BiFunction<NODE, Name, Node> f) {
addVarDeclaredName(binding, context, name, f);
for (ScopeContext next = context.scopeContext.parent; next != null; next = next.parent) {
if (!next.allowVarDeclaredName(name)) {
if (next instanceof CatchContext) {
continue;
}
Node errorNode = f.apply(binding, name);
reportSyntaxError(errorNode, Messages.Key.VariableRedeclaration, name);
}
}
}
private void addVarDeclaredName(Node node, ParseContext context, Name name) {
addVarDeclaredName(node, context, name, Parser::redeclarationNode);
}
private <NODE> void addVarDeclaredName(NODE node, ParseContext context, Name name, BiFunction<NODE, Name, Node> f) {
if (!context.scopeContext.addVarDeclaredName(name)) {
Node errorNode = f.apply(node, name);
reportSyntaxError(errorNode, Messages.Key.VariableRedeclaration, name);
}
}
private void checkVarDeclaredName(Binding binding) {
if (binding instanceof BindingIdentifier) {
checkVarDeclaredName((BindingIdentifier) binding);
} else {
assert binding instanceof BindingPattern;
checkVarDeclaredName((BindingPattern) binding);
}
}
private void checkVarDeclaredName(BindingIdentifier bindingIdentifier) {
Name name = BoundName(bindingIdentifier);
checkVarDeclaredName(bindingIdentifier, name, Parser::redeclarationNode);
}
private void checkVarDeclaredName(BindingPattern bindingPattern) {
for (Name name : BoundNames(bindingPattern)) {
checkVarDeclaredName(bindingPattern, name, Parser::redeclarationBindingPattern);
}
}
private <NODE extends Binding> void checkVarDeclaredName(NODE binding, Name name, BiFunction<NODE, Name, Node> f) {
ScopeContext scope = context.scopeContext;
for (ScopeContext parent; (parent = scope.parent) != null; scope = parent) {
if (!parent.allowVarDeclaredName(name)) {
Node errorNode = f.apply(binding, name);
reportSyntaxError(errorNode, Messages.Key.VariableRedeclaration, name);
}
}
if (context.kind.isScript() && isEnabled(Option.EvalScript)) {
context.scriptContext.addVarForOfDeclaredName(name);
}
}
/**
* <strong>[13.2] Block</strong>
* <p>
* Static Semantics: Early Errors<br>
* <ul>
* <li>It is a Syntax Error if the LexicallyDeclaredNames of StatementList contains any
* duplicate entries.
* <li>It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList also
* occurs in the VarDeclaredNames of StatementList.
* </ul>
*
* @param binding
* the binding to add to the lexical scope
*/
private void addLexDeclaredName(Binding binding) {
if (binding instanceof BindingIdentifier) {
addLexDeclaredName((BindingIdentifier) binding);
} else {
assert binding instanceof BindingPattern;
addLexDeclaredName((BindingPattern) binding);
}
}
private void addLexDeclaredName(BindingIdentifier bindingIdentifier) {
Name name = BoundName(bindingIdentifier);
addLexDeclaredName(bindingIdentifier, context, name);
}
private void addLexDeclaredName(BindingPattern bindingPattern) {
for (Name name : BoundNames(bindingPattern)) {
addLexDeclaredName(bindingPattern, context, name, Parser::redeclarationBindingPattern);
}
}
private void addLexDeclaredName(Node node, ParseContext context, Name name) {
addLexDeclaredName(node, context, name, Parser::redeclarationNode);
}
private <NODE> void addLexDeclaredName(NODE node, ParseContext context, Name name, BiFunction<NODE, Name, Node> f) {
if (context.isIllegalName(name) || !context.scopeContext.addLexDeclaredName(name)) {
Node errorNode = f.apply(node, name);
reportSyntaxError(errorNode, Messages.Key.VariableRedeclaration, name);
}
}
private void addLexDeclaredNames(List<Binding> bindings) {
for (Binding binding : bindings) {
addLexDeclaredName(binding);
}
}
private static Node redeclarationNode(Node node, Name name) {
return node;
}
private static Node redeclarationBindingPattern(BindingPattern node, Name name) {
return FindParameter.findExact(node, name);
}
private NameSet lexicalNames(List<Binding> bindings) {
NameSet names = new NameSet();
for (Binding binding : bindings) {
if (binding instanceof BindingIdentifier) {
names.add(BoundName((BindingIdentifier) binding));
} else {
assert binding instanceof BindingPattern;
names.addAll(BoundNames((BindingPattern) binding));
}
}
return names;
}
private void addExportBindings(long sourcePosition, List<Name> names) {
for (Name name : names) {
addExportBinding(sourcePosition, name);
}
}
private void addExportBinding(long sourcePosition, Name name) {
assert context.scopeContext == context.modContext : "not in module scope";
if (!context.modContext.isDeclared(name)) {
context.modContext.addUndeclaredExportBinding(sourcePosition, name);
}
}
private void addExportBinding(long sourcePosition, String name) {
addExportBinding(sourcePosition, new Name(name));
}
private void addExportNames(ExportClause exportClause) {
IdentifierName defaultEntry = exportClause.getDefaultEntry();
if (defaultEntry != null) {
addExportName(defaultEntry.getBeginPosition(), defaultEntry.getName());
}
IdentifierName nameSpace = exportClause.getNameSpace();
if (nameSpace != null) {
addExportName(nameSpace.getBeginPosition(), nameSpace.getName());
}
for (ExportSpecifier export : exportClause.getExports()) {
addExportName(export.getBeginPosition(), export.getExportName());
}
}
private void addExportNames(long sourcePosition, List<Name> names) {
for (Name name : names) {
addExportName(sourcePosition, name);
}
}
private void addExportName(long sourcePosition, Name name) {
addExportName(sourcePosition, name.getIdentifier());
}
private void addExportName(long sourcePosition, String name) {
assert context.scopeContext == context.modContext : "not in module scope";
if (!context.modContext.addExportName(name)) {
reportSyntaxError(sourcePosition, Messages.Key.DuplicateExport, name);
}
}
private void addImportBinding(BindingIdentifier bindingIdentifier) {
assert context.scopeContext == context.modContext : "not in module scope";
Name name = BoundName(bindingIdentifier);
if (!context.modContext.addImportBinding(name)) {
reportSyntaxError(bindingIdentifier, Messages.Key.DuplicateImport, name);
}
}
private void addLabel(long sourcePosition, LinkedHashSet<String> labelSet, String label) {
if ((context.labelSet != null && context.labelSet.containsKey(label)) || !labelSet.add(label)) {
reportSyntaxError(sourcePosition, Messages.Key.DuplicateLabel, label);
}
}
private LabelContext enterLabelled(StatementType type, Set<String> labelSet) {
LabelContext cx = context.labels = new LabelContext(context.labels, type, labelSet);
if (!labelSet.isEmpty() && context.labelSet == null) {
context.labelSet = new HashMap<>();
}
for (String label : labelSet) {
assert !context.labelSet.containsKey(label);
context.labelSet.put(label, cx);
}
return cx;
}
private LabelContext exitLabelled() {
for (String label : context.labels.labelSet) {
context.labelSet.remove(label);
}
return context.labels = context.labels.parent;
}
private LabelContext enterIteration(Set<String> labelSet) {
return enterLabelled(StatementType.Iteration, labelSet);
}
private void exitIteration() {
exitLabelled();
}
private LabelContext enterBreakable(Set<String> labelSet) {
return enterLabelled(StatementType.Breakable, labelSet);
}
private void exitBreakable() {
exitLabelled();
}
private LabelContext findContinueTarget(String label) {
for (LabelContext cx = context.labels; cx != null; cx = cx.parent) {
if (label == null ? cx.type == StatementType.Iteration : cx.labelSet.contains(label)) {
return cx;
}
}
return null;
}
private LabelContext findBreakTarget(String label) {
for (LabelContext cx = context.labels; cx != null; cx = cx.parent) {
if (label == null ? cx.type != StatementType.Statement : cx.labelSet.contains(label)) {
return cx;
}
}
return null;
}
private static void setFunctionName(Expression expr, BindingIdentifier identifier) {
setFunctionName(expr, identifier.getName().getIdentifier());
}
private static void setFunctionName(Expression expr, IdentifierReference identifier) {
setFunctionName(expr, identifier.getName());
}
private static void setFunctionName(Expression expr, PropertyName propertyName) {
assert !(propertyName instanceof ComputedPropertyName);
setFunctionName(expr, propertyName.getName());
}
private static void setFunctionName(Expression expr, ComputedPropertyName propertyName) {
setFunctionName(expr, propertyName.toString());
}
private static void setFunctionName(Expression expr, String name) {
if (expr instanceof ClassExpression) {
for (MethodDefinition def : ((ClassExpression) expr).getMethods()) {
def.setClassName(name);
}
} else {
assert expr instanceof FunctionNode : expr.getClass();
((FunctionNode) expr).setFunctionName(name);
}
}
private static void setMethodName(Expression expr, LeftHandSideExpression lhs) {
String name = MethodNameVisitor.toMethodName(lhs);
if (name != null && expr instanceof FunctionNode) {
((FunctionNode) expr).setMethodName(name);
}
}
private static <T> InlineArrayList<T> newList() {
return new InlineArrayList<>();
}
private static <T> List<T> merge(List<T> list1, List<T> list2) {
if (!(list1.isEmpty() || list2.isEmpty())) {
List<T> merged = new ArrayList<>(list1.size() + list2.size());
merged.addAll(list1);
merged.addAll(list2);
return merged;
}
return list1.isEmpty() ? list2 : list1;
}
private static int toLine(long sourcePosition) {
return (int) sourcePosition;
}
private static int toColumn(long sourcePosition) {
return (int) (sourcePosition >>> 32);
}
private long beginSource() {
// make columns 1-indexed
return ((long) 1 << 32) | getSourceLine();
}
private ParserException reportException(ParserException exception) {
throw exception;
}
/**
* Report mismatched token error from tokenstream's current position.
*
* @param expected
* the expected token
* @param actual
* the actual token in the token stream
* @return the parser exception
*/
private ParserException reportTokenMismatch(Token expected, Token actual) {
throw reportTokenMismatch(expected.toString(), actual);
}
/**
* Report mismatched token error from tokenstream's current position.
*
* @param actual
* the actual token in the token stream
* @return the parser exception
*/
private ParserException reportTokenNotIdentifier(Token actual) {
if (Token.isIdentifierName(actual)) {
throw reportSyntaxError(Messages.Key.InvalidIdentifier, getName(actual));
}
throw reportTokenMismatch("<identifier>", actual);
}
/**
* Report mismatched token error from tokenstream's current position.
*
* @param actual
* the actual token in the token stream
* @return the parser exception
*/
private ParserException reportTokenNotIdentifierName(Token actual) {
throw reportTokenMismatch("<identifier-name>", actual);
}
/**
* Report mismatched token error from tokenstream's current position.
*
* @param expected
* the expected token description
* @param actual
* the actual token in the token stream
* @return the parser exception
*/
private ParserException reportTokenMismatch(String expected, Token actual) {
if (actual == Token.EOF) {
throw reportEofError(ts.sourcePosition(), Messages.Key.UnexpectedEndOfFile, expected);
}
if (actual == Token.ERROR) {
throw reportError(ExceptionType.SyntaxError, ts.sourcePosition(),
Messages.Key.UnexpectedCharacter, String.valueOf(ts.lastChar()), expected);
}
throw reportError(ExceptionType.SyntaxError, ts.sourcePosition(),
Messages.Key.UnexpectedToken, actual.toString(), expected);
}
/**
* Report parser eof-error with the given position.
*
* @param sourcePosition
* the source position for the error
* @param messageKey
* the error message key
* @param args
* the error message arguments
* @return the parser exception
*/
private ParserEOFException reportEofError(long sourcePosition, Messages.Key messageKey,
String... args) {
int line = toLine(sourcePosition), column = toColumn(sourcePosition);
throw new ParserEOFException(getSourceName(), line, column, messageKey, args);
}
/**
* Report parser error with the given type and position.
*
* @param type
* the exception type for the error
* @param sourcePosition
* the source position for the error
* @param messageKey
* the error message key
* @param args
* the error message arguments
* @return the parser exception
*/
private ParserException reportError(ExceptionType type, long sourcePosition,
Messages.Key messageKey, String... args) {
int line = toLine(sourcePosition), column = toColumn(sourcePosition);
throw new ParserException(type, getSourceName(), line, column, messageKey, args);
}
/**
* Report reference error from the given position.
*
* @param sourcePosition
* the source position for the error
* @param messageKey
* the error message key
* @param args
* the error message arguments
* @return the parser exception
*/
private ParserException reportReferenceError(long sourcePosition, Messages.Key messageKey, String... args) {
throw reportError(ExceptionType.ReferenceError, sourcePosition, messageKey, args);
}
/**
* Report syntax error from the given position.
*
* @param sourcePosition
* the source position for the error
* @param messageKey
* the error message key
* @param args
* the error message arguments
* @return the parser exception
*/
private ParserException reportSyntaxError(long sourcePosition, Messages.Key messageKey,
String... args) {
throw reportError(ExceptionType.SyntaxError, sourcePosition, messageKey, args);
}
/**
* Report syntax error from the node's begin source-position.
*
* @param node
* the node which could not be parsed without errors
* @param messageKey
* the error message key
* @param args
* the error message arguments
* @return the parser exception
*/
private ParserException reportSyntaxError(Node node, Messages.Key messageKey, String... args) {
throw reportSyntaxError(node.getBeginPosition(), messageKey, args);
}
/**
* Report syntax error from tokenstream's current position.
*
* @param messageKey
* the error message key
* @param args
* the error message arguments
* @return the parser exception
*/
private ParserException reportSyntaxError(Messages.Key messageKey, String... args) {
throw reportSyntaxError(ts.sourcePosition(), messageKey, args);
}
/**
* Report syntax error from the node's begin source-position.
*
* @param node
* the node which could not be parsed without errors
* @param messageKey
* the error message key
* @param args
* the error message arguments
* @return the parser exception
*/
private ParserException reportSyntaxError(Node node, Messages.Key messageKey, Name name) {
throw reportSyntaxError(node.getBeginPosition(), messageKey, name.getIdentifier());
}
/**
* Report (or store) strict-mode parser error with the given type and position.
*
* @param type
* the exception type for the error
* @param sourcePosition
* the source position for the error
* @param messageKey
* the error message key
* @param args
* the error message arguments
*/
void reportStrictModeError(ExceptionType type, long sourcePosition, Messages.Key messageKey,
String... args) {
if (context.strictMode == StrictMode.Unknown) {
if (context.strictError == null) {
int line = toLine(sourcePosition), column = toColumn(sourcePosition);
context.strictError = new ParserException(type, getSourceName(), line, column,
messageKey, args);
}
} else if (context.strictMode == StrictMode.Strict) {
reportError(type, sourcePosition, messageKey, args);
}
}
/**
* Report (or store) strict-mode syntax error from the given position.
*
* @param sourcePosition
* the source position for the error
* @param messageKey
* the error message key
* @param args
* the error message arguments
*/
private void reportStrictModeSyntaxError(long sourcePosition, Messages.Key messageKey,
String... args) {
reportStrictModeError(ExceptionType.SyntaxError, sourcePosition, messageKey, args);
}
/**
* Report (or store) strict-mode syntax error from the node's source-position.
*
* @param node
* the node which could not be parsed without errors
* @param messageKey
* the error message key
* @param args
* the error message arguments
*/
private void reportStrictModeSyntaxError(Node node, Messages.Key messageKey, String... args) {
reportStrictModeError(ExceptionType.SyntaxError, node.getBeginPosition(), messageKey, args);
}
/**
* Report (or store) strict-mode syntax error from tokenstream's current position.
*
* @param messageKey
* the error message key
* @param args
* the error message arguments
*/
private void reportStrictModeSyntaxError(Messages.Key messageKey, String... args) {
reportStrictModeError(ExceptionType.SyntaxError, ts.sourcePosition(), messageKey, args);
}
/**
* Report (or store) strict-mode syntax error from the node's source-position.
*
* @param node
* the node which could not be parsed without errors
* @param messageKey
* the error message key
* @param args
* the error message arguments
*/
private void reportStrictModeSyntaxError(Node node, Messages.Key messageKey, Name name) {
reportStrictModeError(ExceptionType.SyntaxError, node.getBeginPosition(), messageKey,
name.getIdentifier());
}
/**
* Peeks the next token in the token-stream.
*
* @return the next token
*/
private Token peek() {
return ts.peekToken();
}
/**
* Checks whether the next token in the token-stream is equal to the input token.
*
* @param token
* the token to test
* @return {@code true} if the next token matches
*/
private boolean LOOKAHEAD(Token token) {
return ts.peekToken() == token;
}
/**
* Returns the current token in the token-stream.
*
* @return the current token
*/
private Token token() {
return ts.currentToken();
}
/**
* Consumes the current token in the token-stream and advances the stream to the next token.
*
* @param tok
* the token to consume
*/
private void consume(Token tok) {
if (tok != token())
reportTokenMismatch(tok, token());
Token next = ts.nextToken();
if (DEBUG)
System.out.printf("consume(%s) -> %s\n", tok, next);
}
/**
* Consumes the current token in the token-stream and advances the stream to the next token.
*
* @param name
* the name string to consume
*/
private void consume(String name) {
long sourcePos = ts.sourcePosition();
String string = ts.getString();
consume(Token.NAME);
if (!name.equals(string))
reportSyntaxError(sourcePos, Messages.Key.UnexpectedName, string, name);
}
/**
* Parses the input source as script code.
*
* @param source
* the source string to parse
* @return the parsed script
* @throws ParserException
* if the input source could not be parsed successfully
*/
public Script parseScript(String source) throws ParserException {
if (ts != null)
throw new IllegalStateException();
ts = new TokenStream(this, new TokenStreamInput(source));
return script();
}
/**
* Parses the input source as module code.
*
* @param source
* the source string to parse
* @return the parsed module
* @throws ParserException
* if the input source could not be parsed successfully
*/
public Module parseModule(String source) throws ParserException {
if (ts != null)
throw new IllegalStateException();
ts = new TokenStream(this, new TokenStreamInput(source));
moduleCode = true;
return module();
}
/**
* Parses the input source as function code.
*
* @param formals
* the function formal parameters source
* @param bodyText
* the function body source text
* @return the parsed function
* @throws ParserException
* if the input source could not be parsed successfully
*/
public FunctionDefinition parseFunction(String formals, String bodyText) throws ParserException {
if (ts != null)
throw new IllegalStateException();
newContext(ContextKind.Script);
try {
applyStrictMode(false);
FunctionDeclaration function;
newContext(ContextKind.Function);
try {
ts = new TokenStream(this, new TokenStreamInput(formals)).initialize();
FormalParameterList parameters = formalParameters(Token.EOF);
if (token() != Token.EOF) {
reportSyntaxError(Messages.Key.InvalidFormalParameterList);
}
String header = formatParameters(formals, ts.position());
ts = new TokenStream(this, new TokenStreamInput(bodyText)).initialize();
List<StatementListItem> statements = functionBody(parameters, Token.EOF);
if (token() != Token.EOF) {
reportSyntaxError(Messages.Key.InvalidFunctionBody);
}
String body = formatBody(bodyText);
String functionName = "anonymous";
BindingIdentifier identifier = new BindingIdentifier(beginSource(), beginSource(),
functionName);
FunctionContext scope = context.funContext;
function = new FunctionDeclaration(beginSource(), ts.endPosition(), scope,
identifier, parameters, statements, functionName, header, body);
scope.setNode(function);
function_EarlyErrors(function);
function = inheritStrictness(function);
} catch (RetryGenerator e) {
// don't bother with legacy support here
throw reportSyntaxError(Messages.Key.InvalidYieldExpression);
} finally {
restoreContext();
}
createScript(function);
return function;
} finally {
restoreContext();
}
}
/**
* Parses the input source as generator function code.
*
* @param formals
* the function formal parameters source
* @param bodyText
* the function body source text
* @return the parsed generator function
* @throws ParserException
* if the input source could not be parsed successfully
*/
public GeneratorDefinition parseGenerator(String formals, String bodyText)
throws ParserException {
if (ts != null)
throw new IllegalStateException();
newContext(ContextKind.Script);
try {
applyStrictMode(false);
GeneratorDeclaration generator;
newContext(ContextKind.Generator);
try {
ts = new TokenStream(this, new TokenStreamInput(formals)).initialize();
FormalParameterList parameters = formalParameters(Token.EOF);
if (token() != Token.EOF) {
reportSyntaxError(Messages.Key.InvalidFormalParameterList);
}
String header = formatParameters(formals, ts.position());
ts = new TokenStream(this, new TokenStreamInput(bodyText)).initialize();
List<StatementListItem> statements = functionBody(parameters, Token.EOF);
if (token() != Token.EOF) {
reportSyntaxError(Messages.Key.InvalidFunctionBody);
}
String body = formatBody(bodyText);
String functionName = "anonymous";
BindingIdentifier identifier = new BindingIdentifier(beginSource(), beginSource(),
functionName);
FunctionContext scope = context.funContext;
generator = new GeneratorDeclaration(beginSource(), ts.endPosition(), scope, generatorKind(),
identifier, parameters, statements, functionName, header, body);
scope.setNode(generator);
generator_EarlyErrors(generator);
generator = inheritStrictness(generator);
} finally {
restoreContext();
}
createScript(generator);
return generator;
} finally {
restoreContext();
}
}
/**
* Parses the input source as async function code.
*
* @param formals
* the function formal parameters source
* @param bodyText
* the function body source text
* @return the parsed async function
* @throws ParserException
* if the input source could not be parsed successfully
*/
public AsyncFunctionDefinition parseAsyncFunction(String formals, String bodyText)
throws ParserException {
if (ts != null)
throw new IllegalStateException();
newContext(ContextKind.Script);
try {
applyStrictMode(false);
AsyncFunctionDeclaration asyncFunction;
newContext(ContextKind.AsyncFunction);
try {
ts = new TokenStream(this, new TokenStreamInput(formals)).initialize();
FormalParameterList parameters = formalParameters(Token.EOF);
if (token() != Token.EOF) {
reportSyntaxError(Messages.Key.InvalidFormalParameterList);
}
String header = formatParameters(formals, ts.position());
ts = new TokenStream(this, new TokenStreamInput(bodyText)).initialize();
List<StatementListItem> statements = functionBody(parameters, Token.EOF);
if (token() != Token.EOF) {
reportSyntaxError(Messages.Key.InvalidFunctionBody);
}
String body = formatBody(bodyText);
String functionName = "anonymous";
BindingIdentifier identifier = new BindingIdentifier(beginSource(), beginSource(),
functionName);
FunctionContext scope = context.funContext;
asyncFunction = new AsyncFunctionDeclaration(beginSource(), ts.endPosition(), scope,
identifier, parameters, statements, functionName, header, body);
scope.setNode(asyncFunction);
asyncFunction_EarlyErrors(asyncFunction);
asyncFunction = inheritStrictness(asyncFunction);
} finally {
restoreContext();
}
createScript(asyncFunction);
return asyncFunction;
} finally {
restoreContext();
}
}
/**
* Parses the input source as async generator code.
*
* @param formals
* the function formal parameters source
* @param bodyText
* the function body source text
* @return the parsed async generator
* @throws ParserException
* if the input source could not be parsed successfully
*/
public AsyncGeneratorDefinition parseAsyncGenerator(String formals, String bodyText) throws ParserException {
if (ts != null)
throw new IllegalStateException();
newContext(ContextKind.Script);
try {
applyStrictMode(false);
AsyncGeneratorDeclaration asyncFunction;
newContext(ContextKind.AsyncGenerator);
try {
ts = new TokenStream(this, new TokenStreamInput(formals)).initialize();
FormalParameterList parameters = formalParameters(Token.EOF);
if (token() != Token.EOF) {
reportSyntaxError(Messages.Key.InvalidFormalParameterList);
}
String header = formatParameters(formals, ts.position());
ts = new TokenStream(this, new TokenStreamInput(bodyText)).initialize();
List<StatementListItem> statements = functionBody(parameters, Token.EOF);
if (token() != Token.EOF) {
reportSyntaxError(Messages.Key.InvalidFunctionBody);
}
String body = formatBody(bodyText);
String functionName = "anonymous";
BindingIdentifier identifier = new BindingIdentifier(beginSource(), beginSource(), functionName);
FunctionContext scope = context.funContext;
asyncFunction = new AsyncGeneratorDeclaration(beginSource(), ts.endPosition(), scope, identifier,
parameters, statements, functionName, header, body);
scope.setNode(asyncFunction);
asyncGenerator_EarlyErrors(asyncFunction);
asyncFunction = inheritStrictness(asyncFunction);
} finally {
restoreContext();
}
createScript(asyncFunction);
return asyncFunction;
} finally {
restoreContext();
}
}
private <FUNDECL extends Declaration & FunctionNode> Script createScript(FUNDECL funDeclaration) {
List<StatementListItem> statements = singletonList((StatementListItem) funDeclaration);
boolean strict = (context.strictMode == StrictMode.Strict);
ScriptContext scope = context.scriptContext;
Script script = new Script(beginSource(), ts.endPosition(), source, scope, statements,
options, parserOptions, strict);
scope.node = script;
return script;
}
private static String formatParameters(String formals, int lastParametersTokenPosition) {
boolean unsafeChars = hasUnsafeTrailingCharacters(formals, lastParametersTokenPosition);
StringBuilder sb = new StringBuilder(formals.length() + 3 + (unsafeChars ? 2 : 0));
sb.append('(');
if (unsafeChars) {
// More input after last token (whitespace, comments), add newlines to handle
// last token is single-line comment case.
sb.append('\n').append(formals).append('\n');
} else {
sb.append(formals);
}
return sb.append(')').append(' ').toString();
}
private static String formatBody(String body) {
return new StringBuilder(body.length() + 2).append('\n').append(body).append('\n')
.toString();
}
private static boolean hasUnsafeTrailingCharacters(String s, int start) {
for (int i = s.length() - 1; i > start; --i) {
char c = s.charAt(i);
if (Characters.isLineTerminator(c)) {
break;
}
if (!Characters.isWhitespace(c)) {
return true;
}
}
return false;
}
/* ***************************************************************************************** */
/**
* <strong>[15.1] Scripts</strong>
*
* <pre>
* Script :
* ScriptBody<span><sub>opt</sub></span>
* ScriptBody :
* StatementList
* </pre>
*
* @return the parsed script node
*/
private Script script() {
newContext(ContextKind.Script);
try {
ts.initialize();
List<StatementListItem> prologue = directivePrologue(true);
List<StatementListItem> body = statementList(Token.EOF);
assert context.assertLiteralsUnchecked(0);
computeBlockFunctionsForScript();
List<StatementListItem> statements = merge(prologue, body);
boolean strict = (context.strictMode == StrictMode.Strict);
ScriptContext scope = context.scriptContext;
Script script = new Script(beginSource(), ts.endPosition(), source, scope, statements,
options, parserOptions, strict);
scope.node = script;
return script;
} finally {
restoreContext();
}
}
/**
* <strong>[15.2] Modules</strong>
*
* <pre>
* Module :
* ModuleBody<span><sub>opt</sub></span>
* ModuleBody :
* ModuleItemList
* </pre>
*
* @return the parsed module node
*/
private Module module() {
newContext(ContextKind.Module);
context.strictMode = StrictMode.Strict;
try {
ts.initialize();
List<ModuleItem> statements = moduleItemList();
assert context.assertLiteralsUnchecked(0);
ModuleContext scope = context.modContext;
Module module = new Module(beginSource(), ts.endPosition(), source, scope, statements,
options, parserOptions);
scope.node = module;
module_EarlyErrors();
return module;
} finally {
restoreContext();
}
}
/**
* 15.2.1.1 Static Semantics: Early Errors
*/
private void module_EarlyErrors() {
assert context.scopeContext == context.modContext;
ModuleContext scope = context.modContext;
if (!scope.undeclaredExportBindings.isEmpty()) {
for (Map.Entry<Name, Long> export : scope.undeclaredExportBindings.entrySet()) {
Name exportBinding = export.getKey();
if (!scope.isDeclared(exportBinding)) {
reportSyntaxError(export.getValue(), Messages.Key.MissingExportBinding,
exportBinding.getIdentifier());
}
}
}
scope.exportNames = null;
scope.undeclaredExportBindings = null;
scope.importBindings = null;
}
/**
* <strong>[15.2] Modules</strong>
*
* <pre>
* ModuleItemList :
* ModuleItem
* ModuleItemList ModuleItem
* ModuleItem :
* ImportDeclaration
* ExportDeclaration
* StatementListItem
* </pre>
*
* @return the list of parsed module items
*/
private List<ModuleItem> moduleItemList() {
InlineArrayList<ModuleItem> moduleItemList = newList();
while (token() != Token.EOF) {
switch (token()) {
case EXPORT:
moduleItemList.add(exportDeclaration());
break;
case IMPORT:
moduleItemList.add(importDeclaration());
break;
default:
moduleItemList.add(statementListItem());
}
}
return moduleItemList;
}
/**
* <strong>[15.2.2] Imports</strong>
*
* <pre>
* ImportDeclaration :
* import ImportClause FromClause ;
* import ModuleSpecifier ;
* </pre>
*
* @return the parsed import declaration
*/
private ImportDeclaration importDeclaration() {
long begin = ts.beginPosition();
consume(Token.IMPORT);
ImportDeclaration decl;
if (token() != Token.STRING) {
ImportClause importClause = importClause();
String moduleSpecifier = fromClause();
semicolon();
decl = new ImportDeclaration(begin, ts.endPosition(), importClause, moduleSpecifier);
} else {
String moduleSpecifier = moduleSpecifier();
semicolon();
decl = new ImportDeclaration(begin, ts.endPosition(), moduleSpecifier);
}
return decl;
}
/**
* <strong>[15.2.2] Imports</strong>
*
* <pre>
* FromClause :
* from ModuleSpecifier
* </pre>
*
* @return the parsed from-clause's module specifier
*/
private String fromClause() {
consume("from");
return moduleSpecifier();
}
/**
* <strong>[15.2.2] Imports</strong>
*
* <pre>
* ImportClause :
* ImportedDefaultBinding
* NameSpaceImport
* NamedImports
* ImportedDefaultBinding , NameSpaceImport
* ImportedDefaultBinding , NamedImports
* ImportedDefaultBinding :
* ImportedBinding
* </pre>
*
* @return the parsed import clause
*/
private ImportClause importClause() {
long begin = ts.beginPosition();
BindingIdentifier defaultEntry = null;
BindingIdentifier nameSpace = null;
List<ImportSpecifier> namedImports = emptyList();
if (isEnabled(CompatibilityOption.TypeAnnotation)) {
// import typeof <ImportClause> from "module";
// import type <ImportClause> from "module";
if (token() == Token.TYPEOF || (isName("type") && !(LOOKAHEAD(Token.COMMA) || isNextName("from")))) {
consume(token());
}
}
if (token() == Token.LC) {
namedImports = namedImports();
} else if (token() == Token.MUL) {
nameSpace = nameSpaceImport();
addImportBinding(nameSpace);
addLexDeclaredName(nameSpace);
} else {
defaultEntry = importedBinding();
addImportBinding(defaultEntry);
addLexDeclaredName(defaultEntry);
if (token() == Token.COMMA) {
consume(Token.COMMA);
if (token() == Token.LC) {
namedImports = namedImports();
} else {
nameSpace = nameSpaceImport();
addImportBinding(nameSpace);
addLexDeclaredName(nameSpace);
}
}
}
return new ImportClause(begin, ts.endPosition(), defaultEntry, namedImports, nameSpace);
}
/**
* <strong>[15.2.2] Imports</strong>
*
* <pre>
* NameSpaceImport :
* * as ImportedBinding
* </pre>
*
* @return the parsed namespace import binding
*/
private BindingIdentifier nameSpaceImport() {
// TODO: Add new ast node?
consume(Token.MUL);
consume("as");
return importedBinding();
}
/**
* <strong>[15.2.2] Imports</strong>
*
* <pre>
* NamedImports :
* { }
* { ImportsList }
* { ImportsList , }
* ImportsList :
* ImportSpecifier
* ImportsList , ImportSpecifier
* </pre>
*
* @return the list of parsed named imports
*/
private List<ImportSpecifier> namedImports() {
InlineArrayList<ImportSpecifier> namedImports = newList();
consume(Token.LC);
while (token() != Token.RC) {
namedImports.add(importSpecifier());
if (token() == Token.COMMA) {
consume(Token.COMMA);
} else {
break;
}
}
consume(Token.RC);
return namedImports;
}
/**
* <strong>[15.2.2] Imports</strong>
*
* <pre>
* ImportSpecifier :
* ImportedBinding
* IdentifierName as ImportedBinding
* </pre>
*
* @return the parsed import specifier
*/
private ImportSpecifier importSpecifier() {
long begin = ts.beginPosition();
String importName;
BindingIdentifier localName;
if (Token.isIdentifierName(token()) && importSpecifierFollowSet(peek())) {
BindingIdentifier binding = importedBinding();
importName = binding.getName().getIdentifier();
localName = binding;
} else {
importName = identifierName();
consume("as");
localName = importedBinding();
}
addImportBinding(localName);
addLexDeclaredName(localName);
return new ImportSpecifier(begin, ts.endPosition(), importName, localName);
}
/**
* Returns FOLLOW(ImportSpecifier): « <code>,</code> , <code>}</code> »
*
* @param token
* the token to test
* @return {@code true} if token is in the follow-set
*/
private boolean importSpecifierFollowSet(Token token) {
switch (token) {
case COMMA:
case RC:
return true;
default:
return false;
}
}
/**
* <strong>[15.2.2] Imports</strong>
*
* <pre>
* ModuleSpecifier :
* StringLiteral
* </pre>
*
* @return the parsed module specifier string
*/
private String moduleSpecifier() {
return stringLiteral();
}
/**
* <strong>[15.2.2] Imports</strong>
*
* <pre>
* ImportedDefaultBinding :
* ImportedBinding
* ImportedBinding :
* BindingIdentifier
* </pre>
*
* @return the parsed imported binding node
*/
private BindingIdentifier importedBinding() {
return bindingIdentifier();
}
/**
* <strong>[15.2.3] Exports</strong>
*
* <pre>
* ExportDeclaration :
* export * FromClause ;
* export ExportClause FromClause ;
* export ExportClause ;
* export VariableStatement
* export Declaration
* export default HoistableDeclaration<span><sub>[Default]</sub></span>
* export default ClassDeclaration<span><sub>[Default]</sub></span>
* export default [LA ∉ { <b>function</b>, <b>class</b> }] AssignmentExpression<span><sub>[In]</sub></span> ;
* </pre>
*
* @return the parsed export declaration
*/
private ExportDeclaration exportDeclaration() {
long begin = ts.beginPosition();
consume(Token.EXPORT);
switch (token()) {
case MUL: {
// export * FromClause ;
// Extension: export ExportFromClause FromClause ;
long beginExportAll = ts.beginPosition();
consume(Token.MUL);
if (isEnabled(CompatibilityOption.ExportFrom) && isName("as")) {
ExportClause exportClause = exportNameSpaceFromClause(beginExportAll);
String moduleSpecifier = fromClause();
semicolon();
// 15.2.3.4 Static Semantics: ExportedNames
addExportNames(exportClause);
return new ExportDeclaration(begin, ts.endPosition(), exportClause, moduleSpecifier);
}
String moduleSpecifier = fromClause();
semicolon();
return new ExportDeclaration(begin, ts.endPosition(), moduleSpecifier);
}
case LC: {
// export ExportClause FromClause ;
// export ExportClause ;
ExportClause exportClause = exportClause();
String moduleSpecifier;
if (isName("from")) {
moduleSpecifier = fromClause();
} else {
moduleSpecifier = null;
// 15.2.3.1 Static Semantics: Early Errors
// 15.2.3.3 Static Semantics: ExportedBindings
// 15.2.3.4 Static Semantics: ExportedNames
for (ExportSpecifier export : exportClause.getExports()) {
String sourceName = export.getSourceName();
if (isModuleReservedName(sourceName)) {
reportSyntaxError(Messages.Key.InvalidIdentifier, sourceName);
}
addExportBinding(export.getBeginPosition(), export.getSourceName());
}
}
semicolon();
// 15.2.3.4 Static Semantics: ExportedNames
addExportNames(exportClause);
return new ExportDeclaration(begin, ts.endPosition(), exportClause, moduleSpecifier);
}
case VAR: {
// export VariableStatement
VariableStatement variableStatement = variableStatement(true);
// 15.2.3.3 Static Semantics: ExportedBindings
// 15.2.3.4 Static Semantics: ExportedNames
addExportBindings(begin, BoundNames(variableStatement));
addExportNames(begin, BoundNames(variableStatement));
return new ExportDeclaration(begin, ts.endPosition(), variableStatement);
}
case ASYNC:
if (!((isEnabled(CompatibilityOption.AsyncFunction) || isEnabled(CompatibilityOption.AsyncGenerator))
&& LOOKAHEAD(Token.FUNCTION) && noNextLineTerminator())) {
break;
}
// fall-through
case FUNCTION:
case CLASS:
case CONST:
case LET:
case AT: {
// export Declaration
Declaration declaration = declaration();
// 15.2.3.3 Static Semantics: ExportedBindings
// 15.2.3.4 Static Semantics: ExportedNames
addExportBindings(begin, BoundNames(declaration));
addExportNames(begin, BoundNames(declaration));
return new ExportDeclaration(begin, ts.endPosition(), declaration);
}
case DEFAULT: {
// export default HoistableDeclaration[Default]
// export default ClassDeclaration[Default]
// export default [LA != {function, class}] AssignmentExpression[In] ;
long beginDefault = ts.beginPosition();
consume(Token.DEFAULT);
switch (token()) {
case FUNCTION: {
HoistableDeclaration declaration = hoistableDeclaration(true);
// 15.2.3.2 Static Semantics: BoundNames
// 15.2.3.3 Static Semantics: ExportedBindings
// 15.2.3.4 Static Semantics: ExportedNames
addExportBinding(begin, declaration.getName());
if (declaration.getIdentifier() != null) {
addExportBinding(begin, DEFAULT_EXPORT_BINDING_NAME);
}
addExportName(begin, DEFAULT_EXPORT_NAME);
return new ExportDeclaration(begin, ts.endPosition(), declaration);
}
case AT: {
ClassDeclaration declaration = classDeclaration(true, decorators());
// 15.2.3.2 Static Semantics: BoundNames
// 15.2.3.3 Static Semantics: ExportedBindings
// 15.2.3.4 Static Semantics: ExportedNames
addExportBinding(begin, declaration.getName());
if (declaration.getIdentifier() != null) {
addExportBinding(begin, DEFAULT_EXPORT_BINDING_NAME);
}
addExportName(begin, DEFAULT_EXPORT_NAME);
return new ExportDeclaration(begin, ts.endPosition(), declaration);
}
case CLASS: {
ClassDeclaration declaration = classDeclaration(true, NO_DECORATORS);
// 15.2.3.2 Static Semantics: BoundNames
// 15.2.3.3 Static Semantics: ExportedBindings
// 15.2.3.4 Static Semantics: ExportedNames
addExportBinding(begin, declaration.getName());
if (declaration.getIdentifier() != null) {
addExportBinding(begin, DEFAULT_EXPORT_BINDING_NAME);
}
addExportName(begin, DEFAULT_EXPORT_NAME);
return new ExportDeclaration(begin, ts.endPosition(), declaration);
}
case ASYNC:
if (isEnabled(CompatibilityOption.AsyncFunction) || isEnabled(CompatibilityOption.AsyncGenerator)) {
if (LOOKAHEAD(Token.FUNCTION) && noNextLineTerminator()) {
HoistableDeclaration declaration = asyncFunctionOrGeneratorDeclaration(true);
// 15.2.3.2 Static Semantics: BoundNames
// 15.2.3.3 Static Semantics: ExportedBindings
addExportBinding(begin, declaration.getName());
if (declaration.getIdentifier() != null) {
addExportBinding(begin, DEFAULT_EXPORT_BINDING_NAME);
}
addExportName(begin, DEFAULT_EXPORT_NAME);
return new ExportDeclaration(begin, ts.endPosition(), declaration);
}
}
// fall-through
default: {
if (isEnabled(CompatibilityOption.ExportFrom) && isName("from")
&& LOOKAHEAD(Token.STRING) && noNextLineTerminator()) {
// Handle: `export default from "module-specifier" ;`
ExportClause exportClause = exportFromClause(beginDefault, true);
String moduleSpecifier = fromClause();
semicolon();
// 15.2.3.4 Static Semantics: ExportedNames
addExportNames(exportClause);
return new ExportDeclaration(begin, ts.endPosition(), exportClause,
moduleSpecifier);
}
ExportDefaultExpression defaultExpression = defaultExpression();
addExportBinding(begin, DEFAULT_EXPORT_BINDING_NAME);
addExportName(begin, DEFAULT_EXPORT_NAME);
return new ExportDeclaration(begin, ts.endPosition(), defaultExpression);
}
}
}
default:
}
if (isEnabled(CompatibilityOption.ExportFrom)) {
ExportClause exportClause = exportFromClause(ts.beginPosition(), false);
String moduleSpecifier = fromClause();
semicolon();
// 15.2.3.4 Static Semantics: ExportedNames
addExportNames(exportClause);
return new ExportDeclaration(begin, ts.endPosition(), exportClause, moduleSpecifier);
}
throw reportTokenMismatch(Token.DEFAULT, token());
}
private ExportDefaultExpression defaultExpression() {
long begin = ts.beginPosition();
BindingIdentifier binding = new BindingIdentifier(begin, ts.endPosition(),
DEFAULT_EXPORT_BINDING_NAME);
Expression expression = assignmentExpression(true);
semicolon();
ExportDefaultExpression defaultExpression = new ExportDefaultExpression(begin,
ts.endPosition(), binding, expression);
addDeclaration(defaultExpression);
return defaultExpression;
}
private static boolean isModuleReservedName(String name) {
Token token = TokenStream.readReservedWord(name);
return Token.isReservedWord(token) || Token.isStrictReservedWord(token)
|| token == Token.AWAIT;
}
/**
* <strong>[15.2.3] Exports</strong>
*
* <pre>
* ExportClause :
* { }
* { ExportsList }
* { ExportsList , }
* ExportsList :
* ExportSpecifier
* ExportsList , ExportSpecifier
* </pre>
*
* @return the parsed exports clause
*/
private ExportClause exportClause() {
long begin = ts.beginPosition();
List<ExportSpecifier> exports = namedExports();
return new ExportClause(begin, ts.endPosition(), exports);
}
/**
* <strong>[15.2.3] Exports</strong>
*
* <pre>
* ExportSpecifier :
* IdentifierName
* IdentifierName as IdentifierName
* </pre>
*
* @return the parsed export specifier
*/
private ExportSpecifier exportSpecifier() {
long begin = ts.beginPosition();
String sourceName = identifierName();
String exportName;
if (isName("as")) {
consume("as");
exportName = identifierName();
} else {
exportName = sourceName;
}
return new ExportSpecifier(begin, ts.endPosition(), sourceName, exportName);
}
/**
* <strong>[Extension] Exports</strong>
*
* <pre>
* ExportFromClause :
* *
* ExportedDefaultBinding
* NameSpaceExport
* NamedExports
* ExportedDefaultBinding , NameSpaceExport
* ExportedDefaultBinding , NamedExports
* </pre>
*
* @param begin
* the begin position
* @param isDefault
* {@code true} if the exported default binding is "default"
* @return the parsed export from clause
*/
private ExportClause exportFromClause(long begin, boolean isDefault) {
assert token() != Token.MUL && token() != Token.LC;
IdentifierName defaultEntry;
if (isDefault) {
defaultEntry = new IdentifierName(begin, ts.endPosition(), "default");
} else {
defaultEntry = exportedDefaultBinding();
}
IdentifierName nameSpace = null;
List<ExportSpecifier> namedExports = emptyList();
if (token() == Token.COMMA) {
consume(Token.COMMA);
if (token() == Token.MUL) {
consume(Token.MUL);
nameSpace = nameSpaceExport();
} else {
namedExports = namedExports();
}
}
return new ExportClause(begin, ts.endPosition(), defaultEntry, namedExports, nameSpace);
}
private ExportClause exportNameSpaceFromClause(long begin) {
IdentifierName defaultEntry = null;
IdentifierName nameSpace = nameSpaceExport();
List<ExportSpecifier> namedExports = emptyList();
return new ExportClause(begin, ts.endPosition(), defaultEntry, namedExports, nameSpace);
}
/**
* <strong>[Extension] Exports</strong>
*
* <pre>
* NameSpaceExport :
* * as IdentifierName
* </pre>
*
* @return the parsed namespace export
*/
private IdentifierName nameSpaceExport() {
// Token.MUL already consumed in caller.
consume("as");
long begin = ts.beginPosition();
String name = identifierName();
return new IdentifierName(begin, ts.endPosition(), name);
}
/**
* <strong>[Extension] Exports</strong>
*
* <pre>
* NamedExports :
* { }
* { ExportsList }
* { ExportsList , }
* </pre>
*
* @return the parsed export from clause
*/
private List<ExportSpecifier> namedExports() {
InlineArrayList<ExportSpecifier> namedExports = newList();
consume(Token.LC);
while (token() != Token.RC) {
namedExports.add(exportSpecifier());
if (token() == Token.COMMA) {
consume(Token.COMMA);
} else {
break;
}
}
consume(Token.RC);
return namedExports;
}
/**
* <strong>[Extension] Exports</strong>
*
* <pre>
* ExportedDefaultBinding :
* IdentifierName
* </pre>
*
* @return the parsed exported default binding
*/
private IdentifierName exportedDefaultBinding() {
long begin = ts.beginPosition();
String name = identifierName();
return new IdentifierName(begin, ts.endPosition(), name);
}
/**
* <strong>[14.1.1] Directive Prologues and the Use Strict Directive</strong>
*
* <pre>
* DirectivePrologue :
* Directive<span><sub>opt</sub></span>
* Directive:
* StringLiteral ;
* Directive StringLiteral ;
* </pre>
*
* @param allowUseStrict
* {@code true} if 'use strict' directives are allowed
* @return the parsed list of directives
*/
private List<StatementListItem> directivePrologue(boolean allowUseStrict) {
if (token() != Token.STRING) {
applyStrictMode(false);
return emptyList();
}
InlineArrayList<StatementListItem> statements = newList();
boolean strict = false;
directive: do {
long begin = ts.beginPosition();
boolean hasEscape = ts.hasEscape(); // peek() clears hasEscape if next token is string
Token next = peek();
switch (next) {
case SEMI:
case RC:
case EOF:
break;
default:
if (noNextLineTerminator() || stringLiteralFollowSetNextLine(next)) {
break directive;
}
break;
}
// found a directive
String string = stringLiteral();
if (!hasEscape && "use strict".equals(string)) {
if (!allowUseStrict) {
reportSyntaxError(begin, Messages.Key.InvalidUseStrictDirective);
}
strict = true;
}
StringLiteral stringLiteral = new StringLiteral(begin, ts.endPosition(), string);
semicolon();
statements.add(new ExpressionStatement(begin, ts.endPosition(), stringLiteral));
} while (token() == Token.STRING);
applyStrictMode(strict);
return statements;
}
private static boolean stringLiteralFollowSetNextLine(Token token) {
switch (token) {
case DOT:
case LB:
case LP:
case TEMPLATE:
case COMMA:
case HOOK:
return true;
default:
return Token.isBinaryOperator(token) || Token.isAssignmentOperator(token);
}
}
private void applyStrictMode(boolean strict) {
if (strict) {
context.strictMode = StrictMode.Strict;
context.explicitStrict = true;
if (context.strictError != null) {
reportException(context.strictError);
}
} else {
if (context.strictMode == StrictMode.Unknown) {
context.strictMode = context.parent.strictMode;
}
}
}
/* ***************************************************************************************** */
private static FunctionNode.StrictMode toFunctionStrictness(boolean strict, boolean explicit) {
if (!strict) {
return FunctionNode.StrictMode.NonStrict;
}
if (explicit) {
return FunctionNode.StrictMode.ExplicitStrict;
}
return FunctionNode.StrictMode.ImplicitStrict;
}
private <FUNCTION extends FunctionNode> FUNCTION inheritStrictness(FUNCTION function) {
ParseContext context = this.context;
if (context.strictMode != StrictMode.Unknown) {
boolean strict = (context.strictMode == StrictMode.Strict);
assert !strict || context.funContext.blockFunctions == null;
function.setStrictMode(toFunctionStrictness(strict, context.explicitStrict));
if (context.deferred != null) {
deferredInheritStrictness(strict);
}
} else {
// This case only applies for functions in default parameter expressions.
deferInheritStrictness(function);
}
return function;
}
private void deferredInheritStrictness(boolean strict) {
ParseContext context = this.context;
for (FunctionNode func : context.deferred) {
func.setStrictMode(toFunctionStrictness(strict, false));
if (strict) {
FunctionContext fc = (FunctionContext) func.getScope();
InlineArrayList<FunctionDeclaration> blockFunctions = fc.blockFunctions;
if (blockFunctions != null) {
for (FunctionDeclaration blockFunction : blockFunctions) {
blockFunction.setLegacyBlockScoped(false);
}
fc.blockFunctions = null;
fc.blockFunctionNames = null;
}
}
}
context.deferred = null;
}
private <FUNCTION extends FunctionNode> void deferInheritStrictness(FUNCTION function) {
ParseContext context = this.context;
assert context.parent.strictMode == StrictMode.Unknown;
ParseContext parent = context.parent;
if (parent.deferred == null) {
parent.deferred = newList();
}
parent.deferred.add(function);
if (context.deferred != null) {
parent.deferred.addAll(context.deferred);
context.deferred = null;
}
}
/**
* <strong>[14.1] Function Definitions</strong>
*
* <pre>
* FunctionDeclaration<span><sub>[Yield, Default]</sub></span> :
* function BindingIdentifier<span><sub>[?Yield]</sub></span> ( FormalParameters ) { FunctionBody }
* <span><sub>[+Default]</sub></span> function ( FormalParameters ) { FunctionBody }
* </pre>
*
* @param isDefault
* the flag to select whether or not the declaration is part of a default export
* @return the parsed function declaration
*/
private FunctionDeclaration functionDeclaration(boolean isDefault) {
newContext(ContextKind.Function);
try {
long begin = ts.beginPosition();
consume(Token.FUNCTION);
boolean hasName = !isDefault || token() != Token.LP;
BindingIdentifier identifier;
String functionName;
if (hasName) {
identifier = bindingIdentifierFunctionName(true);
functionName = identifier.getName().getIdentifier();
} else {
identifier = null;
functionName = DEFAULT_EXPORT_NAME;
}
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = formalParameters(Token.RP);
consume(Token.RP);
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
returnTypeAnnotation();
}
String header, body;
List<StatementListItem> statements;
if (token() != Token.LC && isEnabled(CompatibilityOption.ExpressionClosure)) {
int startBody = ts.position();
statements = expressionClosureBody(parameters);
int endFunction = ts.position();
header = ts.range(startFunction, startBody);
body = "return " + ts.range(startBody, endFunction);
} else {
consume(Token.LC);
int startBody = ts.position();
statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
header = ts.range(startFunction, startBody - 1);
body = ts.range(startBody, endFunction);
}
FunctionContext scope = context.funContext;
FunctionDeclaration function = new FunctionDeclaration(begin, ts.endPosition(), scope,
identifier, parameters, statements, functionName, header, body);
scope.setNode(function);
function_EarlyErrors(function);
addFunctionDeclaration(function, hasName && isDefault);
return inheritStrictness(function);
} finally {
restoreContext();
}
}
/**
* <strong>[14.1] Function Definitions</strong>
*
* <pre>
* FunctionExpression :
* function BindingIdentifier<span><sub>opt</sub></span> ( FormalParameters ) { FunctionBody }
* </pre>
*
* @return the parsed function expression
*/
private FunctionExpression functionExpression() {
long begin = ts.beginPosition();
consume(Token.FUNCTION);
boolean hasName = token() != Token.LP;
if (hasName) {
enterBlockContext();
}
newContext(ContextKind.Function);
try {
BindingIdentifier identifier = null;
if (hasName) {
identifier = bindingIdentifierFunctionName(false);
addLexDeclaredName(identifier, context.parent, BoundName(identifier));
}
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = formalParameters(Token.RP);
consume(Token.RP);
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
returnTypeAnnotation();
}
String header, body;
List<StatementListItem> statements;
if (token() != Token.LC && isEnabled(CompatibilityOption.ExpressionClosure)) {
int startBody = ts.position();
statements = expressionClosureBody(parameters);
int endFunction = ts.position();
header = ts.range(startFunction, startBody);
body = "return " + ts.range(startBody, endFunction);
} else {
consume(Token.LC);
int startBody = ts.position();
statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
header = ts.range(startFunction, startBody - 1);
body = ts.range(startBody, endFunction);
}
FunctionContext scope = context.funContext;
FunctionExpression function = new FunctionExpression(begin, ts.endPosition(), scope,
identifier, parameters, statements, header, body);
scope.setNode(function);
function_EarlyErrors(function);
return inheritStrictness(function);
} finally {
restoreContext();
if (hasName) {
exitBlockContext();
}
}
}
/**
* <strong>[14.1] Function Definitions</strong>
*
* <pre>
* StrictFormalParameters<span><sub>[Yield]</sub></span> :
* FormalParameters<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param end
* the end token
* @return the parsed formal parameters list
*/
private FormalParameterList strictFormalParameters(Token end) {
return formalParameters(end);
}
/**
* <strong>[14.1] Function Definitions</strong>
*
* <pre>
* FormalParameters<span><sub>[Yield]</sub></span> :
* [empty]
* FormalParameterList<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param end
* the end token
* @return the parsed formal parameters list
*/
private FormalParameterList formalParameters(Token end) {
if (token() == end) {
return emptyFormalParameterList();
}
return formalParameterList(end);
}
private FormalParameterList emptyFormalParameterList() {
context.funContext.setParameterNames(Collections.<Name> emptyList());
return new FormalParameterList(ts.beginPosition(), ts.endPosition(),
Collections.<FormalParameter> emptyList());
}
private FormalParameterList arrowFormalParameterList(BindingIdentifier identifier) {
long begin = identifier.getBeginPosition();
BindingElement element = new BindingElement(begin, ts.endPosition(), identifier, null);
context.funContext.setParameterNames(BoundNames(identifier));
FormalParameter parameter = new FormalParameter(begin, ts.endPosition(), element, null);
return new FormalParameterList(begin, ts.endPosition(), singletonList(parameter));
}
/**
* <strong>[14.1] Function Definitions</strong>
*
* <pre>
* FormalParameterList<span><sub>[Yield]</sub></span> :
* FunctionRestParameter<span><sub>[?Yield]</sub></span>
* FormalsList<span><sub>[?Yield]</sub></span>
* FormalsList<span><sub>[?Yield]</sub></span>, FunctionRestParameter<span><sub>[?Yield]</sub></span>
* FormalsList<span><sub>[Yield]</sub></span> :
* FormalParameter<span><sub>[?Yield]</sub></span>
* FormalsList<span><sub>[?Yield]</sub></span>, FormalParameter<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param end
* the end token
* @return the parsed formal parameters list
*/
private FormalParameterList formalParameterList(Token end) {
long begin = ts.beginPosition();
InlineArrayList<FormalParameter> formals = newList();
for (;;) {
if (token() == Token.TRIPLE_DOT) {
formals.add(functionRestParameter());
break;
} else {
formals.add(formalParameter());
if (token() == Token.COMMA) {
consume(Token.COMMA);
if (token() == end && isEnabled(CompatibilityOption.FunctionCallTrailingComma)) {
break;
}
} else {
break;
}
}
}
context.funContext.setParameterNames(BoundNames(formals));
return new FormalParameterList(begin, ts.endPosition(), formals);
}
/**
* <strong>[14.1] Function Definitions</strong>
*
* <pre>
* FunctionRestParameter<span><sub>[Yield]</sub></span> :
* BindingRestElement<span><sub>[?Yield]</sub></span>
* </pre>
*
* @return the parsed formal parameter
*/
private FormalParameter functionRestParameter() {
boolean nonSimple = token() == Token.LB || token() == Token.LC;
FormalParameterContext scope = null;
if (nonSimple) {
scope = enterFormalParameterContext();
}
long begin = ts.beginPosition();
BindingRestElement restElement = bindingRestElement(true);
FormalParameter parameter = new FormalParameter(begin, ts.endPosition(), restElement);
if (nonSimple) {
scope.node = parameter;
exitFormalParameterContext();
}
return parameter;
}
/**
* <strong>[14.1] Function Definitions</strong>
*
* <pre>
* FormalParameter<span><sub>[Yield]</sub></span> :
* BindingElement<span><sub>[?Yield]</sub></span>
* </pre>
*
* @return the parsed formal parameter
*/
private FormalParameter formalParameter() {
boolean nonSimple = token() == Token.LB || token() == Token.LC
|| !(isBindingIdentifier(token()) && !LOOKAHEAD(Token.ASSIGN));
FormalParameterContext scope = null;
if (nonSimple) {
scope = enterFormalParameterContext();
}
long begin = ts.beginPosition();
BindingElement element = bindingElement(true, true);
FormalParameter parameter = new FormalParameter(begin, ts.endPosition(), element, scope);
if (nonSimple) {
scope.node = parameter;
exitFormalParameterContext();
}
return parameter;
}
private static Name containsAny(List<Name> list, NameSet set) {
for (Name element : list) {
if (set.contains(element)) {
return element;
}
}
return null;
}
private void checkFormalParameterRedeclaration(FunctionNode node, List<Name> boundNames,
NameSet declaredNames) {
if (!(declaredNames == null || declaredNames.isEmpty())) {
Name redeclared = containsAny(boundNames, declaredNames);
if (redeclared != null) {
BindingIdentifier parameter = FindParameter.find(node, redeclared);
reportSyntaxError(parameter, Messages.Key.FormalParameterRedeclaration, redeclared);
}
}
}
private static Name findDuplicate(NameSet set, List<Name> list) {
assert list.size() > set.size();
NameSet copy = new NameSet(set);
for (Name element : list) {
if (!copy.remove(element)) {
return element;
}
}
throw new AssertionError(String.format("no duplicate: %s - %s", set, list));
}
private void checkFormalParameterDuplication(FunctionNode node, List<Name> boundNames, NameSet names) {
boolean hasDuplicates = (boundNames.size() != names.size());
if (hasDuplicates) {
Name duplicate = findDuplicate(names, boundNames);
BindingIdentifier parameter = FindParameter.find(node, duplicate);
reportSyntaxError(parameter, Messages.Key.DuplicateFormalParameter, duplicate);
}
}
private void checkFormalParameterDuplicationStrict(FunctionNode node, List<Name> boundNames, NameSet names) {
boolean hasDuplicates = (boundNames.size() != names.size());
if (hasDuplicates) {
Name duplicate = findDuplicate(names, boundNames);
BindingIdentifier parameter = FindParameter.find(node, duplicate);
reportStrictModeSyntaxError(parameter, Messages.Key.DuplicateFormalParameter, duplicate);
}
}
/**
* 14.1.2 Static Semantics: Early Errors
*
* @param function
* the function definition to validate
*/
private void function_EarlyErrors(FunctionDefinition function) {
assert context.scopeContext == context.funContext;
FunctionContext scope = context.funContext;
FormalParameterList parameters = function.getParameters();
List<Name> boundNames = BoundNames(parameters);
if (!IsSimpleParameterList(parameters)) {
checkFormalParameterDuplication(function, boundNames, scope.parameterNames);
} else if (context.strictMode != StrictMode.NonStrict) {
checkFormalParameterDuplicationStrict(function, boundNames, scope.parameterNames);
}
checkFormalParameterRedeclaration(function, boundNames, scope.lexDeclaredNames);
}
/**
* <strong>[14.1] Function Definitions</strong>
*
* <pre>
* FunctionBody<span><sub>[Yield]</sub></span> :
* FunctionStatementList<span><sub>[?Yield]</sub></span>
* FunctionStatementList<span><sub>[Yield]</sub></span> :
* StatementList<span><sub>[?Yield, Return]opt</sub></span>
* </pre>
*
* @param parameters
* the function parameters
* @param end
* the end token
* @return the list of parsed statement list items
*/
private List<StatementListItem> functionBody(FormalParameterList parameters, Token end) {
// enable 'return'
context.returnAllowed = true;
// enable 'yield' if in generator
context.yieldAllowed = context.kind.isGenerator();
// enable 'await' if in async function
context.awaitAllowed = context.kind.isAsync();
boolean allowUseStrict = true;
if (isEnabled(CompatibilityOption.StrictDirectiveSimpleParameterList)) {
allowUseStrict = IsSimpleParameterList(parameters);
}
List<StatementListItem> prologue = directivePrologue(allowUseStrict);
if (parameters.containsExpression()) {
context.funContext.variableScope = enterFunctionBodyContext();
context.funContext.lexicalScope = context.funContext.variableScope;
}
if (context.strictMode != StrictMode.Strict) {
context.funContext.lexicalScope = enterFunctionBodyContext();
}
List<StatementListItem> body = statementList(end);
if (context.strictMode != StrictMode.Strict) {
exitFunctionBodyContext();
}
if (parameters.containsExpression()) {
exitFunctionBodyContext();
}
assert context.assertLiteralsUnchecked(0);
computeBlockFunctions();
return merge(prologue, body);
}
private void computeBlockFunctions() {
// TODO: Consider moving computeBlockFunctions() after node is assigned to funscope.
assert context.kind.isFunction();
if (!isEnabled(CompatibilityOption.BlockFunctionDeclaration)) {
return;
}
FunctionContext funScope = context.funContext;
ScopeContext topScope = funScope.lexicalScope;
InlineArrayList<FunctionDeclaration> functions = funScope.blockFunctions;
if (functions == null) {
return;
}
assert context.strictMode != StrictMode.Strict : "block functions in strict mode";
InlineArrayList<FunctionDeclaration> blockFunctions = new InlineArrayList<>();
for (FunctionDeclaration function : functions) {
if (hasEnclosingLexicalDeclaration(function, topScope)) {
continue;
}
// See 14.1.2 Static Semantics: Early Errors
Name name = function.getIdentifier().getName();
if (topScope.allowVarDeclaredName(name) && !funScope.parameterNames.contains(name)) {
// Function declaration is applicable for legacy semantics, iff
// (1) Adding a VariableStatement with the same name would not produce an error.
// (2) The name is not an element of BoundNames of FormalParameters.
function.setLegacyBlockScoped(true);
blockFunctions.add(function);
}
}
funScope.setBlockFunctions(blockFunctions);
}
private void computeBlockFunctionsForScript() {
assert context.kind.isScript();
if (!isEnabled(CompatibilityOption.BlockFunctionDeclaration)) {
return;
}
ScriptContext scriptScope = context.scriptContext;
InlineArrayList<FunctionDeclaration> functions = scriptScope.blockFunctions;
if (functions == null) {
return;
}
assert context.strictMode != StrictMode.Strict : "block functions in strict mode";
InlineArrayList<FunctionDeclaration> blockFunctions = new InlineArrayList<>();
for (FunctionDeclaration function : functions) {
if (hasEnclosingLexicalDeclaration(function, scriptScope)) {
continue;
}
// See 15.1.1 Static Semantics: Early Errors
Name name = function.getIdentifier().getName();
if (scriptScope.allowVarDeclaredName(name)) {
// Function declaration is applicable for legacy semantics, iff
// (1) Adding a VariableStatement with the same name would not produce an error.
function.setLegacyBlockScoped(true);
blockFunctions.add(function);
}
}
scriptScope.setBlockFunctions(blockFunctions);
}
private boolean hasEnclosingLexicalDeclaration(FunctionDeclaration function, ScopeContext topScope) {
Name name = function.getIdentifier().getName();
ScopeContext enclosingScope = (ScopeContext) function.getScope().getEnclosingScope();
// Top-level function declarations are not applicable for legacy semantics.
assert enclosingScope != topScope : "top-level function declaration";
assert enclosingScope.isDeclared(name) : "undeclared block scoped function: " + name;
for (ScopeContext scope = enclosingScope; (scope = scope.parent) != topScope;) {
// See 13.2.1 Static Semantics: Early Errors
// See 13.12.1 Static Semantics: Early Errors
if (scope.isDeclared(name)) {
// Found a block scoped, lexical declaration - cannot declare function as var.
if (scope instanceof CatchContext) {
// Unless "B.3.5 VariableStatements in Catch blocks" applies.
continue;
}
return true;
}
}
return false;
}
/**
* <strong>[14.1] Function Definitions</strong>
*
* <pre>
* ExpressionClosureBody<span><sub>[Yield]</sub></span> :
* AssignmentExpression<span><sub>[In, ?Yield]</sub></span>
* </pre>
*
* @return the list of parsed statement list items
*/
private List<StatementListItem> expressionClosureBody(FormalParameterList parameters) {
assert !(context.kind.isGenerator() || context.kind.isAsync());
// enable 'return'
context.returnAllowed = true;
// Necessary to call applyStrictMode() manually b/c directivePrologue() is not used.
applyStrictMode(false);
if (parameters.containsExpression()) {
context.funContext.variableScope = enterFunctionBodyContext();
context.funContext.lexicalScope = context.funContext.variableScope;
}
if (context.strictMode != StrictMode.Strict) {
context.funContext.lexicalScope = enterFunctionBodyContext();
}
Expression expr = assignmentExpression(true);
if (context.strictMode != StrictMode.Strict) {
exitFunctionBodyContext();
}
if (parameters.containsExpression()) {
exitFunctionBodyContext();
}
assert context.assertLiteralsUnchecked(0);
return Collections.<StatementListItem> singletonList(new ReturnStatement(
ts.beginPosition(), ts.endPosition(), expr));
}
/**
* <strong>[14.2] Arrow Function Definitions</strong>
*
* <pre>
* ArrowFunction<span><sub>[In, Yield]</sub></span> :
* ArrowParameters<span><sub>[?Yield]</sub></span> [no <i>LineTerminator</i> here] {@literal =>} ConciseBody<span><sub>[?In]</sub></span>
* ArrowParameters<span><sub>[Yield]</sub></span> :
* BindingIdentifier<span><sub>[?Yield]</sub></span>
* CoverParenthesizedExpressionAndArrowParameterList<span><sub>[?Yield]</sub></span>
* ConciseBody<span><sub>[In]</sub></span> :
* [LA ∉ { <b>{</b> }] AssignmentExpression<span><sub>[?In]</sub></span>
* { FunctionBody }
* </pre>
*
* <h2>Supplemental Syntax</h2>
*
* <pre>
* ArrowFormalParameters<span><sub>[Yield]</sub></span> :
* ( StrictFormalParameters<span><sub>[?Yield]</sub></span> )
* </pre>
*
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed arrow function
*/
private ArrowFunction arrowFunction(boolean allowIn) {
newContext(ContextKind.ArrowFunction);
try {
long begin = ts.beginPosition();
int startFunction;
StringBuilder source = new StringBuilder();
FormalParameterList parameters;
if (token() == Token.LP) {
consume(Token.LP);
startFunction = ts.position() - 1;
context.yieldAllowed = context.parent.yieldAllowed;
context.awaitAllowed = context.parent.awaitAllowed;
parameters = strictFormalParameters(Token.RP);
context.yieldAllowed = false;
context.awaitAllowed = false;
consume(Token.RP);
} else {
// Don't need to set {await,yield}Allowed for single parameter case.
BindingIdentifier identifier = bindingIdentifier();
parameters = arrowFormalParameterList(identifier);
startFunction = ts.position();
source.append(identifier.getName().getIdentifier());
}
if (!noLineTerminator()) {
reportSyntaxError(Messages.Key.UnexpectedEndOfLine);
}
consume(Token.ARROW);
if (token() == Token.LC) {
consume(Token.LC);
int startBody = ts.position();
List<StatementListItem> statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
String header = source.append(ts.range(startFunction, startBody - 1)).toString();
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
ArrowFunction function = new ArrowFunction(begin, ts.endPosition(), scope,
parameters, statements, header, body);
scope.setNode(function);
arrowFunction_EarlyErrors(function);
return inheritStrictness(function);
} else {
int startBody = ts.position();
Expression expression = arrowFunctionExpressionBody(parameters, allowIn);
int endFunction = ts.position();
String header = source.append(ts.range(startFunction, startBody)).toString();
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
ArrowFunction function = new ArrowFunction(begin, ts.endPosition(), scope,
parameters, expression, header, body);
scope.setNode(function);
arrowFunction_EarlyErrors(function);
return inheritStrictness(function);
}
} finally {
restoreContext();
}
}
private Expression arrowFunctionExpressionBody(FormalParameterList parameters, boolean allowIn) {
// enable 'return'
context.returnAllowed = true;
// enable 'yield' if in generator
context.yieldAllowed = context.kind.isGenerator();
// enable 'await' if in async function
context.awaitAllowed = context.kind.isAsync();
// Necessary to call applyStrictMode() manually b/c directivePrologue() is not used.
applyStrictMode(false);
if (parameters.containsExpression()) {
context.funContext.variableScope = enterFunctionBodyContext();
context.funContext.lexicalScope = context.funContext.variableScope;
}
if (context.strictMode != StrictMode.Strict) {
context.funContext.lexicalScope = enterFunctionBodyContext();
}
Expression expression = assignmentExpression(allowIn);
if (context.strictMode != StrictMode.Strict) {
exitFunctionBodyContext();
}
if (parameters.containsExpression()) {
exitFunctionBodyContext();
}
assert context.assertLiteralsUnchecked(0);
return expression;
}
/**
* 14.2.1 Static Semantics: Early Errors
*
* @param function
* the arrow function node to validate
*/
private void arrowFunction_EarlyErrors(ArrowFunction function) {
assert context.scopeContext == context.funContext;
FunctionContext scope = context.funContext;
List<Name> boundNames = BoundNames(function.getParameters());
checkFormalParameterDuplication(function, boundNames, scope.parameterNames);
checkFormalParameterRedeclaration(function, boundNames, scope.lexDeclaredNames);
}
/**
* <strong>[14.3] Method Definitions</strong>
*
* <pre>
* MethodDefinition<span><sub>[Yield]</sub></span> :
* PropertyName<span><sub>[?Yield]</sub></span> ( StrictFormalParameters ) { FunctionBody }
* GeneratorMethod<span><sub>[?Yield]</sub></span>
* get PropertyName<span><sub>[?Yield]</sub></span> ( ) { FunctionBody }
* set PropertyName<span><sub>[?Yield]</sub></span> ( PropertySetParameterList ) { FunctionBody }
* </pre>
*
* @param allocation
* the method allocation kind
* @param hasExtends
* {@code true} if the ClassHeritage expression is present
* @param decorators
* the list of method decorators
* @return the parsed method definition
*/
private MethodDefinition methodDefinition(MethodAllocation allocation, boolean hasExtends,
List<Expression> decorators) {
switch (methodType()) {
case AsyncFunction:
return asyncMethod(allocation, decorators);
case AsyncGenerator:
return asyncGeneratorMethod(allocation, decorators);
case Generator:
return generatorMethod(allocation, decorators);
case Getter:
return getterMethod(allocation, decorators);
case Setter:
return setterMethod(allocation, decorators);
default:
return normalMethod(allocation, hasExtends, decorators);
}
}
/**
* <strong>[14.3] Method Definitions</strong>
*
* <pre>
* MethodDefinition<span><sub>[Yield]</sub></span> :
* PropertyName<span><sub>[?Yield]</sub></span> ( StrictFormalParameters ) { FunctionBody }
* </pre>
*
* @param allocation
* the method allocation kind
* @param hasExtends
* {@code true} if the ClassHeritage expression is present
* @param decorators
* the list of method decorators
* @return the parsed method definition
*/
private MethodDefinition normalMethod(MethodAllocation allocation, boolean hasExtends,
List<Expression> decorators) {
long begin = ts.beginPosition();
PropertyName propertyName = propertyName();
return normalMethod(allocation, hasExtends, decorators, begin, propertyName, false);
}
private MethodDefinition callConstructor(boolean hasExtends, List<Expression> decorators) {
// FIXME: spec issue - decorators on call constructors?
if (!decorators.isEmpty()) {
reportSyntaxError(decorators.get(0), Messages.Key.InvalidCallConstructorDecorator);
}
long beginPosition = ts.beginPosition();
consume("call");
assert isName("constructor");
PropertyName propertyName = propertyName();
return normalMethod(MethodAllocation.Prototype, hasExtends, decorators, beginPosition, propertyName, true);
}
private MethodDefinition normalMethod(MethodAllocation allocation, boolean hasExtends, List<Expression> decorators,
long begin, PropertyName propertyName, boolean callConstructor) {
newContext(ContextKind.Method);
try {
MethodType type;
if (callConstructor) {
assert allocation == MethodAllocation.Prototype;
type = MethodType.CallConstructor;
} else if (allocation == MethodAllocation.Prototype && "constructor".equals(propertyName.getName())) {
context.isDerivedClassConstructor = hasExtends;
type = hasExtends ? MethodType.DerivedConstructor : MethodType.BaseConstructor;
} else {
type = MethodType.Function;
}
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = strictFormalParameters(Token.RP);
consume(Token.RP);
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
returnTypeAnnotation();
}
consume(Token.LC);
int startBody = ts.position();
List<StatementListItem> statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
String header = ts.range(startFunction, startBody - 1);
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
MethodDefinition method = new MethodDefinition(begin, ts.endPosition(), scope, type,
allocation, decorators, propertyName, parameters, statements, header, body);
scope.setNode(method);
methodDefinition_EarlyErrors(method);
return inheritStrictness(method);
} finally {
restoreContext();
}
}
/**
* <strong>[14.3] Method Definitions</strong>
*
* <pre>
* MethodDefinition<span><sub>[Yield]</sub></span> :
* get PropertyName<span><sub>[?Yield]</sub></span> ( ) { FunctionBody }
* </pre>
*
* @param allocation
* the method allocation kind
* @param decorators
* the list of method decorators
* @return the parsed getter method definition
*/
private MethodDefinition getterMethod(MethodAllocation allocation, List<Expression> decorators) {
long begin = ts.beginPosition();
consume(Token.NAME); // "get"
PropertyName propertyName = propertyName();
newContext(ContextKind.Method);
try {
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = emptyFormalParameterList();
consume(Token.RP);
List<StatementListItem> statements;
String header, body;
if (token() != Token.LC && isEnabled(CompatibilityOption.ExpressionClosure)) {
if (allocation != MethodAllocation.Object) {
reportTokenMismatch(Token.LC, token());
}
int startBody = ts.position();
statements = expressionClosureBody(parameters);
int endFunction = ts.position();
header = ts.range(startFunction, startBody);
body = "return " + ts.range(startBody, endFunction);
} else {
consume(Token.LC);
int startBody = ts.position();
statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
header = ts.range(startFunction, startBody - 1);
body = ts.range(startBody, endFunction);
}
FunctionContext scope = context.funContext;
MethodType type = MethodType.Getter;
MethodDefinition method = new MethodDefinition(begin, ts.endPosition(), scope, type,
allocation, decorators, propertyName, parameters, statements, header, body);
scope.setNode(method);
methodDefinition_EarlyErrors(method);
return inheritStrictness(method);
} finally {
restoreContext();
}
}
/**
* <strong>[14.3] Method Definitions</strong>
*
* <pre>
* MethodDefinition<span><sub>[Yield]</sub></span> :
* set PropertyName<span><sub>[?Yield]</sub></span> ( PropertySetParameterList ) { FunctionBody }
* </pre>
*
* @param allocation
* the method allocation kind
* @param decorators
* the list of method decorators
* @return the parsed setter method definition
*/
private MethodDefinition setterMethod(MethodAllocation allocation, List<Expression> decorators) {
long begin = ts.beginPosition();
consume(Token.NAME); // "set"
PropertyName propertyName = propertyName();
newContext(ContextKind.Method);
try {
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = propertySetParameterList();
consume(Token.RP);
List<StatementListItem> statements;
String header, body;
if (token() != Token.LC && isEnabled(CompatibilityOption.ExpressionClosure)) {
if (allocation != MethodAllocation.Object) {
reportTokenMismatch(Token.LC, token());
}
int startBody = ts.position();
statements = expressionClosureBody(parameters);
int endFunction = ts.position();
header = ts.range(startFunction, startBody);
body = "return " + ts.range(startBody, endFunction);
} else {
consume(Token.LC);
int startBody = ts.position();
statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
header = ts.range(startFunction, startBody - 1);
body = ts.range(startBody, endFunction);
}
FunctionContext scope = context.funContext;
MethodType type = MethodType.Setter;
MethodDefinition method = new MethodDefinition(begin, ts.endPosition(), scope, type,
allocation, decorators, propertyName, parameters, statements, header, body);
scope.setNode(method);
methodDefinition_EarlyErrors(method);
return inheritStrictness(method);
} finally {
restoreContext();
}
}
/**
* <strong>[14.3] Method Definitions</strong>
*
* <pre>
* PropertySetParameterList :
* FormalParameter
* </pre>
*
* @return the parsed formal parameters list
*/
private FormalParameterList propertySetParameterList() {
long begin = ts.beginPosition();
FormalParameter setParameter = formalParameter();
context.funContext.setParameterNames(BoundNames(setParameter));
return new FormalParameterList(begin, ts.endPosition(), singletonList(setParameter));
}
private MethodType methodType() {
switch (token()) {
case MUL:
return MethodType.Generator;
case NAME:
String name = getName(Token.NAME);
if ("get".equals(name) && isPropertyName(peek())) {
return MethodType.Getter;
}
if ("set".equals(name) && isPropertyName(peek())) {
return MethodType.Setter;
}
break;
case ASYNC:
if (isEnabled(CompatibilityOption.AsyncFunction)) {
if (isPropertyName(peek()) && noNextLineTerminator()) {
return MethodType.AsyncFunction;
}
}
if (isEnabled(CompatibilityOption.AsyncGenerator)) {
if (LOOKAHEAD(Token.MUL) && noNextLineTerminator()) {
return MethodType.AsyncGenerator;
}
}
break;
default:
}
return MethodType.Function; // or Constructor, or Property
}
private static boolean isPropertyName(Token token) {
switch (token) {
case STRING:
case NUMBER:
case LB:
return true;
default:
return Token.isIdentifierName(token);
}
}
/**
* 14.3.1 Static Semantics: Early Errors
*
* @param method
* the method definition node to validate
*/
private void methodDefinition_EarlyErrors(MethodDefinition method) {
assert context.scopeContext == context.funContext;
FunctionContext scope = context.funContext;
List<Name> boundNames = BoundNames(method.getParameters());
checkFormalParameterDuplication(method, boundNames, scope.parameterNames);
checkFormalParameterRedeclaration(method, boundNames, scope.lexDeclaredNames);
}
/**
* <strong>[14.4] Generator Function Definitions</strong>
*
* <pre>
* GeneratorMethod<span><sub>[Yield]</sub></span> :
* * PropertyName<span><sub>[?Yield]</sub></span> ( StrictFormalParameters<span><sub>[Yield]</sub></span> ) { FunctionBody<span><sub>[Yield]</sub></span> }
* </pre>
*
* @param allocation
* the method allocation kind
* @param decorators
* the list of method decorators
* @return the parsed generator method definition
*/
private MethodDefinition generatorMethod(MethodAllocation allocation, List<Expression> decorators) {
long begin = ts.beginPosition();
consume(Token.MUL);
PropertyName propertyName = propertyName();
newContext(ContextKind.GeneratorMethod);
try {
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = strictFormalParameters(Token.RP);
consume(Token.RP);
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
returnTypeAnnotation();
}
consume(Token.LC);
int startBody = ts.position();
List<StatementListItem> statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
String header = ts.range(startFunction, startBody - 1);
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
MethodType type;
if (isEnabled(CompatibilityOption.GeneratorNonConstructor)) {
type = MethodType.Generator;
} else {
type = MethodType.ConstructorGenerator;
}
MethodDefinition method = new MethodDefinition(begin, ts.endPosition(), scope, type,
allocation, decorators, propertyName, parameters, statements, header, body);
scope.setNode(method);
methodDefinition_EarlyErrors(method);
return inheritStrictness(method);
} finally {
restoreContext();
}
}
/**
* <strong>[14.4] Generator Function Definitions</strong>
*
* <pre>
* GeneratorDeclaration<span><sub>[Yield, Default]</sub></span> :
* function * BindingIdentifier<span><sub>[?Yield]</sub></span> ( FormalParameters<span><sub>[Yield]</sub></span> ) { GeneratorBody<span><sub>[Yield]</sub></span> }
* <span><sub>[+Default]</sub></span> function * ( FormalParameters<span><sub>[Yield]</sub></span> ) { GeneratorBody<span><sub>[Yield]</sub></span> }
* GeneratorBody<span><sub>[Yield]</sub></span> :
* FunctionBody<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param isDefault
* the flag to select whether or not the declaration is part of a default export
* @param isLegacy
* the flag to mark this generator as a legacy, star-less generator
* @return the parsed generator declaration
*/
private GeneratorDeclaration generatorDeclaration(boolean isDefault, boolean isLegacy) {
newContext(ContextKind.Generator);
try {
context.legacyGenerator = isLegacy;
long begin = ts.beginPosition();
consume(Token.FUNCTION);
if (!isLegacy) {
consume(Token.MUL);
}
boolean hasName = !isDefault || token() != Token.LP;
BindingIdentifier identifier;
String functionName;
if (hasName) {
identifier = bindingIdentifierFunctionName(true);
functionName = identifier.getName().getIdentifier();
} else {
identifier = null;
functionName = DEFAULT_EXPORT_NAME;
}
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = formalParameters(Token.RP);
consume(Token.RP);
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
returnTypeAnnotation();
}
consume(Token.LC);
int startBody = ts.position();
List<StatementListItem> statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
String header = ts.range(startFunction, startBody - 1);
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
GeneratorDeclaration generator;
if (!isLegacy) {
generator = new GeneratorDeclaration(begin, ts.endPosition(), scope, generatorKind(), identifier,
parameters, statements, functionName, header, body);
} else {
generator = new LegacyGeneratorDeclaration(begin, ts.endPosition(), scope,
identifier, parameters, statements, functionName, header, body);
}
scope.setNode(generator);
generator_EarlyErrors(generator);
addDeclaration(generator, hasName && isDefault);
return inheritStrictness(generator);
} finally {
restoreContext();
}
}
/**
* <strong>[14.4] Generator Function Definitions</strong>
*
* <pre>
* GeneratorExpression :
* function * BindingIdentifier<span><sub>[Yield]opt</sub></span> ( FormalParameters<span><sub>[Yield]</sub></span> ) { FunctionBody<span><sub>[Yield]</sub></span> }
* </pre>
*
* @param isLegacy
* the flag to mark this generator as a legacy, star-less generator
* @return the parsed generator expression declaration
*/
private GeneratorExpression generatorExpression(boolean isLegacy) {
long begin = ts.beginPosition();
consume(Token.FUNCTION);
if (!isLegacy) {
consume(Token.MUL);
}
boolean hasName = token() != Token.LP;
if (hasName) {
enterBlockContext();
}
newContext(ContextKind.Generator);
try {
context.legacyGenerator = isLegacy;
BindingIdentifier identifier = null;
if (hasName) {
identifier = bindingIdentifierFunctionName(false);
addLexDeclaredName(identifier, context.parent, BoundName(identifier));
}
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = formalParameters(Token.RP);
consume(Token.RP);
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
returnTypeAnnotation();
}
consume(Token.LC);
int startBody = ts.position();
List<StatementListItem> statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
String header = ts.range(startFunction, startBody - 1);
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
GeneratorExpression generator;
if (!isLegacy) {
generator = new GeneratorExpression(begin, ts.endPosition(), scope, generatorKind(), identifier,
parameters, statements, header, body);
} else {
generator = new LegacyGeneratorExpression(begin, ts.endPosition(), scope,
identifier, parameters, statements, header, body);
}
scope.setNode(generator);
generator_EarlyErrors(generator);
return inheritStrictness(generator);
} finally {
restoreContext();
if (hasName) {
exitBlockContext();
}
}
}
/**
* 14.4.1 Static Semantics: Early Errors
*
* @param generator
* the generator to validate
*/
private void generator_EarlyErrors(GeneratorDefinition generator) {
assert context.scopeContext == context.funContext;
FunctionContext scope = context.funContext;
FormalParameterList parameters = generator.getParameters();
List<Name> boundNames = BoundNames(parameters);
if (!IsSimpleParameterList(parameters)) {
checkFormalParameterDuplication(generator, boundNames, scope.parameterNames);
} else if (context.strictMode != StrictMode.NonStrict) {
checkFormalParameterDuplicationStrict(generator, boundNames, scope.parameterNames);
}
checkFormalParameterRedeclaration(generator, boundNames, scope.lexDeclaredNames);
}
private GeneratorKind generatorKind() {
if (isEnabled(CompatibilityOption.GeneratorNonConstructor)) {
return GeneratorKind.NonConstructor;
}
return GeneratorKind.Constructor;
}
/**
* <strong>[14.4] Generator Function Definitions</strong>
*
* <pre>
* YieldExpression<span><sub>[In]</sub></span> :
* yield
* yield [no <i>LineTerminator</i> here] AssignmentExpression<span><sub>[?In, Yield]</sub></span>
* yield [no <i>LineTerminator</i> here] * AssignmentExpression<span><sub>[?In, Yield]</sub></span>
* </pre>
*
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed yield expression
*/
private YieldExpression yieldExpression(boolean allowIn) {
assert context.kind.isGenerator() && context.yieldAllowed;
context.yieldOrAwaitExpression |= true;
long begin = ts.beginPosition();
consume(Token.YIELD);
boolean delegatedYield = false;
if (token() == Token.MUL) {
if (!noLineTerminator()) {
reportSyntaxError(Messages.Key.UnexpectedEndOfLine);
}
consume(Token.MUL);
delegatedYield = true;
}
Expression expr;
if (delegatedYield) {
expr = assignmentExpression(allowIn);
} else if (!context.legacyGenerator) {
if (noLineTerminator() && assignmentExpressionFirstSet(token())) {
expr = assignmentExpression(allowIn);
} else {
expr = null;
}
} else {
// slightly different rules for optional AssignmentExpression in legacy generators
if (noLineTerminator() && !assignmentExpressionFollowSet(token())) {
expr = assignmentExpression(allowIn);
} else {
expr = null;
}
}
return new YieldExpression(begin, ts.endPosition(), delegatedYield, expr);
}
private boolean assignmentExpressionFirstSet(Token token) {
// returns FIRST(AssignmentExpression)
switch (token) {
case YIELD:
// FIRST(YieldExpression)
return true;
case DELETE:
case VOID:
case TYPEOF:
case INC:
case DEC:
case ADD:
case SUB:
case BITNOT:
case NOT:
// FIRST(UnaryExpression)
return true;
case SUPER:
case NEW:
// FIRST(LeftHandSideExpression)
return true;
case THIS:
case NULL:
case FALSE:
case TRUE:
case NUMBER:
case STRING:
case LB:
case LC:
case LP:
case FUNCTION:
case CLASS:
case AT:
case TEMPLATE:
// FIRST(PrimaryExpression)
return true;
case DIV:
case ASSIGN_DIV:
// FIRST(RegularExpressionLiteral)
return true;
case AWAIT:
if (context.kind.isAsync()) {
return true;
}
// fall-through
case ASYNC:
case LET:
case IMPLEMENTS:
case INTERFACE:
case PACKAGE:
case PRIVATE:
case PROTECTED:
case PUBLIC:
case STATIC:
case NAME:
case ESCAPED_NAME:
case ESCAPED_RESERVED_WORD:
case ESCAPED_STRICT_RESERVED_WORD:
case ESCAPED_YIELD:
case ESCAPED_ASYNC:
case ESCAPED_AWAIT:
case ESCAPED_LET:
// FIRST(Identifier)
return isIdentifierReference(token);
case DO:
// FIRST(DoExpression)
return isEnabled(CompatibilityOption.DoExpression);
default:
return false;
}
}
private boolean assignmentExpressionFollowSet(Token token) {
// returns FOLLOW(AssignmentExpression) without { "of", "in", "for", "{" }
// NB: not the exact follow set, consider `a = let(x=0)x++ ++`, but not relevant here
switch (token) {
case COLON:
case COMMA:
case RB:
case RC:
case RP:
case SEMI:
case EOF:
return true;
default:
return false;
}
}
/**
* <strong>[14.5] Class Definitions</strong>
*
* <pre>
* ClassDeclaration<span><sub>[Yield, Default]</sub></span> :
* class BindingIdentifier<span><sub>[?Yield]</sub></span> ClassTail<span><sub>[?Yield]</sub></span>
* [+Default] class ClassTail[?Yield]
* ClassTail<span><sub>[Yield]</sub></span> :
* ClassHeritage<span><sub>[?Yield]opt</sub></span> { ClassBody<span><sub>[?Yield]opt</sub></span> }
* </pre>
*
* @param isDefault
* the flag to select whether or not the declaration is part of a default export
* @param decorators
* the list of class decorators
* @return the parsed class declaration
*/
private ClassDeclaration classDeclaration(boolean isDefault, List<Expression> decorators) {
StrictMode strictMode = context.strictMode;
try {
// 10.2.1 - ClassDeclaration and ClassExpression is always strict code
context.strictMode = StrictMode.Strict;
long begin = ts.beginPosition();
consume(Token.CLASS);
boolean hasName = !isDefault || (token() != Token.EXTENDS && token() != Token.LC);
BindingIdentifier identifier;
String className;
if (hasName) {
identifier = bindingIdentifierClassName();
className = identifier.getName().getIdentifier();
} else {
identifier = null;
className = DEFAULT_EXPORT_NAME;
}
BlockContext scope = null;
if (hasName) {
scope = enterBlockContext();
// Create a second inner class binding (NB: distinct Name instance required).
addLexDeclaredName(identifier, context, BoundName(identifier).clone());
}
Expression heritage = null;
if (token() == Token.EXTENDS) {
heritage = classHeritage();
}
consume(Token.LC);
InlineArrayList<MethodDefinition> methods = newList();
List<PropertyDefinition> properties = classBody(identifier, heritage != null, methods);
if (hasName) {
exitBlockContext();
}
consume(Token.RC);
ClassDeclaration decl = new ClassDeclaration(begin, ts.endPosition(), scope,
decorators, identifier, heritage, methods, properties, className);
if (hasName) {
scope.node = decl;
}
addDeclaration(decl, isDefault && hasName);
return decl;
} finally {
context.strictMode = strictMode;
}
}
/**
* <strong>[14.5] Class Definitions</strong>
*
* <pre>
* ClassExpression<span><sub>[Yield]</sub></span> :
* class BindingIdentifier<span><sub>[?Yield]opt</sub></span> ClassTail<span><sub>[?Yield]</sub></span>
* ClassTail<span><sub>[Yield]</sub></span> :
* ClassHeritage<span><sub>[?Yield]opt</sub></span> { ClassBody<span><sub>[?Yield]opt</sub></span> }
* </pre>
*
* @param decorators
* the list of class decorators
* @return the parsed class expression
*/
private ClassExpression classExpression(List<Expression> decorators) {
StrictMode strictMode = context.strictMode;
try {
// 10.2.1 - ClassDeclaration and ClassExpression is always strict code
context.strictMode = StrictMode.Strict;
long begin = ts.beginPosition();
consume(Token.CLASS);
BindingIdentifier name = null;
if (token() != Token.EXTENDS && token() != Token.LC) {
name = bindingIdentifierClassName();
}
BlockContext scope = null;
if (name != null) {
scope = enterBlockContext(name);
}
Expression heritage = null;
if (token() == Token.EXTENDS) {
heritage = classHeritage();
}
consume(Token.LC);
InlineArrayList<MethodDefinition> methods = newList();
List<PropertyDefinition> properties = classBody(name, heritage != null, methods);
if (name != null) {
exitBlockContext();
}
consume(Token.RC);
ClassExpression expr = new ClassExpression(begin, ts.endPosition(), scope, decorators,
name, heritage, methods, properties);
if (name != null) {
scope.node = expr;
}
return expr;
} finally {
context.strictMode = strictMode;
}
}
/**
* <strong>[14.5] Class Definitions</strong>
*
* <pre>
* ClassHeritage<span><sub>[Yield]</sub></span> :
* extends LeftHandSideExpression<span><sub>[?Yield]</sub></span>
* </pre>
*
* @return the parsed class heritage expression
*/
private Expression classHeritage() {
consume(Token.EXTENDS);
return leftHandSideExpressionWithValidation(true);
}
/**
* <strong>[14.5] Class Definitions</strong>
*
* <pre>
* ClassBody<span><sub>[Yield]</sub></span> :
* ClassElementList<span><sub>[?Yield]</sub></span>
* ClassElementList<span><sub>[Yield]</sub></span> :
* ClassElement<span><sub>[?Yield]</sub></span>
* ClassElementList<span><sub>[?Yield]</sub></span> ClassElement<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param className
* the class name if present, otherwise {@code null}
* @param hasExtends
* {@code true} if the ClassHeritage expression is present
* @return the class methods in source order
*/
private List<PropertyDefinition> classBody(BindingIdentifier className, boolean hasExtends,
InlineArrayList<MethodDefinition> methods) {
int beginLine = ts.getLine();
InlineArrayList<PropertyDefinition> properties = newList();
while (token() != Token.RC) {
if (token() == Token.SEMI) {
consume(Token.SEMI);
continue;
}
properties.add(classElement(className, hasExtends, methods));
}
classBody_EarlyErrors(methods);
if (ConstructorMethod(methods) == null) {
MethodDefinition constructor = createSyntheticClassConstructor(beginLine, hasExtends);
if (className != null) {
constructor.setClassName(className.getName().getIdentifier());
}
methods.add(constructor);
properties.add(constructor);
}
return properties;
}
/**
* <strong>[14.5] Class Definitions</strong>
*
* <pre>
* ClassElement<span><sub>[Yield]</sub></span> :
* MethodDefinition<span><sub>[?Yield]</sub></span>
* static MethodDefinition<span><sub>[?Yield]</sub></span>
* ;
* </pre>
*
* @param className
* the class name if present, otherwise {@code null}
* @param hasExtends
* {@code true} if the ClassHeritage expression is present
* @param methods
* the list of method definitions
* @return the parsed class element
*/
private PropertyDefinition classElement(BindingIdentifier className, boolean hasExtends,
InlineArrayList<MethodDefinition> methods) {
List<Expression> decorators = token() == Token.AT ? decorators() : NO_DECORATORS;
MethodDefinition method;
if (token() == Token.STATIC && !LOOKAHEAD(Token.LP)) {
consume(Token.STATIC);
// TODO: Add "static" start position to node?
long begin = ts.beginPosition();
if (token() == Token.LB) {
// either `PropertyName = AssignmentExpression` or MethodDefinition (normal)
ComputedPropertyName propertyName = computedPropertyName();
if (token() == Token.ASSIGN && isEnabled(CompatibilityOption.StaticClassProperties)) {
return classProperty(begin, propertyName, decorators);
}
method = normalMethod(MethodAllocation.Class, hasExtends, decorators, begin, propertyName, false);
} else if (LOOKAHEAD(Token.ASSIGN) && isEnabled(CompatibilityOption.StaticClassProperties)) {
return classProperty(begin, literalPropertyName(), decorators);
} else {
method = methodDefinition(MethodAllocation.Class, hasExtends, decorators);
}
} else if (isName("call") && isNextName("constructor") && isEnabled(CompatibilityOption.CallConstructor)) {
method = callConstructor(hasExtends, decorators);
} else {
method = methodDefinition(MethodAllocation.Prototype, hasExtends, decorators);
}
methods.add(method);
if (className != null) {
method.setClassName(className.getName().getIdentifier());
}
return method;
}
private PropertyDefinition classProperty(long begin, PropertyName propertyName, List<Expression> decorators) {
if (!decorators.isEmpty()) {
reportSyntaxError(Messages.Key.InvalidPropertyDecorator);
}
if ("prototype".equals(propertyName.getName())) {
reportSyntaxError(begin, Messages.Key.InvalidPrototypeProperty);
}
consume(Token.ASSIGN);
Expression propertyValue = assignmentExpression(true);
if (IsAnonymousFunctionDefinition(propertyValue)) {
if (propertyName instanceof ComputedPropertyName) {
setFunctionName(propertyValue, (ComputedPropertyName) propertyName);
} else {
setFunctionName(propertyValue, propertyName);
}
}
return new PropertyValueDefinition(begin, ts.endPosition(), propertyName, propertyValue);
}
private MethodDefinition createSyntheticClassConstructor(int beginLine, boolean hasExtends) {
String sourceText;
if (hasExtends) {
sourceText = "constructor(...args){super(...args);}";
} else {
sourceText = "constructor(){}";
}
TokenStream tokenStream = ts;
TokenStream syntheticStream = new TokenStream(this, new TokenStreamInput(sourceText));
try {
ts = syntheticStream.initialize(beginLine);
return methodDefinition(MethodAllocation.Prototype, hasExtends, NO_DECORATORS);
} finally {
ts = tokenStream;
}
}
/**
* 14.5.1 Static Semantics: Early Errors
*
* @param methods
* the list of method definitions
*/
private void classBody_EarlyErrors(List<MethodDefinition> methods) {
boolean hasConstructor = false, hasCallConstructor = false;
for (MethodDefinition def : methods) {
String key = def.getPropertyName().getName();
if (key == null) {
assert def.getPropertyName() instanceof ComputedPropertyName;
continue;
}
if (def.getAllocation() == MethodAllocation.Class) {
if ("prototype".equals(key)) {
reportSyntaxError(def, Messages.Key.InvalidPrototypeMethod);
}
} else {
assert def.getAllocation() == MethodAllocation.Prototype;
if (!"constructor".equals(key)) {
continue;
}
switch (def.getType()) {
case BaseConstructor:
case DerivedConstructor:
if (hasConstructor) {
reportSyntaxError(def, Messages.Key.DuplicateConstructor, key);
}
hasConstructor = true;
break;
case CallConstructor:
if (hasCallConstructor) {
reportSyntaxError(def, Messages.Key.DuplicateCallConstructor, key);
}
hasCallConstructor = true;
break;
default:
assert SpecialMethod(def);
throw reportSyntaxError(def, Messages.Key.InvalidConstructorMethod);
}
}
}
}
private HoistableDeclaration asyncFunctionOrGeneratorDeclaration(boolean isDefault) {
long begin = ts.beginPosition();
consume(Token.ASYNC);
if (!noLineTerminator()) {
reportSyntaxError(Messages.Key.UnexpectedEndOfLine);
}
consume(Token.FUNCTION);
if (token() == Token.MUL && isEnabled(CompatibilityOption.AsyncGenerator)) {
return asyncGeneratorDeclaration(isDefault, begin);
}
return asyncFunctionDeclaration(isDefault, begin);
}
private Expression asyncFunctionOrGeneratorExpression() {
long begin = ts.beginPosition();
consume(Token.ASYNC);
if (!noLineTerminator()) {
reportSyntaxError(Messages.Key.UnexpectedEndOfLine);
}
consume(Token.FUNCTION);
if (token() == Token.MUL && isEnabled(CompatibilityOption.AsyncGenerator)) {
return asyncGeneratorExpression(begin);
}
return asyncFunctionExpression(begin);
}
/**
* <strong>[Extension] <code>async</code> Function Definitions</strong>
*
* <pre>
* AsyncFunctionDeclaration<span><sub>[Yield, Await, Default]</sub></span> :
* async [no <i>LineTerminator</i> here] function BindingIdentifier<span><sub>[?Yield, ?Await]</sub></span> ( FormalParameters<span><sub>[Await]</sub></span> ) { AsyncFunctionBody }
* <span><sub>[+Default]</sub></span> async [no <i>LineTerminator</i> here] function ( FormalParameters<span><sub>[Await]</sub></span> ) { AsyncFunctionBody }
* AsyncFunctionBody :
* FunctionBody<span><sub>[Await]</sub></span>
* </pre>
*
* @param isDefault
* the flag to select whether or not the declaration is part of a default export
* @param begin
* the begin position
* @return the parsed async function declaration
*/
private AsyncFunctionDeclaration asyncFunctionDeclaration(boolean isDefault, long begin) {
newContext(ContextKind.AsyncFunction);
try {
// `async function` already parsed in caller.
boolean hasName = !isDefault || token() != Token.LP;
BindingIdentifier identifier;
String functionName;
if (hasName) {
identifier = bindingIdentifierFunctionName(true);
functionName = identifier.getName().getIdentifier();
} else {
identifier = null;
functionName = DEFAULT_EXPORT_NAME;
}
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = formalParameters(Token.RP);
consume(Token.RP);
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
returnTypeAnnotation();
}
consume(Token.LC);
int startBody = ts.position();
List<StatementListItem> statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
String header = ts.range(startFunction, startBody - 1);
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
AsyncFunctionDeclaration function = new AsyncFunctionDeclaration(begin,
ts.endPosition(), scope, identifier, parameters, statements, functionName,
header, body);
scope.setNode(function);
asyncFunction_EarlyErrors(function);
addDeclaration(function, hasName && isDefault);
return inheritStrictness(function);
} finally {
restoreContext();
}
}
/**
* <strong>[Extension] <code>async</code> Function Definitions</strong>
*
* <pre>
* AsyncFunctionExpression :
* async [no <i>LineTerminator</i> here] function BindingIdentifier<span><sub>opt</sub></span> ( FormalParameters<span><sub>[Await]</sub></span> ) { AsyncFunctionBody }
* </pre>
*
* @param begin
* the begin position
* @return the parsed async function expression
*/
private AsyncFunctionExpression asyncFunctionExpression(long begin) {
// `async function` already parsed in caller.
boolean hasName = token() != Token.LP;
if (hasName) {
enterBlockContext();
}
newContext(ContextKind.AsyncFunction);
try {
BindingIdentifier identifier = null;
if (hasName) {
identifier = bindingIdentifierFunctionName(false);
addLexDeclaredName(identifier, context.parent, BoundName(identifier));
}
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = formalParameters(Token.RP);
consume(Token.RP);
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
returnTypeAnnotation();
}
consume(Token.LC);
int startBody = ts.position();
List<StatementListItem> statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
String header = ts.range(startFunction, startBody - 1);
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
AsyncFunctionExpression function = new AsyncFunctionExpression(begin, ts.endPosition(),
scope, identifier, parameters, statements, header, body);
scope.setNode(function);
asyncFunction_EarlyErrors(function);
return inheritStrictness(function);
} finally {
restoreContext();
if (hasName) {
exitBlockContext();
}
}
}
private void asyncFunction_EarlyErrors(AsyncFunctionDefinition function) {
assert context.scopeContext == context.funContext;
FunctionContext scope = context.funContext;
FormalParameterList parameters = function.getParameters();
List<Name> boundNames = BoundNames(parameters);
if (!IsSimpleParameterList(parameters)) {
checkFormalParameterDuplication(function, boundNames, scope.parameterNames);
} else if (context.strictMode != StrictMode.NonStrict) {
checkFormalParameterDuplicationStrict(function, boundNames, scope.parameterNames);
}
checkFormalParameterRedeclaration(function, boundNames, scope.lexDeclaredNames);
}
/**
* <strong>[Extension] <code>async</code> Function Definitions</strong>
*
* <pre>
* AsyncArrowFunction<span><sub>[In, Yield]</sub></span> :
* async [no <i>LineTerminator</i> here] AsyncArrowBindingIdentifier<span><sub>[?Yield]</sub></span> [no <i>LineTerminator</i> here] {@literal =>} AsyncConciseBody<span><sub>[?In]</sub></span>
* CoverCallExpressionAndAsyncArrowHead<span><sub>[?Yield, ?Await]</sub></span> [no <i>LineTerminator</i> here] {@literal =>} AsyncConciseBody<span><sub>[?In]</sub></span>
* AsyncConciseBody<span><sub>[In]</sub></span> :
* [lookahead ≠ <b>{</b>] AssignmentExpression<span><sub>[?In, Await]</sub></span>
* { AsyncFunctionBody }
* AsyncArrowBindingIdentifier<span><sub>[Yield]</sub></span> :
* BindingIdentifier<span><sub>[?Yield, Await]</sub></span>
* CoverCallExpressionAndAsyncArrowHead<span><sub>[Yield, Await]</sub></span> :
* MemberExpression<span><sub>[?Yield, ?Await]</sub></span> Arguments<span><sub>[?Yield, ?Await]</sub></span>
* AsyncArrowHead<span><sub>[Yield]</sub></span> :
* async [no <i>LineTerminator</i> here] ArrowFormalParameters<span><sub>[?Yield, Await]</sub></span>
* </pre>
*
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed async arrow function
*/
private AsyncArrowFunction asyncArrowFunction(boolean allowIn) {
newContext(ContextKind.AsyncArrowFunction);
try {
long begin = ts.beginPosition();
consume(Token.ASYNC);
if (!noLineTerminator()) {
reportSyntaxError(Messages.Key.UnexpectedEndOfLine);
}
int startFunction;
StringBuilder source = new StringBuilder();
FormalParameterList parameters;
if (token() == Token.LP) {
consume(Token.LP);
startFunction = ts.position() - 1;
context.yieldAllowed = context.parent.yieldAllowed;
context.awaitAllowed = false;
parameters = strictFormalParameters(Token.RP);
context.yieldAllowed = false;
consume(Token.RP);
} else {
// Don't need to set {await,yield}Allowed for single parameter case.
BindingIdentifier identifier = bindingIdentifier();
parameters = arrowFormalParameterList(identifier);
startFunction = ts.position();
source.append(identifier.getName().getIdentifier());
}
if (!noLineTerminator()) {
reportSyntaxError(Messages.Key.UnexpectedEndOfLine);
}
consume(Token.ARROW);
if (token() == Token.LC) {
consume(Token.LC);
int startBody = ts.position();
List<StatementListItem> statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
String header = source.append(ts.range(startFunction, startBody - 1)).toString();
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
AsyncArrowFunction function = new AsyncArrowFunction(begin, ts.endPosition(),
scope, parameters, statements, header, body);
scope.setNode(function);
asyncArrowFunction_EarlyErrors(function);
return inheritStrictness(function);
} else {
int startBody = ts.position();
Expression expression = arrowFunctionExpressionBody(parameters, allowIn);
int endFunction = ts.position();
String header = source.append(ts.range(startFunction, startBody - 1)).toString();
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
AsyncArrowFunction function = new AsyncArrowFunction(begin, ts.endPosition(),
scope, parameters, expression, header, body);
scope.setNode(function);
asyncArrowFunction_EarlyErrors(function);
return inheritStrictness(function);
}
} finally {
restoreContext();
}
}
/**
* Static Semantics: Early Errors
*
* @param function
* the async arrow function node to validate
*/
private void asyncArrowFunction_EarlyErrors(AsyncArrowFunction function) {
assert context.scopeContext == context.funContext;
FunctionContext scope = context.funContext;
List<Name> boundNames = BoundNames(function.getParameters());
checkFormalParameterDuplication(function, boundNames, scope.parameterNames);
checkFormalParameterRedeclaration(function, boundNames, scope.lexDeclaredNames);
}
/**
* <strong>[Extension] <code>async</code> Function Definitions</strong>
*
* <pre>
* AsyncMethod<span><sub>[Yield]</sub></span> :
* async [no <i>LineTerminator</i> here] PropertyName<span><sub>[?Yield, ?Await]</sub></span> ( StrictFormalParameters<span><sub>[Await]</sub></span> ) { AsyncFunctionBody }
* </pre>
*
* @param allocation
* the method allocation kind
* @param decorators
* the list of method decorators
* @return the parsed async method
*/
private MethodDefinition asyncMethod(MethodAllocation allocation, List<Expression> decorators) {
long begin = ts.beginPosition();
consume(Token.ASYNC);
if (!noLineTerminator()) {
reportSyntaxError(Messages.Key.UnexpectedEndOfLine);
}
PropertyName propertyName = propertyName();
newContext(ContextKind.AsyncMethod);
try {
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = strictFormalParameters(Token.RP);
consume(Token.RP);
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
returnTypeAnnotation();
}
consume(Token.LC);
int startBody = ts.position();
List<StatementListItem> statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
String header = ts.range(startFunction, startBody - 1);
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
MethodType type = MethodType.AsyncFunction;
MethodDefinition method = new MethodDefinition(begin, ts.endPosition(), scope, type,
allocation, decorators, propertyName, parameters, statements, header, body);
scope.setNode(method);
methodDefinition_EarlyErrors(method);
return inheritStrictness(method);
} finally {
restoreContext();
}
}
/**
* <strong>[Extension] <code>async</code> Function Definitions</strong>
*
* <pre>
* AwaitExpression<span><sub>[Yield]</sub></span> :
* await UnaryExpression<span><sub>[?Yield, Await]</sub></span>
* </pre>
*
* @return the parsed await expression
*/
private AwaitExpression awaitExpression() {
assert context.kind.isAsync() && context.awaitAllowed;
context.yieldOrAwaitExpression |= true;
long begin = ts.beginPosition();
consume(Token.AWAIT);
Expression expr = unaryExpression(false);
return new AwaitExpression(begin, ts.endPosition(), expr);
}
/**
* <strong>[Extension] <code>async</code> Generator Function Definitions</strong>
*
* <pre>
* AsyncGeneratorDeclaration<span><sub>[Yield, Await, Default]</sub></span> :
* async [no <i>LineTerminator</i> here] function * BindingIdentifier<span><sub>[?Yield, ?Await]</sub></span> ( FormalParameters<span><sub>[Yield, Await]</sub></span> ) { AsyncGeneratorBody }
* <span><sub>[+Default]</sub></span> async [no <i>LineTerminator</i> here] function * ( FormalParameters<span><sub>[Yield, Await]</sub></span> ) { AsyncGeneratorBody }
* AsyncGeneratorBody :
* FunctionBody<span><sub>[Yield, Await]</sub></span>
* </pre>
*
* @param isDefault
* the flag to select whether or not the declaration is part of a default export
* @param begin
* the begin position
* @return the parsed async generator declaration
*/
private AsyncGeneratorDeclaration asyncGeneratorDeclaration(boolean isDefault, long begin) {
newContext(ContextKind.AsyncGenerator);
try {
// `async function` already parsed in caller.
consume(Token.MUL);
boolean hasName = !isDefault || token() != Token.LP;
BindingIdentifier identifier;
String functionName;
if (hasName) {
identifier = bindingIdentifierFunctionName(true);
functionName = identifier.getName().getIdentifier();
} else {
identifier = null;
functionName = DEFAULT_EXPORT_NAME;
}
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = formalParameters(Token.RP);
consume(Token.RP);
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
returnTypeAnnotation();
}
consume(Token.LC);
int startBody = ts.position();
List<StatementListItem> statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
String header = ts.range(startFunction, startBody - 1);
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
AsyncGeneratorDeclaration function = new AsyncGeneratorDeclaration(begin, ts.endPosition(), scope,
identifier, parameters, statements, functionName, header, body);
scope.setNode(function);
asyncGenerator_EarlyErrors(function);
addDeclaration(function, hasName && isDefault);
return inheritStrictness(function);
} finally {
restoreContext();
}
}
/**
* <strong>[Extension] <code>async</code> Generator Function Definitions</strong>
*
* <pre>
* AsyncGeneratorExpression :
* async [no <i>LineTerminator</i> here] function * BindingIdentifier<span><sub>[Yield, Await]opt</sub></span> ( FormalParameters<span><sub>[Yield, Await]</sub></span> ) { AsyncGeneratorBody }
* </pre>
*
* @param begin
* the begin position
* @return the parsed async generator expression
*/
private AsyncGeneratorExpression asyncGeneratorExpression(long begin) {
// `async function` already parsed in caller.
consume(Token.MUL);
boolean hasName = token() != Token.LP;
if (hasName) {
enterBlockContext();
}
newContext(ContextKind.AsyncGenerator);
try {
BindingIdentifier identifier = null;
if (hasName) {
identifier = bindingIdentifierFunctionName(false);
addLexDeclaredName(identifier, context.parent, BoundName(identifier));
}
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = formalParameters(Token.RP);
consume(Token.RP);
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
returnTypeAnnotation();
}
consume(Token.LC);
int startBody = ts.position();
List<StatementListItem> statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
String header = ts.range(startFunction, startBody - 1);
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
AsyncGeneratorExpression function = new AsyncGeneratorExpression(begin, ts.endPosition(), scope, identifier,
parameters, statements, header, body);
scope.setNode(function);
asyncGenerator_EarlyErrors(function);
return inheritStrictness(function);
} finally {
restoreContext();
if (hasName) {
exitBlockContext();
}
}
}
private void asyncGenerator_EarlyErrors(AsyncGeneratorDefinition function) {
assert context.scopeContext == context.funContext;
FunctionContext scope = context.funContext;
FormalParameterList parameters = function.getParameters();
List<Name> boundNames = BoundNames(parameters);
if (!IsSimpleParameterList(parameters)) {
checkFormalParameterDuplication(function, boundNames, scope.parameterNames);
} else if (context.strictMode != StrictMode.NonStrict) {
checkFormalParameterDuplicationStrict(function, boundNames, scope.parameterNames);
}
checkFormalParameterRedeclaration(function, boundNames, scope.lexDeclaredNames);
}
/**
* <strong>[Extension] <code>async</code> Generator Function Definitions</strong>
*
* <pre>
* AsyncGeneratorMethod<span><sub>[Yield, Await]</sub></span> :
* async [no <i>LineTerminator</i> here] * PropertyName<span><sub>[?Yield, ?Await]</sub></span> ( StrictFormalParameters<span><sub>[Yield, Await]</sub></span> ) { AsyncGeneratorBody }
* </pre>
*
* @param allocation
* the method allocation kind
* @param decorators
* the list of method decorators
* @return the parsed async method
*/
private MethodDefinition asyncGeneratorMethod(MethodAllocation allocation, List<Expression> decorators) {
long begin = ts.beginPosition();
consume(Token.ASYNC);
if (!noLineTerminator()) {
reportSyntaxError(Messages.Key.UnexpectedEndOfLine);
}
consume(Token.MUL);
PropertyName propertyName = propertyName();
newContext(ContextKind.AsyncGeneratorMethod);
try {
consume(Token.LP);
int startFunction = ts.position() - 1;
FormalParameterList parameters = strictFormalParameters(Token.RP);
consume(Token.RP);
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
returnTypeAnnotation();
}
consume(Token.LC);
int startBody = ts.position();
List<StatementListItem> statements = functionBody(parameters, Token.RC);
consume(Token.RC);
int endFunction = ts.position() - 1;
String header = ts.range(startFunction, startBody - 1);
String body = ts.range(startBody, endFunction);
FunctionContext scope = context.funContext;
MethodType type = MethodType.AsyncGenerator;
MethodDefinition method = new MethodDefinition(begin, ts.endPosition(), scope, type,
allocation, decorators, propertyName, parameters, statements, header, body);
scope.setNode(method);
methodDefinition_EarlyErrors(method);
return inheritStrictness(method);
} finally {
restoreContext();
}
}
/**
* <strong>[Extension] Decorators</strong>
*/
private List<Expression> decorators() {
InlineArrayList<Expression> decorators = newList();
do {
// FIXME: bug in spec proposal, AssignmentExpression not valid, cf. `{ @ F * G () {} }`.
// And: `{ @ D ["m"] () {} }`.
// https://github.com/wycats/javascript-decorators/issues/10
// TODO: Add new ast node for decorator to include @ sign?
consume(Token.AT);
decorators.add(leftHandSideExpressionWithValidation(false));
} while (token() == Token.AT);
return decorators;
}
private void parameterTypeAnnotation() {
// NB: Parse and ignore type annotations.
if (token() == Token.HOOK) {
consume(Token.HOOK);
}
if (token() == Token.COLON) {
typeAnnotation();
}
}
private void returnTypeAnnotation() {
// NB: Parse and ignore type annotations.
typeAnnotation();
}
private void typeAnnotation() {
// NB: Parse and ignore type annotations.
consume(Token.COLON);
if (token() == Token.HOOK) {
consume(Token.HOOK);
}
for (;;) {
if (token() == Token.LC) {
objectTypeAnnotation();
} else if (token() == Token.STRING) {
stringLiteral();
} else {
identifier();
if (token() == Token.LT) {
consume(Token.LT);
identifier();
consume(Token.GT);
}
}
if (token() == Token.BITOR) {
consume(Token.BITOR);
continue;
}
break;
}
}
private void objectTypeAnnotation() {
consume(Token.LC);
while (token() != Token.RC) {
identifier();
if (token() == Token.HOOK) {
consume(Token.HOOK);
}
typeAnnotation();
if (token() == Token.SEMI) {
consume(Token.SEMI);
} else {
break;
}
}
consume(Token.RC);
}
/* ***************************************************************************************** */
/**
* <strong>[13] ECMAScript Language: Statements and Declarations</strong>
*
* <pre>
* Statement<span><sub>[Yield, Return]</sub></span> :
* BlockStatement<span><sub>[?Yield, ?Return]</sub></span>
* VariableStatement<span><sub>[?Yield]</sub></span>
* EmptyStatement
* ExpressionStatement<span><sub>[?Yield]</sub></span>
* IfStatement<span><sub>[?Yield, ?Return]</sub></span>
* BreakableStatement<span><sub>[?Yield, ?Return]</sub></span>
* ContinueStatement<span><sub>[?Yield]</sub></span>
* BreakStatement<span><sub>[?Yield]</sub></span>
* <span><sub>[+Return]</sub></span>ReturnStatement<span><sub>[?Yield]</sub></span>
* WithStatement<span><sub>[?Yield, ?Return]</sub></span>
* LabelledStatement<span><sub>[?Yield, ?Return]</sub></span>
* ThrowStatement<span><sub>[?Yield]</sub></span>
* TryStatement<span><sub>[?Yield, ?Return]</sub></span>
* DebuggerStatement
*
* BreakableStatement<span><sub>[Yield, Return]</sub></span> :
* IterationStatement<span><sub>[?Yield, ?Return]</sub></span>
* SwitchStatement<span><sub>[?Yield, ?Return]</sub></span>
* </pre>
*
* @param allowFunction
* {@code true} if labelled function statements are allowed
* @return the parsed statement node
*/
private Statement statement(boolean allowFunction) {
switch (token()) {
case LC:
return block(NO_INHERITED_BINDING);
case VAR:
return variableStatement(true);
case SEMI:
return emptyStatement();
case IF:
return ifStatement();
case FOR:
return forStatementOrForInOfStatement(EMPTY_LABEL_SET);
case WHILE:
return whileStatement(EMPTY_LABEL_SET);
case DO:
return doWhileStatement(EMPTY_LABEL_SET);
case CONTINUE:
return continueStatement();
case BREAK:
return breakStatement();
case RETURN:
return returnStatement();
case WITH:
return withStatement();
case SWITCH:
return switchStatement(EMPTY_LABEL_SET);
case THROW:
return throwStatement();
case TRY:
return tryStatement();
case DEBUGGER:
return debuggerStatement();
case LET:
if (isEnabled(CompatibilityOption.LetStatement)
|| isEnabled(CompatibilityOption.LetExpression)) {
return letStatement();
}
// fall-through
case ASYNC:
case AWAIT:
case YIELD:
case IMPLEMENTS:
case INTERFACE:
case PACKAGE:
case PRIVATE:
case PROTECTED:
case PUBLIC:
case STATIC:
case NAME:
case ESCAPED_NAME:
case ESCAPED_RESERVED_WORD:
case ESCAPED_STRICT_RESERVED_WORD:
case ESCAPED_YIELD:
case ESCAPED_ASYNC:
case ESCAPED_AWAIT:
case ESCAPED_LET:
if (LOOKAHEAD(Token.COLON)) {
return labelledStatement(allowFunction);
}
default:
}
return expressionStatement();
}
/**
* <strong>[13.2] Block</strong>
*
* <pre>
* BlockStatement<span><sub>[Yield, Return]</sub></span> :
* Block<span><sub>[?Yield, ?Return]</sub></span>
* Block<span><sub>[Yield, Return]</sub></span> :
* { StatementList<span><sub>[?Yield, ?Return]opt</sub></span> }
* </pre>
*
* @param inherited
* the list of inherited lexical bindings
* @return the parsed block statement
*/
private BlockStatement block(List<Binding> inherited) {
long begin = ts.beginPosition();
consume(Token.LC);
BlockContext scope = enterBlockContext();
ScopeWithNames previous = null;
if (!inherited.isEmpty()) {
previous = context.setIllegalNames(lexicalNames(inherited));
}
List<StatementListItem> list = statementList(Token.RC);
if (!inherited.isEmpty()) {
context.restoreIllegalNames(previous);
}
exitBlockContext();
consume(Token.RC);
BlockStatement block = new BlockStatement(begin, ts.endPosition(), scope, list);
scope.node = block;
return block;
}
/**
* <strong>[13.2] Block</strong>
*
* <pre>
* StatementList<span><sub>[Yield, Return]</sub></span> :
* StatementItem<span><sub>[?Yield, ?Return]</sub></span>
* StatementList<span><sub>[?Yield, ?Return]</sub></span> StatementListItem<span><sub>[?Yield, ?Return]</sub></span>
* </pre>
*
* @param end
* the end marker token
* @return the list of parsed statement list items
*/
private List<StatementListItem> statementList(Token end) {
if (token() == end) {
return Collections.emptyList();
}
InlineArrayList<StatementListItem> list = newList();
do {
list.add(statementListItem());
} while (token() != end);
return list;
}
/**
* <strong>[13.2] Block</strong>
*
* <pre>
* StatementListItem<span><sub>[Yield, Return]</sub></span> :
* Statement<span><sub>[?Yield, ?Return]</sub></span>
* Declaration<span><sub>[?Yield]</sub></span>
* </pre>
*
* @return the parsed statement list item
*/
private StatementListItem statementListItem() {
switch (token()) {
case FUNCTION:
case CLASS:
case CONST:
case AT:
return declaration();
case ASYNC:
if (isEnabled(CompatibilityOption.AsyncFunction) || isEnabled(CompatibilityOption.AsyncGenerator)) {
if (LOOKAHEAD(Token.FUNCTION) && noNextLineTerminator()) {
return declaration();
}
}
break;
case LET:
if (lexicalBindingFirstSet(peek())) {
return declaration();
}
// 'let' as identifier, e.g. `let + 1`
break;
default:
}
return statement(true);
}
/**
* <strong>[13] ECMAScript Language: Statements and Declarations</strong>
*
* <pre>
* Declaration<span><sub>[Yield]</sub></span> :
* HoistableDeclaration<span><sub>[?Yield]</sub></span>
* ClassDeclaration<span><sub>[?Yield]</sub></span>
* LexicalDeclaration<span><sub>[In, ?Yield]</sub></span>
* </pre>
*
* @return the parsed declaration node
*/
private Declaration declaration() {
switch (token()) {
case FUNCTION:
return hoistableDeclaration(false);
case AT:
return classDeclaration(false, decorators());
case CLASS:
return classDeclaration(false, NO_DECORATORS);
case LET:
case CONST:
return lexicalDeclaration(true);
case ASYNC:
if (isEnabled(CompatibilityOption.AsyncFunction) || isEnabled(CompatibilityOption.AsyncGenerator)) {
return asyncFunctionOrGeneratorDeclaration(false);
}
// fall-through
default:
throw reportSyntaxError(Messages.Key.InvalidToken, token().toString());
}
}
/**
* <strong>[13] ECMAScript Language: Statements and Declarations</strong>
*
* <pre>
* HoistableDeclaration<span><sub>[Yield, Default]</sub></span> :
* FunctionDeclaration<span><sub>[?Yield, ?Default]</sub></span>
* GeneratorDeclaration<span><sub>[?Yield, ?Default]</sub></span>
* </pre>
*
* @param isDefault
* the flag to select whether or not the declaration is part of a default export
* @return the parsed declaration node
*/
private HoistableDeclaration hoistableDeclaration(boolean isDefault) {
if (LOOKAHEAD(Token.MUL)) {
return generatorDeclaration(isDefault, false);
}
return functionDeclarationWithRetry(isDefault);
}
private HoistableDeclaration functionDeclarationWithRetry(boolean isDefault) {
long position = ts.position(), lineinfo = ts.lineinfo();
try {
return functionDeclaration(isDefault);
} catch (RetryGenerator e) {
ts.reset(position, lineinfo);
return generatorDeclaration(isDefault, true);
}
}
/**
* <strong>[13.3.1] Let and Const Declarations</strong>
*
* <pre>
* LexicalDeclaration<span><sub>[In, Yield]</sub></span> :
* LetOrConst BindingList<span><sub>[?In, ?Yield]</sub></span> ;
* LetOrConst :
* let
* const
* </pre>
*
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed lexical declaration
*/
private LexicalDeclaration lexicalDeclaration(boolean allowIn) {
long begin = ts.beginPosition();
LexicalDeclaration.Type type;
if (token() == Token.LET) {
consume(Token.LET);
type = LexicalDeclaration.Type.Let;
} else {
consume(Token.CONST);
type = LexicalDeclaration.Type.Const;
}
List<LexicalBinding> list = bindingList((type == LexicalDeclaration.Type.Const), allowIn);
if (allowIn) {
// semicolon() not called if "in" not allowed, cf. forStatement()
semicolon();
}
LexicalDeclaration decl = new LexicalDeclaration(begin, ts.endPosition(), type, list);
addLexScopedDeclaration(decl);
return decl;
}
/**
* <strong>[13.3.1] Let and Const Declarations</strong>
*
* <pre>
* BindingList<span><sub>[In, Yield]</sub></span> :
* LexicalBinding<span><sub>[?In, ?Yield]</sub></span>
* BindingList<span><sub>[?In, ?Yield]</sub></span>, LexicalBinding<span><sub>[?In, ?Yield]</sub></span>
* </pre>
*
* @param isConst
* the flag for const lexical bindings
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the list of parsed lexical bindings
*/
private List<LexicalBinding> bindingList(boolean isConst, boolean allowIn) {
InlineArrayList<LexicalBinding> list = newList();
list.add(lexicalBinding(isConst, allowIn));
while (token() == Token.COMMA) {
consume(Token.COMMA);
list.add(lexicalBinding(isConst, allowIn));
}
return list;
}
/**
* <strong>[13.3.1] Let and Const Declarations</strong>
*
* <pre>
* LexicalBinding<span><sub>[In, Yield]</sub></span> :
* BindingIdentifier<span><sub>[?Yield]</sub></span> Initializer<span><sub>[?In, ?Yield]opt</sub></span>
* BindingPattern<span><sub>[?Yield]</sub></span> Initializer<span><sub>[?In, ?Yield]</sub></span>
* </pre>
*
* @param isConst
* the flag for const lexical bindings
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed lexical binding
*/
private LexicalBinding lexicalBinding(boolean isConst, boolean allowIn) {
long begin = ts.beginPosition();
Binding binding;
Expression initializer = null;
if (token() == Token.LC || token() == Token.LB) {
BindingPattern bindingPattern = bindingPattern(false);
addLexDeclaredName(bindingPattern);
if (token() == Token.ASSIGN || allowIn) {
// make initializer optional if `allowIn == false`, cf. forStatement()
initializer = initializer(allowIn);
}
binding = bindingPattern;
} else {
BindingIdentifier bindingIdentifier = bindingIdentifier(false);
addLexDeclaredName(bindingIdentifier);
if (token() == Token.ASSIGN) {
initializer = initializer(allowIn);
if (IsAnonymousFunctionDefinition(initializer)) {
setFunctionName(initializer, bindingIdentifier);
}
} else if (isConst && allowIn) {
// `allowIn == false` indicates for-loop, cf. forStatement()
reportSyntaxError(bindingIdentifier, Messages.Key.ConstMissingInitializer);
}
binding = bindingIdentifier;
}
return new LexicalBinding(begin, ts.endPosition(), binding, initializer);
}
/**
* Returns {@code true} iff {@code token} is in the first-set of LexicalBinding.
*
* @param token
* the token to inspect
* @return {@code true} if the token is in the first-set
*/
private boolean lexicalBindingFirstSet(Token token) {
switch (token) {
case LB:
case LC:
return true;
case YIELD:
return !context.yieldAllowed;
case AWAIT:
return !(moduleCode || context.awaitAllowed);
default:
return isBindingIdentifier(token);
}
}
/**
* <strong>[12.2.6] Object Initializer</strong>
*
* <pre>
* Initializer<span><sub>[In, Yield]</sub></span> :
* = AssignmentExpression<span><sub>[?In, ?Yield]</sub></span>
* </pre>
*
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed initializer expression
*/
private Expression initializer(boolean allowIn) {
consume(Token.ASSIGN);
return assignmentExpression(allowIn);
}
/**
* <strong>[13.3.2] Variable Statement</strong>
*
* <pre>
* VariableStatement<span><sub>[Yield]</sub></span> :
* var VariableDeclarationList<span><sub>[In, ?Yield]</sub></span> ;
* </pre>
*
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed variable statement
*/
private VariableStatement variableStatement(boolean allowIn) {
long begin = ts.beginPosition();
consume(Token.VAR);
List<VariableDeclaration> decls = variableDeclarationList(allowIn);
if (allowIn) {
semicolon();
}
VariableStatement varStmt = new VariableStatement(begin, ts.endPosition(), decls);
addVarScopedDeclaration(varStmt);
return varStmt;
}
/**
* <strong>[13.3.2] Variable Statement</strong>
*
* <pre>
* VariableDeclarationList<span><sub>[In, Yield]</sub></span> :
* VariableDeclaration<span><sub>[?In, ?Yield]</sub></span>
* VariableDeclarationList<span><sub>[?In, ?Yield]</sub></span> , VariableDeclaration<span><sub>[?In, ?Yield]</sub></span>
* </pre>
*
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed list of variable declarations
*/
private List<VariableDeclaration> variableDeclarationList(boolean allowIn) {
InlineArrayList<VariableDeclaration> list = newList();
list.add(variableDeclaration(allowIn));
while (token() == Token.COMMA) {
consume(Token.COMMA);
list.add(variableDeclaration(allowIn));
}
return list;
}
/**
* <strong>[13.3.2] Variable Statement</strong>
*
* <pre>
* VariableDeclaration<span><sub>[In, Yield]</sub></span> :
* BindingIdentifier<span><sub>[?Yield]</sub></span> Initializer<span><sub>[?In, ?Yield]opt</sub></span>
* BindingPattern<span><sub>[Yield]</sub></span> Initializer<span><sub>[?In, ?Yield]</sub></span>
* </pre>
*
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed variable declaration
*/
private VariableDeclaration variableDeclaration(boolean allowIn) {
Binding binding;
Expression initializer = null;
if (token() == Token.LC || token() == Token.LB) {
BindingPattern bindingPattern = bindingPattern(true);
addVarDeclaredName(bindingPattern);
if (allowIn) {
initializer = initializer(allowIn);
} else if (token() == Token.ASSIGN) {
// make initializer optional if `allowIn == false`, cf. forStatement()
initializer = initializer(allowIn);
}
binding = bindingPattern;
} else {
BindingIdentifier bindingIdentifier = bindingIdentifier();
addVarDeclaredName(bindingIdentifier);
if (token() == Token.ASSIGN) {
initializer = initializer(allowIn);
if (IsAnonymousFunctionDefinition(initializer)) {
setFunctionName(initializer, bindingIdentifier);
}
}
binding = bindingIdentifier;
}
return new VariableDeclaration(binding, initializer);
}
/**
* <strong>[13.3.3] Destructuring Binding Patterns</strong>
*
* <pre>
* BindingPattern<span><sub>[Yield]</sub></span> :
* ObjectBindingPattern<span><sub>[?Yield]</sub></span>
* ArrayBindingPattern<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param allowLet
* the flag to select if 'let' is allowed as a binding name
* @return the parsed binding pattern
*/
private BindingPattern bindingPattern(boolean allowLet) {
if (token() == Token.LC) {
return objectBindingPattern(allowLet);
} else {
return arrayBindingPattern(allowLet);
}
}
/**
* <strong>[13.3.3] Destructuring Binding Patterns</strong>
*
* <pre>
* ObjectBindingPattern<span><sub>[Yield]</sub></span> :
* { }
* { BindingPropertyList<span><sub>[?Yield]</sub></span> }
* { BindingPropertyList<span><sub>[?Yield]</sub></span> , }
* BindingPropertyList<span><sub>[Yield]</sub></span> :
* BindingProperty<span><sub>[?Yield]</sub></span>
* BindingPropertyList<span><sub>[?Yield]</sub></span> , BindingProperty<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param allowLet
* the flag to select if 'let' is allowed as a binding name
* @return the parsed object binding pattern
*/
private ObjectBindingPattern objectBindingPattern(boolean allowLet) {
long begin = ts.beginPosition();
InlineArrayList<BindingProperty> list = newList();
BindingRestProperty rest = null;
consume(Token.LC);
for (Token tok; (tok = token()) != Token.RC;) {
if (tok == Token.TRIPLE_DOT && isEnabled(CompatibilityOption.ObjectRestDestructuring)) {
rest = bindingRestProperty(allowLet);
break;
} else {
list.add(bindingProperty(allowLet));
if (token() == Token.COMMA) {
consume(Token.COMMA);
} else {
break;
}
}
}
consume(Token.RC);
return new ObjectBindingPattern(begin, ts.endPosition(), list, rest);
}
/**
* <strong>[13.3.3] Destructuring Binding Patterns</strong>
*
* <pre>
* BindingProperty<span><sub>[Yield]</sub></span> :
* SingleNameBinding<span><sub>[?Yield]</sub></span>
* PropertyName<span><sub>[?Yield]</sub></span> : BindingElement<span><sub>[?Yield]</sub></span>
* SingleNameBinding<span><sub>[Yield]</sub></span> :
* BindingIdentifier<span><sub>[?Yield]</sub></span> Initializer<span><sub>[In, ?Yield]opt</sub></span>
* </pre>
*
* @param allowLet
* the flag to select if 'let' is allowed as a binding name
* @return the parsed binding property
*/
private BindingProperty bindingProperty(boolean allowLet) {
if (token() == Token.LB || (isPropertyName(token()) && LOOKAHEAD(Token.COLON))) {
PropertyName propertyName = propertyName();
consume(Token.COLON);
Binding binding = binding(allowLet);
Expression initializer = null;
if (token() == Token.ASSIGN) {
initializer = initializer(true);
if (binding instanceof BindingIdentifier
&& IsAnonymousFunctionDefinition(initializer)) {
setFunctionName(initializer, (BindingIdentifier) binding);
}
}
return new BindingProperty(propertyName, binding, initializer);
} else {
BindingIdentifier binding = bindingIdentifier(allowLet);
Expression initializer = null;
if (token() == Token.ASSIGN) {
initializer = initializer(true);
if (IsAnonymousFunctionDefinition(initializer)) {
setFunctionName(initializer, (BindingIdentifier) binding);
}
}
return new BindingProperty(binding, initializer);
}
}
private BindingRestProperty bindingRestProperty(boolean allowLet) {
long begin = ts.beginPosition();
consume(Token.TRIPLE_DOT);
BindingIdentifier bindingIdentifier = bindingIdentifier(allowLet);
return new BindingRestProperty(begin, ts.endPosition(), bindingIdentifier);
}
/**
* <strong>[13.3.3] Destructuring Binding Patterns</strong>
*
* <pre>
* ArrayBindingPattern<span><sub>[Yield]</sub></span> :
* [ Elision<span><sub>opt</sub></span> BindingRestElement<span><sub>[?Yield]opt</sub></span> ]
* [ BindingElementList<span><sub>[?Yield]</sub></span> ]
* [ BindingElementList<span><sub>[?Yield]</sub></span> , Elision<span><sub>opt</sub></span> BindingRestElement<span><sub>[?Yield]opt</sub></span> ]
* BindingElementList<span><sub>[Yield]</sub></span> :
* BindingElisionElement<span><sub>[?Yield]</sub></span>
* BindingElementList<span><sub>[?Yield]</sub></span> , BindingElisionElement<span><sub>[?Yield]</sub></span>
* BindingElisionElement<span><sub>[Yield]</sub></span>:
* Elision<span><sub>opt</sub></span> BindingElement<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param allowLet
* the flag to select if 'let' is allowed as a binding name
* @return the parsed array binding pattern
*/
private ArrayBindingPattern arrayBindingPattern(boolean allowLet) {
long begin = ts.beginPosition();
InlineArrayList<BindingElementItem> list = newList();
consume(Token.LB);
boolean needComma = false;
for (Token tok; (tok = token()) != Token.RB;) {
if (needComma) {
consume(Token.COMMA);
needComma = false;
} else if (tok == Token.COMMA) {
consume(Token.COMMA);
list.add(new BindingElision(0, 0));
} else if (tok == Token.TRIPLE_DOT) {
list.add(bindingRestElement(allowLet));
break;
} else {
list.add(bindingElement(allowLet, false));
needComma = true;
}
}
consume(Token.RB);
return new ArrayBindingPattern(begin, ts.endPosition(), list);
}
/**
* <pre>
* Binding<span><sub>[Yield]</sub></span> :
* BindingIdentifier<span><sub>[?Yield]</sub></span>
* BindingPattern<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param allowLet
* the flag to select if 'let' is allowed as a binding name
* @return the parsed binding node
*/
private Binding binding(boolean allowLet) {
switch (token()) {
case LC:
return objectBindingPattern(allowLet);
case LB:
return arrayBindingPattern(allowLet);
default:
return bindingIdentifier(allowLet);
}
}
/**
* <strong>[13.3.3] Destructuring Binding Patterns</strong>
*
* <pre>
* BindingElement<span><sub>[Yield]</sub></span> :
* SingleNameBinding<span><sub>[?Yield]</sub></span>
* BindingPattern<span><sub>[?Yield]</sub></span> Initializer<span><sub>[In, ?Yield]opt</sub></span>
* </pre>
*
* @param allowLet
* the flag to select if 'let' is allowed as a binding name
* @param allowType
* the flag to select if type annotations are allowed
* @return the parsed binding element
*/
private BindingElement bindingElement(boolean allowLet, boolean allowType) {
long begin = ts.beginPosition();
Binding binding = binding(allowLet);
if (allowType && binding instanceof BindingIdentifier
&& (token() == Token.COLON || token() == Token.HOOK)
&& isEnabled(CompatibilityOption.TypeAnnotation)) {
parameterTypeAnnotation();
}
Expression initializer = null;
if (token() == Token.ASSIGN) {
initializer = initializer(true);
if (binding instanceof BindingIdentifier && IsAnonymousFunctionDefinition(initializer)) {
setFunctionName(initializer, (BindingIdentifier) binding);
}
}
return new BindingElement(begin, ts.endPosition(), binding, initializer);
}
/**
* <strong>[13.3.3] Destructuring Binding Patterns</strong>
*
* <pre>
* BindingRestElement<span><sub>[Yield]</sub></span> :
* ... BindingIdentifier<span><sub>[?Yield]</sub></span>
* ... BindingPattern<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param allowLet
* the flag to select if 'let' is allowed as a binding name
* @return the parsed binding rest element
*/
private BindingRestElement bindingRestElement(boolean allowLet) {
long begin = ts.beginPosition();
consume(Token.TRIPLE_DOT);
Binding binding;
if (isEnabled(CompatibilityOption.RestBindingPattern)) {
binding = binding(allowLet);
} else {
binding = bindingIdentifier(allowLet);
}
return new BindingRestElement(begin, ts.endPosition(), binding);
}
/**
* <strong>[13.4] Empty Statement</strong>
*
* <pre>
* EmptyStatement:
* ;
* </pre>
*
* @return the parsed empty statement
*/
private EmptyStatement emptyStatement() {
long begin = ts.beginPosition();
consume(Token.SEMI);
return new EmptyStatement(begin, ts.endPosition());
}
/**
* <strong>[13.5] Expression Statement</strong>
*
* <pre>
* ExpressionStatement<span><sub>[Yield]</sub></span> :
* [LA ∉ { <b>{, function, class, let [</b> }] Expression<span><sub>[In, ?Yield]</sub></span> ;
* </pre>
*
* @return the parsed expression statement
*/
private ExpressionStatement expressionStatement() {
switch (token()) {
case LC:
case FUNCTION:
case CLASS:
case AT:
throw reportSyntaxError(Messages.Key.InvalidToken, token().toString());
case LET:
if (LOOKAHEAD(Token.LB)) {
throw reportSyntaxError(Messages.Key.InvalidToken, token().toString());
}
break;
case ASYNC:
if (isEnabled(CompatibilityOption.AsyncFunction) || isEnabled(CompatibilityOption.AsyncGenerator)) {
if (LOOKAHEAD(Token.FUNCTION) && noNextLineTerminator()) {
throw reportSyntaxError(Messages.Key.InvalidToken, token().toString());
}
}
break;
case DO:
if (isEnabled(CompatibilityOption.DoExpression)) {
throw reportSyntaxError(Messages.Key.InvalidToken, token().toString());
}
break;
default:
break;
}
long begin = ts.beginPosition();
Expression expr = expression(true);
semicolon();
return new ExpressionStatement(begin, ts.endPosition(), expr);
}
/**
* <strong>[13.6] The <code>if</code> Statement</strong>
*
* <pre>
* IfStatement<span><sub>[Yield, Return]</sub></span> :
* if ( Expression<span><sub>[In, ?Yield]</sub></span> ) Statement<span><sub>[?Yield, ?Return]</sub></span> else Statement<span><sub>[?Yield, ?Return]</sub></span>
* if ( Expression<span><sub>[In, ?Yield]</sub></span> ) Statement<span><sub>[?Yield, ?Return]</sub></span>
* </pre>
*
* <strong>[B.3.4] FunctionDeclarations in IfStatement Statement Clauses</strong>
*
* <pre>
* IfStatement<span><sub>[Yield, Return]</sub></span> :
* if ( Expression<span><sub>[In, ?Yield]</sub></span> ) FunctionDeclaration<span><sub>[?Yield]</sub></span> else Statement<span><sub>[?Yield, ?Return]</sub></span>
* if ( Expression<span><sub>[In, ?Yield]</sub></span> ) Statement<span><sub>[?Yield, ?Return]</sub></span> else FunctionDeclaration<span><sub>[?Yield]</sub></span>
* if ( Expression<span><sub>[In, ?Yield]</sub></span> ) FunctionDeclaration<span><sub>[?Yield]</sub></span> else FunctionDeclaration<span><sub>[?Yield]</sub></span>
* if ( Expression<span><sub>[In, ?Yield]</sub></span> ) FunctionDeclaration<span><sub>[?Yield]</sub></span>
* </pre>
*
* @return the parsed if-statement
*/
private IfStatement ifStatement() {
long begin = ts.beginPosition();
consume(Token.IF);
consume(Token.LP);
Expression test = expression(true);
consume(Token.RP);
Statement then = statementOrFunctionDeclaration();
Statement otherwise = null;
if (token() == Token.ELSE) {
consume(Token.ELSE);
otherwise = statementOrFunctionDeclaration();
}
return new IfStatement(begin, ts.endPosition(), test, then, otherwise);
}
private Statement statementOrFunctionDeclaration() {
if (token() == Token.FUNCTION
&& isEnabled(CompatibilityOption.IfStatementFunctionDeclaration)) {
return ifStatementFunctionDeclaration();
}
return statement(false);
}
private BlockStatement ifStatementFunctionDeclaration() {
if (context.strictMode != StrictMode.NonStrict) {
reportStrictModeSyntaxError(Messages.Key.InvalidToken, token().toString());
}
long begin = ts.beginPosition();
BlockContext scope = enterBlockContext();
Declaration declaration = functionDeclarationWithRetry(false);
List<StatementListItem> list = singletonList((StatementListItem) declaration);
exitBlockContext();
BlockStatement block = new BlockStatement(begin, ts.endPosition(), scope, list);
scope.node = block;
return block;
}
/**
* <strong>[13.7.2] The <code>do-while</code> Statement</strong>
*
* <pre>
* IterationStatement<span><sub>[Yield, Return]</sub></span> :
* do Statement<span><sub>[?Yield, ?Return]</sub></span> while ( Expression<span><sub>[In, ?Yield]</sub></span> ) ;<span><sub>opt</sub></span>
* </pre>
*
* @param labelSet
* the label set
* @return the parsed do-while statement
*/
private DoWhileStatement doWhileStatement(Set<String> labelSet) {
long begin = ts.beginPosition();
consume(Token.DO);
LabelContext labelCx = enterIteration(labelSet);
Statement stmt = statement(false);
exitIteration();
consume(Token.WHILE);
consume(Token.LP);
Expression test = expression(true);
consume(Token.RP);
if (token() == Token.SEMI) {
consume(Token.SEMI);
}
return new DoWhileStatement(begin, ts.endPosition(), labelCx.abrupts, labelCx.labelSet,
test, stmt);
}
/**
* <strong>[13.7.3] The <code>while</code> Statement</strong>
*
* <pre>
* IterationStatement<span><sub>[Yield, Return]</sub></span> :
* while ( Expression<span><sub>[In, ?Yield]</sub></span> ) StatementStatement<span><sub>[?Yield, ?Return]</sub></span>
* </pre>
*
* @param labelSet
* the label set
* @return the parsed while statement
*/
private WhileStatement whileStatement(Set<String> labelSet) {
long begin = ts.beginPosition();
consume(Token.WHILE);
consume(Token.LP);
Expression test = expression(true);
consume(Token.RP);
LabelContext labelCx = enterIteration(labelSet);
Statement stmt = statement(false);
exitIteration();
return new WhileStatement(begin, ts.endPosition(), labelCx.abrupts, labelCx.labelSet, test,
stmt);
}
private enum ForType {
Await, Each, In, Of
}
/**
* <strong>[13.7.4] The <code>for</code> Statement</strong> <br>
* <strong>[13.7.5] The <code>for-in</code> and <code>for-of</code> Statements</strong>
*
* <pre>
* IterationStatement<span><sub>[Yield, Return]</sub></span> :
* for ( [LA ∉ { <b>let [</b> }] Expression<span><sub>[?Yield]opt</sub></span> ; Expression<span><sub>[In, ?Yield]opt</sub></span> ; Expression<span><sub>[In, ?Yield]opt</sub></span> ) Statement<span><sub>[?Yield, ?Return]</sub></span>
* for ( var VariableDeclarationList<span><sub>[?Yield]</sub></span> ; Expression<span><sub>[In, ?Yield]opt</sub></span> ; Expression<span><sub>[In, ?Yield]opt</sub></span> ) Statement<span><sub>[?Yield, ?Return]</sub></span>
* for ( LexicalDeclaration<span><sub>[?Yield]</sub></span> Expression<span><sub>[In, ?Yield]opt</sub></span> ; Expression<span><sub>[In, ?Yield]opt</sub></span> ) Statement<span><sub>[?Yield, ?Return]</sub></span>
* for ( [LA ∉ { <b>let [</b> }] LeftHandSideExpression<span><sub>[?Yield]</sub></span> in Expression<span><sub>[In, ?Yield]</sub></span> ) Statement<span><sub>[?Yield, ?Return]</sub></span>
* for ( var ForBinding<span><sub>[?Yield]</sub></span> in Expression<span><sub>[In, ?Yield]</sub></span> ) Statement<span><sub>[?Yield, ?Return]</sub></span>
* for ( ForDeclaration<span><sub>[?Yield]</sub></span> in Expression<span><sub>[In, ?Yield]</sub></span> ) Statement<span><sub>[?Yield, ?Return]</sub></span>
* for ( [LA ∉ { <b>let</b> }] LeftHandSideExpression<span><sub>[?Yield]</sub></span> of AssignmentExpression<span><sub>[In, ?Yield]</sub></span> ) Statement<span><sub>[?Yield, ?Return]</sub></span>
* for ( var ForBinding<span><sub>[?Yield]</sub></span> of AssignmentExpression<span><sub>[In, ?Yield]</sub></span> ) Statement<span><sub>[?Yield, ?Return]</sub></span>
* for ( ForDeclaration<span><sub>[?Yield]</sub></span> of AssignmentExpression<span><sub>[In, ?Yield]</sub></span> ) Statement<span><sub>[?Yield, ?Return]</sub></span>
* ForDeclaration<span><sub>[Yield]</sub></span> :
* LetOrConst ForBinding<span><sub>[?Yield]</sub></span>
* ForBinding<span><sub>[Yield]</sub></span> :
* BindingIdentifier<span><sub>[?Yield]</sub></span>
* BindingPattern<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param labelSet
* the label set
* @return the parsed for-loop or for-in/of statement
*/
private IterationStatement forStatementOrForInOfStatement(Set<String> labelSet) {
long begin = ts.beginPosition();
boolean forAwait = false, forEach = false;
consume(Token.FOR);
if (token() == Token.AWAIT && isEnabled(CompatibilityOption.AsyncGenerator)) {
if (context.kind.isAsync() && context.awaitAllowed) {
context.yieldOrAwaitExpression |= true;
} else {
reportSyntaxError(Messages.Key.InvalidAwaitExpression);
}
consume(Token.AWAIT);
forAwait = true;
} else if (isName("each") && isEnabled(CompatibilityOption.ForEachStatement)) {
consume("each");
forEach = true;
}
consume(Token.LP);
// NB: This code needs to be able to parse ForStatement and ForIn/OfStatement
boolean letIdentifier = false;
BlockContext lexBlockContext = null;
Node head;
switch (token()) {
case VAR:
head = variableStatement(false);
break;
case SEMI:
head = null;
break;
case CONST:
lexBlockContext = enterBlockContext();
head = lexicalDeclaration(false);
break;
case LET:
if (forAwait || lexicalBindingFirstSet(peek())) {
lexBlockContext = enterBlockContext();
head = lexicalDeclaration(false);
break;
}
// 'let' as identifier, e.g. `for (let ;;) {}`
// 'let' as identifier, e.g. `for (let in "") {}` or `for (let.prop in "") {}`
letIdentifier = true;
// fall-through
default:
int count = context.countLiterals();
Expression expr = assignmentExpressionNoValidation(false);
if (token() == Token.SEMI || token() == Token.COMMA) {
// ForStatement, apply early error checks for object literals
objectLiteral_EarlyErrors(count);
// Proceed to parse expression tail, if any
if (token() == Token.COMMA) {
head = commaExpression(expr, false);
} else {
head = expr;
}
} else {
// ForInStatement or ForOfStatement, check assignment target
head = validateAssignment(expr, ExceptionType.SyntaxError, Messages.Key.InvalidAssignmentTarget);
// Apply early error checks for remaining object literals
objectLiteral_EarlyErrors(count);
}
break;
}
if (forAwait) {
return forInOfStatement(labelSet, begin, head, lexBlockContext, ForType.Await);
} else if (forEach) {
return forInOfStatement(labelSet, begin, head, lexBlockContext, ForType.Each);
} else if (token() == Token.IN) {
return forInOfStatement(labelSet, begin, head, lexBlockContext, ForType.In);
} else if (token() == Token.NAME && !letIdentifier && isName("of")) {
return forInOfStatement(labelSet, begin, head, lexBlockContext, ForType.Of);
} else {
return forStatement(labelSet, begin, head, lexBlockContext);
}
}
private IterationStatement forInOfStatement(Set<String> labelSet, long begin, Node head,
BlockContext lexBlockContext, ForType type) {
// Only allow single binding without initializer in for-in/of head.
if (head == null) {
// for-each loop without head: `for each (;`
assert type == ForType.Await || type == ForType.Each;
reportSyntaxError(begin, Messages.Key.InvalidForStatementLeftHandSide);
} else if (head instanceof VariableStatement) {
VariableStatement varStmt = (VariableStatement) head;
if (varStmt.getElements().size() != 1) {
reportSyntaxError(varStmt, Messages.Key.InvalidForStatementLeftHandSide);
}
VariableDeclaration varDecl = varStmt.getElements().get(0);
if (varDecl.getInitializer() != null) {
if (type == ForType.Of || varDecl.getBinding() instanceof BindingPattern) {
reportSyntaxError(varDecl, Messages.Key.InvalidForStatementLeftHandSide);
}
if (!isEnabled(CompatibilityOption.ForInVarInitializer)) {
reportSyntaxError(varDecl, Messages.Key.InvalidForStatementLeftHandSide);
}
}
if (type == ForType.Of) {
checkVarDeclaredName(varDecl.getBinding());
}
} else if (head instanceof LexicalDeclaration) {
// Forbid initializer for BindingPattern and const declarations
LexicalDeclaration lexDecl = (LexicalDeclaration) head;
if (lexDecl.getElements().size() != 1) {
reportSyntaxError(lexDecl, Messages.Key.InvalidForStatementLeftHandSide);
}
LexicalBinding lexBinding = lexDecl.getElements().get(0);
if (lexBinding.getInitializer() != null) {
reportSyntaxError(lexBinding, Messages.Key.InvalidForStatementLeftHandSide);
}
} else if (!(head instanceof LeftHandSideExpression)) {
// Handle: `for (a, b in ...` and `for each (false; ...`
assert head instanceof CommaExpression || type == ForType.Await || type == ForType.Each;
reportSyntaxError(head, Messages.Key.InvalidForStatementLeftHandSide);
}
Expression expr;
if (type == ForType.Each || type == ForType.In) {
consume(Token.IN);
expr = expression(true);
} else {
consume("of");
expr = assignmentExpression(true);
}
consume(Token.RP);
LabelContext labelCx = enterIteration(labelSet);
Statement stmt = statement(false);
exitIteration();
if (lexBlockContext != null) {
exitBlockContext();
}
if (type == ForType.Await) {
ForAwaitStatement iteration = new ForAwaitStatement(begin, ts.endPosition(),
lexBlockContext, labelCx.abrupts, labelCx.labelSet, head, expr, stmt);
if (lexBlockContext != null) {
lexBlockContext.node = iteration;
}
return iteration;
} else if (type == ForType.Each) {
ForEachStatement iteration = new ForEachStatement(begin, ts.endPosition(),
lexBlockContext, labelCx.abrupts, labelCx.labelSet, head, expr, stmt);
if (lexBlockContext != null) {
lexBlockContext.node = iteration;
}
return iteration;
} else if (type == ForType.In) {
ForInStatement iteration = new ForInStatement(begin, ts.endPosition(), lexBlockContext,
labelCx.abrupts, labelCx.labelSet, head, expr, stmt);
if (lexBlockContext != null) {
lexBlockContext.node = iteration;
}
return iteration;
} else {
ForOfStatement iteration = new ForOfStatement(begin, ts.endPosition(), lexBlockContext,
labelCx.abrupts, labelCx.labelSet, head, expr, stmt);
if (lexBlockContext != null) {
lexBlockContext.node = iteration;
}
return iteration;
}
}
private ForStatement forStatement(Set<String> labelSet, long begin, Node head,
BlockContext lexBlockContext) {
if (head instanceof VariableStatement) {
// Enforce initializer for BindingPattern
VariableStatement varStmt = (VariableStatement) head;
for (VariableDeclaration decl : varStmt.getElements()) {
if (decl.getBinding() instanceof BindingPattern && decl.getInitializer() == null) {
reportSyntaxError(varStmt, Messages.Key.DestructuringMissingInitializer);
}
}
} else if (head instanceof LexicalDeclaration) {
// Enforce initializer for BindingPattern and const declarations
LexicalDeclaration lexDecl = (LexicalDeclaration) head;
boolean isConst = lexDecl.isConstDeclaration();
for (LexicalBinding decl : lexDecl.getElements()) {
if (decl.getBinding() instanceof BindingPattern && decl.getInitializer() == null) {
reportSyntaxError(lexDecl, Messages.Key.DestructuringMissingInitializer);
}
if (isConst && decl.getInitializer() == null) {
reportSyntaxError(lexDecl, Messages.Key.ConstMissingInitializer);
}
}
}
consume(Token.SEMI);
Expression test = null;
if (token() != Token.SEMI) {
test = expression(true);
}
consume(Token.SEMI);
Expression step = null;
if (token() != Token.RP) {
step = expression(true);
}
consume(Token.RP);
LabelContext labelCx = enterIteration(labelSet);
Statement stmt = statement(false);
exitIteration();
if (lexBlockContext != null) {
exitBlockContext();
}
ForStatement iteration = new ForStatement(begin, ts.endPosition(), lexBlockContext,
labelCx.abrupts, labelCx.labelSet, head, test, step, stmt);
if (lexBlockContext != null) {
lexBlockContext.node = iteration;
}
return iteration;
}
/**
* <strong>[13.8] The <code>continue</code> Statement</strong>
*
* <pre>
* ContinueStatement<span><sub>[Yield]</sub></span> :
* continue ;
* continue [no <i>LineTerminator</i> here] LabelIdentifier<span><sub>[?Yield]</sub></span> ;
* </pre>
*
* @return the parsed continue statement
*/
private ContinueStatement continueStatement() {
long begin = ts.beginPosition();
String label;
consume(Token.CONTINUE);
if (noLineTerminator() && isLabelIdentifier(token())) {
label = labelIdentifier();
} else {
label = null;
}
semicolon();
LabelContext target = findContinueTarget(label);
if (target == null) {
if (label == null) {
reportSyntaxError(begin, Messages.Key.InvalidContinueTarget);
} else {
reportSyntaxError(begin, Messages.Key.LabelTargetNotFound, label);
}
}
if (target.type != StatementType.Iteration) {
reportSyntaxError(begin, Messages.Key.InvalidContinueTarget);
}
target.mark(Abrupt.Continue);
return new ContinueStatement(begin, ts.endPosition(), label);
}
/**
* <strong>[13.9] The <code>break</code> Statement</strong>
*
* <pre>
* BreakStatement<span><sub>[Yield]</sub></span> :
* break ;
* break [no <i>LineTerminator</i> here] LabelIdentifier<span><sub>[?Yield]</sub></span> ;
* </pre>
*
* @return the parsed break statement
*/
private BreakStatement breakStatement() {
long begin = ts.beginPosition();
String label;
consume(Token.BREAK);
if (noLineTerminator() && isLabelIdentifier(token())) {
label = labelIdentifier();
} else {
label = null;
}
semicolon();
LabelContext target = findBreakTarget(label);
if (target == null) {
if (label == null) {
reportSyntaxError(begin, Messages.Key.InvalidBreakTarget);
} else {
reportSyntaxError(begin, Messages.Key.LabelTargetNotFound, label);
}
}
target.mark(Abrupt.Break);
return new BreakStatement(begin, ts.endPosition(), label);
}
/**
* <strong>[13.10] The <code>return</code> Statement</strong>
*
* <pre>
* ReturnStatement<span><sub>[Yield]</sub></span> :
* return ;
* return [no <i>LineTerminator</i> here] Expression<span><sub>[In, ?Yield]</sub></span> ;
* </pre>
*
* @return the parsed return statement
*/
private ReturnStatement returnStatement() {
if (!context.returnAllowed) {
reportSyntaxError(Messages.Key.InvalidReturnStatement);
}
long begin = ts.beginPosition();
Expression expr = null;
consume(Token.RETURN);
if (noLineTerminator()
&& !(token() == Token.SEMI || token() == Token.RC || token() == Token.EOF)) {
expr = expression(true);
}
semicolon();
return new ReturnStatement(begin, ts.endPosition(), expr);
}
/**
* <strong>[13.11] The <code>with</code> Statement</strong>
*
* <pre>
* WithStatement<span><sub>[Yield, Return]</sub></span> :
* with ( Expression<span><sub>[In, ?Yield]</sub></span> ) Statement<span><sub>[?Yield, ?Return]</sub></span>
* </pre>
*
* @return the parsed with statement
*/
private WithStatement withStatement() {
long begin = ts.beginPosition();
reportStrictModeSyntaxError(begin, Messages.Key.StrictModeWithStatement);
consume(Token.WITH);
consume(Token.LP);
Expression expr = expression(true);
consume(Token.RP);
WithContext scope = enterWithContext();
Statement stmt = statement(false);
exitWithContext();
WithStatement withStatement = new WithStatement(begin, ts.endPosition(), scope, expr, stmt);
scope.node = withStatement;
return withStatement;
}
/**
* <strong>[13.12] The <code>switch</code> Statement</strong>
*
* <pre>
* SwitchStatement<span><sub>[Yield, Return]</sub></span> :
* switch ( Expression<span><sub>[In, ?Yield]</sub></span> ) CaseBlock<span><sub>[?Yield, ?Return]</sub></span>
* CaseBlock<span><sub>[Yield, Return]</sub></span> :
* { CaseClauses<span><sub>[?Yield, ?Return]opt</sub></span> }
* { CaseClauses<span><sub>[?Yield, ?Return]opt</sub></span> DefaultClause<span><sub>[?Yield, ?Return]</sub></span> CaseClauses<span><sub>[?Yield, ?Return]opt</sub></span> }
* CaseClauses<span><sub>[Yield, Return]</sub></span> :
* CaseClause<span><sub>[?Yield, ?Return]</sub></span>
* CaseClauses<span><sub>[?Yield, ?Return]</sub></span> CaseClause<span><sub>[?Yield, ?Return]</sub></span>
* CaseClause<span><sub>[Yield, Return]</sub></span> :
* case Expression<span><sub>[In, ?Yield]</sub></span> : StatementList<span><sub>[?Yield, ?Return]opt</sub></span>
* DefaultClause :
* default : StatementList<span><sub>[?Yield, ?Return]opt</sub></span>
* </pre>
*
* @param labelSet
* the label set
* @return the parsed switch statement
*/
private SwitchStatement switchStatement(Set<String> labelSet) {
InlineArrayList<SwitchClause> clauses = newList();
long begin = ts.beginPosition();
consume(Token.SWITCH);
consume(Token.LP);
Expression expr = expression(true);
consume(Token.RP);
consume(Token.LC);
BlockContext scope = enterBlockContext();
LabelContext labelCx = enterBreakable(labelSet);
boolean hasDefault = false;
for (;;) {
long beginClause = ts.beginPosition();
Expression caseExpr;
Token tok = token();
if (tok == Token.CASE) {
consume(Token.CASE);
caseExpr = expression(true);
consume(Token.COLON);
} else if (tok == Token.DEFAULT && !hasDefault) {
hasDefault = true;
consume(Token.DEFAULT);
consume(Token.COLON);
caseExpr = null;
} else {
break;
}
Token next = token();
if (next == Token.CASE || next == Token.DEFAULT) {
// empty case clause
List<StatementListItem> list = emptyList();
clauses.add(new SwitchClause(beginClause, ts.endPosition(), caseExpr, list));
continue;
}
InlineArrayList<StatementListItem> list = newList();
statementlist: for (;;) {
switch (token()) {
case CASE:
case DEFAULT:
case RC:
break statementlist;
default:
list.add(statementListItem());
}
}
clauses.add(new SwitchClause(beginClause, ts.endPosition(), caseExpr, list));
}
exitBlockContext();
exitBreakable();
consume(Token.RC);
SwitchStatement switchStatement = new SwitchStatement(begin, ts.endPosition(), scope,
labelCx.abrupts, labelCx.labelSet, expr, clauses);
scope.node = switchStatement;
return switchStatement;
}
/**
* <strong>[13.13] Labelled Statements</strong>
*
* <pre>
* LabelledStatement<span><sub>[Yield, Return]</sub></span> :
* LabelIdentifier<span><sub>[?Yield]</sub></span> : LabelledItem<span><sub>[?Yield, ?Return]</sub></span>
* LabelledItem<span><sub>[Yield, Return]</sub></span> :
* Statement<span><sub>[?Yield, ?Return]</sub></span>
* FunctionDeclaration<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param allowFunction
* {@code true} if labelled function statements are allowed
* @return the parsed labelled statement
*/
private Statement labelledStatement(boolean allowFunction) {
long begin = ts.beginPosition();
LinkedHashSet<String> labelSet = new LinkedHashSet<>(4);
labels: for (;;) {
switch (token()) {
case FOR:
assert !labelSet.isEmpty();
return forStatementOrForInOfStatement(labelSet);
case WHILE:
assert !labelSet.isEmpty();
return whileStatement(labelSet);
case DO:
assert !labelSet.isEmpty();
return doWhileStatement(labelSet);
case SWITCH:
assert !labelSet.isEmpty();
return switchStatement(labelSet);
case FUNCTION:
if (isEnabled(CompatibilityOption.LabelledFunctionDeclaration)) {
assert !labelSet.isEmpty();
return labelledFunctionStatement(labelSet, allowFunction);
}
break labels;
case LET:
if (isEnabled(CompatibilityOption.LetStatement)
|| isEnabled(CompatibilityOption.LetExpression)) {
break labels;
}
// fall-through
case ASYNC:
case AWAIT:
case YIELD:
case IMPLEMENTS:
case INTERFACE:
case PACKAGE:
case PRIVATE:
case PROTECTED:
case PUBLIC:
case STATIC:
case NAME:
case ESCAPED_NAME:
case ESCAPED_RESERVED_WORD:
case ESCAPED_STRICT_RESERVED_WORD:
case ESCAPED_YIELD:
case ESCAPED_ASYNC:
case ESCAPED_AWAIT:
case ESCAPED_LET:
if (LOOKAHEAD(Token.COLON)) {
break;
}
default:
break labels;
}
long beginLabel = ts.beginPosition();
String name = labelIdentifier();
consume(Token.COLON);
addLabel(beginLabel, labelSet, name);
}
assert !labelSet.isEmpty();
LabelContext labelCx = enterLabelled(StatementType.Statement, labelSet);
Statement stmt = statement(false);
exitLabelled();
return new LabelledStatement(begin, ts.endPosition(), labelCx.abrupts, labelCx.labelSet,
stmt);
}
/**
* B.3.2 Labelled Function Declarations
*
* @param allowFunction
* {@code true} if labelled function statements are allowed
* @return the labelled function statement
*/
private LabelledFunctionStatement labelledFunctionStatement(Set<String> labelSet,
boolean allowFunction) {
if (!allowFunction) {
reportSyntaxError(Messages.Key.InvalidToken, token().toString());
} else if (context.strictMode != StrictMode.NonStrict) {
reportStrictModeSyntaxError(Messages.Key.InvalidToken, token().toString());
}
long begin = ts.beginPosition();
HoistableDeclaration function = functionDeclarationWithRetry(false);
return new LabelledFunctionStatement(begin, ts.endPosition(), labelSet, function);
}
/**
* <strong>[13.14] The <code>throw</code> Statement</strong>
*
* <pre>
* ThrowStatement<span><sub>[Yield]</sub></span> :
* throw [no <i>LineTerminator</i> here] Expression<span><sub>[In, ?Yield]</sub></span> ;
* </pre>
*
* @return the parsed throw statement
*/
private ThrowStatement throwStatement() {
long begin = ts.beginPosition();
consume(Token.THROW);
if (!noLineTerminator()) {
reportSyntaxError(Messages.Key.UnexpectedEndOfLine);
}
Expression expr = expression(true);
semicolon();
return new ThrowStatement(begin, ts.endPosition(), expr);
}
/**
* <strong>[13.15] The <code>try</code> Statement</strong>
*
* <pre>
* TryStatement<span><sub>[Yield, Return]</sub></span> :
* try Block<span><sub>[?Yield, ?Return]</sub></span> Catch<span><sub>[?Yield, ?Return]</sub></span>
* try Block<span><sub>[?Yield, ?Return]</sub></span> Finally<span><sub>[?Yield, ?Return]</sub></span>
* try Block<span><sub>[?Yield, ?Return]</sub></span> Catch<span><sub>[?Yield, ?Return]</sub></span> Finally<span><sub>[?Yield, ?Return]</sub></span>
* Catch<span><sub>[Yield, Return]</sub></span> :
* catch ( CatchParameter<span><sub>[?Yield]</sub></span> ) Block<span><sub>[?Yield, ?Return]</sub></span>
* Finally<span><sub>[Yield, Return]</sub></span> :
* finally Block<span><sub>[?Yield, ?Return]</sub></span>
* CatchParameter<span><sub>[Yield]</sub></span> :
* BindingIdentifier<span><sub>[?Yield]</sub></span>
* BindingPattern<span><sub>[?Yield]</sub></span>
* </pre>
*
* @return the parsed try-statement node
*/
private TryStatement tryStatement() {
BlockStatement tryBlock, finallyBlock = null;
CatchNode catchNode = null;
List<GuardedCatchNode> guardedCatchNodes = emptyList();
long begin = ts.beginPosition();
consume(Token.TRY);
tryBlock = block(NO_INHERITED_BINDING);
Token tok = token();
if (tok == Token.CATCH) {
if (isEnabled(CompatibilityOption.GuardedCatch)) {
guardedCatchNodes = newList();
while (token() == Token.CATCH && catchNode == null) {
long beginCatch = ts.beginPosition();
consume(Token.CATCH);
consume(Token.LP);
BlockScope catchScope = enterCatchScope();
Binding catchParameter = binding(true);
addLexDeclaredName(catchParameter);
Expression guard;
if (token() == Token.IF) {
consume(Token.IF);
guard = expression(true);
} else {
guard = null;
}
consume(Token.RP);
// CatchBlock receives a list of non-available lexical declarable names to
// fulfill the early error restriction that the BoundNames of CatchParameter
// must not also occur in either the LexicallyDeclaredNames or the
// VarDeclaredNames of CatchBlock.
BlockStatement catchBlock = block(singletonList(catchParameter));
exitCatchContext();
if (guard != null) {
GuardedCatchNode guardedCatchNode = new GuardedCatchNode(beginCatch,
ts.endPosition(), catchScope, catchParameter, guard, catchBlock);
assignCatchScopeNode(catchScope, guardedCatchNode);
guardedCatchNodes.add(guardedCatchNode);
} else {
catchNode = new CatchNode(beginCatch, ts.endPosition(), catchScope,
catchParameter, catchBlock);
assignCatchScopeNode(catchScope, catchNode);
}
}
} else {
long beginCatch = ts.beginPosition();
consume(Token.CATCH);
consume(Token.LP);
BlockScope catchScope = enterCatchScope();
Binding catchParameter = binding(true);
addLexDeclaredName(catchParameter);
consume(Token.RP);
// CatchBlock receives a list of non-available lexical declarable names to
// fulfill the early error restriction that the BoundNames of CatchParameter
// must not also occur in either the LexicallyDeclaredNames or the
// VarDeclaredNames of CatchBlock.
BlockStatement catchBlock = block(singletonList(catchParameter));
exitCatchContext();
catchNode = new CatchNode(beginCatch, ts.endPosition(), catchScope, catchParameter,
catchBlock);
assignCatchScopeNode(catchScope, catchNode);
}
if (token() == Token.FINALLY) {
consume(Token.FINALLY);
finallyBlock = block(NO_INHERITED_BINDING);
}
} else {
consume(Token.FINALLY);
finallyBlock = block(NO_INHERITED_BINDING);
}
return new TryStatement(begin, ts.endPosition(), tryBlock, catchNode, guardedCatchNodes,
finallyBlock);
}
private BlockScope enterCatchScope() {
if (isEnabled(CompatibilityOption.CatchVarPattern)) {
if (token() == Token.LB || token() == Token.LC) {
return enterBlockContext();
}
}
if (isEnabled(CompatibilityOption.CatchVarStatement)) {
return enterCatchContext();
}
return enterBlockContext();
}
private void assignCatchScopeNode(BlockScope scope, ScopedNode node) {
if (scope instanceof BlockContext) {
((BlockContext) scope).node = node;
} else {
((CatchContext) scope).node = node;
}
}
/**
* <strong>[13.16] The <code>debugger</code> Statement</strong>
*
* <pre>
* DebuggerStatement :
* debugger ;
* </pre>
*
* @return the parsed debugger statement
*/
private DebuggerStatement debuggerStatement() {
long begin = ts.beginPosition();
consume(Token.DEBUGGER);
semicolon();
return new DebuggerStatement(begin, ts.endPosition());
}
/**
* <strong>[Extension] The <code>let</code> Statement</strong>
*
* <pre>
* LetStatement<span><sub>[Yield, Return]</sub></span> :
* let ( BindingList<span><sub>[In, ?Yield]</sub></span> ) BlockStatement<span><sub>[?Yield, ?Return]</sub></span>
* </pre>
*
* @return the parsed let-statement
*/
private Statement letStatement() {
long begin = ts.beginPosition();
consume(Token.LET);
consume(Token.LP);
List<LexicalBinding> lexicalBindings = letBindingList();
List<Binding> bindings = toBindings(lexicalBindings);
consume(Token.RP);
if (token() != Token.LC && isEnabled(CompatibilityOption.LetExpression)) {
// let expression disguised as let statement - also error in strict mode(!)
reportStrictModeSyntaxError(ts.sourcePosition(), Messages.Key.UnexpectedToken, token().toString(),
Token.LC.toString());
BlockContext scope = enterBlockContext(bindings);
Expression expression = assignmentExpression(true);
exitBlockContext();
LetExpression letExpression = new LetExpression(begin, ts.endPosition(), scope,
lexicalBindings, expression);
scope.node = letExpression;
return new ExpressionStatement(begin, ts.endPosition(), letExpression);
} else {
BlockContext scope = enterBlockContext(bindings);
BlockStatement letBlock = block(bindings);
exitBlockContext();
LetStatement block = new LetStatement(begin, ts.endPosition(), scope, lexicalBindings,
letBlock);
scope.node = block;
return block;
}
}
private List<LexicalBinding> letBindingList() {
InlineArrayList<LexicalBinding> list = newList();
list.add(letBinding());
while (token() == Token.COMMA) {
consume(Token.COMMA);
list.add(letBinding());
}
return list;
}
private LexicalBinding letBinding() {
long begin = ts.beginPosition();
Binding binding;
Expression initializer = null;
if (token() == Token.LC || token() == Token.LB) {
binding = bindingPattern(false);
initializer = initializer(true);
} else {
binding = bindingIdentifier(false);
if (token() == Token.ASSIGN) {
initializer = initializer(true);
if (IsAnonymousFunctionDefinition(initializer)) {
setFunctionName(initializer, (BindingIdentifier) binding);
}
}
}
return new LexicalBinding(begin, ts.endPosition(), binding, initializer);
}
private List<Binding> toBindings(List<LexicalBinding> lexicalBindings) {
ArrayList<Binding> bindings = new ArrayList<>(lexicalBindings.size());
for (LexicalBinding lexicalBinding : lexicalBindings) {
bindings.add(lexicalBinding.getBinding());
}
return bindings;
}
/* ***************************************************************************************** */
/**
* <strong>[12.1] Identifiers</strong>
*
* <pre>
* IdentifierReference<span><sub>[Yield]</sub></span> :
* Identifier
* [~Yield] yield
* </pre>
*
* @return the parsed identifier reference
*/
private IdentifierReference identifierReference() {
long begin = ts.beginPosition();
String identifier = identifier();
if ("arguments".equals(identifier) && context.kind.isFunction()) {
context.funContext.needsArguments(false);
}
return new IdentifierReference(begin, ts.endPosition(), identifier);
}
/**
* <strong>[12.1] Identifiers</strong>
*
* <pre>
* BindingIdentifier<span><sub>[Yield]</sub></span> :
* Identifier
* <span><sub>[~Yield]</sub></span> yield
* </pre>
*
* @return the parsed binding identifier
*/
private BindingIdentifier bindingIdentifier() {
return bindingIdentifier(true);
}
/**
* <strong>[12.1] Identifiers</strong>
*
* <pre>
* BindingIdentifier<span><sub>[Yield]</sub></span> :
* Identifier
* <span><sub>[~Yield]</sub></span> yield
* </pre>
*
* @param allowLet
* the flag to select if 'let' is allowed as a binding name
* @return the parsed binding identifier
*/
private BindingIdentifier bindingIdentifier(boolean allowLet) {
long begin = ts.beginPosition();
if (!allowLet) {
Token tok = token();
if (tok == Token.LET || tok == Token.ESCAPED_LET) {
reportTokenNotIdentifier(Token.LET);
}
}
String identifier = identifier();
if (context.strictMode != StrictMode.NonStrict) {
if ("arguments".equals(identifier) || "eval".equals(identifier)) {
reportStrictModeSyntaxError(begin, Messages.Key.StrictModeRestrictedIdentifier);
}
}
return new BindingIdentifier(begin, ts.endPosition(), identifier);
}
/**
* <strong>[12.1] Identifiers</strong>
* <p>
* Difference when compared to {@link #bindingIdentifier()}:<br>
* Neither "arguments" nor "eval" nor "let" is allowed.
*
* <pre>
* BindingIdentifier<span><sub>[Yield]</sub></span> :
* Identifier
* <span><sub>[~Yield]</sub></span> yield
* </pre>
*
* @return the parsed binding identifier
*/
private BindingIdentifier bindingIdentifierClassName() {
assert context.strictMode == StrictMode.Strict;
return bindingIdentifier(true);
}
/**
* <strong>[12.1] Identifiers</strong>
* <p>
* Special case for {@link Token#YIELD} as {@link BindingIdentifier} in functions and generators
*
* <pre>
* BindingIdentifier<span><sub>[Yield]</sub></span> :
* Identifier
* <span><sub>[~Yield]</sub></span> yield
* </pre>
*
* @param isDeclaration
* {@code true} if the function is declaration node
* @return the parsed binding identifier
*/
private BindingIdentifier bindingIdentifierFunctionName(boolean isDeclaration) {
Token tok = token();
switch (tok) {
case YIELD:
case ESCAPED_YIELD: {
// function declarations inherit the yield mode from the parent context
long begin = ts.beginPosition();
if (!isYieldName(isDeclaration ? context.parent : context)) {
reportTokenNotIdentifier(Token.YIELD);
}
consume(tok);
return new BindingIdentifier(begin, ts.endPosition(), getName(Token.YIELD));
}
case AWAIT:
case ESCAPED_AWAIT: {
// function declarations inherit the await mode from the parent context
long begin = ts.beginPosition();
if (!isAwaitName(isDeclaration ? context.parent : context)) {
reportTokenNotIdentifier(Token.AWAIT);
}
consume(tok);
return new BindingIdentifier(begin, ts.endPosition(), getName(Token.AWAIT));
}
default:
return bindingIdentifier(true);
}
}
/**
* <strong>[12.1] Identifiers</strong>
*
* <pre>
* LabelIdentifier<span><sub>[Yield]</sub></span> :
* Identifier
* <span><sub>[~Yield]</sub></span> yield
* </pre>
*
* @return the parsed label identifier
*/
private String labelIdentifier() {
return identifier();
}
/**
* <strong>[12.1] Identifiers</strong>
*
* <pre>
* Identifier :
* IdentifierName <strong>but not</strong> ReservedWord
* </pre>
*
* @return the parsed identifier
*/
private String identifier() {
Token tok = token();
if (!isIdentifier(tok)) {
reportTokenNotIdentifier(tok);
}
String name = getName(tok);
consume(tok);
return name;
}
/**
* <strong>[12.1] Identifiers</strong>
* <p>
* 12.1.1 Static Semantics: Early Errors
*
* @param tok
* the token to inspect
* @return {@code true} if the token is valid binding identifier
*/
private boolean isBindingIdentifier(Token tok) {
return isIdentifier(tok);
}
/**
* <strong>[12.1] Identifiers</strong>
* <p>
* 12.1.1 Static Semantics: Early Errors
*
* @param tok
* the token to inspect
* @return {@code true} if the token is valid label identifier
*/
private boolean isLabelIdentifier(Token tok) {
return isIdentifier(tok);
}
/**
* <strong>[12.1] Identifiers</strong>
* <p>
* 12.1.1 Static Semantics: Early Errors
*
* @param tok
* the token to inspect
* @return {@code true} if the token is valid identifier reference
*/
private boolean isIdentifierReference(Token tok) {
return isIdentifier(tok);
}
/**
* <strong>[12.1] Identifiers</strong>
* <p>
* 12.1.1 Static Semantics: Early Errors
*
* @param tok
* the token to inspect
* @return {@code true} if the token is valid identifier
*/
private boolean isIdentifier(Token tok) {
switch (tok) {
case NAME:
case ASYNC:
case ESCAPED_NAME:
case ESCAPED_ASYNC:
return true;
case ESCAPED_RESERVED_WORD:
throw reportSyntaxError(Messages.Key.InvalidIdentifier, getName(tok));
case AWAIT:
case ESCAPED_AWAIT:
return isAwaitName(context);
case YIELD:
case ESCAPED_YIELD:
return isYieldName(context);
case LET:
case ESCAPED_LET:
case IMPLEMENTS:
case INTERFACE:
case PACKAGE:
case PRIVATE:
case PROTECTED:
case PUBLIC:
case STATIC:
case ESCAPED_STRICT_RESERVED_WORD:
// Strict mode reserved words
if (context.strictMode != StrictMode.NonStrict) {
reportStrictModeSyntaxError(Messages.Key.StrictModeInvalidIdentifier, getName(tok));
}
return true;
default:
return false;
}
}
/**
* Returns <code>true</code> if {@link Token#YIELD} should be treated as {@link Token#NAME} in
* the supplied context.
*
* @param yieldContext
* the context to use
* @return {@code true} if 'yield' is a valid name in the parse context
*/
private boolean isYieldName(ParseContext yieldContext) {
switch (yieldContext.kind) {
case AsyncGenerator:
case AsyncGeneratorMethod:
case Generator:
case GeneratorMethod:
// 'yield' is always a keyword in generator functions
reportSyntaxError(Messages.Key.InvalidIdentifier, getName(Token.YIELD));
break;
case ArrowFunction:
case AsyncArrowFunction:
case GeneratorComprehension:
if (yieldContext.yieldAllowed) {
// 'yield' in arrow function parameters embedded in generator or generator compr.
// 'yield' in generator comprehension, embedded in generator
reportSyntaxError(Messages.Key.InvalidIdentifier, getName(Token.YIELD));
}
break;
default:
assert !yieldContext.yieldAllowed : String.format(
"unexpected context kind '%s' with yield allowed", yieldContext.kind);
}
// 'yield' is always a keyword in strict-mode (independent of `yieldContext`)
if (context.strictMode != StrictMode.NonStrict) {
reportStrictModeSyntaxError(Messages.Key.StrictModeInvalidIdentifier,
getName(Token.YIELD));
}
return true;
}
/**
* Returns <code>true</code> if {@link Token#AWAIT} should be treated as {@link Token#NAME} in
* the supplied context.
*
* @param awaitContext
* the context to use
* @return {@code true} if 'await' is a valid name in the parse context
*/
private boolean isAwaitName(ParseContext awaitContext) {
switch (awaitContext.kind) {
case AsyncArrowFunction:
case AsyncFunction:
case AsyncGenerator:
case AsyncGeneratorMethod:
case AsyncMethod:
// 'await' is always a keyword in async functions
reportSyntaxError(Messages.Key.InvalidIdentifier, getName(Token.AWAIT));
break;
case ArrowFunction:
case GeneratorComprehension:
if (awaitContext.awaitAllowed) {
// 'await' in arrow function parameters, embedded in async function
// 'await' in generator comprehension, embedded in async function
reportSyntaxError(Messages.Key.InvalidIdentifier, getName(Token.AWAIT));
}
break;
default:
assert !awaitContext.awaitAllowed;
}
// 'await' is always a keyword in module-mode (independent of `awaitContext`)
if (moduleCode) {
reportSyntaxError(Messages.Key.InvalidIdentifier, getName(Token.AWAIT));
}
return true;
}
/**
* <strong>[12.2] Primary Expression</strong>
*
* <pre>
* PrimaryExpresion<span><sub>[Yield]</sub></span> :
* this
* IdentifierReference<span><sub>[?Yield]</sub></span>
* Literal
* ArrayLiteral<span><sub>[?Yield]</sub></span>
* ArrayComprehension<span><sub>[?Yield]</sub></span>
* ObjectLiteral<span><sub>[?Yield]</sub></span>
* FunctionExpression
* ClassExpression
* GeneratorExpression
* GeneratorComprehension<span><sub>[?Yield]</sub></span>
* RegularExpressionLiteral
* TemplateLiteral<span><sub>[?Yield]</sub></span>
* CoverParenthesizedExpressionAndArrowParameterList<span><sub>[?Yield]</sub></span>
* Literal :
* NullLiteral
* BooleanLiteral
* NumericLiteral
* StringLiteral
* </pre>
*
* @return the parsed primary expression node
*/
private Expression primaryExpression() {
long begin = ts.beginPosition();
Token tok = token();
switch (tok) {
case THIS:
consume(tok);
return new ThisExpression(begin, ts.endPosition());
case NULL:
consume(tok);
return new NullLiteral(begin, ts.endPosition());
case FALSE:
case TRUE:
consume(tok);
return new BooleanLiteral(begin, ts.endPosition(), tok == Token.TRUE);
case NUMBER:
double number = numericLiteral();
return new NumericLiteral(begin, ts.endPosition(), number);
case STRING:
String string = stringLiteral();
return new StringLiteral(begin, ts.endPosition(), string);
case DIV:
case ASSIGN_DIV:
return regularExpressionLiteral(tok);
case LB:
return arrayInitializer();
case LC:
return objectLiteral();
case FUNCTION:
return functionOrGeneratorExpression();
case AT:
return classExpression(decorators());
case CLASS:
return classExpression(NO_DECORATORS);
case LP:
if (LOOKAHEAD(Token.FOR) && isEnabled(CompatibilityOption.Comprehension)) {
return generatorComprehension();
} else {
return coverParenthesizedExpressionAndArrowParameterList();
}
case TEMPLATE:
return templateLiteral(false);
case MOD:
if (isEnabled(Option.NativeCall)) {
return nativeCallExpression();
}
break;
case LET:
if (isEnabled(CompatibilityOption.LetExpression)) {
return letExpression();
}
break;
case ASYNC:
if (isEnabled(CompatibilityOption.AsyncFunction) || isEnabled(CompatibilityOption.AsyncGenerator)) {
if (LOOKAHEAD(Token.FUNCTION) && noNextLineTerminator()) {
return asyncFunctionOrGeneratorExpression();
}
}
break;
case DO:
if (isEnabled(CompatibilityOption.DoExpression)) {
return doExpression();
}
break;
default:
}
return identifierReference();
}
private Expression functionOrGeneratorExpression() {
if (LOOKAHEAD(Token.MUL)) {
return generatorExpression(false);
}
long position = ts.position(), lineinfo = ts.lineinfo();
try {
return functionExpression();
} catch (RetryGenerator e) {
ts.reset(position, lineinfo);
return generatorExpression(true);
}
}
/**
* <strong>[12.2] Primary Expression</strong>
*
* <pre>
* CoverParenthesizedExpressionAndArrowParameterList<span><sub>[Yield]</sub></span> :
* ( Expression<span><sub>[In, ?Yield]</sub></span> )
* ( )
* ( ... BindingIdentifier<span><sub>[?Yield]</sub></span> )
* ( Expression<span><sub>[In, ?Yield]</sub></span> , ... BindingIdentifier<span><sub>[?Yield]</sub></span>)
* </pre>
*
* @return the parsed expression node
*/
private Expression coverParenthesizedExpressionAndArrowParameterList() {
long position = ts.position(), lineinfo = ts.lineinfo();
boolean hasAnnotation = false;
long annotationPosition = 0;
consume(Token.LP);
Expression expr;
if (token() == Token.RP) {
expr = arrowFunctionEmptyParameters();
} else if (token() == Token.TRIPLE_DOT) {
expr = arrowFunctionRestParameter();
} else {
// Inlined `expression(true)`, all calls to assignmentExpression() are replaced with
// assignmentExpressionNoValidation() to support cover-init-name and duplicate property
// names in case this production is an ArrowParameterList.
expr = assignmentExpressionNoValidation(true);
if (token() == Token.FOR && isEnabled(CompatibilityOption.LegacyComprehension)) {
// NB: It is not necessary to remove unchecked object literals from
// assignmentExpressionNoValidation(), because any early errors will reappear
// in legacyGeneratorComprehension().
ts.reset(position, lineinfo);
return legacyGeneratorComprehension();
}
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
if (!hasAnnotation) {
hasAnnotation = true;
annotationPosition = ts.sourcePosition();
}
typeAnnotation();
}
if (token() == Token.COMMA) {
InlineArrayList<Expression> list = newList();
list.add(expr);
do {
consume(Token.COMMA);
if (token() == Token.TRIPLE_DOT) {
list.add(arrowFunctionRestParameter());
break;
}
if (token() == Token.RP && LOOKAHEAD(Token.ARROW)
&& isEnabled(CompatibilityOption.FunctionCallTrailingComma)) {
break;
}
expr = assignmentExpressionNoValidation(true);
if (token() == Token.COLON && isEnabled(CompatibilityOption.TypeAnnotation)) {
if (!hasAnnotation) {
hasAnnotation = true;
annotationPosition = ts.sourcePosition();
}
typeAnnotation();
}
list.add(expr);
} while (token() == Token.COMMA);
expr = new CommaExpression(list);
}
}
expr.addParentheses();
consume(Token.RP);
if (hasAnnotation && token() != Token.ARROW) {
reportSyntaxError(annotationPosition, Messages.Key.UnexpectedToken, Token.COLON.toString(),
Token.COMMA.toString());
}
return expr;
}
private EmptyExpression arrowFunctionEmptyParameters() {
if (!(token() == Token.RP && LOOKAHEAD(Token.ARROW))) {
reportSyntaxError(Messages.Key.EmptyParenthesizedExpression);
}
return new EmptyExpression(0, 0);
}
private SpreadElement arrowFunctionRestParameter() {
long begin = ts.beginPosition();
consume(Token.TRIPLE_DOT);
Binding binding;
if (isEnabled(CompatibilityOption.RestBindingPattern)) {
binding = binding(true);
} else {
binding = bindingIdentifier(true);
}
String ident;
if (binding instanceof BindingIdentifier) {
ident = ((BindingIdentifier) binding).getName().getIdentifier();
} else {
ident = "<binding-pattern>";
}
IdentifierReference identifier = new IdentifierReference(ts.beginPosition(), ts.endPosition(), ident);
SpreadElement spread = new SpreadElement(begin, ts.endPosition(), identifier);
if (!(token() == Token.RP && LOOKAHEAD(Token.ARROW))) {
reportSyntaxError(spread, Messages.Key.InvalidSpreadExpression);
}
return spread;
}
/**
* <strong>[12.2.5] Array Initializer</strong>
*
* <pre>
* ArrayInitializer<span><sub>[Yield]</sub></span> :
* ArrayLiteral<span><sub>[?Yield]</sub></span>
* ArrayComprehension<span><sub>[?Yield]</sub></span>
* </pre>
*
* @return the parsed array initializer
*/
private ArrayInitializer arrayInitializer() {
if (LOOKAHEAD(Token.FOR) && isEnabled(CompatibilityOption.Comprehension)) {
return arrayComprehension();
}
long begin = ts.beginPosition();
if (isEnabled(CompatibilityOption.LegacyComprehension)) {
switch (peek()) {
case RB:
case COMMA:
case TRIPLE_DOT:
break;
default:
long position = ts.position(), lineinfo = ts.lineinfo();
consume(Token.LB);
Expression expression = assignmentExpressionNoValidation(true);
if (token() == Token.FOR) {
// NB: It is not necessary to remove unchecked object literals from
// assignmentExpressionNoValidation(), because any early errors will
// reappear in legacyArrayComprehension()
ts.reset(position, lineinfo);
return legacyArrayComprehension();
}
return arrayLiteral(begin, expression);
}
}
return arrayLiteral(begin, null);
}
/**
* <strong>[12.2.5] Array Initializer</strong>
*
* <pre>
* ArrayLiteral<span><sub>[Yield]</sub></span> :
* [ Elision<span><sub>opt</sub></span> ]
* [ ElementList<span><sub>[?Yield]</sub></span> ]
* [ ElementList<span><sub>[?Yield]</sub></span> , Elision<span><sub>opt</sub></span> ]
* ElementList<span><sub>[Yield]</sub></span> :
* Elision<span><sub>opt</sub></span> AssignmentExpression<span><sub>[In, ?Yield]</sub></span>
* Elision<span><sub>opt</sub></span> SpreadElement<span><sub>[?Yield]</sub></span>
* ElementList<span><sub>[?Yield]</sub></span> , Elision<span><sub>opt</sub></span> AssignmentExpression<span><sub>[In, ?Yield]</sub></span>
* ElementList<span><sub>[?Yield]</sub></span> , Elision<span><sub>opt</sub></span> SpreadElement<span><sub>[?Yield]</sub></span>
* Elision :
* ,
* Elision ,
* SpreadElement<span><sub>[Yield]</sub></span> :
* ... AssignmentExpression<span><sub>[In, ?Yield]</sub></span>
* </pre>
*
* @param begin
* the begin source position
* @param expr
* the first array element or {@code null} if not yet parsed
* @return the parsed array literal
*/
private ArrayLiteral arrayLiteral(long begin, Expression expr) {
InlineArrayList<Expression> list = newList();
boolean needComma = false;
if (expr == null) {
consume(Token.LB);
} else {
list.add(expr);
needComma = true;
}
for (Token tok; (tok = token()) != Token.RB;) {
if (needComma) {
consume(Token.COMMA);
needComma = false;
} else if (tok == Token.COMMA) {
consume(Token.COMMA);
list.add(new Elision(0, 0));
} else if (tok == Token.TRIPLE_DOT) {
long beginSpread = ts.beginPosition();
consume(Token.TRIPLE_DOT);
Expression expression = assignmentExpressionNoValidation(true);
list.add(new SpreadElement(beginSpread, ts.endPosition(), expression));
needComma = true;
} else {
list.add(assignmentExpressionNoValidation(true));
needComma = true;
}
}
consume(Token.RB);
return new ArrayLiteral(begin, ts.endPosition(), list, !needComma && !list.isEmpty());
}
/**
* <strong>Array Comprehension</strong>
*
* <pre>
* ArrayComprehension<span><sub>[Yield]</sub></span> :
* [ Comprehension<span><sub>[?Yield]</sub></span> ]
* </pre>
*
* @return the parsed array comprehension
*/
private ArrayComprehension arrayComprehension() {
long begin = ts.beginPosition();
consume(Token.LB);
Comprehension comprehension = comprehension();
consume(Token.RB);
return new ArrayComprehension(begin, ts.endPosition(), comprehension);
}
/**
* <strong>Array Comprehension</strong>
*
* <pre>
* Comprehension<span><sub>[Yield]</sub></span> :
* ComprehensionFor<span><sub>[?Yield]</sub></span> ComprehensionTail<span><sub>[?Yield]</sub></span>
* ComprehensionTail<span><sub>[Yield]</sub></span> :
* AssignmentExpression<span><sub>[In, ?Yield]</sub></span>
* ComprehensionFor<span><sub>[?Yield]</sub></span> ComprehensionTail<span><sub>[?Yield]</sub></span>
* ComprehensionIf<span><sub>[?Yield]</sub></span> ComprehensionTail<span><sub>[?Yield]</sub></span>
* </pre>
*
* @return the parsed comprehension node
*/
private Comprehension comprehension() {
assert token() == Token.FOR;
InlineArrayList<ComprehensionQualifier> list = newList();
int scopes = 0;
for (;;) {
ComprehensionQualifier qualifier;
if (token() == Token.FOR) {
scopes += 1;
qualifier = comprehensionFor();
} else if (token() == Token.IF) {
qualifier = comprehensionIf();
} else {
break;
}
list.add(qualifier);
}
Expression expression = assignmentExpression(true);
while (scopes-- > 0) {
exitBlockContext();
}
return new Comprehension(list, expression);
}
/**
* <strong>Array Comprehension</strong>
*
* <pre>
* ComprehensionFor<span><sub>[Yield]</sub></span> :
* for ( ForBinding<span><sub>[?Yield]</sub></span> of AssignmentExpression<span><sub>[In, ?Yield]</sub></span> )
* ForBinding<span><sub>[Yield]</sub></span> :
* BindingIdentifier<span><sub>[?Yield]</sub></span>
* BindingPattern<span><sub>[?Yield]</sub></span>
* </pre>
*
* @return the parsed comprehension-for node
*/
private ComprehensionFor comprehensionFor() {
long begin = ts.beginPosition();
consume(Token.FOR);
consume(Token.LP);
Binding b = forBinding(false);
consume("of");
Expression expression = assignmentExpression(true);
consume(Token.RP);
BlockContext scope = enterBlockContext(b);
ComprehensionFor node = new ComprehensionFor(begin, ts.endPosition(), scope, b, expression);
scope.node = node;
return node;
}
/**
* <strong>Array Comprehension</strong>
*
* <pre>
* ForBinding<span><sub>[Yield]</sub></span> :
* BindingIdentifier<span><sub>[?Yield]</sub></span>
* BindingPattern<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param allowLet
* the flag to select if 'let' is allowed as a binding name
* @return the parsed for-binding
*/
private Binding forBinding(boolean allowLet) {
return binding(allowLet);
}
/**
* <strong>Array Comprehension</strong>
*
* <pre>
* ComprehensionIf<span><sub>[Yield]</sub></span> :
* if ( AssignmentExpression<span><sub>[In, ?Yield]</sub></span> )
* </pre>
*
* @return the parsed comprehension-if node
*/
private ComprehensionIf comprehensionIf() {
long begin = ts.beginPosition();
consume(Token.IF);
consume(Token.LP);
Expression expression = assignmentExpression(true);
consume(Token.RP);
return new ComprehensionIf(begin, ts.endPosition(), expression);
}
/**
* <strong>Array Comprehension</strong>
*
* <pre>
* LegacyArrayComprehension<span><sub>[Yield]</sub></span> :
* [ LegacyComprehension<span><sub>[?Yield]</sub></span> ]
* </pre>
*
* @return the parsed legacy array comprehension
*/
private ArrayComprehension legacyArrayComprehension() {
long begin = ts.beginPosition();
consume(Token.LB);
LegacyComprehension comprehension = legacyComprehension();
consume(Token.RB);
return new ArrayComprehension(begin, ts.endPosition(), comprehension);
}
/**
* <strong>Array Comprehension</strong>
*
* <pre>
* LegacyComprehension<span><sub>[Yield]</sub></span> :
* AssignmentExpression<span><sub>[In, ?Yield]</sub></span> LegacyComprehensionForList<span><sub>[?Yield]</sub></span> LegacyComprehensionIf<span><sub>[?Yield]opt</sub></span>
* LegacyComprehensionForList<span><sub>[Yield]</sub></span> :
* LegacyComprehensionFor<span><sub>[?Yield]</sub></span> LegacyComprehensionForList<span><sub>[?Yield]opt</sub></span>
* LegacyComprehensionFor<span><sub>[Yield]</sub></span> :
* for ( ForBinding<span><sub>[?Yield]</sub></span> of Expression<span><sub>[In, ?Yield]</sub></span> )
* for ( ForBinding<span><sub>[?Yield]</sub></span> in Expression<span><sub>[In, ?Yield]</sub></span> )
* for each ( ForBinding<span><sub>[?Yield]</sub></span> in Expression<span><sub>[In, ?Yield]</sub></span> )
* LegacyComprehensionIf<span><sub>[Yield]</sub></span> :
* if ( Expression<span><sub>[In, ?Yield]</sub></span> )
* </pre>
*
* @return the parsed legacy comprehension
*/
private LegacyComprehension legacyComprehension() {
BlockContext scope = enterBlockContext();
Expression expr = assignmentExpression(true);
InlineArrayList<ComprehensionQualifier> list = newList();
do {
long begin = ts.beginPosition();
consume(Token.FOR);
boolean each = false;
if (token() != Token.LP && isName("each")) {
consume("each");
each = true;
}
consume(Token.LP);
Binding b = forBinding(false);
addLexDeclaredName(b);
LegacyComprehensionFor.IterationKind iterationKind;
if (each) {
consume(Token.IN);
iterationKind = LegacyComprehensionFor.IterationKind.EnumerateValues;
} else if (token() == Token.IN) {
consume(Token.IN);
iterationKind = LegacyComprehensionFor.IterationKind.Enumerate;
} else {
consume("of");
iterationKind = LegacyComprehensionFor.IterationKind.Iterate;
}
Expression expression = expression(true);
consume(Token.RP);
list.add(new LegacyComprehensionFor(begin, ts.endPosition(), iterationKind, b,
expression));
} while (token() == Token.FOR);
if (token() == Token.IF) {
long begin = ts.beginPosition();
consume(Token.IF);
consume(Token.LP);
Expression expression = expression(true);
consume(Token.RP);
list.add(new ComprehensionIf(begin, ts.endPosition(), expression));
}
exitBlockContext();
LegacyComprehension comprehension = new LegacyComprehension(scope, list, expr);
scope.node = comprehension;
return comprehension;
}
/**
* <strong>[12.2.6] Object Initializer</strong>
*
* <pre>
* ObjectLiteral<span><sub>[Yield]</sub></span> :
* { }
* { PropertyDefinitionList<span><sub>[?Yield]</sub></span> }
* { PropertyDefinitionList<span><sub>[?Yield]</sub></span> , }
* PropertyDefinitionList<span><sub>[Yield]</sub></span> :
* PropertyDefinition<span><sub>[?Yield]</sub></span>
* PropertyDefinitionList<span><sub>[?Yield]</sub></span> , PropertyDefinition<span><sub>[?Yield]</sub></span>
* </pre>
*
* @return the parsed object literal
*/
private ObjectLiteral objectLiteral() {
long begin = ts.beginPosition();
consume(Token.LC);
List<PropertyDefinition> defs;
boolean trailingComma = false;
if (token() == Token.RC) {
defs = Collections.emptyList();
} else {
InlineArrayList<PropertyDefinition> list = newList();
for (;;) {
list.add(propertyDefinition());
if (token() != Token.COMMA) {
break;
}
consume(Token.COMMA);
if (token() == Token.RC) {
trailingComma = true;
break;
}
}
defs = list;
}
consume(Token.RC);
ObjectLiteral object = new ObjectLiteral(begin, ts.endPosition(), defs, trailingComma);
context.addLiteral(object);
return object;
}
private void discardUncheckedObjectLiterals(int oldCount) {
ArrayDeque<ObjectLiteral> literals = context.objectLiterals;
if (literals != null) {
for (int i = oldCount, newCount = literals.size(); i < newCount; ++i) {
literals.pop();
}
}
}
/**
* 12.2.6.1 Static Semantics: Early Errors
*
* @param oldCount
* the previous count of object literals
*/
private void objectLiteral_EarlyErrors(int oldCount) {
ArrayDeque<ObjectLiteral> literals = context.objectLiterals;
if (literals != null) {
for (int i = oldCount, newCount = literals.size(); i < newCount; ++i) {
objectLiteral_EarlyErrors(literals.pop());
}
}
}
/**
* 12.2.6.1 Static Semantics: Early Errors
*
* @param object
* the object literal to check for early errors
*/
private void objectLiteral_EarlyErrors(ObjectLiteral object) {
boolean hasProto = false, checkProto = isEnabled(CompatibilityOption.ProtoInitializer);
for (PropertyDefinition def : object.getProperties()) {
if (def instanceof CoverInitializedName) {
// Always throw a Syntax Error if this production is present
String key = def.getPropertyName().getName();
reportSyntaxError(def, Messages.Key.MissingColonAfterPropertyId, key);
}
if (def instanceof SpreadProperty && !isEnabled(CompatibilityOption.ObjectSpreadInitializer)) {
reportSyntaxError(def, Messages.Key.InvalidDestructuring);
}
if (def instanceof PropertyValueDefinition && checkProto) {
String key = def.getPropertyName().getName();
if (key == null) {
assert def.getPropertyName() instanceof ComputedPropertyName;
continue;
}
if ("__proto__".equals(key)) {
if (hasProto) {
reportSyntaxError(def, Messages.Key.DuplicatePropertyDefinition, key);
}
hasProto = true;
}
}
}
}
/**
* <strong>[12.2.6] Object Initializer</strong>
*
* <pre>
* PropertyDefinition<span><sub>[Yield]</sub></span> :
* IdentifierReference<span><sub>[?Yield]</sub></span>
* CoverInitializedName<span><sub>[?Yield]</sub></span>
* PropertyName<span><sub>[?Yield]</sub></span> : AssignmentExpression<span><sub>[In, ?Yield]</sub></span>
* MethodDefinition<span><sub>[?Yield]</sub></span>
* CoverInitializedName<span><sub>[Yield]</sub></span> :
* IdentifierReference Initializer<span><sub>[In, ?Yield]</sub></span>
* Initializer<span><sub>[In, Yield]</sub></span> :
* = AssignmentExpression<span><sub>[?In, ?Yield]</sub></span>
* </pre>
*
* @return the parsed property definition
*/
private PropertyDefinition propertyDefinition() {
long begin = ts.beginPosition();
if (token() == Token.LB) {
// either `PropertyName : AssignmentExpression` or MethodDefinition (normal)
ComputedPropertyName propertyName = computedPropertyName();
if (token() == Token.COLON) {
consume(Token.COLON);
Expression propertyValue = assignmentExpressionNoValidation(true);
if (IsAnonymousFunctionDefinition(propertyValue)) {
setFunctionName(propertyValue, propertyName);
}
return new PropertyValueDefinition(begin, ts.endPosition(), propertyName, propertyValue);
}
return normalMethod(MethodAllocation.Object, false, NO_DECORATORS, begin, propertyName, false);
}
if (token() == Token.TRIPLE_DOT && (isEnabled(CompatibilityOption.ObjectSpreadInitializer)
|| isEnabled(CompatibilityOption.ObjectRestDestructuring))) {
consume(Token.TRIPLE_DOT);
Expression expression = assignmentExpressionNoValidation(true);
return new SpreadProperty(begin, ts.endPosition(), expression);
}
if (isPropertyName(token()) && LOOKAHEAD(Token.COLON)) {
PropertyName propertyName = literalPropertyName();
consume(Token.COLON);
Expression propertyValue = assignmentExpressionNoValidation(true);
if (IsAnonymousFunctionDefinition(propertyValue)) {
setFunctionName(propertyValue, propertyName);
}
return new PropertyValueDefinition(begin, ts.endPosition(), propertyName, propertyValue);
}
if (Token.isIdentifierName(token()) && (LOOKAHEAD(Token.COMMA) || LOOKAHEAD(Token.RC))) {
IdentifierReference identifier = identifierReference();
return new PropertyNameDefinition(begin, ts.endPosition(), identifier);
}
if (Token.isIdentifierName(token()) && LOOKAHEAD(Token.ASSIGN)) {
IdentifierReference identifier = identifierReference();
consume(Token.ASSIGN);
Expression initializer = assignmentExpression(true);
if (IsAnonymousFunctionDefinition(initializer)) {
setFunctionName(initializer, identifier);
}
return new CoverInitializedName(begin, ts.endPosition(), identifier, initializer);
}
List<Expression> decorators = token() == Token.AT ? decorators() : NO_DECORATORS;
return methodDefinition(MethodAllocation.Object, false, decorators);
}
/**
* <strong>[12.2.6] Object Initializer</strong>
*
* <pre>
* PropertyName<span><sub>[Yield]</sub></span> :
* LiteralPropertyName
* ComputedPropertyName<span><sub>[?Yield]</sub></span>
* </pre>
*
* @return the parsed property name
*/
private PropertyName propertyName() {
if (token() != Token.LB) {
return literalPropertyName();
} else {
return computedPropertyName();
}
}
/**
* <strong>[12.2.6] Object Initializer</strong>
*
* <pre>
* LiteralPropertyName :
* IdentifierName
* StringLiteral
* NumericLiteral
* </pre>
*
* @return the parsed literal property name
*/
private PropertyName literalPropertyName() {
long begin = ts.beginPosition();
switch (token()) {
case STRING:
String string = stringLiteral();
return new StringLiteral(begin, ts.endPosition(), string);
case NUMBER:
double number = numericLiteral();
return new NumericLiteral(begin, ts.endPosition(), number);
default:
String ident = identifierName();
return new IdentifierName(begin, ts.endPosition(), ident);
}
}
/**
* <strong>[12.2.6] Object Initializer</strong>
*
* <pre>
* ComputedPropertyName<span><sub>[Yield]</sub></span> :
* [ AssignmentExpression<span><sub>[In, ?Yield]</sub></span> ]
* </pre>
*
* @return the parsed computed property name
*/
private ComputedPropertyName computedPropertyName() {
long begin = ts.beginPosition();
consume(Token.LB);
Expression expression = assignmentExpression(true);
consume(Token.RB);
return new ComputedPropertyName(begin, ts.endPosition(), expression);
}
/**
* <strong>Generator Comprehensions</strong>
*
* <pre>
* GeneratorComprehension<span><sub>[Yield]</sub></span> :
* ( Comprehension<span><sub>[?Yield]</sub></span> )
* </pre>
*
* @return the parsed generator comprehension
*/
private GeneratorComprehension generatorComprehension() {
newContext(ContextKind.GeneratorComprehension);
try {
long begin = ts.beginPosition();
// generator comprehensions have no named parameters
FormalParameterList parameters = emptyFormalParameterList();
Comprehension comprehension = generatorComprehensionBody();
FunctionContext scope = context.funContext;
GeneratorComprehension generator = new GeneratorComprehension(begin, ts.endPosition(), scope,
generatorKind(), parameters, comprehension);
scope.setNode(generator);
return inheritStrictness(generator);
} finally {
restoreContext();
}
}
private Comprehension generatorComprehensionBody() {
// do not enable 'return' for consistency with 'yield' in comprehensions
context.returnAllowed = false;
// propagate the outer context's 'yield' state
context.yieldAllowed = context.parent.yieldAllowed;
// propagate the outer context's 'await' state
context.awaitAllowed = context.parent.awaitAllowed;
// Necessary to call applyStrictMode() manually b/c directivePrologue() is not used.
applyStrictMode(false);
if (context.strictMode != StrictMode.Strict) {
context.funContext.lexicalScope = enterFunctionBodyContext();
}
consume(Token.LP);
Comprehension comprehension = comprehension();
consume(Token.RP);
if (context.strictMode != StrictMode.Strict) {
exitFunctionBodyContext();
}
assert context.assertLiteralsUnchecked(0);
return comprehension;
}
/**
* <strong>Generator Comprehensions</strong>
*
* <pre>
* LegacyGeneratorComprehension<span><sub>[Yield]</sub></span> :
* ( LegacyComprehension<span><sub>[?Yield]</sub></span> )
* </pre>
*
* @return the parsed legacy generator comprehension
*/
private GeneratorComprehension legacyGeneratorComprehension() {
newContext(ContextKind.GeneratorComprehension);
try {
long begin = ts.beginPosition();
// generator comprehensions have no named parameters
FormalParameterList parameters = emptyFormalParameterList();
LegacyComprehension comprehension = legacyGeneratorComprehensionBody();
FunctionContext scope = context.funContext;
GeneratorComprehension generator = new GeneratorComprehension(begin, ts.endPosition(), scope,
GeneratorKind.Constructor, parameters, comprehension);
scope.setNode(generator);
return inheritStrictness(generator);
} finally {
restoreContext();
}
}
private LegacyComprehension legacyGeneratorComprehensionBody() {
// do not enable 'return' for consistency with 'yield' in comprehensions
context.returnAllowed = false;
// propagate the outer context's 'yield' state
context.yieldAllowed = context.parent.yieldAllowed;
// propagate the outer context's 'await' state
context.awaitAllowed = context.parent.awaitAllowed;
// Necessary to call applyStrictMode() manually b/c directivePrologue() is not used.
applyStrictMode(false);
if (context.strictMode != StrictMode.Strict) {
context.funContext.lexicalScope = enterFunctionBodyContext();
}
consume(Token.LP);
LegacyComprehension comprehension = legacyComprehension();
consume(Token.RP);
if (context.strictMode != StrictMode.Strict) {
exitFunctionBodyContext();
}
assert context.assertLiteralsUnchecked(0);
return comprehension;
}
/**
* <strong>[12.2.8] Regular Expression Literals</strong>
*
* <pre>
* RegularExpressionLiteral ::
* / RegularExpressionBody / RegularExpressionFlags
* </pre>
*
* @param tok
* the start token of the regular expression, must be either {@link Token#DIV} or
* {@link Token#ASSIGN_DIV}
* @return the parsed regular expression literal
*/
private RegularExpressionLiteral regularExpressionLiteral(Token tok) {
long begin = ts.beginPosition();
String pattern = ts.readRegularExpression(tok);
String flags = ts.readRegularExpressionFlags();
// Validate regular expression syntax. (12.2.8.1 Static Semantics: Early Errors)
RegExpParser.syntaxParse(pattern, flags, getSourceName(), toLine(begin), toColumn(begin),
isEnabled(CompatibilityOption.WebRegularExpressions));
consume(tok);
return new RegularExpressionLiteral(begin, ts.endPosition(), pattern, flags);
}
/**
* <strong>[12.2.9] Template Literals</strong>
*
* <pre>
* TemplateLiteral<span><sub>[Yield]</sub></span> :
* NoSubstitutionTemplate
* TemplateHead Expression<span><sub>[In, ?Yield]</sub></span> TemplateSpans<span><sub>[?Yield]</sub></span>
* TemplateSpans<span><sub>[Yield]</sub></span> :
* TemplateTail
* TemplateMiddleList<span><sub>[?Yield]</sub></span> TemplateTail
* TemplateMiddleList<span><sub>[Yield]</sub></span> :
* TemplateMiddle Expression<span><sub>[In, ?Yield]</sub></span>
* TemplateMiddleList<span><sub>[?Yield]</sub></span> TemplateMiddle Expression<span><sub>[In, ?Yield]</sub></span>
* </pre>
*
* @param tagged
* the flag for tagged template literals
* @return the parsed template literal
*/
private TemplateLiteral templateLiteral(boolean tagged) {
long begin = ts.beginPosition();
InlineArrayList<Expression> elements = newList();
elements.add(templateCharacters(Token.TEMPLATE));
while (token() == Token.LC) {
consume(Token.LC);
elements.add(expression(true));
if (token() != Token.RC) {
reportTokenMismatch(Token.RC, token());
}
elements.add(templateCharacters(Token.RC));
}
consume(Token.TEMPLATE);
if (tagged && (elements.size() / 2) + 1 > MAX_ARGUMENTS) {
reportSyntaxError(Messages.Key.FunctionTooManyArguments);
}
return new TemplateLiteral(begin, ts.endPosition(), tagged, elements);
}
private TemplateCharacters templateCharacters(Token start) {
long begin = ts.beginPosition();
String[] values = ts.readTemplateLiteral(start);
return new TemplateCharacters(begin, ts.rawEndPosition(), values[0], values[1]);
}
/**
* <strong>[Extension] Native call expression</strong>
*
* <pre>
* NativeCallExpression :
* % Identifier ( NativeCallArguments )
* </pre>
*/
private NativeCallExpression nativeCallExpression() {
long begin = ts.beginPosition();
consume(Token.MOD);
IdentifierReference name = identifierReference();
List<Expression> args = nativeCallArguments();
return new NativeCallExpression(begin, ts.endPosition(), name, args);
}
/**
* <strong>[Extension] Native call expression</strong>
*
* <pre>
* NativeCallArguments<span><sub>[Yield]</sub></span> :
* ()
* ( NativeCallArgumentList<span><sub>[?Yield]</sub></span> )
* ( NativeCallArgumentList<span><sub>[?Yield]</sub></span> , ... AssignmentExpression<span><sub>[In, ?Yield]</sub></span> )
* ( ... AssignmentExpression<span><sub>[In, ?Yield]</sub></span> )
* NativeCallArgumentList<span><sub>[Yield]</sub></span> :
* AssignmentExpression<span><sub>[In, ?Yield]</sub></span>
* NativeCallArgumentList<span><sub>[?Yield]</sub></span> , AssignmentExpression<span><sub>[In, ?Yield]</sub></span>
* </pre>
*
* @return the list of parsed function call arguments
*/
private List<Expression> nativeCallArguments() {
consume(Token.LP);
if (token() == Token.RP) {
consume(Token.RP);
return Collections.emptyList();
}
InlineArrayList<Expression> args = newList();
for (;;) {
// Allow only trailing ...spread arguments.
if (token() == Token.TRIPLE_DOT) {
long begin = ts.beginPosition();
consume(Token.TRIPLE_DOT);
Expression e = assignmentExpression(true);
args.add(new CallSpreadElement(begin, ts.endPosition(), e));
break;
}
args.add(assignmentExpression(true));
if (token() == Token.COMMA) {
consume(Token.COMMA);
if (token() == Token.RP && isEnabled(CompatibilityOption.FunctionCallTrailingComma)) {
break;
}
} else {
break;
}
}
if (args.size() > NATIVE_CALL_MAX_ARGUMENTS) {
reportSyntaxError(Messages.Key.FunctionTooManyArguments);
}
consume(Token.RP);
return args;
}
/**
* <strong>[Extension] The <code>let</code> Expression</strong>
*
* <pre>
* LetExpression<span><sub>[Yield]</sub></span> :
* let ( BindingList<span><sub>[In, ?Yield]</sub></span> ) AssignmentExpression<span><sub>[In, ?Yield]</sub></span>
* </pre>
*
* @return the parsed let expression
*/
private LetExpression letExpression() {
long begin = ts.beginPosition();
consume(Token.LET);
consume(Token.LP);
List<LexicalBinding> lexicalBindings = letBindingList();
List<Binding> bindings = toBindings(lexicalBindings);
consume(Token.RP);
BlockContext scope = enterBlockContext(bindings);
Expression expression = assignmentExpression(true);
exitBlockContext();
LetExpression letExpression = new LetExpression(begin, ts.endPosition(), scope,
lexicalBindings, expression);
scope.node = letExpression;
return letExpression;
}
/**
* <strong>[Extension] The <code>do</code> Expression</strong>
*
* <pre>
* DoExpression<span><sub>[Yield]</sub></span> :
* do Block<span><sub>[?Yield]</sub></span>
* </pre>
*
* @return the parsed do expression
*/
private DoExpression doExpression() {
long begin = ts.beginPosition();
consume(Token.DO);
boolean oldYieldOrAwaitExpression = context.yieldOrAwaitExpression;
context.yieldOrAwaitExpression = false;
try {
BlockStatement block = block(NO_INHERITED_BINDING);
return new DoExpression(begin, ts.endPosition(), block, context.yieldOrAwaitExpression);
} finally {
context.yieldOrAwaitExpression = oldYieldOrAwaitExpression;
}
}
/**
* <strong>[12.3] Left-Hand-Side Expressions</strong>
*
* <pre>
* MemberExpression<span><sub>[Yield]</sub></span> :
* PrimaryExpression<span><sub>[?Yield]</sub></span>
* MemberExpression<span><sub>[?Yield]</sub></span> [ Expression<span><sub>[In, ?Yield]</sub></span> ]
* MemberExpression<span><sub>[?Yield]</sub></span> . IdentifierName
* MemberExpression<span><sub>[?Yield]</sub></span> TemplateLiteral<span><sub>[?Yield]</sub></span>
* SuperProperty<span><sub>[?Yield]</sub></span>
* NewSuper Arguments<span><sub>[?Yield]</sub></span>
* MetaProperty
* new MemberExpression<span><sub>[?Yield]</sub></span> Arguments<span><sub>[?Yield]</sub></span>
* SuperProperty<span><sub>[Yield]</sub></span> :
* super [ Expression<span><sub>[In, ?Yield]</sub></span> ]
* super . IdentifierName
* MetaProperty :
* NewTarget
* NewTarget :
* new . target
* NewExpression<span><sub>[Yield]</sub></span> :
* MemberExpression<span><sub>[?Yield]</sub></span>
* new NewExpression<span><sub>[?Yield]</sub></span>
* NewSuper
* NewSuper :
* new super
* CallExpression<span><sub>[Yield]</sub></span> :
* MemberExpression<span><sub>[?Yield]</sub></span> Arguments<span><sub>[?Yield]</sub></span>
* SuperCall<span><sub>[?Yield]</sub></span>
* CallExpression<span><sub>[?Yield]</sub></span> Arguments<span><sub>[?Yield]</sub></span>
* CallExpression<span><sub>[?Yield]</sub></span> [ Expression<span><sub>[In, ?Yield]</sub></span> ]
* CallExpression<span><sub>[?Yield]</sub></span> . IdentifierName
* CallExpression<span><sub>[?Yield]</sub></span> TemplateLiteral<span><sub>[?Yield]</sub></span>
* SuperCall<span><sub>[Yield]</sub></span> :
* super Arguments<span><sub>[?Yield]</sub></span>
* LeftHandSideExpression<span><sub>[Yield]</sub></span> :
* NewExpression<span><sub>[?Yield]</sub></span>
* CallExpression<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param allowCall
* the flag to select whether or not call expressions are allowed
* @param allowElement
* the flag to select whether or not element member expressions are allowed
* @return the parsed left-hand side expression
*/
private Expression leftHandSideExpression(boolean allowCall, boolean allowElement) {
long begin = ts.beginPosition();
Expression lhs;
switch (token()) {
case NEW: {
consume(Token.NEW);
if (token() == Token.DOT) {
newTarget();
consume(Token.DOT);
consume("target");
lhs = new NewTarget(begin, ts.endPosition());
} else if (token() == Token.SUPER && !(LOOKAHEAD(Token.DOT) || LOOKAHEAD(Token.LB))
&& isEnabled(CompatibilityOption.NewSuper)) {
newSuper();
consume(Token.SUPER);
if (token() == Token.LP) {
List<Expression> args = arguments();
lhs = new SuperNewExpression(begin, ts.endPosition(), args);
} else {
return new SuperNewExpression(begin, ts.endPosition(),
Collections.<Expression> emptyList());
}
} else {
Expression expr = leftHandSideExpression(false, allowElement);
if (token() == Token.LP) {
List<Expression> args = arguments();
lhs = new NewExpression(begin, ts.endPosition(), expr, args);
} else {
return new NewExpression(begin, ts.endPosition(), expr,
Collections.<Expression> emptyList());
}
}
break;
}
case SUPER: {
consume(Token.SUPER);
switch (token()) {
case DOT:
superPropertyAccess();
consume(Token.DOT);
String name = identifierName();
lhs = new SuperPropertyAccessor(begin, ts.endPosition(), name);
break;
case LB:
superPropertyAccess();
consume(Token.LB);
Expression expr = expression(true);
consume(Token.RB);
lhs = new SuperElementAccessor(begin, ts.endPosition(), expr);
break;
case LP:
if (allowCall) {
superCall();
List<Expression> args = arguments();
lhs = new SuperCallExpression(begin, ts.endPosition(), args);
break;
}
case TEMPLATE:
default:
throw reportSyntaxError(Messages.Key.InvalidToken, token().toString());
}
break;
}
case FUNCTION: {
if (LOOKAHEAD(Token.DOT) && isEnabled(CompatibilityOption.FunctionSent)) {
consume(Token.FUNCTION);
consume(Token.DOT);
consume("sent");
// TODO: function.sent available in async generators?
switch (context.kind) {
case Generator:
case GeneratorMethod:
if (context.yieldAllowed) {
break;
}
// fall-through
default:
reportSyntaxError(Messages.Key.InvalidFunctionSent);
}
lhs = new FunctionSent(begin, ts.endPosition());
break;
}
// fall-through
}
default:
lhs = primaryExpression();
}
for (;;) {
switch (token()) {
case DOT:
consume(Token.DOT);
String name = identifierName();
lhs = new PropertyAccessor(begin, ts.endPosition(), lhs, name);
break;
case LB:
if (!allowElement) {
return lhs;
}
consume(Token.LB);
Expression expr = expression(true);
consume(Token.RB);
lhs = new ElementAccessor(begin, ts.endPosition(), lhs, expr);
break;
case LP:
if (!allowCall) {
return lhs;
}
if (lhs instanceof IdentifierReference
&& "eval".equals(((IdentifierReference) lhs).getName())) {
context.setHasEval();
}
List<Expression> args = arguments();
lhs = new CallExpression(begin, ts.endPosition(), lhs, args);
break;
case TEMPLATE:
TemplateLiteral templ = templateLiteral(true);
lhs = new TemplateCallExpression(begin, ts.endPosition(), lhs, templ);
break;
default:
return lhs;
}
}
}
private void superPropertyAccess() {
ParseContext superContext = context.findSuperContext();
// 14.1.2 Static Semantics: Early Errors
// 15.1.1 Static Semantics: Early Errors
// 15.2.1.1 Static Semantics: Early Errors
if ((superContext.kind == ContextKind.Script && !isEnabled(Option.FunctionThis))
|| superContext.kind == ContextKind.Module
|| superContext.kind == ContextKind.Function
|| superContext.kind == ContextKind.Generator
|| superContext.kind == ContextKind.AsyncFunction
|| superContext.kind == ContextKind.AsyncGenerator) {
reportSyntaxError(Messages.Key.InvalidSuperExpression);
}
superContext.setNeedsSuperBinding();
}
private void superCall() {
ParseContext superContext = context.findSuperContext();
// 12.2.6.1 Static Semantics: Early Errors
// 14.1.2 Static Semantics: Early Errors
// 14.4.1 Static Semantics: Early Errors
// 14.5.1 Static Semantics: Early Errors
// 15.1.1 Static Semantics: Early Errors
// 15.2.1.1 Static Semantics: Early Errors
if ((superContext.kind == ContextKind.Script && !isEnabled(Option.FunctionThis))
|| superContext.kind == ContextKind.Module
|| superContext.kind == ContextKind.Function
|| superContext.kind == ContextKind.Generator
|| superContext.kind == ContextKind.AsyncFunction
|| superContext.kind == ContextKind.AsyncGenerator
|| (superContext.kind.isMethod() && !superContext.isDerivedClassConstructor)) {
reportSyntaxError(Messages.Key.InvalidSuperCallExpression);
}
}
private void newSuper() {
ParseContext superContext = context.findSuperContext();
// 12.2.6.1 Static Semantics: Early Errors
// 14.1.2 Static Semantics: Early Errors
// 14.4.1 Static Semantics: Early Errors
// 14.5.1 Static Semantics: Early Errors
// 15.1.1 Static Semantics: Early Errors
// 15.2.1.1 Static Semantics: Early Errors
if ((superContext.kind == ContextKind.Script && !isEnabled(Option.FunctionThis))
|| superContext.kind == ContextKind.Module
|| superContext.kind == ContextKind.Function
|| superContext.kind == ContextKind.Generator
|| superContext.kind == ContextKind.AsyncFunction
|| superContext.kind == ContextKind.AsyncGenerator
|| (superContext.kind.isMethod() && !superContext.isDerivedClassConstructor)) {
reportSyntaxError(Messages.Key.InvalidNewSuperExpression);
}
}
private void newTarget() {
ParseContext superContext = context.findSuperContext();
// 15.1.1 Static Semantics: Early Errors
// 15.2.1.1 Static Semantics: Early Errors
if ((superContext.kind == ContextKind.Script && !isEnabled(Option.FunctionThis))
|| superContext.kind == ContextKind.Module) {
reportSyntaxError(Messages.Key.InvalidNewTarget);
}
}
/**
* Entry point for {@link #leftHandSideExpression(boolean, boolean)} which additionally performs
* object literal early error checks.
*
* @param allowElement
* the flag to select whether or not element member expressions are allowed
* @return the parsed left-hand side expression
*/
private Expression leftHandSideExpressionWithValidation(boolean allowElement) {
int count = context.countLiterals();
Expression lhs = leftHandSideExpression(true, allowElement);
objectLiteral_EarlyErrors(count);
return lhs;
}
/**
* <strong>[12.3] Left-Hand-Side Expressions</strong>
*
* <pre>
* Arguments<span><sub>[Yield]</sub></span> :
* ()
* ( ArgumentList<span><sub>[?Yield]</sub></span> )
* ArgumentList<span><sub>[Yield]</sub></span> :
* AssignmentExpression<span><sub>[In, ?Yield]</sub></span>
* ... AssignmentExpression<span><sub>[In, ?Yield]</sub></span>
* ArgumentList<span><sub>[?Yield]</sub></span> , AssignmentExpression<span><sub>[In, ?Yield]</sub></span>
* ArgumentList<span><sub>[?Yield]</sub></span> , ... AssignmentExpression<span><sub>[In, ?Yield]</sub></span>
* </pre>
*
* @return the list of parsed function call arguments
*/
private List<Expression> arguments() {
long position = ts.position(), lineinfo = ts.lineinfo();
consume(Token.LP);
if (token() == Token.RP) {
consume(Token.RP);
return Collections.emptyList();
}
InlineArrayList<Expression> args = newList();
if (token() != Token.TRIPLE_DOT && isEnabled(CompatibilityOption.LegacyComprehension)) {
Expression expr = assignmentExpression(true);
if (token() == Token.FOR) {
ts.reset(position, lineinfo);
args.add(legacyGeneratorComprehension());
return args;
}
args.add(expr);
if (token() == Token.COMMA) {
consume(Token.COMMA);
if (token() == Token.RP && isEnabled(CompatibilityOption.FunctionCallTrailingComma)) {
consume(Token.RP);
return args;
}
} else {
consume(Token.RP);
return args;
}
}
for (;;) {
Expression expr;
if (token() == Token.TRIPLE_DOT) {
long begin = ts.beginPosition();
consume(Token.TRIPLE_DOT);
Expression e = assignmentExpression(true);
expr = new CallSpreadElement(begin, ts.endPosition(), e);
} else {
expr = assignmentExpression(true);
}
args.add(expr);
if (token() == Token.COMMA) {
consume(Token.COMMA);
if (token() == Token.RP && isEnabled(CompatibilityOption.FunctionCallTrailingComma)) {
break;
}
} else {
break;
}
}
if (args.size() > MAX_ARGUMENTS) {
reportSyntaxError(Messages.Key.FunctionTooManyArguments);
}
consume(Token.RP);
return args;
}
/**
* <strong>[12.4] Postfix Expressions</strong><br>
* <strong>[12.5] Unary Operators</strong>
*
* <pre>
* PostfixExpression<span><sub>[Yield]</sub></span> :
* LeftHandSideExpression<span><sub>[?Yield]</sub></span>
* LeftHandSideExpression<span><sub>[?Yield]</sub></span> [no <i>LineTerminator</i> here] ++
* LeftHandSideExpression<span><sub>[?Yield]</sub></span> [no <i>LineTerminator</i> here] --
* UnaryExpression<span><sub>[Yield]</sub></span> :
* PostfixExpression<span><sub>[?Yield]</sub></span>
* delete UnaryExpression<span><sub>[?Yield]</sub></span>
* void UnaryExpression<span><sub>[?Yield]</sub></span>
* typeof UnaryExpression<span><sub>[?Yield]</sub></span>
* ++ UnaryExpression<span><sub>[?Yield]</sub></span>
* -- UnaryExpression<span><sub>[?Yield]</sub></span>
* + UnaryExpression<span><sub>[?Yield]</sub></span>
* - UnaryExpression<span><sub>[?Yield]</sub></span>
* ~ UnaryExpression<span><sub>[?Yield]</sub></span>
* ! UnaryExpression<span><sub>[?Yield]</sub></span>
* </pre>
*
* Exponentation operator extension:
*
* <pre>
* ExponentiationExpression<span><sub>[Yield]</sub></span> :
* UnaryExpression<span><sub>[?Yield]</sub></span>
* IncrementExpression<span><sub>[?Yield]</sub></span> ** ExponentiationExpression<span><sub>[?Yield]</sub></span>
* UnaryExpression<span><sub>[Yield]</sub></span> :
* IncrementExpression<span><sub>[?Yield]</sub></span>
* delete UnaryExpression<span><sub>[?Yield]</sub></span>
* void UnaryExpression<span><sub>[?Yield]</sub></span>
* typeof UnaryExpression<span><sub>[?Yield]</sub></span>
* + UnaryExpression<span><sub>[?Yield]</sub></span>
* - UnaryExpression<span><sub>[?Yield]</sub></span>
* ~ UnaryExpression<span><sub>[?Yield]</sub></span>
* ! UnaryExpression<span><sub>[?Yield]</sub></span>
* IncrementExpression<span><sub>[Yield]</sub></span> :
* LeftHandSideExpression<span><sub>[?Yield]</sub></span>
* LeftHandSideExpression<span><sub>[?Yield]</sub></span> [no <i>LineTerminator</i> here] ++
* LeftHandSideExpression<span><sub>[?Yield]</sub></span> [no <i>LineTerminator</i> here] --
* ++ LeftHandSideExpression<span><sub>[?Yield]</sub></span>
* -- LeftHandSideExpression<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param allowExponentiation
* the flag to select whether or not exponentiation expressions are allowed
* @return the parsed unary expression
*/
private Expression unaryExpression(boolean allowExponentiation) {
long begin = ts.beginPosition();
Expression lhs;
Token tok = token();
switch (tok) {
case DELETE: {
consume(tok);
Expression operand = unaryExpression(false);
// 12.5.4.1 Static Semantics: Early Errors
if (operand instanceof IdentifierReference) {
reportStrictModeSyntaxError(operand, Messages.Key.StrictModeInvalidDeleteOperand);
}
return new UnaryExpression(begin, ts.endPosition(), unaryOp(tok), operand);
}
case VOID:
case TYPEOF:
case ADD:
case SUB:
case BITNOT:
case NOT: {
consume(tok);
Expression operand = unaryExpression(false);
return new UnaryExpression(begin, ts.endPosition(), unaryOp(tok), operand);
}
case INC:
case DEC: {
consume(tok);
if (isUnaryOrIncrement(token())) {
if (!isEnabled(CompatibilityOption.Exponentiation)) {
reportReferenceError(ts.sourcePosition(), Messages.Key.InvalidIncDecTarget);
}
}
Expression operand = leftHandSideExpression(true, true);
// 12.5.1 Static Semantics: Early Errors
validateSimpleAssignment(operand, ExceptionType.ReferenceError, Messages.Key.InvalidIncDecTarget);
lhs = new UnaryExpression(begin, ts.endPosition(), incrementOp(tok, false), operand);
Token next = token();
if ((next == Token.INC || next == Token.DEC) && noLineTerminator()) {
if (!isEnabled(CompatibilityOption.Exponentiation)) {
reportReferenceError(operand.getBeginPosition(), Messages.Key.InvalidIncDecTarget);
}
}
break;
}
case AWAIT:
if (isEnabled(CompatibilityOption.AsyncFunction) || isEnabled(CompatibilityOption.AsyncGenerator)) {
switch (context.kind) {
case AsyncArrowFunction:
case AsyncFunction:
case AsyncGenerator:
case AsyncGeneratorMethod:
case AsyncMethod:
if (!context.awaitAllowed) {
// await in async function parameters
reportSyntaxError(Messages.Key.InvalidAwaitExpression);
}
return awaitExpression();
case ArrowFunction:
case GeneratorComprehension:
if (context.awaitAllowed) {
// One of:
// - await in arrow function parameters, nested in async function
// - await in generator comprehension, nested in async function
reportSyntaxError(Messages.Key.InvalidAwaitExpression);
}
break;
default:
assert !context.awaitAllowed;
}
}
// fall-through
default: {
lhs = leftHandSideExpression(true, true);
Token next = token();
if ((next == Token.INC || next == Token.DEC) && noLineTerminator()) {
consume(next);
// 12.4.1 Static Semantics: Early Errors
validateSimpleAssignment(lhs, ExceptionType.ReferenceError, Messages.Key.InvalidIncDecTarget);
lhs = new UnaryExpression(begin, ts.endPosition(), incrementOp(next, true), lhs);
}
break;
}
}
// TODO: Better error message
if (allowExponentiation && token() == Token.EXP) {
consume(Token.EXP);
Expression exponent = unaryExpression(true);
return new BinaryExpression(BinaryExpression.Operator.EXP, lhs, exponent);
}
return lhs;
}
private static UnaryExpression.Operator unaryOp(Token tok) {
switch (tok) {
case DELETE:
return UnaryExpression.Operator.DELETE;
case VOID:
return UnaryExpression.Operator.VOID;
case TYPEOF:
return UnaryExpression.Operator.TYPEOF;
case ADD:
return UnaryExpression.Operator.POS;
case SUB:
return UnaryExpression.Operator.NEG;
case BITNOT:
return UnaryExpression.Operator.BITNOT;
case NOT:
return UnaryExpression.Operator.NOT;
default:
throw new AssertionError();
}
}
private static UnaryExpression.Operator incrementOp(Token tok, boolean postfix) {
if (tok == Token.INC) {
return postfix ? UnaryExpression.Operator.POST_INC : UnaryExpression.Operator.PRE_INC;
}
assert tok == Token.DEC;
return postfix ? UnaryExpression.Operator.POST_DEC : UnaryExpression.Operator.PRE_DEC;
}
private boolean isUnaryOrIncrement(Token tok) {
switch (tok) {
case DELETE:
case VOID:
case TYPEOF:
case ADD:
case SUB:
case BITNOT:
case NOT:
case INC:
case DEC:
return true;
case AWAIT: {
if (isEnabled(CompatibilityOption.AsyncFunction) || isEnabled(CompatibilityOption.AsyncGenerator)) {
switch (context.kind) {
case AsyncArrowFunction:
case AsyncFunction:
case AsyncGenerator:
case AsyncGeneratorMethod:
case AsyncMethod:
if (!context.awaitAllowed) {
// await in async function parameters
reportSyntaxError(Messages.Key.InvalidAwaitExpression);
}
return true;
case ArrowFunction:
case GeneratorComprehension:
if (context.awaitAllowed) {
// One of:
// - await in arrow function parameters, nested in async function
// - await in generator comprehension, nested in async function
reportSyntaxError(Messages.Key.InvalidAwaitExpression);
}
break;
default:
assert !context.awaitAllowed;
}
}
// fall-through
}
default:
return false;
}
}
/**
* <strong>[12.6] Multiplicative Operators</strong><br>
* <strong>[12.7] Additive Operators</strong><br>
* <strong>[12.8] Bitwise Shift Operators</strong><br>
* <strong>[12.9] Relational Operators</strong><br>
* <strong>[12.10] Equality Operators</strong><br>
* <strong>[12.11] Binary Bitwise Operators</strong><br>
* <strong>[12.12] Binary Logical Operators</strong><br>
*
* <pre>
* MultiplicativeExpression<span><sub>[Yield]</sub></span> :
* UnaryExpression<span><sub>[?Yield]</sub></span>
* MultiplicativeExpression<span><sub>[?Yield]</sub></span> * UnaryExpression<span><sub>[?Yield]</sub></span>
* MultiplicativeExpression<span><sub>[?Yield]</sub></span> / UnaryExpression<span><sub>[?Yield]</sub></span>
* MultiplicativeExpression<span><sub>[?Yield]</sub></span> % UnaryExpression<span><sub>[?Yield]</sub></span>
* AdditiveExpression<span><sub>[Yield]</sub></span> :
* MultiplicativeExpression<span><sub>[?Yield]</sub></span>
* AdditiveExpression<span><sub>[?Yield]</sub></span> + MultiplicativeExpression<span><sub>[?Yield]</sub></span>
* AdditiveExpression<span><sub>[?Yield]</sub></span> - MultiplicativeExpression<span><sub>[?Yield]</sub></span>
* ShiftExpression<span><sub>[Yield]</sub></span> :
* AdditiveExpression<span><sub>[?Yield]</sub></span>
* ShiftExpression<span><sub>[?Yield]</sub></span> {@literal <<} AdditiveExpression<span><sub>[?Yield]</sub></span>
* ShiftExpression<span><sub>[?Yield]</sub></span> {@literal >>} AdditiveExpression<span><sub>[?Yield]</sub></span>
* ShiftExpression<span><sub>[?Yield]</sub></span> {@literal >>>} AdditiveExpression<span><sub>[?Yield]</sub></span>
* RelationalExpression<span><sub>[In, Yield]</sub></span> :
* ShiftExpression<span><sub>[?Yield]</sub></span>
* RelationalExpression<span><sub>[?in, ?Yield]</sub></span> {@literal <} ShiftExpression<span><sub>[?Yield]</sub></span>
* RelationalExpression<span><sub>[?in, ?Yield]</sub></span> {@literal >} ShiftExpression<span><sub>[?Yield]</sub></span>
* RelationalExpression<span><sub>[?in, ?Yield]</sub></span> {@literal <=} ShiftExpression<span><sub>[?Yield]</sub></span>
* RelationalExpression<span><sub>[?in, ?Yield]</sub></span> {@literal >=} ShiftExpression<span><sub>[?Yield]</sub></span>
* RelationalExpression<span><sub>[?in, ?Yield]</sub></span> instanceof ShiftExpression<span><sub>[?Yield]</sub></span>
* <span><sub>[+In]</sub></span> RelationalExpression<span><sub>[In, ?Yield]</sub></span> in ShiftExpression<span><sub>[?Yield]</sub></span>
* EqualityExpression<span><sub>[In, Yield]</sub></span> :
* RelationalExpression<span><sub>[?In, ?Yield]</sub></span>
* EqualityExpression<span><sub>[?In, ?Yield]</sub></span> == RelationalExpression<span><sub>[?In, ?Yield]</sub></span>
* EqualityExpression<span><sub>[?In, ?Yield]</sub></span> != RelationalExpression<span><sub>[?In, ?Yield]</sub></span>
* EqualityExpression<span><sub>[?In, ?Yield]</sub></span> === RelationalExpression<span><sub>[?In, ?Yield]</sub></span>
* EqualityExpression<span><sub>[?In, ?Yield]</sub></span> !== RelationalExpression<span><sub>[?In, ?Yield]</sub></span>
* BitwiseANDExpression<span><sub>[In, Yield]</sub></span> :
* EqualityExpression<span><sub>[?In, ?Yield]</sub></span>
* BitwiseANDExpression<span><sub>[?In, ?Yield]</sub></span> {@literal &} EqualityExpression<span><sub>[?In, ?Yield]</sub></span>
* BitwiseXORExpression<span><sub>[In, Yield]</sub></span> :
* EqualityExpression<span><sub>[?In, ?Yield]</sub></span>
* BitwiseXORExpression<span><sub>[?In, ?Yield]</sub></span> ^ EqualityExpression<span><sub>[?In, ?Yield]</sub></span>
* BitwiseORExpression<span><sub>[In, Yield]</sub></span> :
* EqualityExpression<span><sub>[?In, ?Yield]</sub></span>
* BitwiseORExpression<span><sub>[?In, ?Yield]</sub></span> | EqualityExpression<span><sub>[?In, ?Yield]</sub></span>
* LogicalANDExpression<span><sub>[In, Yield]</sub></span> :
* EqualityExpression<span><sub>[?In, ?Yield]</sub></span>
* LogicalANDExpression<span><sub>[?In, ?Yield]</sub></span> {@literal &&} EqualityExpression<span><sub>[?In, ?Yield]</sub></span>
* LogicalORExpression<span><sub>[In, Yield]</sub></span> :
* EqualityExpression<span><sub>[?In, ?Yield]</sub></span>
* LogicalORExpression<span><sub>[?In, ?Yield]</sub></span> || EqualityExpression<span><sub>[?In, ?Yield]</sub></span>
* </pre>
*
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed binary expression
*/
private Expression binaryExpression(boolean allowIn) {
Expression lhs = unaryExpression(true);
return binaryExpression(allowIn, lhs, BinaryExpression.Operator.OR.getPrecedence());
}
private Expression binaryExpression(boolean allowIn, Expression lhs, int minpred) {
// Recursive-descent parsers require multiple levels of recursion to parse binary
// expressions, to avoid this we're using precedence climbing.
for (;;) {
Token tok = token();
if (tok == Token.IN && !allowIn) {
break;
}
BinaryExpression.Operator op = binaryOp(tok);
int pred = (op != null ? op.getPrecedence() : -1);
if (pred < minpred) {
break;
}
consume(tok);
Expression rhs = unaryExpression(true);
for (BinaryExpression.Operator op2; (op2 = binaryOp(token())) != null;) {
int pred2 = op2.getPrecedence();
if (pred2 < pred || pred2 == pred) {
break;
}
rhs = binaryExpression(allowIn, rhs, pred2);
}
lhs = new BinaryExpression(op, lhs, rhs);
}
return lhs;
}
private static BinaryExpression.Operator binaryOp(Token token) {
switch (token) {
case OR:
return BinaryExpression.Operator.OR;
case AND:
return BinaryExpression.Operator.AND;
case BITOR:
return BinaryExpression.Operator.BITOR;
case BITXOR:
return BinaryExpression.Operator.BITXOR;
case BITAND:
return BinaryExpression.Operator.BITAND;
case EQ:
return BinaryExpression.Operator.EQ;
case NE:
return BinaryExpression.Operator.NE;
case SHEQ:
return BinaryExpression.Operator.SHEQ;
case SHNE:
return BinaryExpression.Operator.SHNE;
case LT:
return BinaryExpression.Operator.LT;
case LE:
return BinaryExpression.Operator.LE;
case GT:
return BinaryExpression.Operator.GT;
case GE:
return BinaryExpression.Operator.GE;
case IN:
return BinaryExpression.Operator.IN;
case INSTANCEOF:
return BinaryExpression.Operator.INSTANCEOF;
case SHL:
return BinaryExpression.Operator.SHL;
case SHR:
return BinaryExpression.Operator.SHR;
case USHR:
return BinaryExpression.Operator.USHR;
case ADD:
return BinaryExpression.Operator.ADD;
case SUB:
return BinaryExpression.Operator.SUB;
case MUL:
return BinaryExpression.Operator.MUL;
case DIV:
return BinaryExpression.Operator.DIV;
case MOD:
return BinaryExpression.Operator.MOD;
default:
return null;
}
}
/**
* <strong>[12.13] Conditional Operator</strong><br>
* <strong>[12.14] Assignment Operators</strong>
*
* <pre>
* ConditionalExpression<span><sub>[In, Yield]</sub></span> :
* LogicalORExpression<span><sub>[?In, ?Yield]</sub></span>
* LogicalORExpression<span><sub>[?In, ?Yield]</sub></span> ? AssignmentExpression<span><sub>[In, ?Yield]</sub></span> : AssignmentExpression<span><sub>[?In, ?Yield]</sub></span>
* AssignmentExpression<span><sub>[In, Yield]</sub></span> :
* ConditionalExpression<span><sub>[?In, ?Yield]</sub></span>
* <span><sub>[+Yield]</sub></span> YieldExpression<span><sub>[?In]</sub></span>
* ArrowFunction<span><sub>[?In]</sub></span>
* LeftHandSideExpression<span><sub>[?Yield]</sub></span> = AssignmentExpression<span><sub>[?In, ?Yield]</sub></span>
* LeftHandSideExpression<span><sub>[?Yield]</sub></span> AssignmentOperator AssignmentExpression<span><sub>[?In, ?Yield]</sub></span>
* </pre>
*
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed assignment expression
*/
private Expression assignmentExpression(boolean allowIn) {
int count = context.countLiterals();
Expression expr = assignmentExpression(allowIn, count);
objectLiteral_EarlyErrors(count);
return expr;
}
/**
* Same as {@link #assignmentExpression(boolean)} except object literal early errors are not
* checked. This method needs to be used if the AssignmentExpression is in a possible
* destructuring assignment position.
*
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed assignment expression
*/
private Expression assignmentExpressionNoValidation(boolean allowIn) {
return assignmentExpression(allowIn, context.countLiterals());
}
private Expression assignmentExpression(boolean allowIn, int oldCount) {
if (token() == Token.YIELD) {
switch (context.kind) {
case AsyncGenerator:
case AsyncGeneratorMethod:
case Generator:
case GeneratorMethod:
if (!context.yieldAllowed) {
// Static Semantics: Early Errors for GeneratorDeclaration/GeneratorExpression
// - It is a Syntax Error if FormalParameters Contains YieldExpression.
// Static Semantics: Early Errors for GeneratorMethod
// - It is a Syntax Error if StrictFormalParameters Contains YieldExpression.
reportSyntaxError(Messages.Key.InvalidYieldExpression);
}
return yieldExpression(allowIn);
case ArrowFunction:
case AsyncArrowFunction:
case GeneratorComprehension:
if (context.yieldAllowed) {
// Static Semantics: Early Errors for ArrowFunction
// - It is a Syntax Error if ArrowParameters Contains YieldExpression.
// Static Semantics: Early Errors for GeneratorComprehension
// - `yield` nested in generator comprehension, nested in generator.
reportSyntaxError(Messages.Key.InvalidYieldExpression);
}
break;
case Function:
if (isEnabled(CompatibilityOption.LegacyGenerator)) {
throw new RetryGenerator();
}
// fall-through
default:
assert !context.yieldAllowed;
}
}
long position = ts.position(), lineinfo = ts.lineinfo();
boolean asyncArrow = false;
if (token() == Token.ASYNC && isEnabled(CompatibilityOption.AsyncFunction)) {
Token next = peek();
if (noNextLineTerminator()) {
if (isBindingIdentifier(next)) {
// Production: `async AsyncArrowBindingIdentifier => AsyncConciseBody`
consume(Token.ASYNC);
// Parse in this context, but ignore result (escaped yield)
bindingIdentifier();
assert context.countLiterals() == oldCount;
Token tok = token();
// Reset token stream and parse again
ts.reset(position, lineinfo);
if (tok == Token.ARROW) {
return asyncArrowFunction(allowIn);
}
// Invalid async arrow function, fall through
} else if (next == Token.LP) {
// Production: `CoverCallExpressionAndAsyncArrowHead => AsyncConciseBody`
asyncArrow = true;
}
}
}
Expression left = binaryExpression(allowIn);
Token tok = token();
if (tok == Token.HOOK) {
consume(Token.HOOK);
Expression then = assignmentExpression(true);
consume(Token.COLON);
Expression otherwise = assignmentExpression(allowIn);
return new ConditionalExpression(left, then, otherwise);
} else if (tok == Token.ARROW && isCoveredArrowParameters(left, asyncArrow)) {
// Discard parsed object literals.
discardUncheckedObjectLiterals(oldCount);
ts.reset(position, lineinfo);
if (asyncArrow) {
return asyncArrowFunction(allowIn);
}
return arrowFunction(allowIn);
} else if (tok == Token.ASSIGN) {
LeftHandSideExpression lhs = validateAssignment(left, ExceptionType.ReferenceError,
Messages.Key.InvalidAssignmentTarget);
consume(Token.ASSIGN);
Expression right = assignmentExpression(allowIn);
if (IsIdentifierRef(lhs) && IsAnonymousFunctionDefinition(right)) {
setFunctionName(right, (IdentifierReference) lhs);
} else if ((lhs instanceof ElementAccessor || lhs instanceof PropertyAccessor)
&& IsAnonymousFunctionDefinition(right)) {
setMethodName(right, lhs);
}
return new AssignmentExpression(assignmentOp(tok), lhs, right);
} else if (isAssignmentOperator(tok)) {
LeftHandSideExpression lhs = validateSimpleAssignment(left, ExceptionType.ReferenceError,
Messages.Key.InvalidAssignmentTarget);
consume(tok);
Expression right = assignmentExpression(allowIn);
return new AssignmentExpression(assignmentOp(tok), lhs, right);
} else {
return left;
}
}
private static boolean isCoveredArrowParameters(Expression expr, boolean async) {
if (async) {
return expr instanceof CallExpression
&& ((CallExpression) expr).getBase() instanceof IdentifierReference
&& !expr.isParenthesized();
}
return expr instanceof IdentifierReference || expr.isParenthesized();
}
private static AssignmentExpression.Operator assignmentOp(Token token) {
switch (token) {
case ASSIGN:
return AssignmentExpression.Operator.ASSIGN;
case ASSIGN_ADD:
return AssignmentExpression.Operator.ASSIGN_ADD;
case ASSIGN_SUB:
return AssignmentExpression.Operator.ASSIGN_SUB;
case ASSIGN_MUL:
return AssignmentExpression.Operator.ASSIGN_MUL;
case ASSIGN_DIV:
return AssignmentExpression.Operator.ASSIGN_DIV;
case ASSIGN_MOD:
return AssignmentExpression.Operator.ASSIGN_MOD;
case ASSIGN_SHL:
return AssignmentExpression.Operator.ASSIGN_SHL;
case ASSIGN_SHR:
return AssignmentExpression.Operator.ASSIGN_SHR;
case ASSIGN_USHR:
return AssignmentExpression.Operator.ASSIGN_USHR;
case ASSIGN_BITAND:
return AssignmentExpression.Operator.ASSIGN_BITAND;
case ASSIGN_BITOR:
return AssignmentExpression.Operator.ASSIGN_BITOR;
case ASSIGN_BITXOR:
return AssignmentExpression.Operator.ASSIGN_BITXOR;
case ASSIGN_EXP:
return AssignmentExpression.Operator.ASSIGN_EXP;
default:
throw new AssertionError();
}
}
/**
* <strong>[12.14] Assignment Operators</strong>
*
* <pre>
* AssignmentOperator : <b>one of</b>
* {@literal *= /= %= += -= <<= >>= >>>= &= ^= |=}
* </pre>
*
* @param tok
* the token to inspect
* @return {@code true} if the token is a compound assignment token
*/
private static boolean isAssignmentOperator(Token tok) {
switch (tok) {
case ASSIGN_ADD:
case ASSIGN_BITAND:
case ASSIGN_BITOR:
case ASSIGN_BITXOR:
case ASSIGN_DIV:
case ASSIGN_MOD:
case ASSIGN_MUL:
case ASSIGN_SHL:
case ASSIGN_SHR:
case ASSIGN_SUB:
case ASSIGN_USHR:
case ASSIGN_EXP:
return true;
default:
return false;
}
}
/**
* Static Semantics: IsValidSimpleAssignmentTarget
* <ul>
* <li>12.1.3 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.2.1.5 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.2.10.3 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.3.1.5 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.4.3 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.5.3 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.6.2 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.7.2 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.8.2 Semantics: IsValidSimpleAssignmentTarget
* <li>12.9.2 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.10.2 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.11.2 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.12.2 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.13.2 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.14.3 Static Semantics: IsValidSimpleAssignmentTarget
* <li>12.15.2 Static Semantics: IsValidSimpleAssignmentTarget
* </ul>
*
* @param lhs
* the left-hand side expression to validate
* @param type
* the exception type for errors
* @param messageKey
* the message key for errors
* @return the {@code lhs} parameter as a left-hand side expression node
*/
private LeftHandSideExpression validateSimpleAssignment(Expression lhs, ExceptionType type,
Messages.Key messageKey) {
if (lhs instanceof IdentifierReference) {
IdentifierReference ident = (IdentifierReference) lhs;
if (context.strictMode != StrictMode.NonStrict) {
String name = ident.getName();
if ("eval".equals(name) || "arguments".equals(name)) {
// FIXME: spec issue - early SyntaxError in ES5, but ReferenceError in ES6.
// https://bugs.ecmascript.org/show_bug.cgi?id=4375
reportStrictModeSyntaxError(ident, Messages.Key.StrictModeInvalidAssignmentTarget);
}
}
return ident;
} else if (lhs instanceof ElementAccessor || lhs instanceof PropertyAccessor
|| lhs instanceof SuperElementAccessor || lhs instanceof SuperPropertyAccessor) {
return (LeftHandSideExpression) lhs;
}
// everything else => invalid lhs
throw reportError(type, lhs.getBeginPosition(), messageKey);
}
/**
* <strong>[12.14.5] Destructuring Assignment</strong>
*
* <ul>
* <li>12.14.1 Static Semantics: Early Errors
* <li>12.14.5.1 Static Semantics: Early Errors
* <li>13.7.5.1 Static Semantics: Early Errors
* </ul>
*
* @param lhs
* the left-hand side expression to validate
* @param type
* the exception type for errors
* @param messageKey
* the message key for errors
* @return the {@code lhs} parameter as a left-hand side expression node
*/
private LeftHandSideExpression validateAssignment(Expression lhs, ExceptionType type, Messages.Key messageKey) {
// rewrite object/array literal to destructuring form
if (lhs instanceof ObjectLiteral) {
if (!lhs.isParenthesized()) {
return toDestructuring((ObjectLiteral) lhs);
}
} else if (lhs instanceof ArrayLiteral) {
if (!lhs.isParenthesized()) {
return toDestructuring((ArrayLiteral) lhs);
}
}
return validateSimpleAssignment(lhs, type, messageKey);
}
/**
* <strong>[12.14.5] Destructuring Assignment</strong>
*
* <pre>
* ObjectAssignmentPattern<span><sub>[Yield]</sub></span> :
* { }
* { AssignmentPropertyList<span><sub>[?Yield]</sub></span> }
* { AssignmentPropertyList<span><sub>[?Yield]</sub></span> , }
* AssignmentPropertyList<span><sub>[Yield]</sub></span> :
* AssignmentProperty<span><sub>[?Yield]</sub></span>
* AssignmentPropertyList<span><sub>[?Yield]</sub></span> , AssignmentProperty<span><sub>[?Yield]</sub></span>
* AssignmentProperty<span><sub>[Yield]</sub></span> :
* IdentifierReference<span><sub>[?Yield]</sub></span> Initializer<span><sub>[In, ?Yield]opt</sub></span>
* PropertyName : AssignmentElement<span><sub>[?Yield]</sub></span>
* AssignmentElement<span><sub>[Yield]</sub></span> :
* DestructuringAssignmentTarget<span><sub>[?Yield]</sub></span> Initializer<span><sub>[In, ?Yield]opt</sub></span>
* DestructuringAssignmentTarget<span><sub>[Yield]</sub></span> :
* LeftHandSideExpression<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param object
* the object literal to convert
* @return the object assignment pattern for the object literal
*/
private ObjectAssignmentPattern toDestructuring(ObjectLiteral object) {
assert !object.isParenthesized();
InlineArrayList<AssignmentProperty> list = newList();
AssignmentRestProperty rest = null;
for (Iterator<PropertyDefinition> iterator = object.getProperties().iterator(); iterator.hasNext();) {
PropertyDefinition p = iterator.next();
AssignmentProperty property;
if (p instanceof PropertyValueDefinition) {
// AssignmentProperty : PropertyName ':' AssignmentElement
PropertyValueDefinition def = (PropertyValueDefinition) p;
PropertyName propertyName = def.getPropertyName();
Expression propertyValue = def.getPropertyValue();
LeftHandSideExpression target;
Expression initializer;
if (propertyValue instanceof AssignmentExpression) {
// AssignmentElement : DestructuringAssignmentTarget Initializer
AssignmentExpression assignment = (AssignmentExpression) propertyValue;
if (assignment.getOperator() != AssignmentExpression.Operator.ASSIGN
|| assignment.isParenthesized()) {
reportSyntaxError(p, Messages.Key.InvalidDestructuring);
}
target = destructuringAssignmentTarget(assignment.getLeft());
initializer = assignment.getRight();
} else {
// AssignmentElement : DestructuringAssignmentTarget
target = destructuringAssignmentTarget(propertyValue);
initializer = null;
}
property = new AssignmentProperty(p.getBeginPosition(), p.getEndPosition(), propertyName, target,
initializer);
} else if (p instanceof PropertyNameDefinition) {
// AssignmentProperty : IdentifierReference
PropertyNameDefinition def = (PropertyNameDefinition) p;
IdentifierReference id = assignmentProperty(def.getPropertyName());
property = new AssignmentProperty(p.getBeginPosition(), p.getEndPosition(), id, null);
} else if (p instanceof CoverInitializedName) {
// AssignmentProperty : IdentifierReference Initializer
CoverInitializedName def = (CoverInitializedName) p;
IdentifierReference id = assignmentProperty(def.getPropertyName());
property = new AssignmentProperty(p.getBeginPosition(), p.getEndPosition(), id, def.getInitializer());
} else if (p instanceof SpreadProperty) {
// ... DestructuringAssignmentTarget
if (!isEnabled(CompatibilityOption.ObjectRestDestructuring)) {
reportSyntaxError(p, Messages.Key.InvalidDestructuring);
}
SpreadProperty spread = (SpreadProperty) p;
LeftHandSideExpression target = destructuringAssignmentTarget(spread.getExpression());
// no trailing comma allowed
if (object.hasTrailingComma()) {
reportSyntaxError(spread, Messages.Key.InvalidDestructuring);
}
// no further elements after AssignmentRestProperty allowed
if (iterator.hasNext()) {
reportSyntaxError(iterator.next(), Messages.Key.InvalidDestructuring);
}
rest = new AssignmentRestProperty(p.getBeginPosition(), p.getEndPosition(), target);
continue;
} else {
assert p instanceof MethodDefinition;
throw reportSyntaxError(p, Messages.Key.InvalidDestructuring);
}
list.add(property);
}
context.removeLiteral(object);
return new ObjectAssignmentPattern(object.getBeginPosition(), object.getEndPosition(), list, rest);
}
/**
* <strong>[12.14.5] Destructuring Assignment</strong>
*
* <pre>
* ArrayAssignmentPattern<span><sub>[Yield]</sub></span> :
* [ Elision<span><sub>opt</sub></span> AssignmentRestElement<span><sub>[?Yield]opt</sub></span> ]
* [ AssignmentElementList<span><sub>[?Yield]</sub></span> ]
* [ AssignmentElementList<span><sub>[?Yield]</sub></span> , Elision<span><sub>opt</sub></span> AssignmentRestElement<span><sub>[?Yield]opt</sub></span> ]
* AssignmentElementList<span><sub>[Yield]</sub></span> :
* AssignmentElisionElement<span><sub>[?Yield]</sub></span>
* AssignmentElementList<span><sub>[?Yield]</sub></span> , AssignmentElisionElement<span><sub>[?Yield]</sub></span>
* AssignmentElisionElement<span><sub>[Yield]</sub></span> :
* Elision<span><sub>opt</sub></span> AssignmentElement<span><sub>[?Yield]</sub></span>
* AssignmentElement<span><sub>[Yield]</sub></span> :
* DestructuringAssignmentTarget<span><sub>[?Yield]</sub></span> Initializer<span><sub>[In, ?Yield]opt</sub></span>
* AssignmentRestElement<span><sub>[Yield]</sub></span> :
* ... DestructuringAssignmentTarget<span><sub>[?Yield]</sub></span>
* DestructuringAssignmentTarget<span><sub>[Yield]</sub></span> :
* LeftHandSideExpression<span><sub>[?Yield]</sub></span>
* </pre>
*
* @param array
* the array literal to convert
* @return the array assignment pattern for the array literal
*/
private ArrayAssignmentPattern toDestructuring(ArrayLiteral array) {
assert !array.isParenthesized();
InlineArrayList<AssignmentElementItem> list = newList();
for (Iterator<Expression> iterator = array.getElements().iterator(); iterator.hasNext();) {
Expression e = iterator.next();
AssignmentElementItem element;
if (e instanceof Elision) {
// Elision
element = (Elision) e;
} else if (e instanceof SpreadElement) {
// AssignmentRestElement : ... DestructuringAssignmentTarget
Expression expression = ((SpreadElement) e).getExpression();
LeftHandSideExpression target = destructuringAssignmentTarget(expression);
// no trailing comma allowed
if (array.hasTrailingComma()) {
reportSyntaxError(e, Messages.Key.InvalidDestructuring);
}
// no further elements after AssignmentRestElement allowed
if (iterator.hasNext()) {
reportSyntaxError(iterator.next(), Messages.Key.InvalidDestructuring);
}
element = new AssignmentRestElement(e.getBeginPosition(), e.getEndPosition(), target);
} else {
LeftHandSideExpression target;
Expression initializer;
if (e instanceof AssignmentExpression) {
// AssignmentElement : DestructuringAssignmentTarget Initializer
AssignmentExpression assignment = (AssignmentExpression) e;
if (assignment.getOperator() != AssignmentExpression.Operator.ASSIGN
|| assignment.isParenthesized()) {
reportSyntaxError(e, Messages.Key.InvalidDestructuring);
}
target = destructuringAssignmentTarget(assignment.getLeft());
initializer = assignment.getRight();
} else {
// AssignmentElement : DestructuringAssignmentTarget
target = destructuringAssignmentTarget(e);
initializer = null;
}
element = new AssignmentElement(e.getBeginPosition(), e.getEndPosition(), target, initializer);
}
list.add(element);
}
return new ArrayAssignmentPattern(array.getBeginPosition(), array.getEndPosition(), list);
}
/**
* 12.14.5.1 Static Semantics: Early Errors
*
* @param lhs
* the left-hand side expression to check
* @return the {@code lhs} parameter as a left-hand side expression node
*/
private LeftHandSideExpression destructuringAssignmentTarget(Expression lhs) {
if (lhs instanceof ObjectAssignmentPattern) {
return (ObjectAssignmentPattern) lhs;
} else if (lhs instanceof ArrayAssignmentPattern) {
return (ArrayAssignmentPattern) lhs;
}
return validateAssignment(lhs, ExceptionType.SyntaxError, Messages.Key.InvalidDestructuring);
}
/**
* 12.14.5.1 Static Semantics: Early Errors
*
* @param lhs
* the left-hand side expression to check
* @return the {@code lhs} parameter as an identifier reference node
*/
private IdentifierReference assignmentProperty(IdentifierReference identifier) {
assert !identifier.isParenthesized();
validateSimpleAssignment(identifier, ExceptionType.SyntaxError, Messages.Key.InvalidDestructuring);
return identifier;
}
/**
* <strong>[12.15] Comma Operator</strong>
*
* <pre>
* Expression<span><sub>[In, Yield]</sub></span> :
* AssignmentExpression<span><sub>[?In, ?Yield]</sub></span>
* Expression<span><sub>[?In, ?Yield]</sub></span> , AssignmentExpression<span><sub>[?In, ?Yield]</sub></span>
* </pre>
*
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed expression
*/
private Expression expression(boolean allowIn) {
Expression expr = assignmentExpression(allowIn);
if (token() == Token.COMMA) {
return commaExpression(expr, allowIn);
}
return expr;
}
/**
* <strong>[12.15] Comma Operator</strong>
*
* <pre>
* Expression<span><sub>[In, Yield]</sub></span> :
* AssignmentExpression<span><sub>[?In, ?Yield]</sub></span>
* Expression<span><sub>[?In, ?Yield]</sub></span> , AssignmentExpression<span><sub>[?In, ?Yield]</sub></span>
* </pre>
*
* @param expr
* the first expression
* @param allowIn
* the flag to select whether or not the in-operator is allowed
* @return the parsed comma expression
*/
private CommaExpression commaExpression(Expression expr, boolean allowIn) {
InlineArrayList<Expression> list = newList();
list.add(expr);
do {
consume(Token.COMMA);
expr = assignmentExpression(allowIn);
list.add(expr);
} while (token() == Token.COMMA);
return new CommaExpression(list);
}
/* ***************************************************************************************** */
/**
* <strong>[11.9] Automatic Semicolon Insertion</strong>
*/
private void semicolon() {
switch (token()) {
case SEMI:
consume(Token.SEMI);
// fall-through
case RC:
case EOF:
break;
default:
if (noLineTerminator()) {
reportSyntaxError(Messages.Key.MissingSemicolon);
}
}
}
/**
* Returns {@code true} if the last and the current token are not separated from each other by a
* line-terminator.
*
* @return {@code true} if there is no line separator
*/
private boolean noLineTerminator() {
return !ts.hasCurrentLineTerminator();
}
/**
* Returns {@code true} if the current and the next token are not separated from each other by a
* line-terminator.
*
* @return {@code true} if there is no line separator
*/
private boolean noNextLineTerminator() {
return !ts.hasNextLineTerminator();
}
/**
* Returns true if the current token is of type {@link Token#NAME} and its name is {@code name}.
*
* @param name
* the name to test
* @return {@code true} if the current token is the requested name
*/
private boolean isName(String name) {
Token tok = token();
return tok == Token.NAME && name.equals(getName(tok));
}
/**
* Returns true if the next token is of type {@link Token#NAME} and its name is {@code name}.
*
* @param name
* the name to test
* @return {@code true} if the next token is the requested name
*/
private boolean isNextName(String name) {
Token tok = peek();
return tok == Token.NAME && name.equals(getNextName(tok));
}
/**
* Returns the token's name.
*
* @param tok
* the token to inspect
* @return the token name
*/
private String getName(Token tok) {
switch (tok) {
case NAME:
case ESCAPED_NAME:
case ESCAPED_RESERVED_WORD:
case ESCAPED_STRICT_RESERVED_WORD:
case ESCAPED_YIELD:
case ESCAPED_ASYNC:
case ESCAPED_AWAIT:
case ESCAPED_LET:
return ts.getString();
default:
return tok.getName();
}
}
/**
* Returns the token's name.
*
* @param tok
* the token to inspect
* @return the token name
*/
private String getNextName(Token tok) {
switch (tok) {
case NAME:
case ESCAPED_NAME:
case ESCAPED_RESERVED_WORD:
case ESCAPED_STRICT_RESERVED_WORD:
case ESCAPED_YIELD:
case ESCAPED_ASYNC:
case ESCAPED_AWAIT:
case ESCAPED_LET:
return ts.getNextString();
default:
return tok.getName();
}
}
/**
* <strong>[11.6] Names and Keywords</strong>
*
* @return the parsed identifier name
*/
private String identifierName() {
Token tok = token();
if (!Token.isIdentifierName(tok)) {
reportTokenNotIdentifierName(tok);
}
String name = getName(tok);
consume(tok);
return name;
}
/**
* <strong>[11.8.3] Numeric Literals</strong>
*
* @return the parsed number literal
*/
private double numericLiteral() {
double number = ts.getNumber();
consume(Token.NUMBER);
return number;
}
/**
* <strong>[11.8.4] String Literals</strong>
*
* @return the parsed string literal
*/
private String stringLiteral() {
String string = ts.getString();
consume(Token.STRING);
return string;
}
}