8167289: Backport ES6 updates from Graal.js
Reviewed-by: lagergren, sundar
This commit is contained in:
parent
192f234477
commit
e29b338cd7
@ -635,6 +635,12 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
/** Ranges for splitting up large literals in code generation */
|
||||
private final List<Splittable.SplitRange> splitRanges;
|
||||
|
||||
/** Does this array literal have a spread element? */
|
||||
private final boolean hasSpread;
|
||||
|
||||
/** Does this array literal have a trailing comma?*/
|
||||
private final boolean hasTrailingComma;
|
||||
|
||||
@Override
|
||||
public boolean isArray() {
|
||||
return true;
|
||||
@ -791,11 +797,26 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
* @param value array literal value, a Node array
|
||||
*/
|
||||
protected ArrayLiteralNode(final long token, final int finish, final Expression[] value) {
|
||||
this(token, finish, value, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param value array literal value, a Node array
|
||||
* @param hasSpread true if the array has a spread element
|
||||
* @param hasTrailingComma true if the array literal has a comma after the last element
|
||||
*/
|
||||
protected ArrayLiteralNode(final long token, final int finish, final Expression[] value, final boolean hasSpread, final boolean hasTrailingComma) {
|
||||
super(Token.recast(token, TokenType.ARRAY), finish, value);
|
||||
this.elementType = Type.UNKNOWN;
|
||||
this.presets = null;
|
||||
this.postsets = null;
|
||||
this.splitRanges = null;
|
||||
this.hasSpread = hasSpread;
|
||||
this.hasTrailingComma = hasTrailingComma;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -808,6 +829,24 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
this.postsets = postsets;
|
||||
this.presets = presets;
|
||||
this.splitRanges = splitRanges;
|
||||
this.hasSpread = node.hasSpread;
|
||||
this.hasTrailingComma = node.hasTrailingComma;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this array literal has a spread element.
|
||||
* @return true if this literal has a spread element
|
||||
*/
|
||||
public boolean hasSpread() {
|
||||
return hasSpread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this array literal has a trailing comma.
|
||||
* @return true if this literal has a trailing comma
|
||||
*/
|
||||
public boolean hasTrailingComma() {
|
||||
return hasTrailingComma;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -989,6 +1028,23 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
return new ArrayLiteralNode(parent.getToken(), parent.getFinish(), valueToArray(value));
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new array literal of Nodes from a list of Node values
|
||||
*
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param value literal value list
|
||||
* @param hasSpread true if the array has a spread element
|
||||
* @param hasTrailingComma true if the array literal has a comma after the last element
|
||||
*
|
||||
* @return the new literal node
|
||||
*/
|
||||
public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final List<Expression> value,
|
||||
final boolean hasSpread, final boolean hasTrailingComma) {
|
||||
return new ArrayLiteralNode(token, finish, valueToArray(value), hasSpread, hasTrailingComma);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new array literal of Nodes
|
||||
*
|
||||
|
@ -755,44 +755,11 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
|
||||
private void verifyDestructuringAssignmentPattern(final Expression pattern, final String contextString) {
|
||||
assert pattern instanceof ObjectNode || pattern instanceof LiteralNode.ArrayLiteralNode;
|
||||
pattern.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
pattern.accept(new VerifyDestructuringPatternNodeVisitor(new LexicalContext()) {
|
||||
@Override
|
||||
public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
|
||||
if (literalNode.isArray()) {
|
||||
boolean restElement = false;
|
||||
for (final Expression element : literalNode.getElementExpressions()) {
|
||||
if (element != null) {
|
||||
if (restElement) {
|
||||
throw error(String.format("Unexpected element after rest element"), element.getToken());
|
||||
}
|
||||
if (element.isTokenType(SPREAD_ARRAY)) {
|
||||
restElement = true;
|
||||
final Expression lvalue = ((UnaryNode) element).getExpression();
|
||||
if (!checkValidLValue(lvalue, contextString)) {
|
||||
throw error(AbstractParser.message("invalid.lvalue"), lvalue.getToken());
|
||||
}
|
||||
}
|
||||
element.accept(this);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return enterDefault(literalNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterObjectNode(final ObjectNode objectNode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterPropertyNode(final PropertyNode propertyNode) {
|
||||
if (propertyNode.getValue() != null) {
|
||||
propertyNode.getValue().accept(this);
|
||||
return false;
|
||||
} else {
|
||||
return enterDefault(propertyNode);
|
||||
protected void verifySpreadElement(final Expression lvalue) {
|
||||
if (!checkValidLValue(lvalue, contextString)) {
|
||||
throw error(AbstractParser.message("invalid.lvalue"), lvalue.getToken());
|
||||
}
|
||||
}
|
||||
|
||||
@ -816,27 +783,6 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterBinaryNode(final BinaryNode binaryNode) {
|
||||
if (binaryNode.isTokenType(ASSIGN)) {
|
||||
binaryNode.lhs().accept(this);
|
||||
// Initializer(rhs) can be any AssignmentExpression
|
||||
return false;
|
||||
} else {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterUnaryNode(final UnaryNode unaryNode) {
|
||||
if (unaryNode.isTokenType(SPREAD_ARRAY)) {
|
||||
// rest element
|
||||
return true;
|
||||
} else {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean enterDefault(final Node node) {
|
||||
throw error(String.format("unexpected node in AssignmentPattern: %s", node));
|
||||
@ -1579,7 +1525,50 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
variableDeclarationList(varType, true, -1);
|
||||
}
|
||||
|
||||
private List<Expression> variableDeclarationList(final TokenType varType, final boolean isStatement, final int sourceOrder) {
|
||||
private static final class ForVariableDeclarationListResult {
|
||||
/** First missing const or binding pattern initializer. */
|
||||
Expression missingAssignment;
|
||||
/** First declaration with an initializer. */
|
||||
long declarationWithInitializerToken;
|
||||
/** Destructuring assignments. */
|
||||
Expression init;
|
||||
Expression firstBinding;
|
||||
Expression secondBinding;
|
||||
|
||||
void recordMissingAssignment(final Expression binding) {
|
||||
if (missingAssignment == null) {
|
||||
missingAssignment = binding;
|
||||
}
|
||||
}
|
||||
|
||||
void recordDeclarationWithInitializer(final long token) {
|
||||
if (declarationWithInitializerToken == 0L) {
|
||||
declarationWithInitializerToken = token;
|
||||
}
|
||||
}
|
||||
|
||||
void addBinding(final Expression binding) {
|
||||
if (firstBinding == null) {
|
||||
firstBinding = binding;
|
||||
} else if (secondBinding == null) {
|
||||
secondBinding = binding;
|
||||
}
|
||||
// ignore the rest
|
||||
}
|
||||
|
||||
void addAssignment(final Expression assignment) {
|
||||
if (init == null) {
|
||||
init = assignment;
|
||||
} else {
|
||||
init = new BinaryNode(Token.recast(init.getToken(), COMMARIGHT), init, assignment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isStatement {@code true} if a VariableStatement, {@code false} if a {@code for} loop VariableDeclarationList
|
||||
*/
|
||||
private ForVariableDeclarationListResult variableDeclarationList(final TokenType varType, final boolean isStatement, final int sourceOrder) {
|
||||
// VAR tested in caller.
|
||||
assert varType == VAR || varType == LET || varType == CONST;
|
||||
final int varLine = line;
|
||||
@ -1587,7 +1576,6 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
|
||||
next();
|
||||
|
||||
final List<Expression> bindings = new ArrayList<>();
|
||||
int varFlags = 0;
|
||||
if (varType == LET) {
|
||||
varFlags |= VarNode.IS_LET;
|
||||
@ -1595,7 +1583,7 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
varFlags |= VarNode.IS_CONST;
|
||||
}
|
||||
|
||||
Expression missingAssignment = null;
|
||||
final ForVariableDeclarationListResult forResult = isStatement ? null : new ForVariableDeclarationListResult();
|
||||
while (true) {
|
||||
// Get name of var.
|
||||
if (type == YIELD && inGeneratorFunction()) {
|
||||
@ -1603,7 +1591,7 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
}
|
||||
|
||||
final String contextString = "variable name";
|
||||
Expression binding = bindingIdentifierOrPattern(contextString);
|
||||
final Expression binding = bindingIdentifierOrPattern(contextString);
|
||||
final boolean isDestructuring = !(binding instanceof IdentNode);
|
||||
if (isDestructuring) {
|
||||
final int finalVarFlags = varFlags;
|
||||
@ -1625,6 +1613,9 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
|
||||
// Look for initializer assignment.
|
||||
if (type == ASSIGN) {
|
||||
if (!isStatement) {
|
||||
forResult.recordDeclarationWithInitializer(varToken);
|
||||
}
|
||||
next();
|
||||
|
||||
// Get initializer expression. Suppress IN if not statement.
|
||||
@ -1655,26 +1646,29 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
}
|
||||
// Only set declaration flag on lexically scoped let/const as it adds runtime overhead.
|
||||
final IdentNode name = varType == LET || varType == CONST ? ident.setIsDeclaredHere() : ident;
|
||||
binding = name;
|
||||
if (!isStatement) {
|
||||
if (init == null && varType == CONST) {
|
||||
forResult.recordMissingAssignment(name);
|
||||
}
|
||||
forResult.addBinding(new IdentNode(name));
|
||||
}
|
||||
final VarNode var = new VarNode(varLine, varToken, sourceOrder, finish, name, init, varFlags);
|
||||
appendStatement(var);
|
||||
if (init == null && varType == CONST) {
|
||||
if (missingAssignment == null) {
|
||||
missingAssignment = binding;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert init != null || !isStatement;
|
||||
binding = init == null ? binding : verifyAssignment(Token.recast(varToken, ASSIGN), binding, init);
|
||||
if (isStatement) {
|
||||
appendStatement(new ExpressionStatement(varLine, binding.getToken(), finish, binding, varType));
|
||||
} else if (init == null) {
|
||||
if (missingAssignment == null) {
|
||||
missingAssignment = binding;
|
||||
if (init != null) {
|
||||
final Expression assignment = verifyAssignment(Token.recast(varToken, ASSIGN), binding, init);
|
||||
if (isStatement) {
|
||||
appendStatement(new ExpressionStatement(varLine, assignment.getToken(), finish, assignment, varType));
|
||||
} else {
|
||||
forResult.addAssignment(assignment);
|
||||
forResult.addBinding(assignment);
|
||||
}
|
||||
} else if (!isStatement) {
|
||||
forResult.recordMissingAssignment(binding);
|
||||
forResult.addBinding(binding);
|
||||
}
|
||||
}
|
||||
bindings.add(binding);
|
||||
|
||||
if (type != COMMARIGHT) {
|
||||
break;
|
||||
@ -1685,20 +1679,9 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
// If is a statement then handle end of line.
|
||||
if (isStatement) {
|
||||
endOfLine();
|
||||
} else {
|
||||
if (type == SEMICOLON) {
|
||||
// late check for missing assignment, now we know it's a for (init; test; modify) loop
|
||||
if (missingAssignment != null) {
|
||||
if (missingAssignment instanceof IdentNode) {
|
||||
throw error(AbstractParser.message("missing.const.assignment", ((IdentNode)missingAssignment).getName()));
|
||||
} else {
|
||||
throw error(AbstractParser.message("missing.destructuring.assignment"), missingAssignment.getToken());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bindings;
|
||||
return forResult;
|
||||
}
|
||||
|
||||
private boolean isBindingIdentifier() {
|
||||
@ -1729,50 +1712,91 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class VerifyDestructuringPatternNodeVisitor extends NodeVisitor<LexicalContext> {
|
||||
VerifyDestructuringPatternNodeVisitor(final LexicalContext lc) {
|
||||
super(lc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
|
||||
if (literalNode.isArray()) {
|
||||
if (((LiteralNode.ArrayLiteralNode)literalNode).hasSpread() && ((LiteralNode.ArrayLiteralNode)literalNode).hasTrailingComma()) {
|
||||
throw error("Rest element must be last", literalNode.getElementExpressions().get(literalNode.getElementExpressions().size() - 1).getToken());
|
||||
}
|
||||
boolean restElement = false;
|
||||
for (final Expression element : literalNode.getElementExpressions()) {
|
||||
if (element != null) {
|
||||
if (restElement) {
|
||||
throw error("Unexpected element after rest element", element.getToken());
|
||||
}
|
||||
if (element.isTokenType(SPREAD_ARRAY)) {
|
||||
restElement = true;
|
||||
final Expression lvalue = ((UnaryNode) element).getExpression();
|
||||
verifySpreadElement(lvalue);
|
||||
}
|
||||
element.accept(this);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return enterDefault(literalNode);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void verifySpreadElement(Expression lvalue);
|
||||
|
||||
@Override
|
||||
public boolean enterObjectNode(final ObjectNode objectNode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterPropertyNode(final PropertyNode propertyNode) {
|
||||
if (propertyNode.getValue() != null) {
|
||||
propertyNode.getValue().accept(this);
|
||||
return false;
|
||||
} else {
|
||||
return enterDefault(propertyNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterBinaryNode(final BinaryNode binaryNode) {
|
||||
if (binaryNode.isTokenType(ASSIGN)) {
|
||||
binaryNode.lhs().accept(this);
|
||||
// Initializer(rhs) can be any AssignmentExpression
|
||||
return false;
|
||||
} else {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterUnaryNode(final UnaryNode unaryNode) {
|
||||
if (unaryNode.isTokenType(SPREAD_ARRAY)) {
|
||||
// rest element
|
||||
return true;
|
||||
} else {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify destructuring variable declaration binding pattern and extract bound variable declarations.
|
||||
*/
|
||||
private void verifyDestructuringBindingPattern(final Expression pattern, final Consumer<IdentNode> identifierCallback) {
|
||||
assert (pattern instanceof BinaryNode && ((BinaryNode)pattern).isTokenType(ASSIGN)) ||
|
||||
assert (pattern instanceof BinaryNode && pattern.isTokenType(ASSIGN)) ||
|
||||
pattern instanceof ObjectNode || pattern instanceof LiteralNode.ArrayLiteralNode;
|
||||
pattern.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
pattern.accept(new VerifyDestructuringPatternNodeVisitor(new LexicalContext()) {
|
||||
@Override
|
||||
public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
|
||||
if (literalNode.isArray()) {
|
||||
boolean restElement = false;
|
||||
for (final Expression element : literalNode.getElementExpressions()) {
|
||||
if (restElement) {
|
||||
throw error(String.format("Unexpected element after rest element"), element.getToken());
|
||||
}
|
||||
if (element != null) {
|
||||
if (element.isTokenType(SPREAD_ARRAY)) {
|
||||
restElement = true;
|
||||
if (!(((UnaryNode) element).getExpression() instanceof IdentNode)) {
|
||||
throw error(String.format("Expected a valid binding identifier"), element.getToken());
|
||||
|
||||
}
|
||||
}
|
||||
element.accept(this);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
protected void verifySpreadElement(final Expression lvalue) {
|
||||
if (lvalue instanceof IdentNode) {
|
||||
// checked in identifierCallback
|
||||
} else if (isDestructuringLhs(lvalue)) {
|
||||
verifyDestructuringBindingPattern(lvalue, identifierCallback);
|
||||
} else {
|
||||
return enterDefault(literalNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterObjectNode(final ObjectNode objectNode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterPropertyNode(final PropertyNode propertyNode) {
|
||||
if (propertyNode.getValue() != null) {
|
||||
propertyNode.getValue().accept(this);
|
||||
return false;
|
||||
} else {
|
||||
return enterDefault(propertyNode);
|
||||
throw error("Expected a valid binding identifier", lvalue.getToken());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1782,27 +1806,6 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterBinaryNode(final BinaryNode binaryNode) {
|
||||
if (binaryNode.isTokenType(ASSIGN)) {
|
||||
binaryNode.lhs().accept(this);
|
||||
// Initializer(rhs) can be any AssignmentExpression
|
||||
return false;
|
||||
} else {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterUnaryNode(final UnaryNode unaryNode) {
|
||||
if (unaryNode.isTokenType(SPREAD_ARRAY)) {
|
||||
// rest element
|
||||
return true;
|
||||
} else {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean enterDefault(final Node node) {
|
||||
throw error(String.format("unexpected node in BindingPattern: %s", node));
|
||||
@ -1910,10 +1913,10 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
final ParserContextLoopNode forNode = new ParserContextLoopNode();
|
||||
lc.push(forNode);
|
||||
Block body = null;
|
||||
List<Expression> vars = null;
|
||||
Expression init = null;
|
||||
JoinPredecessorExpression test = null;
|
||||
JoinPredecessorExpression modify = null;
|
||||
ForVariableDeclarationListResult varDeclList = null;
|
||||
|
||||
int flags = 0;
|
||||
boolean isForOf = false;
|
||||
@ -1931,10 +1934,11 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
|
||||
expect(LPAREN);
|
||||
|
||||
TokenType varType = null;
|
||||
switch (type) {
|
||||
case VAR:
|
||||
// Var declaration captured in for outer block.
|
||||
vars = variableDeclarationList(type, false, forStart);
|
||||
varDeclList = variableDeclarationList(varType = type, false, forStart);
|
||||
break;
|
||||
case SEMICOLON:
|
||||
break;
|
||||
@ -1942,12 +1946,12 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
if (useBlockScope() && (type == LET && lookaheadIsLetDeclaration(true) || type == CONST)) {
|
||||
flags |= ForNode.PER_ITERATION_SCOPE;
|
||||
// LET/CONST declaration captured in container block created above.
|
||||
vars = variableDeclarationList(type, false, forStart);
|
||||
varDeclList = variableDeclarationList(varType = type, false, forStart);
|
||||
break;
|
||||
}
|
||||
if (env._const_as_var && type == CONST) {
|
||||
// Var declaration captured in for outer block.
|
||||
vars = variableDeclarationList(TokenType.VAR, false, forStart);
|
||||
varDeclList = variableDeclarationList(varType = TokenType.VAR, false, forStart);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1983,38 +1987,30 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
break;
|
||||
}
|
||||
case IN:
|
||||
|
||||
flags |= isForOf ? ForNode.IS_FOR_OF : ForNode.IS_FOR_IN;
|
||||
test = new JoinPredecessorExpression();
|
||||
if (vars != null) {
|
||||
// for (var i in obj)
|
||||
if (vars.size() == 1) {
|
||||
init = new IdentNode((IdentNode)vars.get(0));
|
||||
if (init.isTokenType(ASSIGN)) {
|
||||
throw error(AbstractParser.message("for.in.loop.initializer"), init.getToken());
|
||||
}
|
||||
assert init instanceof IdentNode || isDestructuringLhs(init);
|
||||
} else {
|
||||
if (varDeclList != null) {
|
||||
// for (var|let|const ForBinding in|of expression)
|
||||
if (varDeclList.secondBinding != null) {
|
||||
// for (var i, j in obj) is invalid
|
||||
throw error(AbstractParser.message("many.vars.in.for.in.loop", isForOf ? "of" : "in"), vars.get(1).getToken());
|
||||
throw error(AbstractParser.message("many.vars.in.for.in.loop", isForOf ? "of" : "in"), varDeclList.secondBinding.getToken());
|
||||
}
|
||||
if (varDeclList.declarationWithInitializerToken != 0 && (isStrictMode || type != TokenType.IN || varType != VAR || varDeclList.init != null)) {
|
||||
// ES5 legacy: for (var i = AssignmentExpressionNoIn in Expression)
|
||||
// Invalid in ES6, but allow it in non-strict mode if no ES6 features used,
|
||||
// i.e., error if strict, for-of, let/const, or destructuring
|
||||
throw error(AbstractParser.message("for.in.loop.initializer", isForOf ? "of" : "in"), varDeclList.declarationWithInitializerToken);
|
||||
}
|
||||
init = varDeclList.firstBinding;
|
||||
assert init instanceof IdentNode || isDestructuringLhs(init);
|
||||
} else {
|
||||
// for (expr in obj)
|
||||
assert init != null : "for..in/of init expression can not be null here";
|
||||
|
||||
// check if initial expression is a valid L-value
|
||||
if (!(init instanceof AccessNode ||
|
||||
init instanceof IndexNode ||
|
||||
init instanceof IdentNode)) {
|
||||
if (!checkValidLValue(init, isForOf ? "for-of iterator" : "for-in iterator")) {
|
||||
throw error(AbstractParser.message("not.lvalue.for.in.loop", isForOf ? "of" : "in"), init.getToken());
|
||||
}
|
||||
|
||||
if (init instanceof IdentNode) {
|
||||
if (!checkIdentLValue((IdentNode)init)) {
|
||||
throw error(AbstractParser.message("not.lvalue.for.in.loop", isForOf ? "of" : "in"), init.getToken());
|
||||
}
|
||||
verifyStrictIdent((IdentNode)init, isForOf ? "for-of iterator" : "for-in iterator");
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
@ -2856,6 +2852,7 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
final List<Expression> elements = new ArrayList<>();
|
||||
// Track elisions.
|
||||
boolean elision = true;
|
||||
boolean hasSpread = false;
|
||||
loop:
|
||||
while (true) {
|
||||
long spreadToken = 0;
|
||||
@ -2879,6 +2876,7 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
|
||||
case ELLIPSIS:
|
||||
if (isES6()) {
|
||||
hasSpread = true;
|
||||
spreadToken = token;
|
||||
next();
|
||||
}
|
||||
@ -2905,7 +2903,7 @@ public class Parser extends AbstractParser implements Loggable {
|
||||
}
|
||||
}
|
||||
|
||||
return LiteralNode.newInstance(arrayToken, finish, elements);
|
||||
return LiteralNode.newInstance(arrayToken, finish, elements, hasSpread, elision);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user